覆盖模板语法、内置函数、扩展方式与常用开发入口

开发指南面向模板修改、组件扩展与二次开发。

`create-page-common` 在二次开发时最常用的内容: 目录结构、模板语法、预设变量、内置函数、UI 组件扩展、模板扩展和 mock 生成。

目录结构

理解目录结构后,才能知道“工作台界面、模板文件、配置文件、目标项目”分别在哪一层。

create-page-common/                                     # 项目根目录
├── web-project                                        # web 项目源码根目录
│   ├── src/apis/create-page.js                        # 前端请求接口
│   ├── src/mixins/create.js                           # 页面生成混入与 API 生成逻辑
│   └── src/views/create/                              # 页面创建主界面
│       ├── components/item-setting/                   # 表单组件配置面板
│       ├── dialog/dialog-form-item-setting.vue        # 组件配置弹窗
│       ├── create-list.vue                            # 列表页创建
│       ├── create-info.vue                            # 详情页创建
│       └── create-form.vue                            # 表单页创建
├── server                                             # 服务端目录
│   ├── create_cfg_tmpl/                               # 源配置与源模板目录
│   │   ├── vue2/config/config.js                      # Vue2 主配置文件
│   │   ├── vue2/config/config-form-valid-msg.js       # Vue2 表单验证提示配置
│   │   ├── vue2/template/base/                        # 基础模板
│   │   ├── vue2/template/dialog/                      # 弹窗模板
│   │   ├── vue2/template/page/                        # 页面模板
│   │   ├── vue3/config/config.js                      # Vue3 主配置文件
│   │   └── vue3/template/                             # Vue3 模板目录
│   ├── web-server.js                                  # 静态资源服务
│   └── main.js                                        # 服务启动入口
└── 目标 Vue 项目                                      # 页面生成目标项目
注释说明:`web-project` 负责提供可视化操作界面;`server/create_cfg_tmpl` 才是模板和配置的核心来源。

模板语法

模板目录: `server/create_cfg_tmpl/**/template/`。修改模板后通常无需重启服务,刷新页面即可生效。

1. 标准输出

用于输出变量、对象属性和表达式结果。

# 直接输出变量
${{value}}

# 输出对象属性
${{data.key}}
${{data["key"]}}

# 输出表达式结果
${{a ? b : c}}
${{a || b}}

# 兼容输出写法
<%= value %>
<%= data.key %>
注释说明:`${{ }}` 和 `<%= %>` 在这里都表示标准输出。

2. 原文输出

用于输出不转义的内容。

# 输出不转义内容
${{@ value }}
<%- value %>
注释说明:适合输出已经拼好的 HTML 或属性片段。

3. 条件判断

按条件输出不同模板片段。

# 单分支条件
${{if value}} ... ${{/if}}

# 多分支条件
${{if v1}} ... ${{else if v2}} ... ${{/if}}

4. 循环

遍历数组或对象,常用于批量拼接表单项、表格列等结构。

# 遍历 target
${{each target}}
  ${{$index}} ${{$value}}
${{/each}}

5. 变量

先声明临时变量,再在同一模板中复用。

# art-template 变量声明
{{set temp = data.sub.content}}

# 兼容写法
<% var temp = data.sub.content; %>

6. 子模板

把重复结构拆分成子模板,便于复用。

# 引入当前目录下的子模板
{{include "./header.art"}}

# 传入数据
{{include "./header.art" data}}

# 根路径会相对 /template 目录解析
# 相对路径会相对当前模板所在目录解析

预设变量

预设变量会在不同类型模板中自动注入。修改模板前,应先确认当前模板能访问到哪些对象。

1. 路由变量

用于路由模板,负责拼接一级路由、二级路由和页面路径。

# 常见使用位置:
# server/create_cfg_tmpl/<模板>/template/router/*.art
{
  path: "/${{lever1Path}}", // 一级路由 path
  component: Layout, // 一级路由组件(通常是布局组件)
  name: "${{lever1RouterName}}", // 一级路由名称
  meta: {
    title: "${{lever1PageName}}", // 一级菜单标题
    icon: "el-icon-orange", // 菜单图标
    code: "" // 预留权限码
  },
  redirect: "${{lever2Path}}", // 默认重定向到二级路由
  children: [
    {
      path: "${{lever2Path}}", // 二级路由 path
      component: () => import("@/views/${{filePath}}"), // 页面组件路径
      name: "${{lever2RouterName}}", // 二级路由名称
      meta: { title: "${{lever2PageName}}", code: "" } // 二级菜单元信息
    }
  ]
}

2. API 变量

API、路由与页面生成都依赖这个对象。

# 常见使用位置:
# server/create_cfg_tmpl/<模板>/template/api/*.art
{
  name, // 接口原始名称
  nameHump, // 驼峰名称(用于函数名)
  nameToPathfilter, // 接口名过滤后的 path 片段
  nameToPath, // 接口名转 path 结果
  desc, // 接口描述
  type, // 请求方法(delete 会转成 del)
  uri, // 接口 URL
  fileName, // 文件名(非驼峰)
  fileNameHump, // 文件名(驼峰)
  fileDesc // 文件描述
}
注释说明:其中 `type` 会把 `delete` 统一转换为 `del`,以适配默认请求方法命名。

