npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

lan-low-code-cli

v2.3.1

Published

lan-low-code cli:低代码表单引擎命令行工具(创建表单、获取可用字段、自动完善表单字段)

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(默认)的选择规则:

  1. 给了 --app-id → modeling(应用下建表单)
  2. 否则 --type general → general
  3. 其余 → 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 --json

control 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)   // 纯 base36

CLI 已按此实现(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.json

lang.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.json

actions.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> --apply

autofill 支持与 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 决定):

  1. view.render.* 中,控件节点被折叠为 { key: <uuid> }(容器节点保留 key/type/kind/controlProps/children)。
  2. 控件的真实定义放在 view.controlMappings(平台 → 控件包元数据)与字段的 fdAttribute.config 中。
  3. 前端加载时 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.json

CLI 会按需拉取并缓存到 ~/.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=General
  • SysXFormBaseDefineService#init:生成 fdId/fdEntityId/fdEntityName/fdRunEntityName/fdTableName
  • SysXFormDesignController / SysXFormDesignService#voCreate:表单引擎原始创建接口
  • FormDefineType:表单类型枚举(1-6)

mk-sys-modeling(业务建模/低代码应用)

  • SysModelingXFormController#create/saveDesign/saveDesignAndPublish/publish/generateTableName
  • SysModelingXFormService#create:写死 fdEntityId=表单id、fdEntityName=fdRunEntityName='sys-modeling',并按 fdAppId 归属应用
  • SysModelingApplication(表 modeling_application):应用实体,fdAppCode 为 URL 段标识
  • SysModelingDataModelService#buildMainTableName:mk_model_yyyyMMdd + 随机 5 位