@longhongguo/form-create-ant-design-vue 3.2.51 → 3.2.53

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@longhongguo/form-create-ant-design-vue",
3
- "version": "3.2.51",
3
+ "version": "3.2.53",
4
4
  "description": "AntDesignVue版本低代码表单|FormCreate 是一个可以通过 JSON 生成具有动态渲染、数据收集、验证和提交功能的低代码表单生成组件。支持6个UI框架,适配移动端,并且支持生成任何 Vue 组件。内置20种常用表单组件和自定义组件,再复杂的表单都可以轻松搞定。",
5
5
  "main": "./dist/form-create.min.js",
6
6
  "module": "./dist/form-create.esm.js",
@@ -198,6 +198,11 @@ export default defineComponent({
198
198
  type: Array,
199
199
  default: () => []
200
200
  },
201
+ // 源选项列表
202
+ sourceItems: {
203
+ type: Array,
204
+ default: () => []
205
+ },
201
206
  // 是否多选
202
207
  multiple: {
203
208
  type: Boolean,
@@ -244,7 +249,7 @@ export default defineComponent({
244
249
  default: true
245
250
  }
246
251
  },
247
- emits: ['update:modelValue', 'change'],
252
+ emits: ['update:modelValue', 'update:sourceItems', 'change'],
248
253
  computed: {
249
254
  // 统一处理值(支持 v-model),确保始终是对象数组格式
250
255
  currentValue: {
@@ -424,6 +429,12 @@ export default defineComponent({
424
429
  }
425
430
  return item !== itemValue
426
431
  })
432
+ const sourceItems = this.sourceItems.filter((item) => {
433
+ return !newValue.some((newItem) => {
434
+ return newItem[this.valueKey] === item[this.valueKey]
435
+ })
436
+ })
437
+ this.$emit('update:sourceItems', sourceItems)
427
438
  this.currentValue = newValue
428
439
  }
429
440
  },
@@ -433,6 +444,12 @@ export default defineComponent({
433
444
  event.stopPropagation()
434
445
  // 统一设置为空数组
435
446
  this.currentValue = []
447
+ const sourceItems = this.sourceItems.filter((item) => {
448
+ return !this.currentValue.some((newItem) => {
449
+ return newItem[this.valueKey] === item[this.valueKey]
450
+ })
451
+ })
452
+ this.$emit('update:sourceItems', sourceItems)
436
453
  }
437
454
  }
438
455
  })
@@ -3,6 +3,7 @@
3
3
  <CusSelect
4
4
  :model-value="modelValue"
5
5
  :options="mergedOptions"
6
+ v-model:source-items="sourceItems"
6
7
  :multiple="multiple"
7
8
  :max-tag-count="maxTagCount"
8
9
  :placeholder="placeholder"
@@ -113,7 +114,8 @@ export default defineComponent({
113
114
  // 存储待处理的回调函数
114
115
  pendingCallbacks: {},
115
116
  // 内部维护的选项列表(合并父窗口返回的源对象)
116
- internalOptions: []
117
+ internalOptions: [],
118
+ sourceItems: []
117
119
  }
118
120
  },