3. 页面变量

列表页、表单页、详情页最常使用的就是页面对象。

# 常见使用位置:
# server/create_cfg_tmpl/<模板>/template/page/*.art
{
  rootPath, // 目标项目根路径
  formData: [
    {
      column, // 字段名
      isShow, // 是否显示
      opts: {
        range, // 范围查询相关配置
        valid, // 表单验证规则
        attr // 组件属性配置
      },
      needValidateOpts, // 是否需要校验
      label, // 字段标题
      labelDesc, // 字段描述
      columnType, // 字段类型
      formItemType, // 表单组件类型
      range: {
        f_, // 范围起始字段名
        to_, // 范围结束字段名
        isDatePickerRange, // 是否是日期范围
        isStart // 是否为起始字段
      }
    }
  ],
  tableData: [
    {
      column, // 列字段名
      label, // 列标题
      columnType, // 列字段类型
      isShow, // 是否显示在列表中
      showTips, // 是否显示 tooltip
      alignType // 对齐方式
    }
  ],
  tableConfig: {
    showBtnCol: false, // 是否显示操作列
    showNumCol: true, // 是否显示序号列
    showFormRightBtns: false // 是否显示右侧表单按钮
  },
  apiConfig // 当前页面关联的 API 配置
}
注释说明:`formData` 和 `tableData` 是页面模板里最核心的两个数组,分别控制检索 / 表单项和列表列。

内置函数

模板函数可全局访问,不区分模板类型。它们的目标是减少模板内部重复拼接逻辑。

1. 获取自定义组件列表

只返回页面中实际使用到的自定义组件,适合按需注入。

# 生成按需导入列表
<% var _CusPluginsList_ = $imports.getCusPluginsListImport(formData) %>
${{each _CusPluginsList_}}
import ${{$value.name}} from '${{$value.path}}'
${{/each}}

# 生成组件注册
export default {
  components: {
    ${{each _CusPluginsList_}}${{$value.name}},${{/each}}
  }
}

2. 获取表单验证规则

把字段验证配置转成页面 `rules` 对象。

# 收集字段验证规则
<% var _ValidationList_ = $imports.getValidationListImport(formData) %>
<el-form ref="claFrom" :model="claForm"
  ${{if _ValidationList_.length}} :rules="rules" ${{/if}}
  label-width="auto" />

# 仅在存在规则时输出 rules
${{if _ValidationList_.length}}
rules: {
  ${{include 'base/base-form-rule.art' _ValidationList_}}
}
${{/if}}

3. 获取组件属性字符串

把 `opts.attr` 转成可直接拼接到组件标签中的属性片段。

# 把 opts.attr 转成组件属性
<el-input
  v-model="claForm.${{column}}"
  placeholder="${{label}}"
  ${{@ opts.attr | getFormItemAttr}}
/>
注释说明:以 `_` 开头且 `_` 结尾的私有属性不会被输出。

4. 获取组件数据源

适用于 `select`、`radioGroup`、`checkboxGroup` 等需要选项数据源的组件。

# 收集数据源并输出为 data
<% var _pluginsDataSourceList_ = $imports.getPluginsDataSourceImport(formData) %>
${{each _pluginsDataSourceList_}}
# ${{$value.label}}
${{$value.name}}:${{@ $value.data}}${{if _pluginsDataSourceList_.length - 1 != $index}},${{/if}}
${{/each}}

5. 获取字段默认值

根据字段类型自动生成 `''`、`[]`、`0` 等初始值。

# 根据字段类型生成默认值
${{each formData}}
${{@ $value.column | getObjectKeyExpr}}: ${{@ $value.columnType | getFormItemValue }}${{if formData.length - 1 != $index}},${{/if}} # ${{@ $value.label}}
${{/each}}

6. 转义对象键名

用于把非法标识符转换成合法的对象键。

# column = "tel-phone"
${{@ column | getObjectKeyExpr}}
# 输出:'tel-phone'

7. 转义对象属性访问方式

用于在 `v-model` 等场景中安全访问非法字段名。

# 适合 v-model / 对象属性访问
<el-input
  type="textarea"
  v-model="claForm${{@ column | getFieldAccessExpr }}">
</el-input>

# column = "tel-phone"
# 输出:claForm['tel-phone']

新增 UI 分组

如果当前预设的 Element UI / Element Plus 配置项不满足项目需求,可以新增自己的 UI 分组。

步骤 1

在 `web-project/src/views/create/components/item-setting/` 下新建目录,例如 `ant`。

web-project/src/views/create/components/item-setting/
├── _com            # 公共组件
├── element-ui      # Element UI 配置组
├── element-plus    # Element Plus 配置组
└── ant             # 新增的 UI 配置组

