# lowcode-form-packages **Repository Path**: ionic2/lowcode-form-packages ## Basic Information - **Project Name**: lowcode-form-packages - **Description**: 低代码组件 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-05-19 - **Last Updated**: 2026-09-09 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # Lowcode Form Packages 基于 Vue 3 的低代码动态表单 Monorepo,提供可视化表单设计能力和强大的动态表单渲染引擎。 ## 项目结构 ``` lowcode-form-packages/ ├── packages/ │ └── core/ # @lowcode-form/core - 核心表单设计器与渲染引擎 └── playground/ # 演示与开发调试应用 ``` | 包名 | 说明 | |------|------| | [@lowcode-form/core](./packages/core) | 核心库:表单设计器、动态表单渲染、联动引擎、表达式引擎、远程数据源等 | | playground | 基于核心库的演示应用,支持 Ant Design Vue / Element Plus 适配器切换 | ## 核心特性 - **可视化设计器** -- 拖拽式表单设计界面,实时预览 - **Schema 驱动** -- 基于 JSON Schema 2020-12 的表单定义,数据与 UI 分离 - **丰富组件库** -- 内置文本、数值、选项、日期、控件、布局六大类组件(另有「其他」「辅助」分类组件已在注册表中注释,可按需自行注册启用) - **适配器模式** -- 通过 Adapter 抽象支持多种 UI 组件库(Ant Design Vue / Element Plus) - **表达式引擎** -- 支持 `x-visible`、`x-disabled` 等动态逻辑控制 - **数据联动** -- 增强的联动规则系统,支持 11 种联动动作,包括跨组件数据驱动 - **远程数据源** -- 选项类组件支持通过 API 动态加载数据,内置缓存与防抖 - **撤销/重做** -- 完整的操作历史管理(响应式 canUndo/canRedo 状态,正确恢复最新状态) - **容器组件与嵌套约束** - 统一约束:**所有容器组件均不允许再嵌套其它容器组件**(Grid / Space / Group / SubForm / TableForm / Collapse / Tabs / Card) - 容器内部可放入任意普通字段组件(如 `Input`、`Select` 等);容器之间不可互相嵌套 - 约束在画布拖拽落点(`useCanvasDrag` 的 `ALL_CONTAINERS` + `isNestingForbidden`)与设计态拖放提示(`CanvasField`)中统一生效 - **SubForm**:子表单容器,可拖入子组件(`x-children` 扁平引用),支持标题/描述/边框配置 - **TableForm**:表格表单,支持动态添加列并为每列指定组件类型 - **Group**:竖向动态表单容器,统一管理子字段;预览端在 Tabs 标签页内部或 Collapse 折叠面板内时**自动隐藏标题和「新增竖向动态表单」按钮**(`tabsContext` / `collapseContext` 标记),避免与 Tab 标签、折叠面板头重复 - **Collapse**:折叠面板容器,以分组(Group)为单位管理子字段;数据以 `{ [分组Key]: [{ 字段Key: 值 }] }` 嵌套结构存储,与验证器及 Tabs 布局保持一致,保证折叠面板内必填字段校验正常 - **级联删除** -- 移除布局/子表单组件时,自动递归清除其内部所有子组件,并同步清理 `required` 数组与选中状态 - **设备视图切换** -- 设计器画布与设计端预览均支持 PC 端 / 移动端视图切换,移动端模式约束为 375px 视口 - **拖拽至任意位置放入** -- 从组件面板拖入字段时,支持落到画布任意位置(已有字段之间插入),拖拽过程中以蓝色插入指示线实时预览目标位置 - **拖入组件自动命名** -- 从组件面板拖入的字段默认以组件名称作为字段标题(如「单行文本」「密码」),与 `store.addField` 行为一致,无需手动补名称;**标签页(Tabs)内部**拖入与排序的子组件同样自动补全名称,确保标签名正常显示 --- ## 设计器增强功能 ### 组件库分类 设计器左侧组件面板按业务语义划分为以下分类,支持 `badge` 标记(如 `NEW`)高亮新能力: | 分类 | 组件 | |------|------| | 文本 | 单行文本、多行文本、密码、手机号、邮箱、说明文本 | | 数值 | 数字、滑块、评分 | | 选项 | 单选、多选、下拉选择、开关、级联选择 | | 日期 | 日期、日期区间 | | 控件 | 横向动态表单、子表单、竖向动态表单、附件、手写签名、穿梭框 | | 布局 | 栅格布局、标签页、折叠面板 | > 注:`componentRegistry` 中另注释了 `RichTextEditor`、`Tree`、`TreeSelect`、`TimePicker`、`TimeRangePicker`、`ColorPicker`、`Image`、`Card`、`Space`、`TableLayout`、`Alert`、`Button`、`Title`、`HTML`、`Divider`、`Tag` 等组件,已有对应 `Lf*` 渲染组件但未默认启用,需在 `x-component` 中直接引用或自行 `registerComponent` 后使用。 ### 子表单组件 | 组件 | 能力 | |------|------| | SubForm | 作为布局容器嵌套子组件(内部不可再放入容器组件) | | TableForm | 表格表单,支持动态添加/删除列,为每列指定组件类型并按列渲染实际表单组件(内部不可再放入容器组件) | | Group | 竖向动态表单容器,统一管理一组相关字段(内部不可再放入容器组件) | ### 画布拖拽:任意位置插入 设计器支持将组件从左侧组件面板拖入画布的**任意位置**,而非固定追加到末尾: - **落点计算**:拖拽经过画布时,`handleDragOver` 实时计算指针所处位置,按各顶层字段卡片中线判断应插到哪个字段「之前」或「之后」,得出插入索引 `dropIndex`。 - **插入指示线**:拖拽过程中,目标位置上方/下方的字段卡片会高亮蓝色插入线(`drop-before` / `drop-after`),实时预览最终摆放位置。 - **落点写入**:松开时 `handleCanvasDrop` 按 `dropIndex` 将新字段插入到对应位置;`dropIndex` 越界时自动追加到末尾。 - 嵌套容器(Grid / Group / Tabs / SubForm / 单元格等)的拖入走各自独立的拖放路径(带 `.stop` 拦截),不受顶层任意位置插入影响,约束互不干扰。 相关源码见 [useCanvasDrag.ts](./packages/core/src/designer/composables/useCanvasDrag.ts)(`dropIndex` / `handleDragOver` / `handleCanvasDrop`)。 ### 容器嵌套约束 设计器**禁止任何容器组件嵌套其它容器组件**,避免出现多层嵌套导致的渲染与数据管理混乱。约束在画布拖拽落点(`useCanvasDrag`)与设计态拖放提示(`CanvasField`)中统一生效。 **容器组件清单** | 容器组件 | |----------| | Grid / Space / Group / SubForm / TableForm / Collapse / Tabs / Card | **嵌套规则** | 父容器 | 可放入 | 禁止放入 | |--------|--------|----------| | 任意容器(Grid / Space / Group / SubForm / TableForm / Collapse / Tabs / Card) | 普通组件(非容器) | 任何容器组件(含 TableForm / Collapse / Tabs / Card 等) | | 顶层画布 | 任意组件 | — | > 说明:所有容器组件的内部均不可再放入容器组件;容器之间不可互相嵌套。 ### 级联删除 移除布局组件或子表单组件时,设计器会自动递归清除其内部所有子组件: - 沿 `x-children` 与 `x-props.cells.children` 引用链 BFS 遍历 - 支持容器 → 普通组件(如 Collapse → Input),删除父容器时其子组件一并清除 - 同步清理父级 `required` 数组与 `selectedFieldKey` 选中状态 支持的容器类型:Grid / Tabs / Card / Space / Collapse / Group / SubForm / TableForm / TableLayout。 ### 设备视图切换 | 切换位置 | 模式 | 说明 | |---------|------|------| | 设计器工具栏 | `viewMode: 'desktop' \| 'mobile'` | 切换画布视口宽度,移动端模式约束为 375px | | 设计端预览卡片头部 | `previewDeviceMode: 'pc' \| 'mobile'` | 切换预览视口宽度,移动端模式模拟手机外壳样式 | 更多详细使用文档请参阅 [核心库 README](./packages/core/README.md)。 --- ## 数据联动 (Data Linkage) 数据联动是核心库的一项关键能力:当某个字段自身的值满足预设条件时,动态驱动目标字段的显示、隐藏、启用、禁用、选项数据、校验规则甚至远程数据重载。 > **本版本说明**:`LinkageEngine` 运行时能力完整保留;设计器属性面板的「组件联动」可视化配置入口已注释关闭(见下文「设计器配置支持」)。 ### 联动引擎 (LinkageEngine) 联动引擎在 [FormCore](./packages/core/src/core/FormCore.ts) 中初始化,当任意字段值变更时自动触发。核心机制: - **规则归属**:每条联动规则定义在**触发字段**自身的 `x-props.linkageRules` 上;当该字段值发生变化时,比较其当前值与规则的 `condition.field`(选项值),满足条件即对 `targetField` 执行动作 - **多层级递归传播**:联动结果可能进一步触发其他联动规则,引擎自动递归处理,最大深度限制为 10 层 - **循环依赖防护**:每次执行使用 `executionStack` 追踪已执行的规则链,防止无限循环 - **模板变量解析**:payload / value 支持 `${fieldKey}` 模板语法,引擎自动替换为对应字段的当前值 - **变更通知**:联动完成后通过回调机制通知 UI 层刷新渲染 相关源码见 [LinkageEngine.ts](./packages/core/src/core/LinkageEngine.ts). ### 支持的联动动作 | 分类 | 动作 | 类型 | 说明 | |------|------|------|------| | 基础控制 | `show` | string | 显示目标组件 | | 基础控制 | `hide` | string | 隐藏目标组件 | | 基础控制 | `enable` | string | 启用目标组件 | | 基础控制 | `disable` | string | 禁用目标组件 | | 基础控制 | `set_value` | string | 设置目标组件的值 | | 数据联动 | `set_options` | string | 动态替换目标组件选项数据(适用于 Select / RadioGroup / CheckboxGroup) | | 数据联动 | `set_tree_data` | string | 动态替换目标组件树形数据(适用于 Cascader / TreeSelect / Tree) | | 数据联动 | `set_transfer_data` | string | 动态替换目标组件穿梭框数据(适用于 Transfer) | | 数据联动 | `refresh_remote` | string | 触发目标组件重新请求远程数据,支持通过模板引用其他字段值作为查询参数 | | 数据联动 | `set_validation` | string | 动态设置目标组件的校验规则(required / min / max 等) | | 数据联动 | `set_props` | string | 动态设置目标组件的任意属性(placeholder / allowClear 等) | ### 支持的触发条件 | 运算符 | 说明 | |--------|------| | `equals` | 值等于 | | `not_equals` | 值不等于 | | `contains` | 包含(支持字符串和数组) | | `greater_than` | 大于 | | `less_than` | 小于 | | `is_empty` | 值为空 | | `is_not_empty` | 值不为空 | ### 联动规则数据结构 每条联动规则定义在**触发字段**的 `x-props.linkageRules` 数组中,当该字段自身的值满足 `condition` 时,对 `targetField` 执行 `action`: ```typescript interface LinkageRule { id: string // 规则唯一标识 targetField: string // 目标字段(被影响的字段 key) action: string // 执行动作(见上方表格) condition: { field: string // 触发条件:当前字段的选项值(与当前字段实际值比较) operator: string // 条件运算符 value: string // 兼容字段(当前版本比较使用 field 作为选项值) } enabled: boolean // 是否启用 value?: any // 联动值(set_value 的目标值或数据联动项的值) payload?: any // 数据联动补充数据(选项数组 / 查询参数 / 校验规则 / 属性对象) } ``` ### 使用示例 #### 1. 省市区三级联动 (set_options) 当省份字段值等于某个选项值时,动态替换城市选项;城市字段值等于某个选项值时,动态替换区县选项。规则定义在**触发字段**(province / city)上: ```json { "province": { "type": "string", "title": "省份", "x-component": "Select", "x-props": { "options": [ { "value": "bj", "label": "北京" }, { "value": "gd", "label": "广东" } ], "linkageRules": [ { "id": "city_options_bj", "targetField": "city", "action": "set_options", "condition": { "field": "bj", "operator": "equals", "value": "" }, "enabled": true, "payload": [ { "value": "dc", "label": "东城" }, { "value": "cy", "label": "朝阳" } ] }, { "id": "city_options_gd", "targetField": "city", "action": "set_options", "condition": { "field": "gd", "operator": "equals", "value": "" }, "enabled": true, "payload": [ { "value": "gz", "label": "广州" }, { "value": "sz", "label": "深圳" } ] } ] } }, "city": { "type": "string", "title": "城市", "x-component": "Select", "x-props": { "linkageRules": [ { "id": "district_options_dc", "targetField": "district", "action": "set_options", "condition": { "field": "dc", "operator": "equals", "value": "" }, "enabled": true, "payload": [ { "value": "dch", "label": "东华门" }, { "value": "jgs", "label": "景山" } ] }, { "id": "district_options_gz", "targetField": "district", "action": "set_options", "condition": { "field": "gz", "operator": "equals", "value": "" }, "enabled": true, "payload": [ { "value": "th", "label": "天河" }, { "value": "yx", "label": "越秀" } ] } ] } }, "district": { "type": "string", "title": "区县", "x-component": "Select" } } ``` #### 2. 远程数据刷新 (refresh_remote + 模板引用) 当公司字段值非空时,重新请求部门列表,将公司 ID 作为查询参数传入。规则定义在**触发字段**(company)上: ```json { "company": { "type": "string", "title": "公司", "x-component": "Select", "x-props": { "options": [ { "value": "c001", "label": "A 公司" }, { "value": "c002", "label": "B 公司" } ], "linkageRules": [ { "id": "dept_refresh", "targetField": "department", "action": "refresh_remote", "condition": { "field": "", "operator": "is_not_empty", "value": "" }, "enabled": true, "payload": { "companyId": "${company}" } } ] } }, "department": { "type": "string", "title": "部门", "x-component": "Select", "x-remote": { "url": "/api/departments", "method": "GET", "dataField": "data" }, "x-props": { "placeholder": "请先选择公司" } } } ``` #### 3. 动态校验 (set_validation) 当"是否加急"开关打开时,为"期望交付日期"字段添加必填校验。规则定义在**触发字段**(isUrgent)上;布尔字段的值按字符串比较(true → `"true"`): ```json { "isUrgent": { "type": "boolean", "title": "是否加急", "x-component": "Switch", "x-props": { "linkageRules": [ { "id": "date_required", "targetField": "expectDate", "action": "set_validation", "condition": { "field": "true", "operator": "equals", "value": "" }, "enabled": true, "payload": { "required": true } } ] } }, "expectDate": { "type": "string", "title": "期望交付日期", "x-component": "DatePicker" } } ``` #### 4. 动态设置组件属性 (set_props) 根据类型切换,动态修改输入框的 placeholder 和输入类型。规则定义在**触发字段**(contentType)上: ```json { "contentType": { "type": "string", "title": "内容类型", "x-component": "Select", "x-props": { "options": [ { "value": "email", "label": "邮箱" }, { "value": "phone", "label": "手机号" } ], "linkageRules": [ { "id": "content_props_email", "targetField": "content", "action": "set_props", "condition": { "field": "email", "operator": "equals", "value": "" }, "enabled": true, "payload": { "placeholder": "请输入邮箱地址", "type": "email" } }, { "id": "content_props_phone", "targetField": "content", "action": "set_props", "condition": { "field": "phone", "operator": "equals", "value": "" }, "enabled": true, "payload": { "placeholder": "请输入手机号码", "maxlength": 11 } } ] } }, "content": { "type": "string", "title": "内容", "x-component": "Input" } } ``` #### 5. 条件显示 (show + hide) 当选择"其他"时显示补充说明输入框,否则隐藏。规则定义在**触发字段**(reason)上: ```json { "reason": { "type": "string", "title": "申请原因", "x-component": "Select", "x-props": { "options": [ { "value": "business", "label": "商务需求" }, { "value": "personal", "label": "个人原因" }, { "value": "other", "label": "其他" } ], "linkageRules": [ { "id": "show_remark", "targetField": "remark", "action": "show", "condition": { "field": "other", "operator": "equals", "value": "" }, "enabled": true }, { "id": "hide_remark", "targetField": "remark", "action": "hide", "condition": { "field": "other", "operator": "not_equals", "value": "" }, "enabled": true } ] } }, "remark": { "type": "string", "title": "补充说明", "x-component": "Input" } } ``` ### 设计器配置支持 > **本版本说明**:设计器属性面板的「组件联动」配置入口(`RadioGroup` / `CheckboxGroup` / `Select` / `Switch` / `Cascader` 模板中的 `LinkageConfig`)已在本版本注释关闭,`LinkageEngine` 运行时能力与以下数据结构/示例仍有效。待后续版本重新开放面板入口即可恢复可视化配置。 设计器 (FormDesigner) 属性面板 (PropertyPanel) 曾支持通过 **联动配置** 可视化添加和管理联动规则: - 下拉选择动作时按 **基础控制** / **数据联动** 两个竖向动态表单展示 - 选择数据联动类动作后,自动显示 JSON 编辑器输入区域,支持 `${fieldKey}` 模板语法 - 每条规则实时预览触发条件与执行动作的摘要 - 规则可随时启用/禁用/删除 相关源码见 [LinkageConfig.vue](./packages/core/src/designer/LinkageConfig.vue). ### 响应式架构 联动功能的响应式数据流如下: ``` 用户修改字段值 -> DynamicForm.handleFieldUpdate() -> FormCore.updateFieldValue() -> LinkageEngine.executeLinkageRules() -> 递归执行匹配的联动规则 -> updateField() / updateFieldProps() 修改 schema -> FormCore.subscribeToChanges() 触发回调 -> schemaVersion++ (Vue ref) -> fields computed 重新求值 -> FormField 组件重渲染 -> 联动选项变化 -> watch -> 自动清除无效值 -> _linkageRefresh 变化 -> watch -> reload() 重载远程数据 ``` ### 值有效性自动校验 当联动规则通过 `set_options` / `set_tree_data` 动态替换目标组件的选项数据后,FormField 自动检测当前值是否仍在有效选项中。若当前选中的选项已被联动移除,自动清空该字段的值,避免提交无效数据。 相关源码见 [FormField.vue](./packages/core/src/renderer/FormField.vue#L182-L223)(`linkageDrivenOptions` 计算属性 + 选项 watch,自动清空被联动移除的无效值). --- ## 快速开始 ### 前置条件 - Node.js >= 18 - pnpm >= 8 ### 安装与运行 ```bash # 克隆项目 git clone cd lowcode-form-packages # 安装依赖 pnpm install # 启动开发服务器 (playground) pnpm dev # 构建核心库 pnpm build ``` ### 基本使用 ```vue ``` 更多详细使用文档请参阅 [核心库 README](./packages/core/README.md)。 --- ## 技术栈 | 技术 | 说明 | |------|------| | Vue 3.4+ | 前端框架 | | TypeScript 5.7 | 类型系统 | | Vite 5 | 构建工具 | | Pinia | 状态管理 | | AJV 8 | JSON Schema 校验 | | Tiptap | 富文本编辑器 | | pnpm | 包管理器 (Monorepo) | ## 开发指南 ### 项目结构详情 ``` lowcode-form-packages/ ├── packages/ │ └── core/ │ ├── src/ │ │ ├── adapters/ # 适配器抽象(BaseAdapter / createAdapter) │ │ ├── components/ # 内置组件(input / selection / display / subform) │ │ ├── composables/ # Vue 组合函数(useRemoteData) │ │ ├── core/ # 核心引擎 │ │ │ ├── FormCore.ts # 表单核心:数据管理 / 验证 / 联动协调 │ │ │ ├── LinkageEngine.ts # 联动引擎:规则匹配 / 递归传播 / 模板解析 │ │ │ ├── ExpressionEngine.ts # 表达式引擎 │ │ │ ├── SafeExpressionEvaluator.ts # 安全表达式求值 │ │ │ ├── HistoryManager.ts # 撤销/重做 │ │ │ └── SchemaValidator.ts # Schema 校验 │ │ ├── designer/ # 设计器组件 │ │ │ ├── FormDesigner.vue # 设计器主入口(含 PC/移动端视图切换) │ │ │ ├── Canvas.vue # 画布 │ │ │ ├── CanvasField.vue # 画布字段卡片(拖放提示 / 嵌套约束) │ │ │ ├── CanvasRenderer.vue # 画布渲染 │ │ │ ├── ComponentPanel.vue # 组件面板 │ │ │ ├── PropertyPanel.vue # 属性面板(含联动配置、TableForm 列管理、SubForm 配置) │ │ │ ├── LinkageConfig.vue # 联动规则配置 UI │ │ │ ├── LogicConfig.vue # 逻辑配置 │ │ │ ├── RemoteDataConfig.vue # 远程数据配置 │ │ │ ├── TreeNodeEditor.vue # 级联 / Tree 选项树编辑器 │ │ │ ├── componentRegistry.ts # 组件注册表 │ │ │ ├── iconRegistry.ts # 图标注册表 │ │ │ ├── components/ # 设计器专属组件(CollapseDesigner / TabsDesigner / GroupDesigner / LayoutDesigner / FieldTitleInput) │ │ │ ├── composables/ # 设计器组合函数 │ │ │ │ ├── useFieldOperations.ts # 字段操作(含级联删除) │ │ │ │ ├── useComponentTypeDetection.ts # 组件类型检测 │ │ │ │ ├── useCanvasDrag.ts # 画布拖拽 │ │ │ │ ├── useChildDrag.ts # 容器子字段拖拽排序 │ │ │ │ ├── useCollapseDesigner.ts # 折叠面板设计 │ │ │ │ ├── useTabsDesigner.ts # 标签页设计 │ │ │ │ ├── useComponentHierarchy.ts # 组件层级分析 │ │ │ │ ├── useImageUpload.ts # 图片上传 │ │ │ │ ├── useOptionsEditor.ts # 选项编辑 │ │ │ │ ├── usePropertyEditor.ts # 属性编辑 │ │ │ │ └── useTableLayout.ts # 布局白名单 │ │ │ └── stores/ # Pinia Store(designerStore) │ │ ├── renderer/ # 渲染器 │ │ │ ├── DynamicForm.vue # 动态表单渲染 │ │ │ ├── FormField.vue # 字段渲染(联动刷新 / 值有效性检查) │ │ │ ├── collapseContext.ts # Collapse 上下文标记(Collapse 内隐藏 Group 标题与新增按钮) │ │ │ ├── tabsContext.ts # Tabs 上下文标记(Tabs 内隐藏 Group 新增按钮) │ │ │ ├── tableRowContext.ts # 表格行上下文 │ │ │ ├── formActions.ts # 表单操作上下文(FormActions / FORM_ACTIONS_KEY) │ │ │ ├── TableRowProvider.vue # 表格行 Provider │ │ │ ├── index.ts # 渲染器导出 │ │ │ └── layout/ # 布局组件 │ │ │ ├── CardLayout.vue │ │ │ ├── CollapseLayout.vue │ │ │ ├── GridLayout.vue │ │ │ ├── GroupLayout.vue │ │ │ ├── SpaceLayout.vue │ │ │ ├── SubFormLayout.vue │ │ │ ├── TableLayout.vue │ │ │ └── TabsLayout.vue │ │ ├── schema-generator/ # Schema 生成器(SchemaGeneratorCore / SchemaGenerator / DataGenerator / DataMerger / ComponentTypeRegistry) │ │ ├── types/ # TypeScript 类型定义 │ │ ├── utils/ # 工具函数(deepClone / fieldNaming / collapseSchema / tabsSchema / jsonSchemaPure) │ │ └── styles/ # 样式(CSS 变量 / 设计 Token) │ └── package.json ├── playground/ │ ├── public/ # 静态资源(form-schema.json 示例) │ ├── src/ │ │ ├── adapters/ # Ant Design Vue / Element Plus 适配器示例 │ │ ├── mock/ # 后端 Mock(FormDefinition / 远程数据源) │ │ └── App.vue # 含设计器 / 设计端预览(PC/移动端切换)/ 使用端预览 │ └── package.json ├── package.json └── pnpm-workspace.yaml ``` ### 常用命令 ```bash # 安装依赖 pnpm install # 启动 playground 开发服务器 pnpm dev # 构建核心库 pnpm build # 构建 playground pnpm build:playground ``` ## 许可证 MIT License