@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/dist/form-create.esm.js +2 -2
- package/dist/form-create.esm.js.map +1 -1
- package/dist/form-create.js +2 -2
- package/dist/form-create.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CusSelect/index.vue +18 -1
- package/src/components/CusStoreSelect/index.vue +7 -4
- package/src/components/CusUserSelect/index.vue +8 -4
- package/src/core/maker.js +5 -0
- package/src/parsers/index.js +2 -0
- package/src/parsers/text.js +422 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@longhongguo/form-create-ant-design-vue",
|
|
3
|
-
"version": "3.2.
|
|
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.
|
|
350
|
+
this.sourceItems && this.sourceItems.length > 0
|
|
348
351
|
? this.multiple
|
|
349
|
-
? this.
|
|
350
|
-
: this.
|
|
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.
|
|
356
|
+
this.sourceItems && this.sourceItems.length > 0
|
|
353
357
|
? this.multiple
|
|
354
|
-
? this.
|
|
355
|
-
: this.
|
|
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
|
package/src/parsers/index.js
CHANGED
|
@@ -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
|
+
}
|