flame-plus 1.3.26 → 1.3.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "flame-plus",
3
3
  "type": "module",
4
- "version": "1.3.26",
4
+ "version": "1.3.27",
5
5
  "description": "基于 element-plus 的组件库",
6
6
  "main": "./flameDist/flame-plus.es.js",
7
7
  "module": "./flameDist/flame-plus.es.js",
@@ -1,18 +0,0 @@
1
- {
2
- "permissions": {
3
- "allow": [
4
- "Bash(python3:*)",
5
- "Bash(npm run:*)",
6
- "mcp__ide__getDiagnostics",
7
- "Bash(cat /Users/albert/Workspaces/poba/flame-plus/node_modules/flame-types/lib/*.d.ts 2>/dev/null | head -30)",
8
- "Bash(wc -l /Users/albert/Workspaces/poba/flame-plus/src/packages/components/framework/*.ts /Users/albert/Workspaces/poba/flame-plus/src/packages/components/framework/*.vue 2>/dev/null | head -20)",
9
- "Bash(ls -la /Users/albert/Workspaces/spark/flame-types/src/ 2>/dev/null || find /Users/albert/Workspaces/spark/flame-types -name \"*.ts\" -o -name \"*.d.ts\" | head -30)",
10
- "Bash(cd /Users/albert/Workspaces/spark/flame-types && npm install 2>&1)",
11
- "Bash(npm pack:*)",
12
- "Bash(cd /Users/albert/Workspaces/poba/flame-plus && npm install /Users/albert/Workspaces/spark/flame-types/flame-types-1.1.7.tgz 2>&1)",
13
- "Bash(npm install:*)",
14
- "Bash(npx vue-tsc:*)",
15
- "Bash(grep -l \"framework.vue\" /Users/albert/.claude/plans/*.md 2>/dev/null | head -5)"
16
- ]
17
- }
18
- }
@@ -1,153 +0,0 @@
1
- # 动态表单取值与提交数据详解
2
-
3
- 本文说明 **`flmDynamicForm` 运行时**如何汇总各分组数据、数据在 **`flmGridForm`** 中如何维护,以及 **`componentList` 自定义控件**为何必须遵守 `onChange` / `onInput` 契约才能出现在提交结果中。
4
-
5
- 与外部组件注册、设计器配置形态相关的说明见:[framework-external-components.md](./framework-external-components.md)。
6
-
7
- ---
8
-
9
- ## 1. 总览:数据从哪来
10
-
11
- ```
12
- ┌─────────────────────────────────────────────────────────────────┐
13
- │ flmDynamicForm (config.groups + config.buttons) │
14
- │ ├─ type === 'form' → flmGridForm (每个分组一个实例) │
15
- │ │ └─ 内部 reactive: formModel { [prop]: value } │
16
- │ └─ type === 'subForm' → flm-sub-form + 本组件内 subFormData │
17
- │ │
18
- │ 底部按钮 submit/cancel/reset → getDynamicFormData() → emit │
19
- └─────────────────────────────────────────────────────────────────┘
20
- ```
21
-
22
- **要点**:
23
-
24
- - 动态表单**不会**遍历 DOM 或调用各控件的「取值 API」;**只读 `flmGridForm` 暴露的 `formModel` 与子表单的 `subFormData`**。
25
- - 任一表单项的值要进提交 payload,必须在用户交互时通过 `flmGridForm` 已注入的回调写进 `formModel[prop]`(见第 3 节)。
26
-
27
- ---
28
-
29
- ## 2. 提交时:`getDynamicFormData` 行为
30
-
31
- 源码:`src/packages/components/complex/flmDynamicForm/flmDynamicForm.vue`。
32
-
33
- ### 2.1 `type === 'form'` 分组
34
-
35
- 1. 用分组 **`id`** 在 `formRefs` 里取对应的 **`flmGridForm` 实例**(通过 `ref` 回调挂载)。
36
- 2. 从实例上解析 **`formModel`**(兼容多种 Vue 3 实例形态):
37
-
38
- - `inst.formModel`
39
- - `inst.exposed.formModel`
40
- - `inst.$.exposed.formModel`
41
- - `inst.$.setupState.formModel`
42
-
43
- 3. 若当前 `id` 取不到 `formModel`,会**兜底**:在 `formRefs` 里找**第一个**能 `pickFormModel` 成功的实例(多表单分组时若 `id` 不一致可能出现合并错乱,应保证配置里 **`group.id` 与 ref 使用的 id 一致**)。
44
- 4. 将 `formModel` **深拷贝为普通对象**后 **`Object.assign` 到结果根对象**,即:**同一动态表单里多个 `form` 分组的字段会摊平到同一层**;若不同分组出现相同 `prop` 键,**后遍历的分组会覆盖先遍历的**。
45
-
46
- ### 2.2 `type === 'subForm'` 分组
47
-
48
- - 数据来自 **`subFormData[id]`**(与 `flm-sub-form` 的 `v-model` 同步),在结果中为 **`payload[groupId] = 行数组`**,**不摊平到根对象**。
49
-
50
- ### 2.3 底部按钮与事件参数
51
-
52
- - `submit` / `cancel` / `reset`:`emit(event, getDynamicFormData())`,即上述合并后的 **payload**。
53
- - 其他按钮:`emit('customEvent', { event, data: btn })`,**不带**自动汇总的表单数据;若业务需要当前表单值,需自行 `ref` 动态表单或在外层监听并缓存。
54
-
55
- ---
56
-
57
- ## 3. `flmGridForm` 内:`formModel` 如何写入
58
-
59
- 源码:`src/packages/components/complex/flmGirdForm/flmGridForm.vue`。
60
-
61
- ### 3.1 核心结构
62
-
63
- - `formModel`:`reactive<Record<string, any>>({})`,通过 **`expose({ formModel, reset })`** 暴露给父级。
64
- - 任意字段更新统一走 **`formChange(value, prop)`** → **`formModel[prop] = value`**。
65
-
66
- ### 3.2 与配置的双向同步
67
-
68
- - **`props.config.model`** 变化时:`updateFormModel` 把外部 model 合并进 `formModel`;`updateControl` 把 `formModel` 同步回各 `controlConfig` 的展示值(`modelValue` 或 `model-value`,由 `isInputControl` 决定)。
69
- - **`formModel` 变化时:`watch(formModel, …)` → `updateControl`,保证控件 config 上的当前值与模型一致。
70
-
71
- ### 3.3 表单项 `prop` 即 `formModel` 的键
72
-
73
- 每个 `items[]` 项有 **`prop`(字符串)**。`controlDom` 里注入回调时使用的就是这个 **`prop`**:
74
-
75
- ```ts
76
- ;(config as any)[event] = (value: any) => formChange(value, prop)
77
- ```
78
-
79
- 因此 **提交对象里字段名 = 各表单项的 `prop`**。设计器保存时,`FlameFramework` 通常用控件内部 id 作为 `prop`(如 `ctrl_xxx_时间戳`),业务若需要固定业务字段名,需在**加载配置后映射**或改设计器生成规则(见外部组件指南第 5 节)。
80
-
81
- ---
82
-
83
- ## 4. 控件渲染分支与「写回」方式
84
-
85
- `controlDom` 对不同类型处理不同,**只有写回路径接上了 `formChange`,值才会进 `formModel`**。
86
-
87
- | 类型 / 分支 | 写回方式 |
88
- |-------------|----------|
89
- | **`flmInput`** | `config.onInput` → `formChange(value, prop)`(注意不是 `onChange`) |
90
- | **`flmSearchSelect`** | 单独渲染,使用 `onUpdate` 等,内部再 `formChange` |
91
- | **`flmDatePicker`** | `onUpdate:modelValue` → `formChange` |
92
- | **`flmImage` / 上传** | `onUpload` 等 → `formChange` |
93
- | **`FlmEditor`** | `onUpdate` → `formChange` |
94
- | **其余(含 `resolveComponent` 自定义)** | 拷贝后的 `config` 上挂 **`onChange`**(非 flmInput)→ `formChange`;渲染 **`<DynamicComp config={config} />`** |
95
-
96
- **自定义组件(`componentList` 拖入、保存为某 `controlType`)**落在最后一行:**必须**在用户改值时调用传入的 **`config.onChange(newValue)`**。若错误地只使用 `emit('update:modelValue')` 而父级未绑定,**不会**更新 `formModel`。
97
-
98
- 若某自定义控件**错误地把 `controlType` 设成 `flmInput`**,则框架会注入 **`onInput`**;此时组件内应调用 **`config.onInput`**,而不是 `onChange`。
99
-
100
- ---
101
-
102
- ## 5. 单独使用 `flmGridForm` 时的取值
103
-
104
- 未包在 `flmDynamicForm` 里时:
105
-
106
- - **提交类按钮**:`flmGridForm` 内在校验通过后 **`emit('submit', formModel)`**(引用同一 reactive 对象)。
107
- - **程序化读取**:父组件通过 **`ref` + `expose`** 拿到子组件实例,读取 **`formModel`**(与 `flmDynamicForm` 的 `pickFormModel` 目标一致)。
108
-
109
- `flmDynamicForm` 正是依赖子级 **`expose` 的 `formModel`** 做聚合,而不是依赖 `submit` 事件参数。
110
-
111
- ---
112
-
113
- ## 6. 常见问题与排障
114
-
115
- ### 6.1 基础控件有值,自定义控件提交没有
116
-
117
- 1. 确认运行时 **`resolveComponent(controlType)`** 能解析到正确组件(全局注册名与 JSON 中 `controlType` 一致)。
118
- 2. 在自定义组件内确认用户操作后是否调用了 **`config.onChange`**(或 **`config.onInput`** 当类型为 `flmInput`)。
119
- 3. 确认没有把 `formModel` 当作只读:应通过回调写回,而不是仅本地 `ref`。
120
-
121
- ### 6.2 多个 `form` 分组字段「串了」或只有一部分有数据
122
-
123
- - 检查每个 **`form` 分组的 `id`** 是否与 `flmDynamicForm` 里 `setFormRef(id, el)` 使用的 id 一致。
124
- - 若 `id` 缺失或重复,`pickFormModel` 可能走到兜底逻辑,导致读到错误或单个实例。
125
-
126
- ### 6.3 `prop` 为设计器 id,与后端字段不一致
127
-
128
- - 属**配置形态**问题:在加载 JSON 后批量改写 `items[].prop`,或在保存/加载管线做映射;不在 `getDynamicFormData` 内自动转换。
129
-
130
- ### 6.4 `subForm` 有数据,`form` 没有
131
-
132
- - `subForm` 走 **`subFormData`**;`form` 走 **`flmGridForm.formModel`**。两套机制独立,勿混用。
133
-
134
- ---
135
-
136
- ## 7. 相关源码索引
137
-
138
- | 主题 | 路径 |
139
- |------|------|
140
- | 汇总提交、`formRefs`、`pickFormModel`、`getDynamicFormData` | `src/packages/components/complex/flmDynamicForm/flmDynamicForm.vue` |
141
- | `formModel`、`formChange`、`controlDom`、各特殊分支、`expose` | `src/packages/components/complex/flmGirdForm/flmGridForm.vue` |
142
- | 保存时 `items[].prop` / `controlType` / `controlConfig` | `src/packages/components/framework/framework.vue` → `buildFrameworkConfig` |
143
- | 自定义控件契约(注册与设计器) | [framework-external-components.md](./framework-external-components.md) |
144
-
145
- ---
146
-
147
- ## 8. 自检清单(取值相关)
148
-
149
- - [ ] 自定义控件在变更时调用 **`config.onChange`**(或 **`config.onInput`** 当 `controlType === 'flmInput'`)
150
- - [ ] 从 **`config.modelValue` / `config['model-value']`** 读取当前值并与展示同步
151
- - [ ] 多 **`form` 分组**时 **`group.id` 唯一且稳定**,与 ref 键一致
152
- - [ ] 需要固定业务字段名时,已对 **`items[].prop`** 或 payload 做映射
153
- - [ ] **`subForm`** 数据在 payload 中为 **`payload[groupId]`** 数组,而非根级扁平字段
@@ -1,182 +0,0 @@
1
- # FlameFramework 外部组件接入指南
2
-
3
- 本文说明**外部系统**如何开发(或封装)Vue 组件,并通过 `componentList` 传入 `FlameFramework`(表单设计器),使设计结果在 **`flmDynamicForm` / `flmGridForm` 运行时**中正确渲染。
4
-
5
- **运行时如何汇总表单值、为何自定义控件必须 `onChange`/`onInput`:** 见 [framework-dynamic-form-value-retrieval.md](./framework-dynamic-form-value-retrieval.md)。
6
-
7
- ---
8
-
9
- ## 1. 整体流程
10
-
11
- 1. 在宿主应用中**全局注册**组件,**注册名**必须与表单项里的 `controlType` 字符串**完全一致**(例如 `flmSysUserSelect`)。
12
- 2. 通过 `app.use(FlamePlus, …, [YourComponent])` 或 `app.component(name, YourComponent)` 完成注册(见第 3 节)。
13
- 3. 将预设条目数组传给 `<FlameFramework :component-list="…" />`。
14
- 4. 设计器保存后,配置中的 `items[].controlType` 即为上述名称;运行时由 `flmGridForm` 使用 `resolveComponent(controlType)` 解析。
15
-
16
- ---
17
-
18
- ## 2. `componentList` 数据结构
19
-
20
- `FlameFramework` 接收可选 prop:`componentList`。每一项在设计器左栏「**组件库**」中显示为可拖拽预设,拖入表单后会与内置默认配置合并。
21
-
22
- | 字段 | 类型 | 必填 | 说明 |
23
- |------|------|------|------|
24
- | `key` | `string` | 是 | 列表唯一标识(如 `sys-user-select`),便于外部列表渲染与去重 |
25
- | `label` | `string` | 是 | 左栏显示名称(如「人员单选」) |
26
- | `type` | `string` | 是 | **控件类型名**,写入保存结果的 `controlType`,须与全局注册的组件名一致 |
27
- | `icon` | `string` | 否 | 左栏图标,默认 `🧩` |
28
- | `defaultConfig` | `Record<string, unknown>` | 否 | 与 `getDefaultConfig(type)` 结果**浅合并**,覆盖默认 `controlConfig` |
29
- | `defaultFormItem` | 对象 | 否 | 覆盖默认表单项元数据(如 `label`、`required`、`rules`) |
30
-
31
- `defaultFormItem` 常用字段与 `FormItemSettings` 对齐,例如:`label`、`required`、`rules`、`labelWidth` 等。
32
-
33
- **注意**:源码中 `FrameworkComponentItem.type` 目前标注为 `BasicControlType`(仅内置基础控件)。传入 **`flmSearchSelect`、自定义 `flmXxx`** 等在运行时通常仍可用,但 TypeScript 可能需要 `as any` 或本地扩展类型声明,直至类型定义放宽。
34
-
35
- ---
36
-
37
- ## 3. 在宿主应用中注册组件
38
-
39
- ### 3.1 使用 Flame-Plus `install` 第三参数合并
40
-
41
- `install` 会将自定义组件与内置表合并后统一 `app.component(name, component)`:
42
-
43
- ```ts
44
- import { createApp } from 'vue'
45
- import FlamePlus from 'flame-plus'
46
- import FlmSysUserSelect from './components/FlmSysUserSelect.vue'
47
-
48
- const app = createApp(App)
49
- app.use(FlamePlus, undefined, { FlmSysUserSelect }) // 键名即全局组件名
50
- ```
51
-
52
- 对象形式下,**对象的 key** 为组件名(须与 `componentList[].type`、`controlType` 一致)。
53
-
54
- ### 3.2 单独注册
55
-
56
- ```ts
57
- app.component('FlmSysUserSelect', FlmSysUserSelect)
58
- ```
59
-
60
- 未全局注册的 `controlType` 在运行时会解析失败或无法渲染。
61
-
62
- ---
63
-
64
- ## 4. 传入 `FlameFramework`
65
-
66
- ```vue
67
- <template>
68
- <FlameFramework
69
- :component-list="systemComponentPresets"
70
- @save="onSave"
71
- />
72
- </template>
73
-
74
- <script setup lang="ts">
75
- const systemComponentPresets = [
76
- {
77
- key: 'sys-user-single',
78
- label: '人员(弹窗单选)',
79
- icon: '👤',
80
- type: 'flmSearchSelect',
81
- defaultConfig: {
82
- service_name: 'your-service',
83
- fk_table_name: 'your_person_table',
84
- placeholder: '请选择人员'
85
- },
86
- defaultFormItem: { label: '人员' }
87
- },
88
- {
89
- key: 'sys-dept-tree',
90
- label: '部门(下拉)',
91
- type: 'flmSelect',
92
- defaultConfig: {
93
- placeholder: '请选择部门',
94
- options: [],
95
- clearable: true
96
- },
97
- defaultFormItem: { label: '部门' }
98
- }
99
- ]
100
- </script>
101
- ```
102
-
103
- `defaultConfig` 的内容即落库后的 `controlConfig` 初始值(设计器内还可继续改)。
104
-
105
- ---
106
-
107
- ## 5. 保存结果中的表单项形态
108
-
109
- 设计器 `save` 事件抛出配置,其中表单分组下的 `items` 与 `FormConfig['items']` 一致。每个控件对应一项,核心字段包括:
110
-
111
- - `prop`:设计器生成的控件 id(字符串),**业务侧若需固定字段名,应在加载配置后做映射或在二次开发设计器中改生成规则**。
112
- - `controlType`:与 `componentList[].type` / 全局组件名一致。
113
- - `controlConfig`:控件配置快照(含 `model-value` 等)。
114
- - `gridX` / `gridY` / `gridW` / `gridH`:栅格布局(默认 24 列)。
115
-
116
- 运行时由 `flmDynamicForm` → `flmGridForm` 根据 `controlType` 渲染。
117
-
118
- ---
119
-
120
- ## 6. 自定义控件与 `flmGridForm` 的契约
121
-
122
- `flmGridForm` 对**大部分**控件采用统一方式(非 `flmInput`、`flmSearchSelect`、`flmDatePicker`、`flmImage`、`FlmEditor` 等特殊分支):
123
-
124
- - 渲染为:`<YourComponent config={mergedConfig} />`
125
- - `mergedConfig` 在原有 `controlConfig` 基础上会注入:
126
- - `modelValue`:来自 `controlConfig['model-value']`
127
- - **`onChange`**:表单写入用(`flmInput` 类型使用 **`onInput`** 而非 `onChange`)
128
-
129
- 因此,**推荐**自定义控件与内置 `flmSelect` 等保持一致:
130
-
131
- 1. **Props**:声明 `config: Object`(或具体类型)。
132
- 2. **取值**:从 `config['model-value']` 或 `config.modelValue` 读取当前值(以你方 `filterConfig` 习惯为准)。
133
- 3. **改值**:用户操作后调用 **`config.onChange?.(newValue)`**(若为输入类且走 `flmInput` 分支则调用 **`config.onInput`**——自定义类型一般不会走 `flmInput` 分支,用 `onChange` 即可)。
134
-
135
- 若控件需要 `isSearch` 等额外 props,需在 `flmGridForm` 增加分支或复用现有特殊类型模式;**默认动态分支不会传递 `isSearch`**。
136
-
137
- ### 6.1 与 `flmSearchSelect` 对齐的特殊协议
138
-
139
- 若 `controlType` 为 `flmSearchSelect`,`flmGridForm` 会单独传 `onUpdate`、合并 `ref` 等,**不能**仅靠通用 `config` 协议实现,需直接使用或封装 `flmSearchSelect`。
140
-
141
- ---
142
-
143
- ## 7. 表单设计器画布(设计态)限制
144
-
145
- 设计区内预览使用 `FormDesignArea` 内置的 `controlComponents` 映射:**仅内置基础控件有真实预览**。未在映射表中的 `type` 会**回退为 `flmInput` 占位**,不影响已保存 JSON 在 **`flmGridForm` 运行态**的解析,但设计时视觉与内置控件不一致。
146
-
147
- 可选改进方向(需改本库):
148
-
149
- - 为 `flmSearchSelect` 等补充映射;或
150
- - 为 `FormDesignArea` 增加可选 prop,合并外部传入的 `type → 组件` 表。
151
-
152
- ---
153
-
154
- ## 8. 与 `flame-types` / `ControlTypes`
155
-
156
- 消费端若使用 TypeScript 维护表单配置,内置枚举见包内 `ControlTypes`。**自定义 `controlType` 不会出现在枚举中**,可:
157
-
158
- - 使用 `string` 扩展类型;或
159
- - 在业务侧声明:`type ControlType = ControlTypes | 'FlmSysUserSelect' | …`。
160
-
161
- ---
162
-
163
- ## 9. 自检清单
164
-
165
- - [ ] 全局注册名与 `componentList[].type`、`items[].controlType` 一致
166
- - [ ] 自定义组件实现 `config` + `onChange`(或对齐 `flmSearchSelect` 等特殊协议)
167
- - [ ] 宿主已安装 `flame-request`、`element-plus` 等依赖(若组件内部使用)
168
- - [ ] 接受设计器画布对非内置 `type` 可能显示为输入框占位
169
- - [ ] 保存后的 `prop` 为设计器 id,业务落库字段映射方案已明确
170
-
171
- ---
172
-
173
- ## 10. 相关源码位置(本仓库)
174
-
175
- | 内容 | 路径 |
176
- |------|------|
177
- | `componentList` prop 与 `componentWidgetList` | `src/packages/components/framework/framework.vue` |
178
- | 保存时 `controlType` / `controlConfig` 生成 | `buildFrameworkConfig`(同文件) |
179
- | 左栏「组件库」 | `src/packages/components/framework/components/WidgetPanel.vue` |
180
- | 设计区控件映射 | `src/packages/components/framework/components/FormDesignArea.vue` |
181
- | 运行时解析与事件注入 | `src/packages/components/complex/flmGirdForm/flmGridForm.vue` |
182
- | 全局注册合并 | `src/packages/index.ts` → `install` |