步骤 2

在分组目录中新增对应组件配置文件,例如 `set-input.vue`。

# 文件命名规则
set-input.vue
set-datePicker.vue
set-select.vue

步骤 3

修改 `_UI_TEMP_PATH_`,让工作台加载新分组。

# 单独加载 ant 组
const _$cusConfig$_ = {
  _UI_TEMP_PATH_: "ant"
}

# 或组合加载多个组
const _$cusConfig$_ = {
  _UI_TEMP_PATH_: ["element-ui", "element-plus"]
}
注释说明:数组写法时,右侧同名组件会覆盖左侧组件,适合局部覆盖差异。

扩展组件模板

如果你要新增一个自定义表单组件,通常需要同时处理配置声明、组件模板和可视化配置组件。

1. 在 `formItemOpts` 中声明组件

# 配置路径:
# server/create_cfg_tmpl/<模板>/config/config.js -> formItemOpts
formItemOpts: [
  {
    value: "cusDatePicker", // 组件类型标识(需和模板文件名一致)
    label: "日期选择器(双)", // 在工作台展示的名称
    path: "@/components/cusDatePicker/index.vue", // 组件导入路径
    valid: {
      trigger: "change", // 校验触发时机
      type: "date" // 校验数据类型
    }
  }
]

2. 在模板目录中新增组件模板

文件名必须与 `value` 一致,例如 `cusDatePicker.art`。

server/create_cfg_tmpl/vue2/template/base/form/
└── cusDatePicker.art    # 文件名必须与 value 一致

3. 如需可视化配置,再新增组件配置文件

组件配置文件继承 `inject: ['itemSetIns']`,通过原型链直接修改当前行数据。

# 文件路径:
# web-project/src/views/create/components/item-setting//set-input.vue
export default {
  name: "SetInput", // 配置组件名称
  inject: ["itemSetIns"], // 从父级注入当前字段上下文
  data() {
    return {
      rowData: this.itemSetIns.rowData, // 当前字段原始数据
      tmpRowData: this.itemSetIns.tmpRowData, // 当前字段临时编辑数据
      formData: {
        _prependType_: "", // 前缀类型
        _prependVal_: "", // 前缀内容
        _appendType_: "", // 后缀类型
        _appendVal_: "" // 后缀内容
      },
      rewriteAttr: [] // 需重写的属性列表
    }
  },
  methods: {
    init() {
      if (this.tmpRowData.opts.attr) {
        // 把已配置属性回填到表单
        Object.assign(this.formData, this.tmpRowData.opts.attr || {})
      }
    },
    toSubmitFn() {
      // 返回 true 表示允许提交当前配置
      return true
    }
  }
}

4. 如果是新的 UI 框架,还要同步改模板语法

例如把 Element UI 的 `el-input` 改成 Ant Design Vue 的 `a-input`。

# 原模板
<el-input
  placeholder="${{label}}"
  ${{@ opts.attr | getFormItemAttr}}
></el-input>

# 适配后的模板
<a-input
  v-model:value="claForm.${{column}}"
  placeholder="${{label}}"
  ${{@ opts.attr | getFormItemAttr}}
></a-input>

生成 mock 文件

默认可关闭。如果需要生成 mock 文件,可以使用基础 mock 模板进行扩展。

# 配置路径:
# server/create_cfg_tmpl/<模板>/config/config.js -> makeFile -> isMakeMock
{
  url: '${{apiConfig.uri | getMockRouterUrl}}', // mock 路由地址
  type: '${{apiConfig.type}}', // 请求方法
  response: config => {
    // 首次请求时初始化缓存样例数据
    if (!demo.__cacheKey__['${{apiConfig.name}}']) {
      const tobj = {
        // 根据 tableData 自动生成字段 mock 规则
        ${{@ tableData | getMockRouterResData}}
      }
      demo.demoFnGetTheListKey(tobj)
      demo.__cacheKey__['${{apiConfig.name}}'] = { ...tobj }
    }

    // 按分页参数返回列表数据
    return demo.demoFnMakeListPageData('${{apiConfig.name}}', config.query)
  }
}
注释说明:如果是 Vite 场景,也可以按项目里的 mock 方案进一步适配,重点在于复用字段推断和分页处理逻辑。

其它入口

1. 访问配置对象

模板中可以直接读取全局配置对象。

# 访问全局配置对象
$imports.comConfig

# 示例
输出作者:${{$imports.comConfig.author}}

2. 修改模板生效范围

只有 `server/create_cfg_tmpl` 下的源模板和源配置是长期有效的。

# 有效目录
server/create_cfg_tmpl/vue2/
server/create_cfg_tmpl/vue3/

# 临时目录
web-project/public/tmpl_cfg/
server/www/tmpl_cfg/

建议改动顺序: 先改配置,再改模板,最后才扩展工作台里的组件配置界面。这样更容易定位问题,也更不容易把影响面放大。