diff --git a/.cursor/rules/Code-Generation.mdc b/.cursor/rules/Code-Generation.mdc new file mode 100644 index 00000000..48619352 --- /dev/null +++ b/.cursor/rules/Code-Generation.mdc @@ -0,0 +1,31 @@ +--- +description: 代码生成双端产出与 uniapp C 端三页模板约定 +alwaysApply: true +--- + +# 代码生成(双端前端) + +`CodeGenerationService::generate` 一次生成必须包含: + +1. **后端**:Controller / Service / Model / 路由 / 建表 / PC 菜单 +2. **PC 前端 zip**:`view//` → `nl-admin-view/.../views/my-gen/` +3. **uniapp 前端 zip**:`uniapp//` → `nl-admin-uniapp/src/pages-sub/my-gen//` + +模板目录: + +- `app/template/view/` — Vben Admin +- `app/template/uniapp/` — 移动超管(C 端) + +# uniapp 生成物强制 + +- 兼容 **微信小程序 + App + H5** +- **交互必须是三页**:`pages/list.vue` → `pages/detail.vue` → `pages/form.vue` +- 列表点击进详情;新增/编辑用全屏表单;**禁止**底部抽屉做主 CRUD +- **C 端 UI**:大留白卡片、DetailHero、InfoGroup、BottomBar、PageForm;禁止 PC 宽表 +- 复用:`AppCardList` / `AppFilterBar` / `AppSearchBar` / `AppDetailHero` / `AppInfoGroup` / `AppBottomBar` / `AppPageForm` +- `AppFormDrawer` 不得作为生成主路径 +- zip 内附带 `PAGES_JSON_SNIPPET.md`(注册 list/detail/form 三页 + features.ts) + +# 与移动端规则对齐 + +以 `nl-admin-uniapp` 的 `Code-Standards.mdc` 为准;本仓库生成模板不得与之冲突。 diff --git a/.cursor/rules/Code-Standards.mdc b/.cursor/rules/Code-Standards.mdc index f7c71173..303da0e3 100644 --- a/.cursor/rules/Code-Standards.mdc +++ b/.cursor/rules/Code-Standards.mdc @@ -99,5 +99,11 @@ alwaysApply: true ## 代码生成 -- 模板在 `app/template/`,服务在 `app/Service/core/CodeGenerationService` -- 生成物需仍遵守本文件的分层、基类、路由注册与命名约定;生成后记得在 `routes/api.php` 的 `autoRouteRegister` 中挂上控制器 +- 模板在 `app/template/`:`view/`(PC Vben)与 `uniapp/`(移动端)两套并存;服务在 `app/Service/core/CodeGenerationService` +- **`generate` 必须同时产出** zip 内的 `view//` 与 `uniapp//`,缺一不可 +- 生成物需仍遵守本文件的分层、基类、路由注册与命名约定;生成后记得在 `routes/api.php` 的 `autoRouteRegister` 中挂上控制器(当前 `genRoute` 会自动写入) +- **uniapp 模板强制约定**(与移动端规则同口径): + - 必须兼容 **微信小程序 + App + H5** + - 交互必须是 **list → detail → form 三页**;禁止底部抽屉做主 CRUD;禁止 PC 宽表 + - 复用:`AppCardList` / `AppFilterBar` / `AppDetailHero` / `AppInfoGroup` / `AppBottomBar` / `AppPageForm` + - 粘贴落点:`nl-admin-uniapp/src/pages-sub/my-gen//`,并同步 `pages.json`(三页)与 `features.ts` diff --git a/app/Service/core/CodeGenerationService.php b/app/Service/core/CodeGenerationService.php index 0f65d287..a6309f9f 100755 --- a/app/Service/core/CodeGenerationService.php +++ b/app/Service/core/CodeGenerationService.php @@ -67,6 +67,19 @@ class CodeGenerationService private string $viewSearch = ''; private string $searchField = ''; private string $viewData = ''; + /** uniapp C 端表单字段 HTML 片段 */ + private string $uniFormFields = ''; + /** uniapp 表单默认值片段 */ + private string $uniFormDefaults = ''; + /** uniapp 表单编辑回填片段 */ + private string $uniFormAssign = ''; + /** uniapp 详情信息行片段 */ + private string $uniDetailRows = ''; + /** uniapp 卡片标题字段名 */ + private string $uniTitleField = 'id'; + /** uniapp 卡片副标题字段名 */ + private string $uniSubtitleField = ''; + private bool $uniTitlePicked = false; private array $tmpFile = []; public function __construct() @@ -228,6 +241,23 @@ class CodeGenerationService // 生成唯一的临时目录名 $this->tempDir = 'temp/templates/' . $this->upperCamelCase . '-' . $this->uniqid; Storage::makeDirectory($this->tempDir); + // 重置动态拼接字段,避免批量生成串数据 + $this->selectField = '"id"'; + $this->insertField = ''; + $this->updateField = ''; + $this->viewForm = ''; + $this->viewSearch = ''; + $this->searchField = ''; + $this->viewData = ''; + $this->uniFormFields = ''; + $this->uniFormDefaults = ''; + $this->uniFormAssign = ''; + $this->uniDetailRows = ''; + $this->uniTitleField = 'id'; + $this->uniSubtitleField = ''; + $this->uniTitlePicked = false; + $this->sql = ''; + $this->tmpFile = []; } /** @@ -256,8 +286,10 @@ class CodeGenerationService $this->genRoute(); // 执行sql $this->querySql(); - // 生成视图 + // 生成 PC 视图(Vben) $this->genViews(); + // 生成 uniapp 视图(微信小程序 + App + H5,C 端风格) + $this->genUniappViews(); // 生成菜单 $this->genMenu(); DB::commit(); @@ -389,7 +421,7 @@ class CodeGenerationService } /** - * 生成前端部分文件 + * 生成 PC 前端文件(Vben),写入 zip 的 view/ 目录 * @return void */ private function genViews(): void @@ -413,6 +445,31 @@ class CodeGenerationService } } + /** + * 生成 uniapp 前端文件(C 端 list/detail/form 三页),写入 zip 的 uniapp/ 目录 + * 禁止抽屉主路径;必须兼容微信小程序+App+H5 + * @return void + */ + private function genUniappViews(): void + { + $appPath = app_path(); + $uniList = [ + 'uni-api' => 'api/index.ts', + 'uni-list' => 'pages/list.vue', + 'uni-detail' => 'pages/detail.vue', + 'uni-form' => 'pages/form.vue', + 'uni-readme' => 'PAGES_JSON_SNIPPET.md', + ]; + foreach ($uniList as $k => $v) { + $this->strReplaceTmp( + $appPath . '/template/uniapp/' . $v, + $this->tempDir . "/uniapp/{$this->dashCase}/" . $v, + 'uniapp', + $k + ); + } + } + /** * 生成菜单/授权 * @@ -477,6 +534,20 @@ class CodeGenerationService case 'view-table': $newFile = str_replace("// 生成的列表字段", "// 生成的列表字段 \r ". $this->viewData, $newFile); break; + case 'uni-list': + $newFile = str_replace('UNI_TITLE_FIELD', $this->uniTitleField, $newFile); + $newFile = str_replace('UNI_SUBTITLE_FIELD', $this->uniSubtitleField ?: $this->uniTitleField, $newFile); + break; + case 'uni-detail': + $newFile = str_replace('// UNI_DETAIL_ROWS', $this->uniDetailRows ?: '', $newFile); + $newFile = str_replace('UNI_TITLE_FIELD', $this->uniTitleField, $newFile); + $newFile = str_replace('UNI_SUBTITLE_FIELD', $this->uniSubtitleField ?: $this->uniTitleField, $newFile); + break; + case 'uni-form': + $newFile = str_replace('', $this->uniFormFields ?: '', $newFile); + $newFile = str_replace('// UNI_FORM_DEFAULTS', $this->uniFormDefaults ?: '', $newFile); + $newFile = str_replace('// UNI_FORM_ASSIGN', $this->uniFormAssign ?: '', $newFile); + break; } $this->tmpFile[$type][$fileKey] = $newFile; @@ -510,6 +581,22 @@ class CodeGenerationService $this->insertField .= !empty($this->insertField) ? '","' . $item['name'] : $item['name']; $this->updateField .= !empty($this->updateField) ? '","' . $item['name'] : 'id","' . $item['name']; $this->viewForm .= "{\n fieldName: '" . $item['name'] . "',\n label: '" . $item['comment'] . "',\n component: '" . $item['formType'] . "',\n rules: " . $isNull . ",\n },\n"; + // uniapp C 端表单:用 input 承载,避免生成 Vben 组件名 + $name = $item['name']; + $label = $item['comment'] ?: $name; + $this->uniFormDefaults .= " {$name}: '',\n"; + $this->uniFormAssign .= " {$name}: info?.{$name} ?? '',\n"; + $this->uniDetailRows .= " { label: '{$label}', value: info.value.{$name} },\n"; + $this->uniFormFields .= "\n" + . " {$label}\n" + . " \n" + . " \n"; + if (!$this->uniTitlePicked) { + $this->uniTitleField = $name; + $this->uniTitlePicked = true; + } elseif ($this->uniSubtitleField === '') { + $this->uniSubtitleField = $name; + } } // 设置搜索字段 if ($item['search']) { diff --git a/app/template/uniapp/PAGES_JSON_SNIPPET.md b/app/template/uniapp/PAGES_JSON_SNIPPET.md new file mode 100644 index 00000000..c75da55f --- /dev/null +++ b/app/template/uniapp/PAGES_JSON_SNIPPET.md @@ -0,0 +1,35 @@ +# 模板名称 — uniapp 粘贴说明(C 端三页) + +## 落点 + +`nl-admin-uniapp/src/pages-sub/my-gen/dashCase/` + +## pages.json 片段 + +在 `subPackages` → `root: pages-sub/my-gen` 的 `pages` 追加: + +```json +{ "path": "dashCase/pages/list", "style": { "navigationBarTitleText": "模板名称" } }, +{ "path": "dashCase/pages/detail", "style": { "navigationBarTitleText": "模板名称详情" } }, +{ "path": "dashCase/pages/form", "style": { "navigationBarTitleText": "编辑模板名称" } } +``` + +## features.ts 片段 + +```ts +{ + key: 'dashCase', + title: '模板名称', + desc: '代码生成模块', + icon: '码', + category: 'gen', + path: '/pages-sub/my-gen/dashCase/pages/list', + keywords: ['模板名称'], +} +``` + +## 强制约定 + +- 兼容微信小程序 + App + H5 +- 交互:列表 → 详情 → 全屏表单;**禁止**底部抽屉做主 CRUD +- 复用 `AppCardList` / `AppDetailHero` / `AppInfoGroup` / `AppBottomBar` / `AppPageForm` diff --git a/app/template/uniapp/api/index.ts b/app/template/uniapp/api/index.ts new file mode 100644 index 00000000..814f7e10 --- /dev/null +++ b/app/template/uniapp/api/index.ts @@ -0,0 +1,33 @@ +/** + * 模板名称 API(uniapp) + * 粘贴后路径:pages-sub/my-gen/dashCase/api/index.ts + * 必须兼容微信小程序 + App + H5 + */ +import { get, post } from '@/utils/request' + +const prefix = 'dashCase/' + +/** 分页列表 */ +export function getupperCamelCaseList(params: Record = {}) { + return get(`${prefix}list`, params) +} + +/** 详情 */ +export function getupperCamelCaseInfo(id: number) { + return get(`${prefix}detail`, { id }) +} + +/** 新增 */ +export function createupperCamelCase(data: Record) { + return post(`${prefix}create`, data) +} + +/** 更新 */ +export function updateupperCamelCase(data: Record) { + return post(`${prefix}update`, data) +} + +/** 删除 */ +export function deleteupperCamelCase(data: Record) { + return post(`${prefix}delete`, data) +} diff --git a/app/template/uniapp/pages/detail.vue b/app/template/uniapp/pages/detail.vue new file mode 100644 index 00000000..1b2e125b --- /dev/null +++ b/app/template/uniapp/pages/detail.vue @@ -0,0 +1,58 @@ + + + diff --git a/app/template/uniapp/pages/form.vue b/app/template/uniapp/pages/form.vue new file mode 100644 index 00000000..e21c6c40 --- /dev/null +++ b/app/template/uniapp/pages/form.vue @@ -0,0 +1,57 @@ + + + + + diff --git a/app/template/uniapp/pages/list.vue b/app/template/uniapp/pages/list.vue new file mode 100644 index 00000000..585dd8d5 --- /dev/null +++ b/app/template/uniapp/pages/list.vue @@ -0,0 +1,120 @@ + + + + +