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

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.52",
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,422 @@
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
+ // 添加 init 方法,用于调试确认 parser 是否被使用
10
+ init(ctx) {
11
+ console.log(
12
+ '[Text Parser] Text parser initialized for rule:',
13
+ ctx.rule.field || ctx.rule.name || 'unnamed'
14
+ )
15
+ },
16
+ // 组件挂载后,手动触发一次 loadData 更新,确保表单数据准备好后再获取初始值
17
+ mounted(ctx) {
18
+ // 如果是动态绑定的 Text 组件,在挂载后手动解析模板并更新值
19
+ const props = ctx.rule.props || {}
20
+ const bindField =
21
+ ctx.rule.bindField || props.bindField || ctx.prop.props?.bindField
22
+ const template =
23
+ ctx.rule.template || props.template || ctx.prop.props?.template
24
+
25
+ if (template || bindField) {
26
+ // 延迟执行,确保表单数据已经准备好
27
+ setTimeout(() => {
28
+ if (ctx.$handle && ctx.$handle.api) {
29
+ try {
30
+ let value = ''
31
+
32
+ // 如果是模板模式,手动解析模板字符串
33
+ if (template) {
34
+ // 先获取所有表单数据,用于调试
35
+ const formData = ctx.$handle.api.formData()
36
+ console.log('[Text Parser] Current form data:', formData)
37
+
38
+ // 使用 loadStrVar 解析模板,传入一个 get 函数来获取表单数据
39
+ value = ctx.$handle.loadStrVar(
40
+ template,
41
+ (field) => {
42
+ // 使用 api.getValue 获取表单字段值
43
+ const val = ctx.$handle.api.getValue(field)
44
+ console.log(
45
+ '[Text Parser] Getting field value:',
46
+ field,
47
+ '=',
48
+ val,
49
+ 'type:',
50
+ typeof val
51
+ )
52
+ // 如果 getValue 返回 undefined,尝试从 formData 直接获取
53
+ if (
54
+ val === undefined &&
55
+ formData &&
56
+ formData[field] !== undefined
57
+ ) {
58
+ console.log(
59
+ '[Text Parser] Fallback to formData:',
60
+ field,
61
+ '=',
62
+ formData[field]
63
+ )
64
+ return formData[field]
65
+ }
66
+ return val
67
+ },
68
+ null
69
+ )
70
+ console.log(
71
+ '[Text Parser] Manually parsed template:',
72
+ template,
73
+ 'result:',
74
+ value,
75
+ 'type:',
76
+ typeof value
77
+ )
78
+ }
79
+ // 如果是字段绑定模式,直接获取字段值
80
+ else if (bindField) {
81
+ value = ctx.$handle.api.getValue(bindField) || ''
82
+ console.log(
83
+ '[Text Parser] Manually got field value:',
84
+ bindField,
85
+ 'result:',
86
+ value
87
+ )
88
+ }
89
+
90
+ // 更新 rule.children(转换为字符串)
91
+ if (value != null) {
92
+ if (!ctx.rule.children) {
93
+ ctx.rule.children = []
94
+ }
95
+ ctx.rule.children[0] = String(value)
96
+ // 触发同步和刷新
97
+ ctx.$handle.api.sync(ctx.rule)
98
+ ctx.$handle.refresh()
99
+ console.log(
100
+ '[Text Parser] Updated rule.children to:',
101
+ ctx.rule.children,
102
+ 'value:',
103
+ value
104
+ )
105
+ } else {
106
+ console.log(
107
+ '[Text Parser] Value is null/undefined, not updating children'
108
+ )
109
+ }
110
+ } catch (e) {
111
+ console.error('[Text Parser] Error in mounted hook:', e)
112
+ }
113
+ }
114
+ }, 500) // 增加延迟,确保表单数据完全准备好
115
+ }
116
+ },
117
+ // 处理 children 的渲染,将 children 数组转换为可渲染的内容
118
+ renderChildren(children, ctx) {
119
+ // 使用 computed 确保响应式更新
120
+ // 每次 rule.children 变化时,都会重新计算
121
+ return {
122
+ default: () => {
123
+ // 每次都重新从 rule.children 获取最新值(loadData 会更新这个值)
124
+ // 不要使用闭包中的 children 参数,因为它可能是旧值
125
+ let currentChildren = ctx.rule?.children
126
+
127
+ // 如果 rule.children 不存在,才使用传入的 children 参数(初始值)
128
+ if (!currentChildren) {
129
+ currentChildren = children
130
+ }
131
+
132
+ // 确保是数组
133
+ const childrenArray = Array.isArray(currentChildren)
134
+ ? currentChildren
135
+ : [currentChildren]
136
+
137
+ // 过滤并转换字符串,参考 html parser 的实现
138
+ const text = childrenArray
139
+ .filter((v) => v !== null && v !== undefined)
140
+ .map((v) => {
141
+ if (is.String(v)) {
142
+ return v
143
+ }
144
+ // 如果是对象或其他类型,转换为字符串
145
+ return String(v)
146
+ })
147
+ .join('')
148
+
149
+ // 添加调试日志,确认 renderChildren 是否被重新执行
150
+ console.log(
151
+ '[Text Parser] renderChildren default() called, text:',
152
+ text,
153
+ 'for rule:',
154
+ ctx.rule.field || ctx.rule.name || 'unnamed'
155
+ )
156
+
157
+ return text || ''
158
+ }
159
+ }
160
+ },
161
+ mergeProp(ctx) {
162
+ console.log(
163
+ '[Text Parser] mergeProp called for rule:',
164
+ ctx.rule.field || ctx.rule.name || 'unnamed',
165
+ 'type:',
166
+ ctx.rule.type
167
+ )
168
+ // 确保没有 field 的组件能正常显示
169
+ // 如果 prop.native 未设置,默认设置为 false,让组件被 makeWrap 包装(这样可以显示 title)
170
+ // 但如果需要直接渲染,可以设置 native: true
171
+
172
+ // 支持从多个位置读取配置:
173
+ // 1. ctx.rule.bindField / ctx.rule.template(直接在 rule 中)
174
+ // 2. ctx.rule.props.bindField / ctx.rule.props.template(在 props 中)
175
+ // 3. ctx.prop.props.bindField / ctx.prop.props.template(在 prop.props 中)
176
+ const props = ctx.rule.props || {}
177
+ const bindField =
178
+ ctx.rule.bindField || props.bindField || ctx.prop.props?.bindField
179
+ const template =
180
+ ctx.rule.template || props.template || ctx.prop.props?.template
181
+ let bindMode =
182
+ ctx.rule.bindMode || props.bindMode || ctx.prop.props?.bindMode
183
+
184
+ // 如果没有设置 bindMode,根据是否有 bindField 或 template 自动判断
185
+ if (!bindMode) {
186
+ if (template) {
187
+ bindMode = 'template'
188
+ } else if (bindField) {
189
+ bindMode = 'field'
190
+ } else {
191
+ bindMode = 'static'
192
+ }
193
+ }
194
+
195
+ // 如果配置了 bindField(绑定单个字段),使用 loadData 来实现动态绑定
196
+ if (bindMode === 'field' && bindField) {
197
+ const loadDataConfig = {
198
+ attr: bindField,
199
+ to: 'child',
200
+ modify: true,
201
+ // 增加 debounce 延迟,避免频繁更新导致循环
202
+ wait: 300,
203
+ // 确保 watch 开启,但通过 debounce 控制频率
204
+ watch: true
205
+ }
206
+
207
+ // 添加到 effect 中(避免重复添加)
208
+ if (!ctx.rule.effect) {
209
+ ctx.rule.effect = {}
210
+ }
211
+ if (!ctx.rule.effect.loadData) {
212
+ ctx.rule.effect.loadData = []
213
+ }
214
+ // 检查是否已存在相同的配置,避免重复添加
215
+ const exists = ctx.rule.effect.loadData.some(
216
+ (item) => item.attr === bindField && item.to === 'child'
217
+ )
218
+ if (!exists) {
219
+ ctx.rule.effect.loadData.push(loadDataConfig)
220
+ console.log(
221
+ '[Text Parser] Added loadData config (field mode):',
222
+ loadDataConfig,
223
+ 'to effect.loadData:',
224
+ ctx.rule.effect.loadData,
225
+ 'rule effect:',
226
+ ctx.rule.effect,
227
+ 'ctx:',
228
+ ctx
229
+ )
230
+ // 手动触发 effect 来确保 loadData provider 被调用
231
+ // 使用 effect 方法,它会正确设置 ctx 和 input
232
+ if (ctx.$handle && ctx.$handle.effect) {
233
+ console.log(
234
+ '[Text Parser] Manually calling effect(ctx, "loaded") for loadData (field)'
235
+ )
236
+ ctx.$handle.effect(ctx, 'loaded')
237
+ } else {
238
+ console.log(
239
+ '[Text Parser] ctx.$handle.effect not available:',
240
+ !!ctx.$handle,
241
+ !!ctx.$handle?.effect
242
+ )
243
+ }
244
+ }
245
+ }
246
+ // 如果配置了 template(模板字符串),支持多个字段绑定
247
+ else if (bindMode === 'template' && template) {
248
+ console.log(
249
+ '[Text Parser] Setting up template binding:',
250
+ template,
251
+ 'for rule:',
252
+ ctx.rule.field || ctx.rule.name || 'unnamed'
253
+ )
254
+ const loadDataConfig = {
255
+ template: template,
256
+ to: 'child',
257
+ modify: true,
258
+ // 增加 debounce 延迟,避免频繁更新导致循环
259
+ wait: 300,
260
+ // 确保 watch 开启,但通过 debounce 控制频率
261
+ watch: true
262
+ }
263
+
264
+ if (!ctx.rule.effect) {
265
+ ctx.rule.effect = {}
266
+ }
267
+ if (!ctx.rule.effect.loadData) {
268
+ ctx.rule.effect.loadData = []
269
+ }
270
+ // 检查是否已存在相同的配置,避免重复添加
271
+ const exists = ctx.rule.effect.loadData.some(
272
+ (item) => item.template === template && item.to === 'child'
273
+ )
274
+ if (!exists) {
275
+ ctx.rule.effect.loadData.push(loadDataConfig)
276
+ console.log(
277
+ '[Text Parser] Added loadData config:',
278
+ loadDataConfig,
279
+ 'to effect.loadData:',
280
+ ctx.rule.effect.loadData,
281
+ 'rule effect:',
282
+ ctx.rule.effect,
283
+ 'ctx:',
284
+ ctx
285
+ )
286
+ // 手动触发 effect 来确保 loadData provider 被调用
287
+ if (ctx.$handle && ctx.$handle.effect) {
288
+ console.log(
289
+ '[Text Parser] Manually calling effect(ctx, "loaded") for loadData (template)'
290
+ )
291
+ ctx.$handle.effect(ctx, 'loaded')
292
+ // 同时手动添加 watch,监听 effect.loadData 的变化
293
+ // 这样当 effect.loadData 被修改时,也会触发 provider
294
+ if (!ctx._textLoadDataWatched) {
295
+ ctx._textLoadDataWatched = true
296
+ const loadDataRef = toRef(ctx.rule.effect, 'loadData')
297
+ ctx.watch.push(
298
+ watch(
299
+ loadDataRef,
300
+ (newVal, oldVal) => {
301
+ console.log(
302
+ '[Text Parser] effect.loadData changed, triggering watch',
303
+ newVal,
304
+ oldVal
305
+ )
306
+ ctx.$handle.effect(ctx, 'watch', { loadData: newVal })
307
+ },
308
+ { deep: true }
309
+ )
310
+ )
311
+ }
312
+ } else {
313
+ console.log(
314
+ '[Text Parser] ctx.$handle.effect not available:',
315
+ !!ctx.$handle,
316
+ !!ctx.$handle?.effect
317
+ )
318
+ }
319
+ } else {
320
+ console.log('[Text Parser] loadData config already exists, skipping')
321
+ }
322
+ }
323
+ // 静态模式:使用 formCreateChild 或 children 作为内容
324
+ // formCreateChild 会作为 children[0] 渲染,如果已有 children,也会正常渲染
325
+
326
+ // 如果配置了 formCreateChild,将其转换为 children
327
+ if (ctx.rule.formCreateChild != null && !ctx.rule.children) {
328
+ ctx.rule.children = [ctx.rule.formCreateChild]
329
+ }
330
+
331
+ // 确保 children 存在(如果没有设置,默认为空数组)
332
+ if (!ctx.rule.children) {
333
+ ctx.rule.children = []
334
+ }
335
+
336
+ // 对于动态绑定的情况,初始化时设置一个占位符,确保组件能被渲染
337
+ // loadData 会在初始化后立即执行一次,更新这个值
338
+ if (
339
+ (bindMode === 'field' && bindField) ||
340
+ (bindMode === 'template' && template)
341
+ ) {
342
+ // 如果 children 是空数组或未设置,设置为一个非空字符串
343
+ // 这样确保组件会被渲染,然后 loadData 会更新内容
344
+ if (
345
+ !ctx.rule.children ||
346
+ (Array.isArray(ctx.rule.children) && ctx.rule.children.length === 0)
347
+ ) {
348
+ // 对于 template 模式,先不设置占位符,等待 loadData 执行
349
+ // 因为如果设置了占位符,可能会覆盖 loadData 的结果
350
+ if (bindMode === 'template') {
351
+ // template 模式下,等待 loadData 执行后更新
352
+ // 但为了确保组件能渲染,至少设置一个空字符串
353
+ ctx.rule.children = ['']
354
+ } else {
355
+ // field 模式下,设置一个空格字符串作为占位符
356
+ ctx.rule.children = [' ']
357
+ }
358
+ }
359
+ console.log(
360
+ '[Text Parser] Initialized children for dynamic binding:',
361
+ ctx.rule.children,
362
+ 'bindMode:',
363
+ bindMode
364
+ )
365
+ }
366
+ },
367
+ render(children, ctx) {
368
+ console.log(
369
+ '[Text Parser] render called for rule:',
370
+ ctx.rule.field || ctx.rule.name || 'unnamed',
371
+ 'children:',
372
+ children,
373
+ 'prop:',
374
+ ctx.prop,
375
+ 'hidden:',
376
+ ctx.prop?.hidden,
377
+ 'display:',
378
+ ctx.prop?.display
379
+ )
380
+ // text 组件使用 div 渲染,内容来自 children
381
+ // 使用 children.default() 获取响应式内容,参考 html parser 的实现
382
+
383
+ // 获取文本内容(children.default() 会从 renderChildren 返回的响应式函数中获取)
384
+ // renderChildren 返回的 default 函数会从 ctx.rule.children 获取最新值
385
+ let text = ''
386
+ if (children && typeof children.default === 'function') {
387
+ try {
388
+ text = children.default() || ''
389
+ console.log('[Text Parser] text content:', text)
390
+ } catch (e) {
391
+ console.warn('Text component render children error:', e)
392
+ text = ''
393
+ }
394
+ }
395
+
396
+ // 确保 text 是字符串
397
+ if (text == null || text === undefined) {
398
+ text = ''
399
+ } else {
400
+ text = String(text)
401
+ }
402
+
403
+ // 复制 prop,避免修改原始对象
404
+ const prop = { ...ctx.prop }
405
+ if (!prop.props) {
406
+ prop.props = {}
407
+ }
408
+
409
+ // 确保 type 是 'div'
410
+ if (prop.type === 'text') {
411
+ prop.type = 'div'
412
+ }
413
+
414
+ delete prop.props.innerHTML
415
+
416
+ // 直接使用 vNode.make 渲染 div,文本作为子节点
417
+ // 即使 text 为空,也传递 [text] 确保渲染出 div
418
+ const vnode = ctx.vNode.make('div', prop, [text])
419
+ console.log('[Text Parser] created vnode:', vnode)
420
+ return vnode
421
+ }
422
+ }