119
121
  computed: {
@@ -279,6 +281,7 @@ export default defineComponent({
279
281
  this.mergeOptions(sourceItems)
280
282
  }
281
283
 
284
+ this.sourceItems = sourceItems
282
285
  this.handleUpdate(value)
283
286
  this.handleChange(value)
284
287
  }
@@ -344,10 +347,10 @@ export default defineComponent({
344
347
  this.$emit(
345
348
  'change',
346
349
  value,
347
- this.internalOptions && this.internalOptions.length > 0
350
+ this.sourceItems && this.sourceItems.length > 0
348
351
  ? this.multiple
349
- ? this.internalOptions
350
- : this.internalOptions[0]
352
+ ? this.sourceItems
353
+ : this.sourceItems[0]
351
354
  : this.multiple
352
355
  ? []
353
356
  : null
@@ -3,6 +3,7 @@
3
3
  <CusSelect
4
4
  :model-value="modelValue"
5
5
  :options="mergedOptions"
6
+ v-model:source-items="sourceItems"
6
7
  :multiple="multiple"
7
8
  :max-tag-count="maxTagCount"
8
9
  :placeholder="placeholder"
@@ -116,7 +117,8 @@ export default defineComponent({
116
117
  // 存储待处理的回调函数
117
118
  pendingCallbacks: {},
118
119
  // 内部维护的选项列表(合并父窗口返回的源对象)
119
- internalOptions: []
120
+ internalOptions: [],
121
+ sourceItems: []
120
122
  }
121
123
  },
122
124
  computed: {
@@ -284,6 +286,8 @@ export default defineComponent({
284
286
  this.mergeOptions(sourceItems)
285
287
  }
286
288
 
289
+ this.sourceItems = sourceItems
290
+
287
291
  this.handleUpdate(value)
288
292
  this.handleChange(value)
289
293
  }
@@ -349,10 +353,10 @@ export default defineComponent({
349
353
  this.$emit(
350
354
  'change',
351
355
  value,
352
- this.internalOptions && this.internalOptions.length > 0
356
+ this.sourceItems && this.sourceItems.length > 0
353
357
  ? this.multiple
354
- ? this.internalOptions
355
- : this.internalOptions[0]
358
+ ? this.sourceItems
359
+ : this.sourceItems[0]
356
360
  : this.multiple
357
361
  ? []
358
362
  : null
package/src/core/maker.js CHANGED
@@ -94,6 +94,10 @@ function useCusUserSelect(maker) {
94
94
  maker.userSelect = maker.cusUserSelect
95
95
  }
96
96
 
97
+ function useText(maker) {
98
+ maker.text = creatorFactory('text')
99
+ }
100
+
97
101
  useAlias(maker)
98
102
  useSlider(maker)
99
103
  useFrame(maker)
@@ -101,5 +105,6 @@ useUpload(maker)
101
105
  useSelect(maker)
102
106
  useCusStoreSelect(maker)
103
107
  useCusUserSelect(maker)
108
+ useText(maker)
104
109
 
105
110
  export default maker
@@ -4,6 +4,7 @@ import select from './select'
4
4
  import cascader from './cascader'
5
5
  import datePicker from './datePicker'
6
6
  import hidden from './hidden'
7
+ import text from './text' // text parser 必须在 input 之前注册,避免被 input 的 maker.text 覆盖
7
8
  import input from './input'
8
9
  import timePicker from './timePicker'
9
10
  import tree from './tree'
@@ -18,6 +19,7 @@ export default [
18
19
  datePicker,
19
20
  rangePicker,
20
21
  hidden,
22
+ text, // text parser 必须在 input 之前,确保 type: 'text' 时优先匹配 text parser
21
23
  input,
22
24
  timePicker,
23
25
  timeRangePicker,
@@ -0,0 +1,288 @@
1
+ import is from '@form-create/utils/lib/type'
2
+ import { watch, toRef } from 'vue'
3
+
4
+ export default {
5
+ name: 'text',
6
+ // 设置 loadChildren 为 false,避免 children 被当作子组件处理
7
+ // text 的 children 只是简单的文本内容,不需要作为子组件加载
8
+ loadChildren: false,
9
+ // 组件挂载后,手动触发一次 loadData 更新,确保表单数据准备好后再获取初始值
10
+ mounted(ctx) {
11
+ // 如果是动态绑定的 Text 组件,在挂载后手动解析模板并更新值
12
+ const props = ctx.rule.props || {}
13
+ const bindField =
14
+ ctx.rule.bindField || props.bindField || ctx.prop.props?.bindField
15
+ const template =
16
+ ctx.rule.template || props.template || ctx.prop.props?.template
17
+
18
+ if (template || bindField) {
19
+ // 延迟执行,确保表单数据已经准备好
20
+ setTimeout(() => {
21
+ if (ctx.$handle && ctx.$handle.api) {
22
+ try {
23
+ let value = ''
24
+
25
+ // 如果是模板模式,手动解析模板字符串
26
+ if (template) {
27
+ const formData = ctx.$handle.api.formData()
28
+ // 使用 loadStrVar 解析模板,传入一个 get 函数来获取表单数据
29
+ value = ctx.$handle.loadStrVar(
30
+ template,
31
+ (field) => {
32
+ // 使用 api.getValue 获取表单字段值
33
+ const val = ctx.$handle.api.getValue(field)
34
+ // 如果 getValue 返回 undefined,尝试从 formData 直接获取
35
+ if (
36
+ val === undefined &&
37
+ formData &&
38
+ formData[field] !== undefined
39
+ ) {
40
+ return formData[field]
41
+ }
42
+ return val
43
+ },
44
+ null
45
+ )
46
+ }
47
+ // 如果是字段绑定模式,直接获取字段值
48
+ else if (bindField) {
49
+ value = ctx.$handle.api.getValue(bindField) || ''
50
+ }
51
+
52
+ // 更新 rule.children(转换为字符串)
53
+ if (value != null) {
54
+ if (!ctx.rule.children) {
55
+ ctx.rule.children = []
56
+ }
57
+ ctx.rule.children[0] = String(value)
58
+ // 触发同步和刷新
59
+ ctx.$handle.api.sync(ctx.rule)
60
+ ctx.$handle.refresh()
61
+ }
62
+ } catch (e) {
63
+ // 静默处理错误
64
+ }
65
+ }
66
+ }, 500) // 增加延迟,确保表单数据完全准备好
67
+ }
68
+ },
69
+ // 处理 children 的渲染,将 children 数组转换为可渲染的内容
70
+ renderChildren(children, ctx) {
71
+ // 使用 computed 确保响应式更新
72
+ // 每次 rule.children 变化时,都会重新计算
73
+ return {
74
+ default: () => {
75
+ // 每次都重新从 rule.children 获取最新值(loadData 会更新这个值)
76
+ // 不要使用闭包中的 children 参数,因为它可能是旧值
77
+ let currentChildren = ctx.rule?.children
78
+
79
+ // 如果 rule.children 不存在,才使用传入的 children 参数(初始值)
80
+ if (!currentChildren) {
81
+ currentChildren = children
82
+ }
83
+
84
+ // 确保是数组
85
+ const childrenArray = Array.isArray(currentChildren)
86
+ ? currentChildren
87
+ : [currentChildren]
88
+
89
+ // 过滤并转换字符串,参考 html parser 的实现
90
+ const text = childrenArray
91
+ .filter((v) => v !== null && v !== undefined)
92
+ .map((v) => {
93
+ if (is.String(v)) {
94
+ return v
95
+ }
96
+ // 如果是对象或其他类型,转换为字符串
97
+ return String(v)
98
+ })
99
+ .join('')
100
+
101
+ return text || ''
102
+ }
103
+ }
104
+ },
105
+ mergeProp(ctx) {
106
+ // 确保没有 field 的组件能正常显示
107
+ // 如果 prop.native 未设置,默认设置为 false,让组件被 makeWrap 包装(这样可以显示 title)
108
+ // 但如果需要直接渲染,可以设置 native: true
109
+
110
+ // 支持从多个位置读取配置:
111
+ // 1. ctx.rule.bindField / ctx.rule.template(直接在 rule 中)
112
+ // 2. ctx.rule.props.bindField / ctx.rule.props.template(在 props 中)
113
+ // 3. ctx.prop.props.bindField / ctx.prop.props.template(在 prop.props 中)
114
+ const props = ctx.rule.props || {}
115
+ const bindField =
116
+ ctx.rule.bindField || props.bindField || ctx.prop.props?.bindField
117
+ const template =
118
+ ctx.rule.template || props.template || ctx.prop.props?.template
119
+ let bindMode =
120
+ ctx.rule.bindMode || props.bindMode || ctx.prop.props?.bindMode
121
+
122
+ // 如果没有设置 bindMode,根据是否有 bindField 或 template 自动判断
123
+ if (!bindMode) {
124
+ if (template) {
125
+ bindMode = 'template'
126
+ } else if (bindField) {
127
+ bindMode = 'field'
128
+ } else {
129
+ bindMode = 'static'
130
+ }
131
+ }
132
+
133
+ // 如果配置了 bindField(绑定单个字段),使用 loadData 来实现动态绑定
134
+ if (bindMode === 'field' && bindField) {
135
+ const loadDataConfig = {
136
+ attr: bindField,
137
+ to: 'child',
138
+ modify: true,
139
+ // 增加 debounce 延迟,避免频繁更新导致循环
140
+ wait: 300,
141
+ // 确保 watch 开启,但通过 debounce 控制频率
142
+ watch: true
143
+ }
144
+
145
+ // 添加到 effect 中(避免重复添加)
146
+ if (!ctx.rule.effect) {
147
+ ctx.rule.effect = {}
148
+ }
149
+ if (!ctx.rule.effect.loadData) {
150
+ ctx.rule.effect.loadData = []
151
+ }
152
+ // 检查是否已存在相同的配置,避免重复添加
153
+ const exists = ctx.rule.effect.loadData.some(
154
+ (item) => item.attr === bindField && item.to === 'child'
155
+ )
156
+ if (!exists) {
157
+ ctx.rule.effect.loadData.push(loadDataConfig)
158
+ // 手动触发 effect 来确保 loadData provider 被调用
159
+ // 使用 effect 方法,它会正确设置 ctx 和 input
160
+ if (ctx.$handle && ctx.$handle.effect) {
161
+ ctx.$handle.effect(ctx, 'loaded')
162
+ }
163
+ }
164
+ }
165
+ // 如果配置了 template(模板字符串),支持多个字段绑定
166
+ else if (bindMode === 'template' && template) {
167
+ const loadDataConfig = {
168
+ template: template,
169
+ to: 'child',
170
+ modify: true,
171
+ // 增加 debounce 延迟,避免频繁更新导致循环
172
+ wait: 300,
173
+ // 确保 watch 开启,但通过 debounce 控制频率
174
+ watch: true
175
+ }
176
+
177
+ if (!ctx.rule.effect) {
178
+ ctx.rule.effect = {}
179
+ }
180
+ if (!ctx.rule.effect.loadData) {
181
+ ctx.rule.effect.loadData = []
182
+ }
183
+ // 检查是否已存在相同的配置,避免重复添加
184
+ const exists = ctx.rule.effect.loadData.some(
185
+ (item) => item.template === template && item.to === 'child'
186
+ )
187
+ if (!exists) {
188
+ ctx.rule.effect.loadData.push(loadDataConfig)
189
+ // 手动触发 effect 来确保 loadData provider 被调用
190
+ if (ctx.$handle && ctx.$handle.effect) {
191
+ ctx.$handle.effect(ctx, 'loaded')
192
+ // 同时手动添加 watch,监听 effect.loadData 的变化
193
+ // 这样当 effect.loadData 被修改时,也会触发 provider
194
+ if (!ctx._textLoadDataWatched) {
195
+ ctx._textLoadDataWatched = true
196
+ const loadDataRef = toRef(ctx.rule.effect, 'loadData')
197
+ ctx.watch.push(
198
+ watch(
199
+ loadDataRef,
200
+ (newVal, oldVal) => {
201
+ ctx.$handle.effect(ctx, 'watch', { loadData: newVal })
202
+ },
203
+ { deep: true }
204
+ )
205
+ )
206
+ }
207
+ }
208
+ }
209
+ }
210
+ // 静态模式:使用 formCreateChild 或 children 作为内容
211
+ // formCreateChild 会作为 children[0] 渲染,如果已有 children,也会正常渲染
212
+
213
+ // 如果配置了 formCreateChild,将其转换为 children
214
+ if (ctx.rule.formCreateChild != null && !ctx.rule.children) {
215
+ ctx.rule.children = [ctx.rule.formCreateChild]
216
+ }
217
+
218
+ // 确保 children 存在(如果没有设置,默认为空数组)
219
+ if (!ctx.rule.children) {
220
+ ctx.rule.children = []
221
+ }
222
+
223
+ // 对于动态绑定的情况,初始化时设置一个占位符,确保组件能被渲染
224
+ // loadData 会在初始化后立即执行一次,更新这个值
225
+ if (
226
+ (bindMode === 'field' && bindField) ||
227
+ (bindMode === 'template' && template)
228
+ ) {
229
+ // 如果 children 是空数组或未设置,设置为一个非空字符串
230
+ // 这样确保组件会被渲染,然后 loadData 会更新内容
231
+ if (
232
+ !ctx.rule.children ||
233
+ (Array.isArray(ctx.rule.children) && ctx.rule.children.length === 0)
234
+ ) {
235
+ // 对于 template 模式,先不设置占位符,等待 loadData 执行
236
+ // 因为如果设置了占位符,可能会覆盖 loadData 的结果
237
+ if (bindMode === 'template') {
238
+ // template 模式下,等待 loadData 执行后更新
239
+ // 但为了确保组件能渲染,至少设置一个空字符串
240
+ ctx.rule.children = ['']
241
+ } else {
242
+ // field 模式下,设置一个空格字符串作为占位符
243
+ ctx.rule.children = [' ']
244
+ }
245
+ }
246
+ }
247
+ },
248
+ render(children, ctx) {
249
+ // text 组件使用 div 渲染,内容来自 children
250
+ // 使用 children.default() 获取响应式内容,参考 html parser 的实现
251
+
252
+ // 获取文本内容(children.default() 会从 renderChildren 返回的响应式函数中获取)
253
+ // renderChildren 返回的 default 函数会从 ctx.rule.children 获取最新值
254
+ let text = ''
255
+ if (children && typeof children.default === 'function') {
256
+ try {
257
+ text = children.default() || ''
258
+ } catch (e) {
259
+ text = ''
260
+ }
261
+ }
262
+
263
+ // 确保 text 是字符串
264
+ if (text == null || text === undefined) {
265
+ text = ''
266
+ } else {
267
+ text = String(text)
268
+ }
269
+
270
+ // 复制 prop,避免修改原始对象
271
+ const prop = { ...ctx.prop }
272
+ if (!prop.props) {
273
+ prop.props = {}
274
+ }
275
+
276
+ // 确保 type 是 'div'
277
+ if (prop.type === 'text') {
278
+ prop.type = 'div'
279
+ }
280
+
281
+ delete prop.props.innerHTML
282
+
283
+ // 直接使用 vNode.make 渲染 div,文本作为子节点
284
+ // 即使 text 为空,也传递 [text] 确保渲染出 div
285
+ const vnode = ctx.vNode.make('div', prop, [text])
286
+ return vnode
287
+ }
288
+ }