el-form-renderer-vue3 1.0.7 → 1.0.9
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/README.md +413 -409
- package/el-form-renderer-vue3.es.js +649 -646
- package/el-form-renderer-vue3.umd.js +4 -4
- package/femessage/components/render-form-group.vue +46 -46
- package/femessage/components/render-form-item.vue +335 -335
- package/femessage/el-form-renderer.vue +313 -299
- package/femessage/util/CustomComponent.js +28 -28
- package/femessage/util/VNode.js +9 -9
- package/femessage/util/enable-when.js +26 -26
- package/femessage/util/keys.js +3 -3
- package/femessage/util/ployfill.js +14 -14
- package/femessage/util/transform-content.js +48 -48
- package/femessage/util/utils.js +126 -126
- package/package.json +4 -10
|
@@ -1,299 +1,313 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<el-form
|
|
3
|
-
ref="myelForm"
|
|
4
|
-
v-bind="$attrs"
|
|
5
|
-
:model="value"
|
|
6
|
-
class="el-form-renderer"
|
|
7
|
-
>
|
|
8
|
-
<template v-for="item in innerContent" :key="item.id">
|
|
9
|
-
<slot :name="`id:${item.id}`" />
|
|
10
|
-
<slot :name="`$id:${item.id}`" />
|
|
11
|
-
|
|
12
|
-
<component
|
|
13
|
-
:is="item.type === GROUP ? RenderFormGroup : RenderFormItem"
|
|
14
|
-
:ref="
|
|
15
|
-
(el) => {
|
|
16
|
-
customComponent[item.id] = el;
|
|
17
|
-
}
|
|
18
|
-
"
|
|
19
|
-
:data="item"
|
|
20
|
-
:value="value"
|
|
21
|
-
:item-value="value[item.id]"
|
|
22
|
-
:disabled="
|
|
23
|
-
disabled ||
|
|
24
|
-
(typeof item.disabled === 'function'
|
|
25
|
-
? item.disabled(value)
|
|
26
|
-
: item.disabled)
|
|
27
|
-
"
|
|
28
|
-
:readonly="readonly || item.readonly"
|
|
29
|
-
:options="options[item.id]"
|
|
30
|
-
@updateValue="updateValue"
|
|
31
|
-
/>
|
|
32
|
-
</template>
|
|
33
|
-
<slot />
|
|
34
|
-
</el-form>
|
|
35
|
-
</template>
|
|
36
|
-
|
|
37
|
-
<script setup>
|
|
38
|
-
// passive 是用于控制浏览器是否可以在滚动时取消事件的默认行为。当 passive 设置为 true 时,表示事件处理函数不会调用 preventDefault() 来阻止默认的滚动行为。
|
|
39
|
-
//在一些滚动事件处理中,如果事件处理函数中调用了 preventDefault(),浏览器会等待该函数执行完毕后再进行滚动,这可能导致滚动的延迟。通过将 passive 设置为 true,可以告诉浏览器事件处理函数不会调用 preventDefault(),从而使滚动更加流畅。
|
|
40
|
-
import "./util/ployfill";
|
|
41
|
-
import RenderFormGroup from "./components/render-form-group.vue";
|
|
42
|
-
import RenderFormItem from "./components/render-form-item.vue";
|
|
43
|
-
import {
|
|
44
|
-
reactive,
|
|
45
|
-
computed,
|
|
46
|
-
ref,
|
|
47
|
-
watch,
|
|
48
|
-
onMounted,
|
|
49
|
-
nextTick,
|
|
50
|
-
provide,
|
|
51
|
-
getCurrentInstance,
|
|
52
|
-
} from "vue";
|
|
53
|
-
import transformContent from "./util/transform-content";
|
|
54
|
-
import _set from "lodash.set";
|
|
55
|
-
import _isequal from "lodash.isequal";
|
|
56
|
-
import _clonedeep from "lodash.clonedeep";
|
|
57
|
-
|
|
58
|
-
import {
|
|
59
|
-
methodsSymbol,
|
|
60
|
-
updateFormsSymbol,
|
|
61
|
-
setOptionsSymbol,
|
|
62
|
-
} from "./util/keys";
|
|
63
|
-
import {
|
|
64
|
-
collect,
|
|
65
|
-
mergeValue,
|
|
66
|
-
transformOutputValue,
|
|
67
|
-
transformInputValue,
|
|
68
|
-
correctValue,
|
|
69
|
-
} from "./util/utils";
|
|
70
|
-
let GROUP = "group";
|
|
71
|
-
/**
|
|
72
|
-
* inputFormat 让整个输入机制复杂了很多。value 有以下输入路径:
|
|
73
|
-
* 1. 传入的 form => inputFormat 处理
|
|
74
|
-
* 2. updateForm => inputFormat 处理
|
|
75
|
-
* 3. 但 content 中的 default 没法经过 inputFormat 处理,因为 inputFormat 要接受整个 value 作为参数
|
|
76
|
-
* 4. 组件内部更新 value,不需要走 inputFormat
|
|
77
|
-
*/
|
|
78
|
-
let value = reactive({}); // 表单数据对象
|
|
79
|
-
let options = reactive({});
|
|
80
|
-
let initValue = reactive({});
|
|
81
|
-
let myelForm = ref();
|
|
82
|
-
let methods = {};
|
|
83
|
-
const customComponent = ref([]);
|
|
84
|
-
let emit = defineEmits(["update:FormData"]);
|
|
85
|
-
// 注入 element ui form 方法
|
|
86
|
-
/**
|
|
87
|
-
* 与 element 相同,在 mounted 阶段存储 initValue
|
|
88
|
-
* @see https://github.com/ElemeFE/element/blob/6ec5f8e900ff698cf30e9479d692784af836a108/packages/form/src/form-item.vue#L304
|
|
89
|
-
*/
|
|
90
|
-
onMounted(async () => {
|
|
91
|
-
initValue = _clonedeep(value);
|
|
92
|
-
await nextTick();
|
|
93
|
-
// 检查 myelForm 是否已经初始化
|
|
94
|
-
if (myelForm && myelForm.value) {
|
|
95
|
-
Object.keys(myelForm.value).forEach((item) => {
|
|
96
|
-
// 检查属性是否存在于 methods 对象中
|
|
97
|
-
if (myelForm.value[item] && !(item in methods)) {
|
|
98
|
-
methods[item] = myelForm.value[item];
|
|
99
|
-
}
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
/**
|
|
103
|
-
* 有些组件会 created 阶段更新初始值为合法值,这会触发 validate。目前已知的情况有:
|
|
104
|
-
* - el-select 开启 multiple 时,会更新初始值 undefined 为 []
|
|
105
|
-
* @hack
|
|
106
|
-
*/
|
|
107
|
-
methods.clearValidate();
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
let props = defineProps({
|
|
111
|
-
//表单项
|
|
112
|
-
content: {
|
|
113
|
-
type: Array,
|
|
114
|
-
required: true,
|
|
115
|
-
},
|
|
116
|
-
// 禁用
|
|
117
|
-
disabled: {
|
|
118
|
-
type: [Boolean, Function],
|
|
119
|
-
default: false,
|
|
120
|
-
},
|
|
121
|
-
//只读
|
|
122
|
-
readonly: {
|
|
123
|
-
type: Boolean,
|
|
124
|
-
default: false,
|
|
125
|
-
},
|
|
126
|
-
/**
|
|
127
|
-
* v-model 的值。传入后会优先使用
|
|
128
|
-
*/
|
|
129
|
-
FormData: {
|
|
130
|
-
type: Object,
|
|
131
|
-
default: undefined,
|
|
132
|
-
},
|
|
133
|
-
});
|
|
134
|
-
//兼容处理
|
|
135
|
-
let innerContent = computed(() => transformContent(props.content));
|
|
136
|
-
// 初始化默认值
|
|
137
|
-
let setValueFromModel = () => {
|
|
138
|
-
if (innerContent.length) return;
|
|
139
|
-
/**
|
|
140
|
-
* 没使用 v-model 时才从 default 采集数据
|
|
141
|
-
* default 值没法考虑 inputFormat
|
|
142
|
-
* 参考 value-format.md 的案例。那种情况下,default 该传什么?
|
|
143
|
-
*/
|
|
144
|
-
let newValue = props.FormData
|
|
145
|
-
? transformInputValue(props.FormData, innerContent.value)
|
|
146
|
-
: collect(innerContent.value, "default");
|
|
147
|
-
correctValue(newValue, innerContent.value);
|
|
148
|
-
if (!_isequal(value, newValue)) value = Object.assign(value, newValue);
|
|
149
|
-
};
|
|
150
|
-
// v-model初始化默认数据
|
|
151
|
-
watch(
|
|
152
|
-
() => props.FormData,
|
|
153
|
-
(newForm) => {
|
|
154
|
-
if (!newForm) return;
|
|
155
|
-
setValueFromModel();
|
|
156
|
-
},
|
|
157
|
-
{ immediate: true, deep: true }
|
|
158
|
-
);
|
|
159
|
-
// 初始化默认数据
|
|
160
|
-
watch(
|
|
161
|
-
innerContent,
|
|
162
|
-
(newContent) => {
|
|
163
|
-
try {
|
|
164
|
-
if (!newContent) return;
|
|
165
|
-
|
|
166
|
-
// 如果 content 没有变动 remote 的部分,这里需要保留之前 remote 注入的 options
|
|
167
|
-
Object.assign(options, collect(newContent, "options"));
|
|
168
|
-
setValueFromModel();
|
|
169
|
-
} catch (error) {
|
|
170
|
-
console.log(error);
|
|
171
|
-
}
|
|
172
|
-
},
|
|
173
|
-
{ immediate: true }
|
|
174
|
-
);
|
|
175
|
-
|
|
176
|
-
// v-model 传递值
|
|
177
|
-
watch(value, (newValue, oldValue) => {
|
|
178
|
-
try {
|
|
179
|
-
if (!newValue) return;
|
|
180
|
-
if (props.FormData) {
|
|
181
|
-
let data = Object.assign(
|
|
182
|
-
props.FormData,
|
|
183
|
-
transformOutputValue(newValue, innerContent)
|
|
184
|
-
);
|
|
185
|
-
emit("update:FormData", data);
|
|
186
|
-
}
|
|
187
|
-
} catch (error) {
|
|
188
|
-
console.log(error, "-----");
|
|
189
|
-
}
|
|
190
|
-
// deep: true, // updateValue 是全量更新,所以不用
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
/**
|
|
194
|
-
* 更新表单数据
|
|
195
|
-
* @param {String} options.id 表单ID
|
|
196
|
-
* @param {All} options.value 表单数据
|
|
197
|
-
*/
|
|
198
|
-
let updateValue = ({ id, value: v }) => {
|
|
199
|
-
value[id] = v;
|
|
200
|
-
};
|
|
201
|
-
/**
|
|
202
|
-
* 重置表单为初始值
|
|
203
|
-
*
|
|
204
|
-
* @public
|
|
205
|
-
*/
|
|
206
|
-
let resetFields = async () => {
|
|
207
|
-
/**
|
|
208
|
-
* 之所以不用 el-form 的 resetFields 机制,有以下原因:
|
|
209
|
-
* - el-form 的 resetFields 无视 el-form-renderer 的自定义组件
|
|
210
|
-
* - el-form 的 resetFields 不会触发 input & change 事件,无法监听
|
|
211
|
-
* - bug1: https://github.com/FEMessage/el-data-table/issues/176#issuecomment-587280825
|
|
212
|
-
* - bug2:
|
|
213
|
-
* 0. 建议先在监听器 watch.value 里 console.log(v.name, oldV.name)
|
|
214
|
-
* 1. 打开 basic 示例
|
|
215
|
-
* 2. 在 label 为 name 的输入框里输入 1,此时 log:'1' ''
|
|
216
|
-
* 3. 点击 reset 按钮,此时 log 两条数据: '1' '1', '' ''
|
|
217
|
-
* 4. 因为 _isequal(v, oldV),所以没有触发 v-model 更新
|
|
218
|
-
*/
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
*
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
1
|
+
<template>
|
|
2
|
+
<el-form
|
|
3
|
+
ref="myelForm"
|
|
4
|
+
v-bind="$attrs"
|
|
5
|
+
:model="value"
|
|
6
|
+
class="el-form-renderer"
|
|
7
|
+
>
|
|
8
|
+
<template v-for="item in innerContent" :key="item.id">
|
|
9
|
+
<slot :name="`id:${item.id}`" />
|
|
10
|
+
<slot :name="`$id:${item.id}`" />
|
|
11
|
+
|
|
12
|
+
<component
|
|
13
|
+
:is="item.type === GROUP ? RenderFormGroup : RenderFormItem"
|
|
14
|
+
:ref="
|
|
15
|
+
(el) => {
|
|
16
|
+
customComponent[item.id] = el;
|
|
17
|
+
}
|
|
18
|
+
"
|
|
19
|
+
:data="item"
|
|
20
|
+
:value="value"
|
|
21
|
+
:item-value="value[item.id]"
|
|
22
|
+
:disabled="
|
|
23
|
+
disabled ||
|
|
24
|
+
(typeof item.disabled === 'function'
|
|
25
|
+
? item.disabled(value)
|
|
26
|
+
: item.disabled)
|
|
27
|
+
"
|
|
28
|
+
:readonly="readonly || item.readonly"
|
|
29
|
+
:options="options[item.id]"
|
|
30
|
+
@updateValue="updateValue"
|
|
31
|
+
/>
|
|
32
|
+
</template>
|
|
33
|
+
<slot />
|
|
34
|
+
</el-form>
|
|
35
|
+
</template>
|
|
36
|
+
|
|
37
|
+
<script setup>
|
|
38
|
+
// passive 是用于控制浏览器是否可以在滚动时取消事件的默认行为。当 passive 设置为 true 时,表示事件处理函数不会调用 preventDefault() 来阻止默认的滚动行为。
|
|
39
|
+
//在一些滚动事件处理中,如果事件处理函数中调用了 preventDefault(),浏览器会等待该函数执行完毕后再进行滚动,这可能导致滚动的延迟。通过将 passive 设置为 true,可以告诉浏览器事件处理函数不会调用 preventDefault(),从而使滚动更加流畅。
|
|
40
|
+
import "./util/ployfill";
|
|
41
|
+
import RenderFormGroup from "./components/render-form-group.vue";
|
|
42
|
+
import RenderFormItem from "./components/render-form-item.vue";
|
|
43
|
+
import {
|
|
44
|
+
reactive,
|
|
45
|
+
computed,
|
|
46
|
+
ref,
|
|
47
|
+
watch,
|
|
48
|
+
onMounted,
|
|
49
|
+
nextTick,
|
|
50
|
+
provide,
|
|
51
|
+
getCurrentInstance,
|
|
52
|
+
} from "vue";
|
|
53
|
+
import transformContent from "./util/transform-content";
|
|
54
|
+
import _set from "lodash.set";
|
|
55
|
+
import _isequal from "lodash.isequal";
|
|
56
|
+
import _clonedeep from "lodash.clonedeep";
|
|
57
|
+
|
|
58
|
+
import {
|
|
59
|
+
methodsSymbol,
|
|
60
|
+
updateFormsSymbol,
|
|
61
|
+
setOptionsSymbol,
|
|
62
|
+
} from "./util/keys";
|
|
63
|
+
import {
|
|
64
|
+
collect,
|
|
65
|
+
mergeValue,
|
|
66
|
+
transformOutputValue,
|
|
67
|
+
transformInputValue,
|
|
68
|
+
correctValue,
|
|
69
|
+
} from "./util/utils";
|
|
70
|
+
let GROUP = "group";
|
|
71
|
+
/**
|
|
72
|
+
* inputFormat 让整个输入机制复杂了很多。value 有以下输入路径:
|
|
73
|
+
* 1. 传入的 form => inputFormat 处理
|
|
74
|
+
* 2. updateForm => inputFormat 处理
|
|
75
|
+
* 3. 但 content 中的 default 没法经过 inputFormat 处理,因为 inputFormat 要接受整个 value 作为参数
|
|
76
|
+
* 4. 组件内部更新 value,不需要走 inputFormat
|
|
77
|
+
*/
|
|
78
|
+
let value = reactive({}); // 表单数据对象
|
|
79
|
+
let options = reactive({});
|
|
80
|
+
let initValue = reactive({});
|
|
81
|
+
let myelForm = ref();
|
|
82
|
+
let methods = {};
|
|
83
|
+
const customComponent = ref([]);
|
|
84
|
+
let emit = defineEmits(["update:FormData"]);
|
|
85
|
+
// 注入 element ui form 方法
|
|
86
|
+
/**
|
|
87
|
+
* 与 element 相同,在 mounted 阶段存储 initValue
|
|
88
|
+
* @see https://github.com/ElemeFE/element/blob/6ec5f8e900ff698cf30e9479d692784af836a108/packages/form/src/form-item.vue#L304
|
|
89
|
+
*/
|
|
90
|
+
onMounted(async () => {
|
|
91
|
+
initValue = _clonedeep(value);
|
|
92
|
+
await nextTick();
|
|
93
|
+
// 检查 myelForm 是否已经初始化
|
|
94
|
+
if (myelForm && myelForm.value) {
|
|
95
|
+
Object.keys(myelForm.value).forEach((item) => {
|
|
96
|
+
// 检查属性是否存在于 methods 对象中
|
|
97
|
+
if (myelForm.value[item] && !(item in methods)) {
|
|
98
|
+
methods[item] = myelForm.value[item];
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 有些组件会 created 阶段更新初始值为合法值,这会触发 validate。目前已知的情况有:
|
|
104
|
+
* - el-select 开启 multiple 时,会更新初始值 undefined 为 []
|
|
105
|
+
* @hack
|
|
106
|
+
*/
|
|
107
|
+
methods.clearValidate();
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
let props = defineProps({
|
|
111
|
+
//表单项
|
|
112
|
+
content: {
|
|
113
|
+
type: Array,
|
|
114
|
+
required: true,
|
|
115
|
+
},
|
|
116
|
+
// 禁用
|
|
117
|
+
disabled: {
|
|
118
|
+
type: [Boolean, Function],
|
|
119
|
+
default: false,
|
|
120
|
+
},
|
|
121
|
+
//只读
|
|
122
|
+
readonly: {
|
|
123
|
+
type: Boolean,
|
|
124
|
+
default: false,
|
|
125
|
+
},
|
|
126
|
+
/**
|
|
127
|
+
* v-model 的值。传入后会优先使用
|
|
128
|
+
*/
|
|
129
|
+
FormData: {
|
|
130
|
+
type: Object,
|
|
131
|
+
default: undefined,
|
|
132
|
+
},
|
|
133
|
+
});
|
|
134
|
+
//兼容处理
|
|
135
|
+
let innerContent = computed(() => transformContent(props.content));
|
|
136
|
+
// 初始化默认值
|
|
137
|
+
let setValueFromModel = () => {
|
|
138
|
+
if (innerContent.length) return;
|
|
139
|
+
/**
|
|
140
|
+
* 没使用 v-model 时才从 default 采集数据
|
|
141
|
+
* default 值没法考虑 inputFormat
|
|
142
|
+
* 参考 value-format.md 的案例。那种情况下,default 该传什么?
|
|
143
|
+
*/
|
|
144
|
+
let newValue = props.FormData
|
|
145
|
+
? transformInputValue(props.FormData, innerContent.value)
|
|
146
|
+
: collect(innerContent.value, "default");
|
|
147
|
+
correctValue(newValue, innerContent.value);
|
|
148
|
+
if (!_isequal(value, newValue)) value = Object.assign(value, newValue);
|
|
149
|
+
};
|
|
150
|
+
// v-model初始化默认数据
|
|
151
|
+
watch(
|
|
152
|
+
() => props.FormData,
|
|
153
|
+
(newForm) => {
|
|
154
|
+
if (!newForm) return;
|
|
155
|
+
setValueFromModel();
|
|
156
|
+
},
|
|
157
|
+
{ immediate: true, deep: true }
|
|
158
|
+
);
|
|
159
|
+
// 初始化默认数据
|
|
160
|
+
watch(
|
|
161
|
+
innerContent,
|
|
162
|
+
(newContent) => {
|
|
163
|
+
try {
|
|
164
|
+
if (!newContent) return;
|
|
165
|
+
|
|
166
|
+
// 如果 content 没有变动 remote 的部分,这里需要保留之前 remote 注入的 options
|
|
167
|
+
Object.assign(options, collect(newContent, "options"));
|
|
168
|
+
setValueFromModel();
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.log(error);
|
|
171
|
+
}
|
|
172
|
+
},
|
|
173
|
+
{ immediate: true }
|
|
174
|
+
);
|
|
175
|
+
|
|
176
|
+
// v-model 传递值
|
|
177
|
+
watch(value, (newValue, oldValue) => {
|
|
178
|
+
try {
|
|
179
|
+
if (!newValue) return;
|
|
180
|
+
if (props.FormData) {
|
|
181
|
+
let data = Object.assign(
|
|
182
|
+
props.FormData,
|
|
183
|
+
transformOutputValue(newValue, innerContent)
|
|
184
|
+
);
|
|
185
|
+
emit("update:FormData", data);
|
|
186
|
+
}
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.log(error, "-----");
|
|
189
|
+
}
|
|
190
|
+
// deep: true, // updateValue 是全量更新,所以不用
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 更新表单数据
|
|
195
|
+
* @param {String} options.id 表单ID
|
|
196
|
+
* @param {All} options.value 表单数据
|
|
197
|
+
*/
|
|
198
|
+
let updateValue = ({ id, value: v }) => {
|
|
199
|
+
value[id] = v;
|
|
200
|
+
};
|
|
201
|
+
/**
|
|
202
|
+
* 重置表单为初始值
|
|
203
|
+
*
|
|
204
|
+
* @public
|
|
205
|
+
*/
|
|
206
|
+
let resetFields = async () => {
|
|
207
|
+
/**
|
|
208
|
+
* 之所以不用 el-form 的 resetFields 机制,有以下原因:
|
|
209
|
+
* - el-form 的 resetFields 无视 el-form-renderer 的自定义组件
|
|
210
|
+
* - el-form 的 resetFields 不会触发 input & change 事件,无法监听
|
|
211
|
+
* - bug1: https://github.com/FEMessage/el-data-table/issues/176#issuecomment-587280825
|
|
212
|
+
* - bug2:
|
|
213
|
+
* 0. 建议先在监听器 watch.value 里 console.log(v.name, oldV.name)
|
|
214
|
+
* 1. 打开 basic 示例
|
|
215
|
+
* 2. 在 label 为 name 的输入框里输入 1,此时 log:'1' ''
|
|
216
|
+
* 3. 点击 reset 按钮,此时 log 两条数据: '1' '1', '' ''
|
|
217
|
+
* 4. 因为 _isequal(v, oldV),所以没有触发 v-model 更新
|
|
218
|
+
*/
|
|
219
|
+
|
|
220
|
+
// value = _clonedeep(initValue); //不能直接修改,会改变引用地址
|
|
221
|
+
// 遍历value中的每个字段
|
|
222
|
+
for (let key in value) {
|
|
223
|
+
// 检查该字段是否在initValue中存在
|
|
224
|
+
if (initValue.hasOwnProperty(key)) {
|
|
225
|
+
// 如果存在,重置为初始值
|
|
226
|
+
value[key] = _cloneDeep(initValue[key]);
|
|
227
|
+
} else {
|
|
228
|
+
// 如果不存在,删除该字段
|
|
229
|
+
delete value[key];
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
await nextTick();
|
|
234
|
+
methods.clearValidate();
|
|
235
|
+
};
|
|
236
|
+
/**
|
|
237
|
+
* 当 strict 为 true 时,只返回设置的表单项的值, 过滤掉冗余字段, 更多请看 update-form 示例
|
|
238
|
+
* @param {{strict: Boolean}} 默认 false
|
|
239
|
+
* @return {object} key is item's id, value is item's value
|
|
240
|
+
* @public
|
|
241
|
+
*/
|
|
242
|
+
let getFormValue = ({ strict = false } = {}) => {
|
|
243
|
+
return transformOutputValue(value, innerContent, { strict });
|
|
244
|
+
};
|
|
245
|
+
/**
|
|
246
|
+
* update form values
|
|
247
|
+
* @param {object} newValue - key is item's id, value is the new value
|
|
248
|
+
* @public
|
|
249
|
+
*/
|
|
250
|
+
let updateForm = (newValue) => {
|
|
251
|
+
newValue = transformInputValue(newValue, innerContent);
|
|
252
|
+
mergeValue(value, newValue, innerContent);
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* update select options
|
|
256
|
+
* @param {string} id<br>
|
|
257
|
+
* @param {array} options
|
|
258
|
+
* @public
|
|
259
|
+
*/
|
|
260
|
+
let setOptions = (id, O) => {
|
|
261
|
+
_set(options, id, O);
|
|
262
|
+
options = Object.assign(options); // 设置之前不存在的 options 时需要重新设置响应式更新
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* get custom component
|
|
267
|
+
* @param {string} id<br>
|
|
268
|
+
* @public
|
|
269
|
+
*/
|
|
270
|
+
const getComponentById = (id) => {
|
|
271
|
+
let content = [];
|
|
272
|
+
props.content.forEach((item) => {
|
|
273
|
+
if (item.type === GROUP) {
|
|
274
|
+
const items = item.items.map((formItem) => {
|
|
275
|
+
formItem.groupId = item.id;
|
|
276
|
+
return formItem;
|
|
277
|
+
});
|
|
278
|
+
content.push(...items);
|
|
279
|
+
} else {
|
|
280
|
+
content.push(item);
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
const itemContent = content.find((item) => item.id === id);
|
|
284
|
+
if (!itemContent) {
|
|
285
|
+
return undefined;
|
|
286
|
+
}
|
|
287
|
+
if (!itemContent.groupId) {
|
|
288
|
+
return customComponent.value[id].customComponent;
|
|
289
|
+
} else {
|
|
290
|
+
const componentRef =
|
|
291
|
+
customComponent.value[itemContent.groupId].customComponent;
|
|
292
|
+
return componentRef[`formItem-${id}`].customComponent;
|
|
293
|
+
}
|
|
294
|
+
};
|
|
295
|
+
provide(methodsSymbol, methods);
|
|
296
|
+
provide(updateFormsSymbol, updateForm);
|
|
297
|
+
provide(setOptionsSymbol, setOptions);
|
|
298
|
+
defineExpose({
|
|
299
|
+
...methods,
|
|
300
|
+
updateValue,
|
|
301
|
+
resetFields,
|
|
302
|
+
getFormValue,
|
|
303
|
+
updateForm,
|
|
304
|
+
setOptions,
|
|
305
|
+
methods,
|
|
306
|
+
getComponentById,
|
|
307
|
+
});
|
|
308
|
+
</script>
|
|
309
|
+
<script>
|
|
310
|
+
export default {
|
|
311
|
+
name: "ElFormRenderer",
|
|
312
|
+
};
|
|
313
|
+
</script>
|
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { h, resolveComponent } from "vue";
|
|
2
|
-
|
|
3
|
-
// Vue3 中函数式组件需要提供一个渲染函数
|
|
4
|
-
const CustomComponent = (props, context) => {
|
|
5
|
-
let dom =
|
|
6
|
-
typeof props.component == "string"
|
|
7
|
-
? resolveComponent(hyphenToPascal(props.component))
|
|
8
|
-
: "Input";
|
|
9
|
-
|
|
10
|
-
// 返回一个渲染函数,可以使用 h 函数创建虚拟节点
|
|
11
|
-
return h(dom, props, context.slots);
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
function hyphenToPascal(str) {
|
|
15
|
-
let arr = str.split("-");
|
|
16
|
-
|
|
17
|
-
let resStr = arr.reduce(function (prev, cur) {
|
|
18
|
-
let str = prev + cur.slice(0, 1).toUpperCase() + cur.slice(1);
|
|
19
|
-
return str;
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
// 转小驼峰这一行不需要
|
|
23
|
-
resStr = resStr.slice(0, 1).toUpperCase() + resStr.slice(1);
|
|
24
|
-
|
|
25
|
-
return resStr;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export default CustomComponent;
|
|
1
|
+
import { h, resolveComponent } from "vue";
|
|
2
|
+
|
|
3
|
+
// Vue3 中函数式组件需要提供一个渲染函数
|
|
4
|
+
const CustomComponent = (props, context) => {
|
|
5
|
+
let dom =
|
|
6
|
+
typeof props.component == "string"
|
|
7
|
+
? resolveComponent(hyphenToPascal(props.component))
|
|
8
|
+
: "Input";
|
|
9
|
+
|
|
10
|
+
// 返回一个渲染函数,可以使用 h 函数创建虚拟节点
|
|
11
|
+
return h(dom, props, context.slots);
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
function hyphenToPascal(str) {
|
|
15
|
+
let arr = str.split("-");
|
|
16
|
+
|
|
17
|
+
let resStr = arr.reduce(function (prev, cur) {
|
|
18
|
+
let str = prev + cur.slice(0, 1).toUpperCase() + cur.slice(1);
|
|
19
|
+
return str;
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// 转小驼峰这一行不需要
|
|
23
|
+
resStr = resStr.slice(0, 1).toUpperCase() + resStr.slice(1);
|
|
24
|
+
|
|
25
|
+
return resStr;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default CustomComponent;
|