lan-low-code-cli
v2.3.1
Published
lan-low-code cli:低代码表单引擎命令行工具(创建表单、获取可用字段、自动完善表单字段)
Maintainers
Readme
low-code-cli(llc)
低代码表单引擎命令行工具。在 Node 端独立实现,不依赖任何前端包(@ekp-infra/*、@ekp-runtime/*、@lui/*),
直接组装数据并调用后台接口,支持:
- 创建表单:覆盖 6 种表单类型(普通 / 审批意见 / 全局配置项 / 弹窗 / 通用 / 引用者)
- 自动获取可用字段:表单数据模型字段、外部映射表字段、字段库
- 自动完善表单字段:依据数据模型自动生成完整表单配置(
fdConfig:字段控件 + 场景 viewModel + 权限 + 多语言 + 全局规则)并保存
一、安装与构建
本项目使用 pnpm 管理依赖(package.json 已声明 packageManager: [email protected])。
cd D:\project\aiWork\low-code-cli
# 若本机未安装 pnpm:node -v >= 16 时可直接启用 corepack
corepack enable
pnpm install # 安装依赖(会自动执行 prepare -> build)
pnpm build # 构建
pnpm link --global # 可选:全局注册 llc 命令请勿混用 npm/yarn,以免生成多余的
package-lock.json/yarn.lock。 常用脚本:pnpm build、pnpm typecheck、pnpm start、pnpm run rebuild(clean + build,注意pnpm rebuild是 pnpm 内置命令,脚本需用pnpm run rebuild)。 非交互式 CI 可使用pnpm install --frozen-lockfile。打包分发:
pnpm run pack(scripts/pack.js,注意与rebuild一样要用run,pnpm pack是 pnpm 内置命令)把 CLI 与 skill 打成压缩包,输出到release/(dist/只放构建产物), 文件名拼接版本号:lan-low-code-cli-<version>.tgz、lan-low-code-cli-skill-<version>.tgz, 外加--with-deps的解压即用包(-standalone)。默认 tgz(npm 可直接npm i -g ./xxx.tgz),--format zip|both可切换;--target cli|skill只打其一,--list预览文件清单,--no-build跳过构建。
二、鉴权与服务端地址
请求头统一携带 X-AUTH-TOKEN,服务端基址由用户提供。三种配置方式,优先级从高到低:
| 方式 | 示例 |
|---|---|
| 命令行参数 | --base-url <url> --token <token> |
| 环境变量 | LLC_BASE_URL / LLC_TOKEN |
| 配置文件 | llc config set(写入 ~/.lcode/config.json)或项目根目录 lcode.config.json |
llc config set --base-url http://mkdev01.ywork.me/data --token <X-AUTH-TOKEN>
llc config show
baseUrl需包含/data前缀。完整请求地址 =${baseUrl}/sys-xform/<module>/<method>。
三、命令一览
创建表单
llc form create --name <名称> [选项]所属应用(低代码应用)决定表单挂在哪个应用下,是 modeling 接口族的核心参数:
| 选项 | 说明 |
|---|---|
| --app-id <appId> | 表单所属应用 id(应用主键 fdId)。给出后默认走 modeling 接口族,写入 fdAppId |
| --group-id <fdGroupId> | 表单所属分组 id(modeling 族) |
| --edit-draft / --no-edit-draft | 草稿状态是否允许编辑(modeling 族,默认允许) |
| --reader-flag | 阅读者为空时所有人可阅读(modeling 族) |
| --data-source-id <id> | 外部数据源 id(modeling 族) |
| --outer-mapping | 是否映射外部表(modeling 族) |
| --fd-entity-id <id> | 业务建模对应 appId / 应用模块对应 templateId(默认取 --app-id) |
| --fd-entity-name <name> | 所属实体 model 名称(modeling 族由服务端固定写入,通常无需传) |
| --fd-entity-key <key> | 实体扩展键(预留) |
| --fd-run-entity-name <name> | 表单运行时实体名称 |
| --table-name <name> | 主表名(不传则调用对应接口自动生成) |
其余选项:
| 选项 | 说明 |
|---|---|
| --name <name> | 表单名称(必填) |
| --code <code> | 业务编码,默认 template_xxxxxx |
| --type <type> | 表单类型:common | general(默认)| config | modal | template | refer,也可用 1-6 |
| --api <family> | 接口族:auto(默认)| modeling | general | design,见下节 |
| --source-form-id <fdId> | 引用源表单 id(type=refer 必填) |
| --category <fdId> | 表单分类 id(general 族) |
| --icon <icon> | 页面图标(图标名或图标 JSON)。必填语义:不给则使用内置默认图标 |
| --readers <ids> / --editors <ids> | 可阅读者 / 可编辑者,逗号分隔(general 族) |
| --mechanism-entity-name <name> | 机制实体名(决定可用控件库),默认按接口族推导 |
| --mechanism-entity-code <code> | 机制编码,默认按接口族推导 |
| --from-model <fdId> | 从指定数据模型拷贝字段(默认用新表单自身数据模型) |
| --fields <names> | 仅生成指定字段,逗号分隔 |
| --add-field <spec> | 新增/调整字段,格式 name:label[:dataType\|controlType[:controlType]],可重复;见「控件类型」章节 |
| --option <spec> | 为下拉/单选/多选新建选项:"字段名=选项1,选项2:值2",可重复 |
| --detail <spec> | 新建/调整明细表:"明细表标题=字段1:标题1[:控件类型],...",可重复,见「明细表」章节 |
| --include-system | 同时把系统字段渲染到表单上(默认只渲染业务字段) |
| --per-row <n> | 每行控件数,默认 2 |
| --span <n> | 控件栅格宽度,默认 12 |
| --locale <locales> | 多语言,逗号分隔,第一个为主语言(默认 Cn);all = Cn,Us,Tw |
| --lang-file <path> | 多语言文案文件(JSON),见「多语言」章节 |
| --layout <mode> | 渲染布局:rows(默认)| grid 栅格 | tabs 多页签,见「布局」章节 |
| --grid-columns <n> | 栅格列数(--layout grid,默认取 --per-row) |
| --tabs <spec> | 多页签定义:"标题:字段1,字段2\|标题2:字段3"(--layout tabs) |
| --tab-layout <mode> | 页签内层布局:rows(默认)| grid 栅格,可组合出「页签 + 栅格」的嵌套结构 |
| --required <fields> | 必填字段(逗号分隔,作用于 add/edit) |
| --readonly <fields> | 只读字段(逗号分隔,editable=false,作用于 add/edit) |
| --hidden <fields> | 隐藏字段(逗号分隔,visible=false,作用于 add/edit/view) |
| --auth-file <path> | 精细字段权限(JSON),见「字段权限」章节 |
| --action <spec> | 控件动作:"字段名:事件:函数源码或@js文件",可重复;全局动作用 $global,见「控件动作」章节 |
| --action-file <path> | 控件动作定义文件(JSON),见「控件动作」章节 |
| --relation <spec> | 业务关联:"字段名=目标表单[:显示值=字段][:显示项=字段1,字段2]",可重复,见「关联类控件」章节 |
| --no-config | 仅创建空表单,不生成字段配置 |
| --publish | 创建后立即发布 |
| --dry-run | 只打印将提交的数据,不发起请求 |
接口族与表单类型的对应关系(重要)
后端 SysGeneralFormDefineService 对类型有强校验:
case add:
case update:
if (!Objects.equals(formDefinedVO.getFdFormDefineType(), FormDefineType.General)) {
throw new ParamsNotValidException();
}即 generalFormDefine/* 只能建审批意见表单。CLI 提供三种接口族:
| 接口族 | 适用场景 | 接口链路 |
|---|---|---|
| modeling | 低代码应用下建表单(推荐) | sysModelingXForm/generateTableName → create → saveDesign(或 saveDesignAndPublish) |
| general | 审批意见表单(后台"表单管理") | generalFormDefine/init → add → save → publish |
| design | 表单引擎原始接口,任意类型 | sysXFormDesign/create → save → publish |
--api auto(默认)的选择规则:
- 给了
--app-id→modeling(应用下建表单) - 否则
--type general→general - 其余 →
design
显式指定与类型冲突的组合会被直接拦截(如 --type common --api general)。
各接口族的关键差异
| | modeling | general | design |
|---|---|---|---|
| 表单归属 | fdAppId(modeling_xform.fd_app_id 外键) | 独立定义表 | 表单设计表 |
| fdEntityName | 服务端写死 sys-modeling | init 返回 ...SysGeneralFormDefine | 需自行指定 |
| fdRunEntityName | 服务端写死 sys-modeling | init 返回 | 需自行指定 |
| fdEntityId | 服务端写死 = 表单 id | init 返回 = 表单 id | 需自行指定 |
| 表名规则 | mk_model_yyyyMMdd + 随机 5 位 | 服务端生成 | sysXFormDataModel/generateTableName |
| 发布 | saveDesignAndPublish(含发布校验与默认视图) | publish | publish(XFormDesignPushVO) |
机制默认值:general 族 → sys-xform / ...SysGeneralFormDefine;modeling 与 design 族 → sys-modeling / ...SysModelingMainEntity(可用 --mechanism-entity-* 覆盖)。
应用(创建 / 查询 / 修改 / 删除)
llc app list # 列出应用,返回的 fdId 即 --app-id
llc app list --keyword 请假 # 按名称搜索
llc app list --no-auth # 使用不做权限校验的接口(sysModelingApplication/listNotAuth)
llc app get --id <appId> # 查看详情(含可访问者/可维护者)
# 新建应用(后端会同时创建默认首页)
llc app create --name 客户管理 --code crm --desc 客户全生命周期 --icon iconCommon_line_16_file
# 修改应用(只传需要改的项;不会丢失可访问者/可维护者)
llc app update --id <appId> --name 客户管理2.0 --desc 新描述
# 删除应用(连带其下表单,需 --yes 确认)
llc app delete --id <appId> --yes
llc app delete-data --id <appId> --yes # 只清业务数据,保留应用应用编码
--code只允许字母/数字/连字符(平台校验^[a-zA-Z-0-9]*$),不能使用下划线;CLI 会本地预校验。
删除表单
llc form delete --id <fdId> --yes # 删除表单(连同数据模型,需 --yes)
llc form delete-data --id <fdId> --yes # 只删运行时业务数据,保留表单定义获取可用字段
llc field list --form-id <fdId> # 表单已有字段(数据模型)
llc field list --model-id <fdId> # 指定数据模型字段
llc field list --table <tableName> # 外部映射表字段
llc field library [--keyword <kw>] # 字段库
llc field gen-key --name <名称> # 生成字段编码控件类型
先查环境再选控件:
llc control list # 列出环境全部可用控件(含桌面/移动端包、数据项类型、是否登记、是否支持选项)
llc control list --all # 含布局/外观类
llc control show --type moneytext # 查看某控件的属性面板配置项与默认值
llc control show --element @elem/xform-select --jsoncontrol list 的数据源是 {origin}/web/sys/artifact/elements/index.json + 各控件 config.json(首次运行会逐个拉取并落盘缓存,之后秒回)。备注 含义:已登记 = 可直接用于 --add-field;支持选项 = 可用 --option(dictType=optionDict);容器类 = 明细/分页等需专用参数;已废弃 = 平台标记废弃(如 relation);需设计器配置 = 关键属性是环境对象选择,CLI 只建骨架。
--add-field 的第三个 token 既可写数据项类型(varchar/number/…),
也可直接写控件类型(moneytext/select/…),第四 token 为控件类型:
--add-field "fd_a:项目名称" # 默认单行文本
--add-field "fd_b:预算金额:moneytext" # 按控件类型
--add-field "fd_c:评分:number:rate" # 数据项类型 + 控件类型若字段名已存在,则视为「调整该字段」:覆盖其标题与控件类型,保留 fdId、fdOrder 等,
因此可以对既有表单做控件替换(llc form autofill --add-field "fd_amt:预算金额:moneytext" --apply)。
支持的全部控件(src/constants.ts#CONTROL_TYPE_MAP,均经真实环境 config.json 的
xform.type 成对校验;无移动端包的控件回落为桌面端包,与平台 platform=all 行为一致):
| 分类 | 控件类型 |
|---|---|
| 文本 | text、textarea、rich-text、upcasing、phoneNumber、hyperlinks |
| 数值 | numbertext、moneytext、rate、slider、timepicker、daterange |
| 选择 | select、radio、checkbox(均支持 --option 配选项) |
| 日期 | timestamp、timeZone |
| 组织人员 | address(地址本,选人/选组织,非地址文本,见下) |
| 附件/媒体 | attachment、image、video |
| 编码/码图 | barcode、qrcode |
| 其它 | switch、hidden、dividing、subject、relation、relationQuery |
| 高级·元素类 | desc(说明文字)、interval(分割线)、button、html、iframe、review(审批意见)、timeAxis(时间轴)、associatedFields(关联字段)、serial-number(明细表序号) |
| 高级·编号类 | mechnumber(机制编号)、manualNumber(手工编号)、reserveNumber(预留编号) |
| 高级·数据类 | association(关联选择)、refTemplate(关联模板)、foreignKey(外键)、cfg(配置项)、mdm(主数据)、dynamic(动态控件)、component、agency、area、tags、location、calculate、fileEdit、invoice、ocr、auditNote、template、ikm-author、ikm-category、ikm-content、ikm-player |
高级控件(环境中 67 个 kind=control 控件已登记 64 个,其余 3 个是容器类)使用注意:
- 元素类(
desc/html/interval/button/iframe/review/timeAxis/associatedFields/serial-number)fdIsStored=false:仍写入dataModel,但不建物理列(与设计器一致); - 容器类(
detail明细表、bigDetail大数据明细表、paged分页容器)不是数据项, 不能用--add-field创建——明细表用--detail;bigDetail/paged暂不支持(环境 1628 个表单中无使用样例); - 需设计器补充配置的控件(关联范围 / 主数据 / 配置项 / 编号规则 / 识别类型 …):CLI 只创建骨架, 生成时会打印提示;缺必填属性时发布校验会拦截(见「控件必填属性校验」)。
# 高级控件用法与普通控件一致(第三个 token 写控件类型)
llc form autofill --id <fdId> --apply --add-field "fd_note:说明文字:desc" --add-field "fd_code:平台编码:mechnumber"address:地址本(组织 / 人员选择器)
⚠️ address 不是「地址(文本)」控件,而是平台的地址本——用来选人、选部门/组织/岗位/用户组。
凡涉及组织架构与人员的字段(责任人、经办人、所属部门、归属单位…)都用这个类型。
落库特征(与真实表单核对):fdDataType=address、fdDictType=orgElementDict(组织人员字典,
环境内唯一的 orgElementDict 控件)、fdIsStored=true。
| 属性 | 含义 |
|---|---|
| range | 选择范围:all(全部组织,默认)/ custom(自定义——此时才出现预选相关属性) |
| org.orgTypeArr | 可选类型,位掩码数组:1=组织、2=部门、4=岗位、8=人员、16=用户组;组合值如 3=组织或部门、12=岗位或人员、31=全部。CLI 默认 ['8'](仅人员) |
| multi | 是否多选(默认 false,单选) |
| preSelectType / preSelectValue / nextDept | 仅 range='custom' 生效(预选类型 / 预选值 / 越过部门) |
| relation | 联动传值(relationKey/relationValue/relationEnum/relationTableName) |
| orgAvailable / showAvatar / showType | 是否可选组织、显示头像、显示类型 |
平台把上述属性同时存两份:控件属性 controlProps 与数据项 fdFontExtendData
(前端 syncApi.frontExtendData2Config 反向同步;公式/接口 Schema 的 $ref 依赖它——
如 orgTypeArr:['8'] → ORG_PERSON、缺省则为 ORG_ALL)。CLI 两者都会写入并保持一致。
# 选人字段(默认:全部范围 / 仅人员 / 单选)
llc form autofill --id <fdId> --apply --add-field "fd_owner:责任人:address"需要「多选 / 自定义范围 / 可选部门」等配置时,用设计器改(CLI 会把这些配置原样保留, 不会因为重新生成而丢失);平台预置的
fd_creator/fd_owner/fd_alter等也是address类型, 但只靠fdFontExtendData.orgTypeArr表达类型(fd_creator=人员、fd_creator_dept=部门)。
关联类控件(relation / relationQuery / association / …)
| 控件 | 含义 | CLI 状态 |
|---|---|---|
| relation | 业务关联(关联其它表单的业务数据) | ⚠️ 控件包被平台标记废弃(xform.group=deprecated,但存量表单大量在用:环境内 1371 个字段)。控件包 config.json 里没有任何必填属性,但属性面板由设计器拼装、有强制要求——CLI 已按设计器规则做语义校验(见下),裸字段会被拦截。新表单建议改用 association |
| relationQuery | 关联查询 | 需设计器配置查询页签(tabs 必填 → 校验拦截) |
| association | 关联选择 | 需设计器配置关联范围与列表列(xlistColumn 必填 → 校验拦截) |
| foreignKey | 外键关联 | 需设计器配置映射类型与目标模型(mappedType 必填 → 校验拦截) |
| refTemplate | 关联模板 | 需设计器选择关联模板(校验拦截) |
| associatedFields | 关联字段(展示关联表单的字段) | 无必填属性,但未配置关联源时后端发布会 500(生成时告警) |
业务关联的真实落库结构(已用《科技成果登记》等真实表单核对):fdDataType=relation、
fdDictType=querySourceDict、fdLength=200,核心配置在 controlProps 与 fdFontExtendData:
controlProps.relationCfg = { appCode, xformName, modelId, tableName, refFieldName, fdFormType }
// refFieldName 是 `$<字段名>$` 引用(如 `$fd_material_name$`),运行时解析为所选记录的显示值;
// 不能写成 `$<表名>.<字段名>$`(解析不到会把字面量显示出来),relationCfg 也没有 showFields 键
controlProps.datasource = { columns:[{name,label}], queryCondition, tree, relationConditions }
controlProps.outParams = { params:[{ sourceField:{fdName,tableName,tableType}, targetField:{…} }] }(可选)
controlProps.mappedType / multi / renderMode / direction / rowCount / isForwardView / modelId / modelName这些是「选业务对象」的结果,CLI 无法推断、也不伪造——需在设计器里选好(选好后
autofill 会原样保留这些属性,见「设计器配置的控件属性会被保留」)。
业务关联的必填项(CLI 已按设计器规则校验,见「控件语义校验」):
| 必填项 | controlProps | 规则来源 |
|---|---|---|
| 关联表单(业务对象)+ 显示值 | relationCfg.appCode / relationCfg.refFieldName | IDE controlConfig.tsx 的 relationForm 校验器「关联表单不能为空」 |
| 数据源显示项 | datasource.columns 非空 | IDE dataSource 校验器 + dataSource/formDetails.tsx 显示项 required: true(适用显示方式 singlelist/mullist/treeList/multiTreeList) |
| 卡片设置(卡片类显示方式) | datasource.card.displayName / displayImage + columns | 「卡片设置为必填项」 |
| 导航设置(树+卡片/列表类) | datasource.tree.conditionsFromTree / relationConditions / relationCfg | 「导航设置为必填项」(分类导航除外) |
| 树层级设置(panel/tree 类) | datasource.tree.relationConditions | 「树层级设置为必填项」 |
CLI 生成业务关联字段时会写入设计器的默认属性(依据环境 1371 个真实字段的分布):
renderMode: 'singlelist'(95%)、direction: 'column'、rowCount: 3、isForwardView: 'no'
(mappedType 仅 5% 字段使用,属按需配置,不做默认)。
llc form autofill --id <fdId> --apply --add-field "fd_r:业务关联:relation"
# → ✖ 字段 fd_r(@elem/xform-relation):关联表单不能为空:需在设计器中选择业务对象并设置显示值…
# → ✖ 字段 fd_r(@elem/xform-relation):列表设置为必填项:需在数据源配置中选择显示项(datasource.columns)
# 退出码 1(--force 可跳过,但该控件仍需到设计器补配置)用 --relation 配置业务关联(含业务对象与显示项)
--relation 把「字段 = 目标表单」解析成完整的业务关联配置(form create / form autofill 均支持):
# 最简:目标按表单名(同名多份时优先当前表单所属应用)或 fdId
llc form autofill --id <fdId> --apply --detail "出库明细=fd_dtl_out_name:物料名称:relation" \
--relation "fd_dtl_out_name=物料信息"
# 指定显示值(选人看到的文案)与显示项(列表列)
llc form autofill --id <fdId> --apply \
--relation "fd_customer=客户信息:显示值=fd_cust_name:显示项=fd_cust_name,fd_cust_code,fd_level"| 部分 | 说明 | 缺省 |
|---|---|---|
| 字段名 | 目标字段物理名(主表或明细表字段,命中后自动改写为 relation 控件) | — |
| 目标表单 | 表单名或 fdId;按名匹配到多份时优先当前表单所属应用,仍歧义则报错列出候选 | — |
| 显示值=字段 | 存入 relationCfg.refFieldName(写 $字段名$,多项用逗号拼接) | 目标表首个文本类业务字段 |
| 显示项=字段1,字段2 | 存入 datasource.columns(设计器强制必填) | 目标表全部业务字段 |
解析后会写入:relationCfg(业务对象)、datasource(数据源骨架 + 显示项 + 筛选项 + 树设置)、
renderMode: 'singlelist'、modelName,并把这些键同步到 fdFontExtendData(控件源码 JSDoc 的
@frontExtendData 声明);datasource 骨架结构取自真实表单,保证设计器打开该控件时结构完整。
实测案例(物料管理应用):把《物料出库单》明细表的「物料名称」由单行文本改为关联《物料信息》:
llc form autofill --id 1k4ihs42swdw2vebw38dpgr036rj6uqi0tw0 --apply --publish \
--detail "出库明细=fd_dtl_out_name:物料名称:relation" \
--relation "fd_dtl_out_name=物料信息"
# → 业务关联:fd_dtl_out_name → 物料信息(显示值 物料名称,显示项 11 项:物料名称/规格型号/…/默认供应商)
# → 落库:fdType=relation、fdDictType=querySourceDict、renderMode=singlelist、fdStatus=official注意:切换为业务关联后,该字段历史数据(原文本值)不会自动解析成关联记录——设计器切换 关联表单时也有同样提示。另外「选中物料后自动带出其它列」(传出参数
outParams,如把 物料编码/参考单价写回明细行)尚未提供 CLI 参数,需要的话可以再加。
平台标记废弃的控件共 3 个:
relation(业务关联)、associatedFields(关联字段)、timeAxis(时间轴)。llc control list会以已废弃备注并汇总列出。
--option 为选择类控件建选项,写入 controlProps.options 与 fdFontExtendData.options,
并生成 prop:'option' 的多语言条目:
--option "fd_col_level=高:high,中:mid,低:low"
--option "fd_col_type=研发:rd,实施:impl,运维:ops" # 省略值则按 1..n明细表
llc form autofill --id <fdId> --apply --publish \
--detail "设备清单=fd_sname:设备名称,fd_scount:设备数量:numbertext,fd_sstatus:设备状态:select" \
--detail "人员明细=fd_pname:姓名,fd_pwork:是否在职:switch" \
--option "fd_sstatus=在用,报废,维修中" \
--required "fd_sname,fd_pname"明细表会生成一整个子数据模型(fdType:'detail'),落库位置:
| 位置 | 内容 |
|---|---|
| dataModel | 新增一个 detail 模型:fdId 来自 sysXFormDataModel/getSystemDefaultModel,表名来自 generateTableName({fdType:'detail'}),系统字段 fd_id/fd_main_id/fd_order,fdAttribute 为明细表控件配置 |
| view.render.desktop | main 末尾追加独立行(span 24):row → { key:<明细表名>, children:[明细字段引用] } |
| view.render.mobile | 平铺追加明细表节点与字段引用 |
| view.controlMappings | 追加 @elem/xform-detail-table / @elem/xform-m-detail-table(明细表控件需要进 mappings) |
| view.controlStyle | 明细表自身条目(desktop + mobile);明细字段不需要 |
| auth | 按表名新增条目:add/edit 带 canAddRow/canDeleteRow/canImport 与行操作,view 带 canExport |
| lang | 明细表标题(prop:'label',含 default)与明细字段标题/占位/选项 |
要点:
- 再次执行
--detail同名标题会复用已有明细表(保留fdId、系统字段与已有列),可继续调整字段; 不传--detail时已有明细表原样保留。 - 明细字段的控件类型、选项、权限与主表字段完全一致(
--option/--required/--readonly/--hidden对明细字段同样生效,按字段名匹配)。 - 明细表控件的默认属性(
mode/defaultRowNumber/showNumber/showRowSelection等) 不在config.json里,而由前端写死;CLI 按src/constants.ts#DETAIL_CONTROL_DEFAULTS复刻。
布局
--layout 决定 viewModel.view.render.desktop 的结构。移动端始终是平铺的字段引用
(实测真实表单的 render.mobile 不含任何布局容器),因此布局只影响桌面端。
| --layout | 桌面端结构 | 真实样本依据 |
|---|---|---|
| rows(默认) | main → 若干 @elem/xform-row → 字段引用 | ✔ |
| grid | main → layout → @elem/layout-grid → 若干 @elem/layout-grid.GridItem → 字段引用 | ✔ |
| tabs | main → @elem/xform-tabs → 若干 @elem/xform-tabpane → @elem/xform-row → 字段引用 | ✔ |
要点(均为真实表单核对结论):
- 栅格始终是满格的:
GridItem总数 =columns × rows,最后一行的空位也会保留 (children: []、controlProps: { id, column, row })——与设计器一致(真实样本如 《业务审批管理》columns=3但只有 2 个字段,第 3 格仍存在)。旧版本会删掉末行多余格子,已修复。 - 栅格布局控件不进
controlMappings;多页签控件必须进(tab-appearance/tabs/tabpane三件套)。 - 多页签的 key 约定:外观 key 形如
@elem/xform-tab-appearance~xxxxxx, tabs 节点 key 为<外观key>-tabItem,面板为<外观key>-tabN, 且controlProps.parent = { name: '@elem/xform-tab-appearance', type: 'appearance' }。 渲染树中没有独立的tab-appearance节点。 - 页签标题走多语言:
controlProps.tabMessageKey, 对应lang[..] = { prop:'tabs', name:<字段名>, type:'input', content:{ Cn, Us, Tw, default } }。 --tabs未列出的字段会自动归入末尾的「其它」页;未提供--tabs时全部字段进单个「其它」页。
llc form create --name 复杂表单 --app-id <appId> --layout tabs \
--tabs "基本信息:fd_col_a,fd_col_b|金额信息:fd_col_c,fd_col_d" \
--add-field "fd_col_a:姓名" --add-field "fd_col_b:部门" \
--add-field "fd_col_c:金额:number" --add-field "fd_col_d:日期:timestamp" \
--publish字段权限(必填 / 可见 / 只读)
对应控件属性面板的「字段权限」(各控件 config.json 的
controlGroup > fieldPermission > { required, editable, visible }),
落库位置是 auth[0].{add|edit|view}[表名].fields[字段名]:
{ "visible": true, "editable": true, "required": false, "hide": false }快捷参数:
| 参数 | 写入 |
|---|---|
| --required <fields> | add / edit 的 required: true |
| --readonly <fields> | add / edit 的 editable: false |
| --hidden <fields> | add / edit / view 的 visible: false |
精细控制用 --auth-file:
{
"add": { "fd_col_a": { "required": true } },
"edit": { "fd_col_b": { "visible": false }, "fd_col_c": { "editable": false } },
"view": { "fd_col_d": { "visible": false } }
}注意:
--required写入的是权限(auth),不是数据项本身。 数据项的「是否显示」是fdDisplay,本 CLI 默认只把业务字段渲染成控件,不写fdDisplay。
多语言
表单标题、控件提示等文案统一存放在 fdConfig.lang 中,key 形如 !{mv0dg6b08kjn1fost7o}。
1. messageKey 格式(重要)
前端识别多语言标记的正则是 /\!\{([\w\.]*)\}/gi,\w = [A-Za-z0-9_],不含连字符。
key 主体必须与前端 sys-xform/src/util/runtimeUtil.ts#uuid 一致:
Date.now().toString(36) + Math.random().toString(36).slice(2) // 纯 base36CLI 已按此实现(src/utils/random.ts#generateMessageKey),并对历史非法 key 自动重建。
2. 语言解析顺序
sys-xform/src/components/runtime/utils.ts 的规则是 content[当前语言] → content['Cn']。
因此只写 Cn 时任何语言的用户都能看到中文兜底;要展示真实翻译需显式开启多语言。
3. 用法
# 只写中文(默认)
llc form create --name 请假单 --type general
# 同时写入 简体/英文/繁体(两者等价)
llc form create --name 请假单 --type general --locale Cn,Us,Tw
llc form create --name 请假单 --type general --locale all
# 提供真实翻译
llc form create --name 请假单 --type general --locale Cn,Us --lang-file ./lang.jsonlang.json 结构:
{
"Us": { "姓名": "Name", "金额": "Amount", "请输入": "Please enter" },
"Tw": { "金额": "金額" }
}非主语言的取值优先级:
| 顺序 | 来源 | 说明 |
|---|---|---|
| 1 | --lang-file | 用户翻译,按「字段标题」再按「字段物理名」匹配 |
| 2 | 控件自带译文 | 从控件多语言资源接口取(如英文 placeholder Please enter) |
| 3 | 主语言文案 | 兜底,与前端 ide-model/business/input/inputLocale.tsx 行为一致 |
第 3 条保证不会出现空白标题;语言码大小写自动归一(us / US → Us)。
--locale / --lang-file 同样适用于 llc form autofill。
4. 覆盖范围
| lang[].prop | 对应文案 | 是否由 CLI 生成 |
|---|---|---|
| label | 字段标题 | 是 |
| placeholder | 控件输入提示 | 是 |
| option | 下拉 / 单选 / 多选的选项文案 | 是(已有选项时完善) |
选项多语言的落库结构(与真实环境数据一致):
lang["!{xxx}"] = { prop:'option', name:<fdName>, type:'input', content:{ Cn:'选项1' } }
fdAttribute…controlProps.options[] = { label:'!{xxx}', value:'1' }
fdFontExtendData.options[] = { messageKey:'!{xxx}', label:'选项1', value:'1', … }要点:
- 选项必须先存在。CLI 不会凭空创建选项;选项由数据项管理 / 设计器 / Excel 导入向导配置,
CLI 负责把它们的文案补成多语言(并保留
colorSwitch、type、value等原有字段)。 - 数据字典选项会被跳过。
messageKey形如${sys-xform:enum.X}/sys-xform:enum.X的选项走平台静态多语言资源,写入lang反而会污染,因此原样保留。 - 选项的
--lang-file匹配支持两种 key:选项文案(如选项1)与字段名#选项值(如fd_col_x#2,用于多个选项文案重复时的精确匹配)。
控件动作(onChange / onLoad 等)
许多控件支持配置动作。动作存放于 fdConfig.attribute.formAttr.controlAction(真实表单结构已核对):
controlAction.control['<表名>.<控件uuid>'][<事件>] = [{ name, id, function }]
controlAction.global[<事件>] = [{ name, id, function }]
controlAction.javascript = 所有函数源码拼接(设计器 JS 面板按此展示)function 是函数源码字符串,运行时经
new Function('MKXFORM', 'return (' + source + ')') 求值后挂到控件的
controlProps.controlActions[事件](sys-xform/src/components/runtime/business/action.ts)。
1. 两种写法
# 内联源码(可重复)
llc form autofill --id <fdId> --apply --publish \
--action "fd_col_a:onChange:function onChange(value){ MKXFORM.setValue('fd_col_b', value) }"
# 复杂函数建议放 js 文件(@开头)
llc form autofill --id <fdId> --apply --publish --action "fd_col_a:onChange:@./on-change.js"或用 JSON 文件(适合多条 / 全局动作):
llc form autofill --id <fdId> --apply --publish --action-file ./actions.jsonactions.json:
{
"fd_col_a": { "onChange": "function onChange(value){ … }" },
"$global": { "onLoad": "function onLoad(){ … }" }
}也支持数组形式:[{ "target": "fd_col_a", "event": "onChange", "source": "…", "name": "可选函数名" }]。
2. 规则与边界
| 项 | 说明 |
|---|---|
| 目标 | 字段物理名(fd_col_a);$global 为全局动作,仅支持 onLoad / onBeforeSubmit / onAfterSubmit |
| 控件级事件 | 任意标识符(onChange / onFocus / onBlur…),运行时按控件实际支持的事件触发 |
| 函数签名 | 控件级 function onChange(value, rowNum)(value 当前值,rowNum 明细行号);运行时闭包内可用 MKXFORM(如 MKXFORM.setValue('表名.字段名', v)) |
| key | 表名.控件uuid(与运行时 addActionToControlProps 的查找规则一致);明细表字段同样适用 |
| 函数重名 | 自动追加随机后缀(与前端 mergeControlAction 行为一致) |
| 函数命名 | 控件级动作禁止裸事件名(onChange 等)——CLI 自动规范为 onChange_<控件id> 并同步重写函数源码声明;$global 全局动作保持裸事件名(如 onLoad) |
| 目标字段不存在 | 跳过并告警,不阻断 |
| 已有动作 | 保留。autofill 会加载已有 formAttr 合并写入,不会清掉设计器里配置的动作/表单规则 |
form create 同样支持 --action / --action-file。
查询环境语言
llc locale list
llc locale list --element @elem/xform-select --json数据源是控件多语言资源接口 POST {baseUrl}/sys-lang/getFrontResource
(path=sys/artifact/elements/i18n),其响应的 data[0].data 按原始语言码(zh-cn)分组:
环境为「@elem/xform-input」提供的语言(共 1 个)
zh-cn -> Cn 请输入
可直接用于 --locale 的取值
Cn用途是回答「哪些语言有现成译文」——这些语言在 --locale 里能自动填上真实译文(如英文 placeholder)。
环境配置的语言全集(前端 mk.getUserConfig('allLocales'))未暴露后端接口,如需其它语言请直接 --locale 指定。
控件必填属性校验(发布前)
CLI 在 form create / form autofill 保存前会校验控件必填属性值,规则对齐设计器
(formBuilderHelper 必填规则 + 发布时 validates.validating() 阻断):
- 校验对象:控件
config.json属性面板中满足required && !disabled && !protected && !slotScope的叶子属性项; - 值为空(
undefined/null/'')即报错,格式请输入<label>(key); 空数组不算空——设计器规则是{required:true}(不带type),async-validator 仅在type === 'array'时才把[]判为空,因此preSelectValue: [](地址本自定义范围的真实结构) 是合法的,CLI 必须一致,否则会对合法表单误报并中止保存; - 与设计器一致地跳过:带
enableWhen/hidden的条件渲染项(未渲染不校验)、 分组容器(baseGroup/styleGroup 等)、inputLocale类标题项(取title值); - 校验失败默认中止保存/发布(预览模式仅提示);加
--force可跳过。
llc form autofill --id <fdId> --apply --publish # 校验失败会中止
llc form autofill --id <fdId> --apply --publish --force # 跳过校验必填属性兜底默认值:部分控件声明了必填属性但无默认值(设计器中靠用户填写),
CLI 按 src/constants.ts#REQUIRED_PROP_DEFAULTS 自动补齐,
取值来自设计器创建表单的实测值或控件约定:
| 控件 | 属性 | 默认值 |
|---|---|---|
| textarea | height | "3"(3 行,设计器实测) |
| address(地址本) | org / multi | {"orgTypeArr":["8"],"defaultValueType":"null"} / false(设计器实测;8=人员) |
| rate | max | 5 |
| slider | min / max | 0 / 100 |
| rich-text | height | 300(config.json min 200) |
| bar-code | height | 80 |
| hyperlinks | largestSet | "infinit"(选项首项) |
| description | defaultTextValue | $title(字段标题的多语言 key,见下方占位符) |
| tag | tabsRange / multi | {categorys:[],targs:[],rangeType:'all'} / true |
| location | allowed / mapContent / searchNearBy | false / {address:'formattedAddress'} / {…}(实测) |
| area | defaultValueType / precisionRegion / showHierarchy | 'null' / 'county' / 'all' |
| html | source | {html:'',css:'',javascript:''} |
| iframe | heigth / expressionFormulaVO | 150 / {} |
| file-edit | height / allowImport / accept | 600 / true / {docx,[.doc,.docx]}(实测) |
| association | mode / processStatus | 'multiple' / ['30'](实测) |
| mdm | renderMode / direction / rowCount | 'singlelist' / 'column' / 3 |
| cfg | renderMode / cascader / showLevel / selectLevel | 'select' / true / 'lastLevel' / 'allLevel' |
| calculate | statisticMode / defaultValueType | 'SUM' / 'empty' |
| interval / button / agency / associated-fields / time-axis / review / attach-demo / dynamic | 样式与开关类 | 设计器实测值 |
REQUIRED_PROP_DEFAULTS 支持两个字段级占位符(生成时解析):
$title → 字段多语言 key(langKey)、$name → 字段物理名(fdName)。
设计器默认属性(src/constants.ts#CONTROL_DEFAULT_PROPS):控件包 config.json 未声明、
但设计器新建字段时一定会写入的项。这类值不属于「必填」,缺失时运行时兜底不可靠:
| 控件 | 属性 | 默认值 | 依据 |
|---|---|---|---|
| relation(业务关联) | renderMode / direction / rowCount / isForwardView | 'singlelist' / 'column' / 3 / 'no' | 环境 1371 个关联字段分布 95%/99%/99%/98% |
| moneytext(金额) | precision / symbol | 2 / '' | 环境 115 个金额字段 precision 出现率 100%(88% 为 2);平台数据迁移代码缺省时按 0 位小数落库,故显式写 2 位。symbol(币种)属业务选择(69% CNY、28% 空),取中立空值 |
已有字段的该项值若已由设计器配置,原值优先(carried 覆盖默认值),autofill 不会改写。
需要「环境对象选择」的属性 CLI 不伪造(选了才有意义):关联范围 xlistColumn、
主数据 mdm、配置项 cfg、接口 renderMode(dynamic)、编号规则 fdSysNumber、
映射类型 mappedType、识别类型 discernType、审批显示字段 defaultFields、
默认附件/图片 defaultValue 等。生成时会给出「需在设计器中配置」提示,
保存时被发布前校验拦截(--force 可跳过,但生成的控件需要人工补配置才能真正可用)。
特例:associatedFields(关联字段)没有必填属性,但未配置关联源时后端发布会直接
失败(HTTP 500,已实测)——CLI 会在生成时提示,需到设计器指定关联源字段。
例外:upcasing.relationControl(联动控件)是语义性选择,CLI 不兜底;
未指定时校验会拦截,需显式给出(存于 fdFontExtendData.relationControl = {fdName, fdLabel})。
条件必填:带 enableWhen 的属性项按条件判定——条件为「键 = 简单值」且在 controlProps 中成立时才校验
(如 datetime 的 defaultValueType='formula' 时公式配置 defaultValueFormulaVO 必填);
条件不成立或为复杂表达式时跳过,与设计器一致。
控件语义校验(src/constants.ts#CONTROL_SEMANTIC_RULES):有些控件的属性面板由设计器拼装
(控件包 config.json 的 config 为空),必填项不在 config.json 里,通用必填校验覆盖不到。
CLI 按设计器源码中的校验器补了一层「语义校验」,每条规则可声明 renderModes(仅特定显示方式生效)。
目前覆盖 relation(业务关联):relationCfg(关联表单)+ datasource.columns(显示项)
- 卡片/导航/树层级设置,文案与设计器一致(关联表单不能为空 / 列表设置为必填项 / 卡片设置为必填项 /
导航设置为必填项 / 树层级设置为必填项)。存量影响面已实测:环境 1371 个业务关联字段中
仅 34 个(2.5%,涉及 26 个表单)不合规,这些历史表单
autofill时报错,需--force或到设计器补齐。
初始值模式:新建字段 defaultValueType 默认为 null(无初始值),与控件 config.json 默认一致;
不要写 formula——公式模式下 defaultValueFormulaVO 必填,设计器提交会被拦截。
若需要公式初始值,按以下结构写入 fdFontExtendData(迁移自前端 dataMigration.ts):
{
"defaultValueType": "formula",
"defaultValueFormulaVO": { "script": "\"2026-01-01\"", "type": "Eval", "vo": { "mode": "formula", "content": "2026-01-01" } }
}另:labelProps.layout 默认写入 horizontal(左右布局)、labelTextAlign: 'left';
如需上下布局可在生成后调整或后续提供选项。
自动完善表单字段
布局保护:
autofill不带--layout时会沿用已有视图——设计器里的栅格布局、 多页签等布局不会被动过;同时保留已配置的控件动作(controlAction)与表单规则。 显式传入--layout(如--layout grid --grid-columns 3)才会按参数重建布局。 通过--add-field新增的字段不在旧布局中,保存时会告警提示,需显式--layout才会展示。
# 预览(不写入)
llc form autofill --id <fdId>
# 确认后写入
llc form autofill --id <fdId> --applyautofill 支持与 create 相同的 --add-field / --include-system / --icon / --fields / --per-row / --span 选项,
同样会重建每个字段的 fdAttribute.config、view.controlMappings 与多语言(lang)。
saveDesign会把表单置为草稿态。修复已发布表单时请加--publish(modeling族会走saveDesignAndPublish)。 不加--publish且原状态为official时会给出提示。 它会先尝试sysModelingXForm/loadBase,命中则按modeling族处理;否则读取表单的fdFormDefineType自动在general/design之间选择。
若表单是早期版本 CLI 生成的(缺少
fdAttribute.config/ 渲染节点不是{key}引用), 执行一次llc form autofill --id <fdId> --apply即可修复为可加载格式。
其他
llc app list [--keyword <name>]
llc form list [--keyword <kw>]
llc form get --id <fdId> [--with-config]
llc form publish --id <fdId> [--api modeling|general|design] [--desc <说明>]
llc form category-list
llc config show | set | clear四、调用的后台接口
| 功能 | 接口 |
|---|---|
| 控件多语言资源(语言探测) | POST sys-lang/getFrontResource |
| 应用新建(含默认首页) | POST sys-modeling/sysModelingApplication/add |
| 应用编辑页数据 | POST sys-modeling/sysModelingApplication/edit |
| 应用修改 | POST sys-modeling/sysModelingApplication/update |
| 应用删除 | POST sys-modeling/sysModelingApplication/delete |
| 应用业务数据删除 | POST sys-modeling/sysModelingApplication/deleteData |
| 表单删除 | POST sys-modeling/sysModelingXForm/delete |
| 表单业务数据删除 | POST sys-modeling/sysModelingXForm/deleteData |
| 【modeling 族】生成主表名 | POST sys-modeling/sysModelingXForm/generateTableName |
| 【modeling 族】检查表名 | POST sys-modeling/sysModelingXForm/checkTableNameExist |
| 【modeling 族】检查表单名 | POST sys-modeling/sysModelingXForm/checkXFormNameExist |
| 【modeling 族】表单新建 | POST sys-modeling/sysModelingXForm/create |
| 【modeling 族】保存设计 | POST sys-modeling/sysModelingXForm/saveDesign |
| 【modeling 族】保存并发布 | POST sys-modeling/sysModelingXForm/saveDesignAndPublish |
| 【modeling 族】发布 | POST sys-modeling/sysModelingXForm/publish |
| 【modeling 族】加载设计 | POST sys-modeling/sysModelingXForm/loadDesign |
| 【modeling 族】加载基本信息 | POST sys-modeling/sysModelingXForm/loadBase |
| 【modeling 族】应用列表 | POST sys-modeling/sysModelingApplication/list |
| 【modeling 族】应用列表(免权限) | POST sys-modeling/sysModelingApplication/listNotAuth |
| 【general 族】表单初始化 | POST sys-xform/generalFormDefine/init |
| 【general 族】表单新建 | POST sys-xform/generalFormDefine/add |
| 【general 族】表单加载 | POST sys-xform/generalFormDefine/get |
| 【general 族】表单保存 | POST sys-xform/generalFormDefine/save |
| 【general 族】表单发布 | POST sys-xform/generalFormDefine/publish |
| 【general 族】表单列表 | POST sys-xform/generalFormDefine/list |
| 【design 族】表单新建 | POST sys-xform/sysXFormDesign/create |
| 【design 族】表单加载 | POST sys-xform/sysXFormDesign/load |
| 【design 族】表单保存 | POST sys-xform/sysXFormDesign/save |
| 【design 族】表单发布 | POST sys-xform/sysXFormDesign/publish |
| 表单分类列表 | POST sys-xform/generalFormCategory/list |
| 表单数据模型(字段) | POST sys-xform/sysXFormDataModel/loadByFormId |
| 数据模型字段 | POST sys-xform/sysXFormDataModel/getModelFields |
| 数据模型(按 id) | POST sys-xform/sysXFormDataModel/load |
| 主表名生成 | POST sys-xform/sysXFormDataModel/generateTableName |
| 外部表字段 | POST sys-xform/sysXFormDataModelField/loadFieldMeta |
| 字段库 | POST sys-xform/sysXFormWordsField/list |
| 字段编码生成 | POST sys-xform/sysXFormWordsField/generateFdKey |
五、生成的表单配置结构(fdConfig,持久化格式)
⚠️ 表单引擎有两套格式:IDE 内存中的 form-builder 树 ≠ 落库格式。 CLI 必须生成后者,否则前端加载时控件无法还原。
落库格式的关键点(由 sys-xform/components/ide/store/viewModel/view/pureUtils.ts#getRenderLayout 决定):
view.render.*中,控件节点被折叠为{ key: <uuid> }(容器节点保留key/type/kind/controlProps/children)。- 控件的真实定义放在
view.controlMappings(平台 → 控件包元数据)与字段的fdAttribute.config中。 - 前端加载时
cacheConfig[attr.uuid] = attr.config,再按{ key }回填 ——fdAttribute缺失或结构不符,控件就会丢失。
{
"dataModel": [
{
"fdTableName": "mk_model_xxx",
"fdType": "main",
"fdFields": [
{
"fdName": "fd_col_text01",
"fdLabel": "单行文本",
"fdLabelLangKey": "!{...}",
"fdType": "text",
"fdDataType": "varchar",
"fdDictType": "textDict",
"fdLength": 200,
"fdIsSystem": false,
"fdDisplay": true,
"fdState": "effective",
"fdFontExtendData": "{\"passValue\":false,\"trace\":false,\"encrypt\":false,\"defaultValueType\":\"formula\",\"encryptDefinition\":{}}",
// ↓ 关键:控件配置唯一来源
"fdAttribute": "{\"uuid\":\"fd_col_text01\",\"env\":[\"xform\"],\"config\":{\"key\":\"@elem/xform-input~xxxxxx\",\"type\":\"text\",\"kind\":\"control\",\"label\":\"!{...}\",\"controlProps\":{\"aiFill\":false,\"showText\":false,\"passValue\":false,\"trace\":false,\"encrypt\":false,\"defaultValueType\":\"formula\",\"placeholder\":\"!{...}\",\"maskFormatType\":\"********\",\"encryptMonth\":9999,\"failureRule\":\"ANONYMIZE\",\"id\":\"@elem/xform-input~xxxxxx\",\"desktop\":{\"type\":\"@elem/xform-input\"},\"mobile\":{\"type\":\"@elem/xform-m-input\"},\"name\":\"fd_col_text01\",\"uuid\":\"fd_col_text01\",\"title\":\"!{...}\",\"span\":12,\"$$tableType\":\"main\",\"$$tableName\":\"mk_model_xxx\",\"maxLength\":200},\"labelProps\":{\"desktop\":{},\"mobile\":{},\"title\":\"!{...}\",\"showText\":false,\"trace\":false}}}"
}
]
}
],
"auth": [ /* 新增/编辑/查看 权限(按表按字段) */ ],
"attribute": { "formAttr": "{\"subjectRule\":{\"type\":\"Eval\",\"script\":\"${data.biz.fd_doc_subject}\",\"vo\":{\"mode\":\"formula\",\"content\":\"$<模型名>.<标题>$\"}},\"formRule\":{\"pattern\":{}},\"dataUnique\":{},\"controlAction\":{},\"currentTableName\":\"<主表名>\"}" },
"error": "{}",
"lang": "{ \"!{labelKey}\": {\"prop\":\"label\",\"name\":\"fd_col_text01\",\"type\":\"input\",\"content\":{\"Cn\":\"单行文本\"}}, \"!{phKey}\": {\"prop\":\"placeholder\",...} }",
"authFilter": { "detailAuthDefine": {} },
"extendMap": {},
"sign": { "formula": {} },
"viewModel": [
{
"fdCode": "SCENE_DEFAULT",
"fdName": "默认",
"fdStatus": "draft",
"fdLockMode": "none",
"fdOrder": 0,
"dynamicProps": {},
"needDelete": false,
// ↑ 同样是 JSON 字符串
"fdConfig": "{\"view\":{\"render\":{\"desktop\":[{\"key\":\"@elem/xform-appearance\",\"type\":\"@elem/xform-appearance\",\"kind\":\"container\",\"controlProps\":{\"$$tableName\":\"mk_model_xxx\"},\"children\":[{\"key\":\"main\",\"type\":\"main\",\"kind\":\"container\",\"children\":[{\"key\":\"@elem/xform-row~xxxxxx\",\"type\":\"@elem/xform-row\",\"kind\":\"container\",\"controlProps\":{\"id\":\"@elem/xform-row~xxxxxx\",\"spans\":[12]},\"children\":[{\"key\":\"fd_col_text01\"}]}]}]}],\"mobile\":[...]},\"controlMappings\":{\"desktop\":[{\"name\":\"@elem/xform-input\",\"platform\":\"desktop\",\"xform\":{\"type\":\"text\",\"dataType\":\"varchar\",\"dictType\":\"textDict\",\"span\":12,\"env\":[\"xform\"],...}}],\"mobile\":[{\"name\":\"@elem/xform-m-input\",...}]}},\"theme\":{\"desktop\":{\"name\":\"default\",\"status\":\"done\",\"viewAttr\":{...}},\"mobile\":{...}},\"controlStyle\":{\"fd_col_text01\":{\"desktop\":{...}}}}"
}
]
}要点:
- 外层
fdConfig、内层viewModel[].fdConfig、lang、error都是 JSON 字符串。 - 渲染树为
@elem/xform-appearance→main→@elem/xform-row→{ key: <uuid> }。 - 系统字段(
fdIsSystem: true)默认不渲染到表单上,可用--include-system打开。 id/name/tableName/entityName/entityCode会被后端DesginConfigVO丢弃,前端加载 IDE 时从 VO 回填(正常行为)。
新增字段
llc form create --name 请假单 --app-id <appId> --add-field "fd_col_text01:单行文本" --publish--add-field 格式:name:label[:dataType[:controlType]],可重复。只给 label 时自动生成 fd_col_xxxxxx。
字段会写入 dataModel[].fdFields,发布时由后端创建真实数据库列。
六、控件元数据与类型映射
控件类型/字典类型/默认长度/默认属性等不来自静态映射表,而是运行时读取控件包的 config.json:
{origin}/web/sys/artifact/elements/{pkg}/config.json
例:http://host/web/sys/artifact/elements/xform-input/config.jsonCLI 会按需拉取并缓存到 ~/.llc/widgets-cache.json。从中获取:
xform:type/dataType/dictType/defaultLength/span/order/kind/for/isStored/...env:运行环境(["xform"])config[].default:控件默认属性(aiFill/showText/passValue/encrypt/maskFormatType/encryptMonth/failureRule等)
placeholder(提示文字)通过 POST {baseUrl}/sys-lang/getFrontResource 获取并写入 lang。资源键名因控件而异
(来源:控件源码 defaultProps.placeholder,见 src/constants.ts#CONTROL_PLACEHOLDER_LANG_KEYS):
| 资源键 | 控件 | 环境文案(zh-cn) |
|---|---|---|
| pleaseEnter | @elem/xform-datetime、@elem/xform-timepicker(含移动端 timepicker) | 请选择日期 / 请选择时间 |
| enter | @elem/xform-m-datetime | 请选择日期 |
| pleaseSelect | @elem/xform-area 系列 | 请选择 |
| placeholder | 其余控件(input/select/money/number/textarea/phone-number/rich-text…) | 请输入 / 请选择 |
此前 CLI 一律查
###langs###placeholder,导致日期控件取不到文案、提示文字缺失;现已按控件取键, 并保留未命中时的兜底探测(placeholder → pleaseEnter → enter → pleaseSelect)。 已有提示文字优先沿用(设计器里的自定义文案不会被覆盖,如「请选择月份」)。
内置的 fdDataType → 控件包 入口映射(见 src/constants.ts),可通过项目级 lcode.config.json 覆盖:
{
"baseUrl": "http://mkdev01.ywork.me/data",
"controlMap": {
"clob": {
"desktop": "@elem/xform-rich-text",
"mobile": "@elem/xform-m-rich-text",
"logicalType": "rich-text"
},
"myType": {
"desktop": "@elem/xform-custom",
"mobile": "@elem/xform-m-custom",
"logicalType": "custom"
}
}
}| fdDataType | desktop 控件 | mobile 控件 | logicalType |
|---|---|---|---|
| varchar | @elem/xform-input | @elem/xform-m-input | text |
| number | @elem/xform-number | @elem/xform-m-number | numbertext |
| boolean | @elem/xform-switch | @elem/xform-m-switch | switch |
| timestamp | @elem/xform-datetime | @elem/xform-m-datetime | timestamp |
| address | @elem/xform-address | @elem/xform-m-address | address |
| attach | @elem/xform-attachment | @elem/xform-m-attachment | attachment |
| clob | @elem/xform-textarea | @elem/xform-m-textarea | textarea |
| relation | @elem/xform-relation | @elem/xform-m-relation | relation |
高级控件映射与「未登记控件」保护
CONTROL_TYPE_MAP(src/constants.ts)覆盖环境中的 64/67 个 kind=control 控件。
高级控件条目逐项按真实环境核对,除控件包外还携带落库元信息:
| 字段 | 写入 | 说明 |
|---|---|---|
| dataType | fdDataType | 控件包 config.json 未声明时以映射表兜底(desc/serial-number/bigDetail 等) |
| dictType | fdDictType | 如 serialNumberDict、manyToManyDict、mapDict、attachmentDict |
| defaultLength | fdLength | 如 tags=2000、component=100 |
| isStored | fdIsStored | 元素类控件为 false(不建物理列) |
对未登记的控件(平台新增或定制控件包),autofill 不再降级为输入框:
resolveControl 会从字段已有 fdAttribute.config.controlProps.desktop.type 还原控件包,
并原样保留 fdType,避免把环境里的高级控件改写成普通文本框(可安全地对存量表单做
autofill)。llc control list 会同时打印未登记控件与容器类控件清单。
设计器配置的控件属性会被保留
生成 controlProps 时,同控件包(controlProps.desktop.type 未变)的已有属性会被沿用,
仅身份信息(id/name/uuid/title/desktop/mobile/span/$$tableType/$$tableName)、
options、maxLength 由生成器统一重建。换控件类型时不沿用(避免残留旧属性)。
placeholder(提示文字)是「已有则沿用、缺失则补默认」:设计器里的自定义文案(如
「请选择月份」)连同其 lang 条目一起保留;没有时才按控件资源键补上默认文案(如
日期控件→「请选择日期」、时间→「请选择时间」、文本类→「请输入」、下拉→「请选择」)。
因此设计器里配置过的属性不会因为重新生成而丢失,例如:
| 控件 | 保留下来的属性 |
|---|---|
| address(地址本) | range、multi、org.orgTypeArr、preSelectType/preSelectValue、nextDept … |
| moneytext | precision、symbol、showCount … |
| 任意控件 | placeholder(自定义提示文字 + 对应多语言) |
同时这些「前端扩展属性」(config.json 中声明 frontExtendData 的项)会同步写入
fdFontExtendData,与平台两处并存的约定一致。
输出约定:除
--json的结果外,所有日志/表格输出到 stderr,便于llc ... --json > out.json。
七、目录结构
low-code-cli/
├── bin/llc.js # 可执行入口
├── src/
│ ├── index.ts # commander 入口与命令注册
│ ├── client.ts # HTTP 客户端(fetch + X-AUTH-TOKEN)
│ ├── config.ts # 配置解析与持久化
│ ├── constants.ts # 表单类型 / 控件映射常量
│ ├── types.ts # 类型定义(对齐后端 DTO)
│ ├── widgets.ts # 控件元数据注册表(拉取 config.json + 控件多语言,落盘缓存)
│ ├── api/formDefine.ts # 后台接口封装(modeling / general / design 三族)
│ ├── generator/index.ts # 表单配置生成器(自动完善字段核心)
│ ├── commands/ # config / app / form / field 子命令
│ └── utils/ # 日志与工具函数
└── package.json八、常见问题(前端报错)
进入表单编辑器报 SyntaxError: "undefined" is not valid JSON
原因:sys-modeling 表单编辑页存在如下写法(manage/pages/sysModelingForm/edit/content.tsx):
fdIcon: JSON.parse(`${fdIcon}` || '{}')当 fdIcon 为 undefined 时,`${fdIcon}` 会得到真值字符串 "undefined",|| '{}' 兜底失效 →
JSON.parse("undefined") 抛错;而 init() 是 async 且未被 await/catch → Uncaught (in promise)。
修复:
- 新建:CLI 已默认写入图标,无需处理;
- 已有表单:执行一次
llc form autofill --id <fdId> --apply(会自动补默认图标), 或llc form autofill --id <fdId> --icon <图标名> --apply指定图标。
图标必须写入
modeling_xform镜像表,所以走sysModelingXForm/saveBase(saveDesign只写表单引擎表,不生效)。
界面上标题/提示显示成 !{46573e93-82ef-4def-b184-33f74b2681e6} 这类原文
原因:多语言 messageKey 的格式不合法。
前端识别多语言标记统一使用正则 /\!\{([\w\.]*)\}/gi
(sys-xform/src/components/ide/store/i18n/syncApi.ts#isCustomI18n、components/runtime/utils.ts),
\w = [A-Za-z0-9_],不包含连字符 -。
若用 RFC4122 UUID(带 -)作 key,isCustomI18n() 判定为 false,前端就不会做替换,直接原样显示。
正确的 key 由前端 sys-xform/src/util/runtimeUtil.ts#uuid 生成:
Date.now().toString(36) + Math.random().toString(36).slice(2) // 纯 base36,形如 mv0dg6b08kjn1fost7o修复:CLI 已按上式生成 key(src/utils/random.ts#generateMessageKey),
并会对「不符合 [\w.] 的历史 key」自动重建。
- 新建:无需处理;
- 已有表单:
llc form autofill --id <fdId> --apply --publish。
表单能打开但控件不显示 / 报“找不到默认视图”
一般是 fdConfig 结构不对(用了 IDE 内存格式而非落库格式),
执行 llc form autofill --id <fdId> --apply 重新生成即可(见第五章)。
九、参考源码
接口契约依据以下后端源码整理(非本仓库,仅供核对):
mk-sys-xform(表单引擎)
SysGeneralFormDefineService#beforeExecute:generalFormDefine仅接受fdFormDefineType=GeneralSysXFormBaseDefineService#init:生成fdId/fdEntityId/fdEntityName/fdRunEntityName/fdTableNameSysXFormDesignController/SysXFormDesignService#voCreate:表单引擎原始创建接口FormDefineType:表单类型枚举(1-6)
mk-sys-modeling(业务建模/低代码应用)
SysModelingXFormController#create/saveDesign/saveDesignAndPublish/publish/generateTableNameSysModelingXFormService#create:写死fdEntityId=表单id、fdEntityName=fdRunEntityName='sys-modeling',并按fdAppId归属应用SysModelingApplication(表modeling_application):应用实体,fdAppCode为 URL 段标识SysModelingDataModelService#buildMainTableName:mk_model_yyyyMMdd+ 随机 5 位
