From 8716e1285dc0fea91c5689c4d19b68b0aeea47bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=96=AF=E7=8B=82=E7=9A=84=E7=8B=AE=E5=AD=90Li?= <15040126243@163.com> Date: Thu, 18 Jun 2026 15:32:30 +0800 Subject: [PATCH] refactor gen templates to freemarker --- .codex/skills/frontend-crud-coding/SKILL.md | 10 +- .../references/examples.md | 2 +- .../references/frontend.md | 14 +- gen/{api.ts.vm => api.ts.ftl} | 15 +- gen/{index-tree.vue.vm => index-tree.vue.ftl} | 570 ++++++++---------- gen/{index.vue.vm => index.vue.ftl} | 503 +++++++--------- gen/types.ts.ftl | 50 ++ gen/types.ts.vm | 61 -- 8 files changed, 570 insertions(+), 655 deletions(-) rename gen/{api.ts.vm => api.ts.ftl} (86%) rename gen/{index-tree.vue.vm => index-tree.vue.ftl} (56%) rename gen/{index.vue.vm => index.vue.ftl} (55%) create mode 100644 gen/types.ts.ftl delete mode 100644 gen/types.ts.vm diff --git a/.codex/skills/frontend-crud-coding/SKILL.md b/.codex/skills/frontend-crud-coding/SKILL.md index 79c66da2..97c52c7b 100644 --- a/.codex/skills/frontend-crud-coding/SKILL.md +++ b/.codex/skills/frontend-crud-coding/SKILL.md @@ -17,10 +17,10 @@ description: 在当前 plus-ui-new 前端项目中按真实 Vue 3 + TypeScript + - 系统复杂页优先看 `src/views/system/user/index.vue`、`system/role`、`system/post`、`system/config`。 - workflow 业务页优先看 `src/views/workflow/*` 同类页面。 4. 新增标准页面前,对照项目内模板确认基础骨架: - - API 模板:`gen/api.ts.vm` - - types 模板:`gen/types.ts.vm` - - 标准单表页模板:`gen/index.vue.vm` - - 树表页模板:`gen/index-tree.vue.vm` + - API 模板:`gen/api.ts.ftl` + - types 模板:`gen/types.ts.ftl` + - 标准单表页模板:`gen/index.vue.ftl` + - 树表页模板:`gen/index-tree.vue.ftl` 5. 新增代码时通常同步维护 `src/api///index.ts`、`types.ts`、`src/views///index.vue`。 6. 增强已有页面时只做增量修改,保留原页面的树筛选、导入导出、列显隐、权限、字典、弹窗和路由跳转能力。 7. 修改完成后按影响范围运行验证:优先 `pnpm exec vue-tsc --noEmit`,改动页面或导入时再跑 `pnpm lint`,大范围变更再跑 `pnpm build`。 @@ -85,7 +85,7 @@ description: 在当前 plus-ui-new 前端项目中按真实 Vue 3 + TypeScript + ### 1. 标准单表 CRUD -以 `gen/index.vue.vm`、`gen/api.ts.vm`、`gen/types.ts.vm` 和 `src/views/demo/demo/index.vue` 为主要起点,补齐列表、搜索、分页、新增、编辑、删除、导出、权限、类型和验证。 +以 `gen/index.vue.ftl`、`gen/api.ts.ftl`、`gen/types.ts.ftl` 和 `src/views/demo/demo/index.vue` 为主要起点,补齐列表、搜索、分页、新增、编辑、删除、导出、权限、类型和验证。 ### 2. 树表 CRUD diff --git a/.codex/skills/frontend-crud-coding/references/examples.md b/.codex/skills/frontend-crud-coding/references/examples.md index 19af145e..fe7ab4cd 100644 --- a/.codex/skills/frontend-crud-coding/references/examples.md +++ b/.codex/skills/frontend-crud-coding/references/examples.md @@ -14,7 +14,7 @@ - 先看 `src/api/system/client/*` 和 `src/views/system/client/index.vue` 是否已存在。 - 再看 `src/views/demo/demo/index.vue` 的标准 hooks 版 CRUD 骨架。 -- 对照项目内 `gen/api.ts.vm`、`gen/types.ts.vm`、`gen/index.vue.vm`。 +- 对照项目内 `gen/api.ts.ftl`、`gen/types.ts.ftl`、`gen/index.vue.ftl`。 - 生成或修改 `api/index.ts`、`types.ts`、`views/.../index.vue`。 - 使用 `AxiosPromise` from `@/utils/api-types`、`PageResult` from `@/api/types`、`useLoading`、`useFormDialog`、`useSearchReset`、`useTableSelection`。 diff --git a/.codex/skills/frontend-crud-coding/references/frontend.md b/.codex/skills/frontend-crud-coding/references/frontend.md index 46dc5f4f..a934b322 100644 --- a/.codex/skills/frontend-crud-coding/references/frontend.md +++ b/.codex/skills/frontend-crud-coding/references/frontend.md @@ -10,10 +10,10 @@ - 监控页:`src/views/monitor/*`、`src/api/monitor/*`。 - 公共 hooks:`src/hooks/async/useLoading.ts`、`src/hooks/dialog/*`、`src/hooks/form/*`、`src/hooks/table/*`、`src/hooks/tree/*`。 - 项目内 generator 模板: - `gen/api.ts.vm` - `gen/types.ts.vm` - `gen/index.vue.vm` - `gen/index-tree.vue.vm` + `gen/api.ts.ftl` + `gen/types.ts.ftl` + `gen/index.vue.ftl` + `gen/index-tree.vue.ftl` ## 基础栈与格式 @@ -44,7 +44,7 @@ `changeXxxStatus` -> `PUT ///changeStatus` - query string 用 `params`,请求体用 `data`。 - 加密、防重复提交等 headers 直接写在请求配置里,例如用户重置密码中的 `isEncrypt`、`repeatSubmit`。 -- 当前仓库有些 API 使用 `export const`,有些使用 `export function`;新增标准 CRUD 优先跟随 `gen/api.ts.vm` 和相邻模块。 +- 当前仓库有些 API 使用 `export const`,有些使用 `export function`;新增标准 CRUD 优先跟随 `gen/api.ts.ftl` 和相邻模块。 - 只有相邻模块已有 `export default { ... }` 聚合时才新增默认导出。 ## 类型文件规则 @@ -128,8 +128,8 @@ ## 与 gen 模板的关系 - `gen` 是当前前端项目内的生成模板,优先于外部后端工程拷贝的模板。 -- 新增标准单表页面时读取 `gen/index.vue.vm`、`gen/api.ts.vm`、`gen/types.ts.vm`。 -- 新增树表页面时读取 `gen/index-tree.vue.vm`、`gen/api.ts.vm`、`gen/types.ts.vm`。 +- 新增标准单表页面时读取 `gen/index.vue.ftl`、`gen/api.ts.ftl`、`gen/types.ts.ftl`。 +- 新增树表页面时读取 `gen/index-tree.vue.ftl`、`gen/api.ts.ftl`、`gen/types.ts.ftl`。 - `gen` 模板是标准骨架,不是最终答案;落地时仍要对照目标模块真实页面和公共 hooks。 - 当前前端项目已经把生成页升级为 hooks 版:`useLoading`、`useFormDialog`、`useSearchReset`、`useTableSelection`、`useDateRangeQuery`。 - 新增标准 CRUD 时,先从 `gen` 确认字段、权限、导出、状态切换、排序、日期范围等,再落成当前项目的实际页面壳。 diff --git a/gen/api.ts.vm b/gen/api.ts.ftl similarity index 86% rename from gen/api.ts.vm rename to gen/api.ts.ftl index 79205e39..62c1ae2c 100644 --- a/gen/api.ts.vm +++ b/gen/api.ts.ftl @@ -52,13 +52,13 @@ export const update${BusinessName} = (data: ${BusinessName}Form) => { }); }; -#if($enableStatus) +<#if enableStatus> /** * 修改${functionName}状态 * @param ${pkColumn.javaField} * @param status */ -export const change${BusinessName}Status = (${pkColumn.javaField}: string | number, status: #if($statusColumn.javaType == 'String')string#else number#end) => { +export const change${BusinessName}Status = (${pkColumn.javaField}: string | number, status: <#if statusColumn.javaType == 'Boolean'>boolean<#elseif statusColumn.javaType == 'String'>string<#else> number) => { return request({ url: '/${moduleName}/${businessName}/changeStatus', method: 'put', @@ -68,15 +68,15 @@ export const change${BusinessName}Status = (${pkColumn.javaField}: string | numb } }); }; -#end + -#if($enableSort) +<#if enableSort> /** * 调整${functionName}排序 * @param ${pkColumn.javaField} * @param sortValue */ -export const update${BusinessName}Sort = (${pkColumn.javaField}: string | number, sortValue: #if($sortColumn.javaType == 'String' || $sortColumn.javaType == 'LocalDateTime')string#else number#end) => { +export const update${BusinessName}Sort = (${pkColumn.javaField}: string | number, sortValue: <#if sortColumn.javaType == 'String' || sortColumn.javaType == 'LocalDateTime'>string<#else> number) => { return request({ url: '/${moduleName}/${businessName}/updateSort', method: 'put', @@ -86,7 +86,7 @@ export const update${BusinessName}Sort = (${pkColumn.javaField}: string | number } }); }; -#end + /** * 删除${functionName} @@ -98,3 +98,6 @@ export const del${BusinessName} = (${pkColumn.javaField}: string | number | Arra method: 'delete' }); }; + + + diff --git a/gen/index-tree.vue.vm b/gen/index-tree.vue.ftl similarity index 56% rename from gen/index-tree.vue.vm rename to gen/index-tree.vue.ftl index e81a40a6..cbceb9cb 100644 --- a/gen/index-tree.vue.vm +++ b/gen/index-tree.vue.ftl @@ -8,70 +8,62 @@ -#foreach($column in $columns) -#if($column.query) -#set($dictType=$column.dictType) -#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) -#set($parentheseIndex=$column.columnComment.indexOf("(")) -#if($parentheseIndex != -1) -#set($comment=$column.columnComment.substring(0, $parentheseIndex)) -#else -#set($comment=$column.columnComment) -#end -#if($column.htmlType == "input" || $column.htmlType == "textarea") - - +<#list columns as column> +<#if column.query> +<#if column.htmlType == "input" || column.htmlType == "textarea"> + + -#elseif($column.htmlType == "inputNumber") - +<#elseif column.htmlType == "inputNumber"> + -#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType) - - - +<#elseif (column.htmlType == "select" || column.htmlType == "radio") && column.dictType?has_content> + + + -#elseif($column.htmlType == "switch" && "" != $dictType) - - - +<#elseif column.htmlType == "switch" && column.dictType?has_content> + + + -#elseif($column.htmlType == "switch") - - -#if($column.javaType == "Boolean") +<#elseif column.htmlType == "switch"> + + +<#if column.javaType == "Boolean"> -#elseif($column.javaType == "Integer" || $column.javaType == "Long") +<#elseif column.javaType == "Integer" || column.javaType == "Long"> -#else +<#else> -#end + -#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType) - - +<#elseif (column.htmlType == "select" || column.htmlType == "radio") && column.dictType?has_content> + + -#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN") - +<#elseif column.htmlType == "datetime" && column.queryType != "BETWEEN"> + -#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN") - +<#elseif column.htmlType == "datetime" && column.queryType == "BETWEEN"> + -#end -#end -#end + + + 搜索 重置 @@ -100,9 +92,9 @@
新增 展开/折叠 -#if($enableExport) +<#if enableExport> 导出 -#end +
@@ -117,122 +109,115 @@ :default-expand-all="isExpandAll" :tree-props="{ children: 'children', hasChildren: 'hasChildren' }" > -#set($firstTreeListField = "") -#foreach($tempColumn in $columns) -#if(!$tempColumn.pk && $tempColumn.list && "" != $tempColumn.javaField && $firstTreeListField == "") -#set($firstTreeListField = $tempColumn.javaField) -#end -#end -#foreach($column in $columns) -#set($javaField=$column.javaField) -#set($parentheseIndex=$column.columnComment.indexOf("(")) -#if($parentheseIndex != -1) -#set($comment=$column.columnComment.substring(0, $parentheseIndex)) -#else -#set($comment=$column.columnComment) -#end -#if($column.pk) -#elseif($enableStatus && $statusField == $javaField) -#if($javaField == $firstTreeListField) - -#else - -#end +<#assign firstTreeListField = ""> +<#list columns as tempColumn> +<#if !tempColumn.pk && tempColumn.list && "" != tempColumn.javaField && firstTreeListField == ""> +<#assign firstTreeListField = tempColumn.javaField> + + +<#list columns as column> +<#if column.pk> +<#elseif enableStatus && statusField == column.javaField> +<#if column.javaField == firstTreeListField> + +<#else> + + -#elseif($enableSort && $sortField == $javaField) -#if($javaField == $firstTreeListField) - -#else - -#end +<#elseif enableSort && sortField == column.javaField> +<#if column.javaField == firstTreeListField> + +<#else> + + -#elseif($column.list && $column.htmlType == "switch") -#if($javaField == $firstTreeListField) - -#else - -#end +<#elseif column.list && column.htmlType == "switch"> +<#if column.javaField == firstTreeListField> + +<#else> + + -#elseif($column.list && $column.htmlType == "datetime") -#if($javaField == $firstTreeListField) - -#else - -#end +<#elseif column.list && column.htmlType == "datetime"> +<#if column.javaField == firstTreeListField> + +<#else> + + -#elseif($column.list && $column.htmlType == "imageUpload") -#if($javaField == $firstTreeListField) - -#else - -#end +<#elseif column.list && column.htmlType == "imageUpload"> +<#if column.javaField == firstTreeListField> + +<#else> + + -#elseif($column.list && $column.dictColumn) -#if($javaField == $firstTreeListField) - -#else - -#end +<#elseif column.list && column.dictColumn> +<#if column.javaField == firstTreeListField> + +<#else> + + -#elseif($column.list && "" != $javaField) -#if($javaField == $firstTreeListField) - -#else - -#end -#end -#end -#if($enableStatus && !$statusColumn.list) +<#elseif column.list && "" != column.javaField> +<#if column.javaField == firstTreeListField> + +<#else> + + + + +<#if enableStatus && !statusColumn.list> -#end -#if($enableSort && !$sortColumn.list) + +<#if enableSort && !sortColumn.list> -#end + -#foreach($column in $columns) -#if($column.query) -#set($dictType=$column.dictType) -#set($AttrName=$column.javaField.substring(0,1).toUpperCase() + ${column.javaField.substring(1)}) -#set($parentheseIndex=$column.columnComment.indexOf("(")) -#if($parentheseIndex != -1) -#set($comment=$column.columnComment.substring(0, $parentheseIndex)) -#else -#set($comment=$column.columnComment) -#end -#if($column.htmlType == "input" || $column.htmlType == "textarea") - - +<#list columns as column> +<#if column.query> +<#if column.htmlType == "input" || column.htmlType == "textarea"> + + -#elseif($column.htmlType == "inputNumber") - +<#elseif column.htmlType == "inputNumber"> + -#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && "" != $dictType) - - - +<#elseif (column.htmlType == "select" || column.htmlType == "radio") && column.dictType?has_content> + + + -#elseif($column.htmlType == "switch" && "" != $dictType) - - - +<#elseif column.htmlType == "switch" && column.dictType?has_content> + + + -#elseif($column.htmlType == "switch") - - -#if($column.javaType == "Boolean") +<#elseif column.htmlType == "switch"> + + +<#if column.javaType == "Boolean"> -#elseif($column.javaType == "Integer" || $column.javaType == "Long") +<#elseif column.javaType == "Integer" || column.javaType == "Long"> -#else +<#else> -#end + -#elseif(($column.htmlType == "select" || $column.htmlType == "radio") && $dictType) - - +<#elseif (column.htmlType == "select" || column.htmlType == "radio") && column.dictType?has_content> + + -#elseif($column.htmlType == "datetime" && $column.queryType != "BETWEEN") - +<#elseif column.htmlType == "datetime" && column.queryType != "BETWEEN"> + -#elseif($column.htmlType == "datetime" && $column.queryType == "BETWEEN") - +<#elseif column.htmlType == "datetime" && column.queryType == "BETWEEN"> + -#end -#end -#end + + + 搜索 重置 @@ -101,9 +93,9 @@ 新增 修改 删除 -#if($enableExport) +<#if enableExport> 导出 -#end + @@ -111,112 +103,105 @@ -#foreach($column in $columns) -#set($javaField=$column.javaField) -#set($parentheseIndex=$column.columnComment.indexOf("(")) -#if($parentheseIndex != -1) -#set($comment=$column.columnComment.substring(0, $parentheseIndex)) -#else -#set($comment=$column.columnComment) -#end -#if($column.pk) - -#elseif($enableStatus && $statusField == $javaField) - +<#list columns as column> +<#if column.pk && column.list> + +<#elseif enableStatus && statusField == column.javaField> + -#elseif($enableSort && $sortField == $javaField) - +<#elseif enableSort && sortField == column.javaField> + -#elseif($column.list && $column.htmlType == "switch") - +<#elseif column.list && column.htmlType == "switch"> + -#elseif($column.list && $column.htmlType == "datetime") - +<#elseif column.list && column.htmlType == "datetime"> + -#elseif($column.list && $column.htmlType == "imageUpload") - +<#elseif column.list && column.htmlType == "imageUpload"> + -#elseif($column.list && $column.dictColumn) - +<#elseif column.list && column.dictColumn> + -#elseif($column.list && "" != $javaField) - -#end -#end -#if($enableStatus && !$statusColumn.list) +<#elseif column.list && "" != column.javaField> + + + +<#if enableStatus && !statusColumn.list> -#end -#if($enableSort && !$sortColumn.list) + +<#if enableSort && !sortColumn.list> -#end +