From 636fc411d25bb6654f44a18bc8795d0df1974d0d Mon Sep 17 00:00:00 2001 From: lq Date: Tue, 13 May 2025 10:39:21 +0800 Subject: [PATCH] =?UTF-8?q?=E5=89=8D=E7=AB=AF=E6=A8=A1=E6=9D=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../core/CodeGenerationController.php | 41 +++++ .../core/codeGenerationController.php | 38 ----- app/Service/core/CodeGenerationService.php | 158 +++++++++--------- app/template/view/api/index.ts | 42 +++++ app/template/view/components/modal.vue | 62 +++++++ app/template/view/config/form.ts | 28 ++++ app/template/view/config/search.ts | 20 +++ app/template/view/config/table.ts | 66 ++++++++ app/template/view/index.vue | 143 ++++++++++++++++ routes/api.php | 2 +- 10 files changed, 484 insertions(+), 116 deletions(-) create mode 100644 app/Http/Controllers/core/CodeGenerationController.php delete mode 100644 app/Http/Controllers/core/codeGenerationController.php create mode 100644 app/template/view/api/index.ts create mode 100644 app/template/view/components/modal.vue create mode 100644 app/template/view/config/form.ts create mode 100644 app/template/view/config/search.ts create mode 100644 app/template/view/config/table.ts create mode 100644 app/template/view/index.vue diff --git a/app/Http/Controllers/core/CodeGenerationController.php b/app/Http/Controllers/core/CodeGenerationController.php new file mode 100644 index 00000000..68d2aa93 --- /dev/null +++ b/app/Http/Controllers/core/CodeGenerationController.php @@ -0,0 +1,41 @@ +service = CodeGenerationService::getInstance(); + } + + public function generation() + { +// $this->insertField = [ +// 'class_name', +// 'class_comment', +// 'sort', +// 'icon', +// 'pid', +// 'field', +// ]; +// +// $params = $this->checkRequiredFields(request()->post()); + + $params = json_decode('{"class_name":"testTemplate","class_comment":"测试代码生成","sort":"9999999","icon":"ant-design:api-outlined","pid":14,"field":[{"name":"name","type":"VARCHAR","type_number":"32","default":null,"comment":"测试名称","null":true,"formShow":true,"tableShow":true,"search":true,"searchValue":"like","formType":"VbenInput"},{"name":"test_name_1","type":"VARCHAR","type_number":"32","default":null,"comment":"测试名称","null":true,"formShow":true,"tableShow":true,"search":true,"searchValue":"like","formType":"VbenInput"}]}', true); +// ds($params); + return $this->service->generate($params); +// return jok( +// $this->service->generate([]) +// ); + } + +} diff --git a/app/Http/Controllers/core/codeGenerationController.php b/app/Http/Controllers/core/codeGenerationController.php deleted file mode 100644 index 1f0ea6f6..00000000 --- a/app/Http/Controllers/core/codeGenerationController.php +++ /dev/null @@ -1,38 +0,0 @@ -service = CodeGenerationService::getInstance(); - } - - public function generation() - { - $this->insertField = [ - 'class_name', - 'class_comment', - 'sort', - 'icon', - 'pid', - 'field', - ]; - - $params = $this->checkRequiredFields(request()->post()); - return $this->service->generate($params); -// return jok( -// $this->service->generate([]) -// ); - } - -} diff --git a/app/Service/core/CodeGenerationService.php b/app/Service/core/CodeGenerationService.php index 3086c609..d18054ee 100644 --- a/app/Service/core/CodeGenerationService.php +++ b/app/Service/core/CodeGenerationService.php @@ -2,18 +2,12 @@ namespace App\Service\core; -use App\BaseApp\BaseNotAuthService; -use App\Models\AdminModel; -use App\Service\common\JWTService; use App\Service\common\UtilsService; use Exception; use Illuminate\Http\JsonResponse; use Illuminate\Support\Facades\DB; -use Illuminate\Support\Facades\File; use Illuminate\Support\Facades\Storage; -use Illuminate\Support\Str; use Symfony\Component\HttpFoundation\BinaryFileResponse; -use ZipArchive; class CodeGenerationService { @@ -33,6 +27,8 @@ class CodeGenerationService private ?UtilsService $utils; + private string $uniqid = ''; + private string $tempDir = ''; // 大驼峰 private string $upperCamelCase = ''; @@ -85,34 +81,25 @@ class CodeGenerationService public function download(): BinaryFileResponse|JsonResponse { try { - // 模块名称 - $moduleName = '会员模块'; - // 检查/app/public/zip/code-generation/ - if (!Storage::exists('zip/code-generation')) { - Storage::makeDirectory('zip/code-generation'); - } - // 生成唯一的临时目录名 - $tempDir = 'temp/templates/'. $moduleName. '-' . uniqid(); - Storage::makeDirectory($tempDir); - - // 获取模板文件内容 - $templatePath = app_path('template/api/TemplateController.php'); - if (!File::exists($templatePath)) { - return response()->json(['error' => '模板文件不存在'], 404); - } - - $templateContent = File::get($templatePath); - $tempFilePath = "{$tempDir}/TemplateController.php"; - Storage::put($tempFilePath, $templateContent); + // 将后端模板文件内容写入临时目录中 + $tempFilePath = "{$this->tempDir}/api/{$this->upperCamelCase}Controller.php"; + Storage::put($tempFilePath, $this->tmpFile['server']['controller']); + $tempFilePath = "{$this->tempDir}/api/{$this->upperCamelCase}Service.php"; + Storage::put($tempFilePath, $this->tmpFile['server']['service']); + $tempFilePath = "{$this->tempDir}/api/{$this->upperCamelCase}Model.php"; + Storage::put($tempFilePath, $this->tmpFile['server']['model']); + // 创建ZipService实例 $zip = new ZipService(); - $zipFile = storage_path("/app/public/zip/code-generation/{$moduleName}.zip");//生成压缩文件的路径 - $path = app_path('template');//被压缩文件夹的路径 - $zip->zip($zipFile ,$path ); + // 生成压缩文件的路径 + $zipFile = storage_path("/app/public/zip/code-generation/{$this->classComment}-{$this->uniqid}.zip"); + // 被压缩文件夹的路径 + $path = Storage::path($this->tempDir); + // 执行文件压缩 + $zip->zip($zipFile, $path); // 返回下载 $path 文件流 - return response()->download($zipFile, "{$moduleName}.zip", ['Content-Type' => 'application/zip']); - + return response()->download($zipFile, "{$this->classComment}-{$this->uniqid}.zip", ['Content-Type' => 'application/zip']); } catch (\Exception $e) { return response()->json([ 'error' => '生成模板文件失败', @@ -123,12 +110,24 @@ class CodeGenerationService private function init() { + // 生成uuid + $this->uniqid = uniqid(); // 处理类名、表名 $this->upperCamelCase = ucfirst($this->param['class_name']); $this->lowerCamelCase = lcfirst($this->param['class_name']); $this->underLineCase = strtolower(preg_replace('/([A-Z])/', '_$1', $this->param['class_name'])); $this->dashCase = str_replace('_', '-', $this->underLineCase); $this->classComment = $this->param['class_comment']; + + // 生成文件 + // 检查/app/public/zip/code-generation/目录是否存在,如果不存在则创建 + if (!Storage::exists('zip/code-generation')) { + Storage::makeDirectory('zip/code-generation'); + } + + // 生成唯一的临时目录名 + $this->tempDir = 'temp/templates/' . $this->upperCamelCase . '-' . $this->uniqid; + Storage::makeDirectory($this->tempDir); } /** @@ -156,32 +155,22 @@ class CodeGenerationService $this->genRoute(); // 执行sql $this->querySql(); - ds([ - 'data' => [ - 'sql' => $this->sql, - 'upperCamelCase' => $this->upperCamelCase, - 'lowerCamelCase' => $this->lowerCamelCase, - 'underLineCase' => $this->underLineCase, - 'dashCase' => $this->dashCase, - 'classComment' => $this->classComment, - ], - 'tmp' => $this->tmpFile, - 'params' => $this->param, - ]); + // 生成视图 + $this->genViews(); +// // 生成菜单 +// $this->genMenu(); + return $this->download(); // // 生成导出层 // $this->genExport(); // // 生成导入层 // $this->genImport(); -// // 生成视图 -// $this->genViews(); -// // 生成菜单 -// $this->genMenu(); } catch (Exception $e) { DB::rollBack(); ds([ 'error' => '生成代码失败', 'message' => $e->getMessage(), 'line' => $e->getLine(), + 'file' => $e->getFile(), ]); $this->utils->errorThrow($e->getMessage()); } @@ -197,7 +186,7 @@ class CodeGenerationService */ public function genDatabase(): bool { - $tableName = config('database.prefix', 'nl_'). $this->underLineCase; + $tableName = config('database.prefix', 'nl_') . $this->underLineCase; // 默认sql语句 $sql = "CREATE TABLE `{$tableName}` ( `id` int(11) NOT NULL AUTO_INCREMENT COMMENT 'ID', "; @@ -329,23 +318,31 @@ class CodeGenerationService // ); // } // -// /** -// * 生成前端部分文件 -// * @return void -// */ -// private function genViews(): void -// { -// foreach ($this->tmpPath['view'] as $k => $v) { -// $this->utils->createFileHolder($this->baseViewPath . $this->newPath['view_folder'][$k]); -// $this->strReplaceTmp( -// $this->basePath . $v, -// $this->baseViewPath . $this->newPath['view'][$k], -// 'view', -// $k -// ); -// } -// } -// + /** + * 生成前端部分文件 + * @return void + */ + private function genViews(): void + { + $appPath = app_path(); + $viewList = [ + 'view-api' => 'api/index.ts', + 'view-modal' => 'components/modal.vue', + 'view-form' => 'config/form.ts', + 'view-search' => 'config/search.ts', + 'view-table' => 'config/table.ts', + 'view-index' => 'index.vue', + ]; + foreach ($viewList as $k => $v) { + $this->strReplaceTmp( + $appPath. '/template/view/' . $v, + $this->tempDir. "/view/{$this->dashCase}/" . $v, + 'view', + $k + ); + } + } + // /** // * 生成菜单/授权 // * @@ -354,7 +351,6 @@ class CodeGenerationService // */ // private function genMenu(): void // { -// ds(123); // RoleService::getInstance()->createRoleMenuRelation(UsersEnum::ADMIN_ID, MenuService::getInstance()->save($this->menu)); // } @@ -372,11 +368,13 @@ class CodeGenerationService $tmpFile = file_get_contents($template); $newFile = str_replace('upperCamelCase', $this->upperCamelCase, $tmpFile); + $newFile = str_replace('lowerCamelCase', $this->lowerCamelCase, $newFile); + $newFile = str_replace('dashCase', $this->dashCase, $newFile); $newFile = str_replace('模板名称', $this->classComment, $newFile); switch ($fileKey) { case 'service': $newFile = str_replace("'tmpSelectField'", $this->selectField . ', "status", "created_at", "updated_at", "deleted_at"', $newFile); - $newFile = str_replace("tmpSearchField", $this->searchField . ',status@=', $newFile); + $newFile = str_replace("tmpSearchField", $this->searchField . ',status=', $newFile); break; case 'controller': $newFile = str_replace("'temInsertField'", '"' . $this->insertField . '"', $newFile); @@ -392,18 +390,24 @@ class CodeGenerationService case 'model': $newFile = str_replace('underLineCase', cc_camel_case_to_underscore($this->underLineCase), $newFile); break; - case 'data': - $newFile = str_replace("// 测试列表字段", $this->viewData, $newFile); - $newFile = str_replace("// 测试搜索字段", $this->viewSearch, $newFile); - $newFile = str_replace("// 测试表单字段", $this->viewForm, $newFile); + case 'view-form': + $newFile = str_replace("// 生成的表单字段", "// 生成的表单字段 \r ". $this->viewForm, $newFile); + break; + case 'view-search': + $newFile = str_replace("// 生成的搜索字段", "// 生成的搜索字段 \r ". $this->viewSearch, $newFile); + break; + case 'view-table': + $newFile = str_replace("// 生成的列表字段", "// 生成的列表字段 \r ". $this->viewData, $newFile); break; } $this->tmpFile[$type][$fileKey] = $newFile; - if (!file_exists($path)) { + if (!file_exists($path) && in_array($fileKey, ['controller', 'service', 'model'])) { $this->utils->createFile($path); + file_put_contents($path, $newFile); + } else { + Storage::put($path, $newFile); } - file_put_contents($path, $newFile); } /** @@ -421,15 +425,15 @@ class CodeGenerationService } // 设置表单字段 if ($item['formShow']) { - $isNull = $item['null'] == 1 ? 'true' : 'false'; + $isNull = $item['null'] == 1 ? 'required' : ''; $this->insertField .= !empty($this->insertField) ? '","' . $item['name'] : $item['name']; $this->updateField .= !empty($this->updateField) ? '","' . $item['name'] : 'id","' . $item['name']; - $this->viewForm .= "{\n field: '" . $item['name'] . "',\n label: '" . $item['comment'] . "',\n component: '" . $item['formType'] . "',\n required: " . $isNull . ",\n },\n "; + $this->viewForm .= "{\n fieldName: '" . $item['name'] . "',\n label: '" . $item['comment'] . "',\n component: '" . $item['formType'] . "',\n rules: " . $isNull . ",\n },\n "; } // 设置搜索字段 if ($item['search']) { - $this->viewSearch .= "{\n field: '" . $item['name'] . "',\n label: '" . $item['comment'] . "',\n component: '" . $item['formType'] . "',\n colProps: { span: 8 },\n },\n "; - $this->searchField .= !empty($this->searchField) ? ',' . $item['name'] . '@' . $item['searchValue'] : $item['name'] . '@' . $item['searchValue']; + $this->viewSearch .= "{\n fieldName: '" . $item['name'] . "',\n label: '" . $item['comment'] . "',\n component: '" . $item['formType'] . "',\n },\n "; + $this->searchField .= !empty($this->searchField) ? ',' . $item['name'] . '\' => \'' . $item['searchValue'] : $item['name'] . '\' => \'' . $item['searchValue']; } // // 设置导出字段 // $this->exportField .= !empty($this->exportField) ? ',"' . $item['comment'] . '"' : '"' . $item['comment'] . '"'; @@ -437,7 +441,7 @@ class CodeGenerationService // // 设置导入字段 // $this->importField .= "'" . $item['name'] . "' => \$row['" . $item['comment'] . "'],\n "; // 设置视图数据字段 - $this->viewData .= "{\n title: '" . $item['comment'] . "',\n dataIndex: '" . $item['name'] . "',\n },\n "; + $this->viewData .= "{ field: '{$item['name']}', title: '{$item['comment']}' },\n "; } /** diff --git a/app/template/view/api/index.ts b/app/template/view/api/index.ts new file mode 100644 index 00000000..d048f18a --- /dev/null +++ b/app/template/view/api/index.ts @@ -0,0 +1,42 @@ +import { requestClient } from '#/api/request'; + +const prefix = 'dashCase/'; +/** + * 分页查询用户列表 + * @param data + */ +export async function getupperCamelCaseList(data: any) { + return requestClient.get(`${prefix}list`, { params: data }); +} + +/** + * 获取模板名称详情 + * @param id + */ +export async function getupperCamelCaseInfo(id: number) { + return requestClient.get(`${prefix}detail`, { params: { id } }); +} + +/** + * 新增模板名称 + * @param data + */ +export async function createupperCamelCase(data: Record) { + return requestClient.post(`${prefix}create`, data); +} + +/** + * 编辑模板名称 + * @param data + */ +export async function updateupperCamelCase(data: Record) { + return requestClient.post(`${prefix}update`, data); +} + +/** + * 删除模板名称 + * @param data + */ +export async function deleteupperCamelCase(data: Record) { + return requestClient.post(`${prefix}delete`, data); +} diff --git a/app/template/view/components/modal.vue b/app/template/view/components/modal.vue new file mode 100644 index 00000000..38744a58 --- /dev/null +++ b/app/template/view/components/modal.vue @@ -0,0 +1,62 @@ + + diff --git a/app/template/view/config/form.ts b/app/template/view/config/form.ts new file mode 100644 index 00000000..50598741 --- /dev/null +++ b/app/template/view/config/form.ts @@ -0,0 +1,28 @@ +import type { VbenFormProps } from '#/adapter/form'; + +export const modalFormProps: VbenFormProps = { + wrapperClass: 'grid-cols-12', // 24栅格, + commonConfig: { + formItemClass: 'col-span-12', + // 所有表单项 + componentProps: { + class: 'w-full', + }, + }, + // handleSubmit: onSubmit, + layout: 'horizontal', + schema: [ + { + component: 'VbenInput', + fieldName: 'id', + label: 'ID', + formItemClass: 'col-span-6', + dependencies: { + show: false, + triggerFields: ['id'], + }, + }, + // 生成的表单字段 + ], + showDefaultActions: false, +}; diff --git a/app/template/view/config/search.ts b/app/template/view/config/search.ts new file mode 100644 index 00000000..eb02cc33 --- /dev/null +++ b/app/template/view/config/search.ts @@ -0,0 +1,20 @@ +import type { VbenFormProps } from '#/adapter/form'; + +// import dayjs from 'dayjs'; + +export const formOptions: VbenFormProps = { + // 默认展开 + collapsed: false, + schema: [ + // 生成的搜索字段 + ], + // 控制表单是否显示折叠按钮 + showCollapseButton: true, + submitButtonOptions: { + content: '查询', + }, + // 是否在字段值改变时提交表单 + submitOnChange: true, + // 按下回车时是否提交表单 + submitOnEnter: false, +}; diff --git a/app/template/view/config/table.ts b/app/template/view/config/table.ts new file mode 100644 index 00000000..173f0c30 --- /dev/null +++ b/app/template/view/config/table.ts @@ -0,0 +1,66 @@ +import type { VxeGridProps } from '#/adapter/vxe-table'; + +import { getupperCamelCaseList } from '../api'; + +interface RowType { + id: string; + name: string; + logo: string; + introduce: string; + created_at: string; +} + +export const gridOptions: VxeGridProps = { + checkboxConfig: { + highlight: true, + labelField: '', + }, + columnConfig: { + useKey: true, + }, + rowConfig: { + useKey: true, + }, + columns: [ + { type: 'checkbox', width: 60 }, + { field: 'id', align: 'left', title: 'ID', width: 100 }, + // 生成的列表字段 + { field: 'created_at', title: '创建时间' }, + { field: 'updated_at', title: '编辑时间' }, + { type: 'html', title: '操作', slots: { default: 'action' } }, + ], + keepSource: true, + pagerConfig: {}, + proxyConfig: { + ajax: { + // 请求后端接口方法 + query: async ({ page }, formValues) => { + return await getupperCamelCaseList({ + page: page.currentPage, + pageSize: page.pageSize, + ...formValues, + }); + }, + }, + }, + height: 'auto', + border: false, + toolbarConfig: { + // 是否显示搜索表单控制按钮 + // @ts-ignore 正式环境时有完整的类型声明 + search: true, + refresh: true, // 刷新 + print: false, // 打印 + export: false, // 导出 + // custom: true, // 自定义列 + zoom: true, // 最大化最小化 + slots: { + buttons: 'toolbar-buttons', + }, + custom: { + // 自定义列-图标 + icon: 'vxe-icon-menu', + }, + }, + showOverflow: false, +}; diff --git a/app/template/view/index.vue b/app/template/view/index.vue new file mode 100644 index 00000000..72daea59 --- /dev/null +++ b/app/template/view/index.vue @@ -0,0 +1,143 @@ + + + diff --git a/routes/api.php b/routes/api.php index dcb9ae4b..c0abb99e 100644 --- a/routes/api.php +++ b/routes/api.php @@ -17,7 +17,7 @@ Route::get('/', function () { */ UtilsService::class::getInstance()->autoRouteRegister([ '' => \App\Http\Controllers\Api\LoginController::class, // 登录控制器 - 'code' => \App\Http\Controllers\core\codeGenerationController::class, // 代码生成控制器 + 'code' => \App\Http\Controllers\core\CodeGenerationController::class, // 代码生成控制器 // 不需要登录的路由生成地址 ]);