el-form-renderer-vue3 1.0.6 → 1.0.8

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.
@@ -1,293 +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
- import {
58
- collect,
59
- mergeValue,
60
- transformOutputValue,
61
- transformInputValue,
62
- correctValue,
63
- } from "./util/utils";
64
- let GROUP = "group";
65
- /**
66
- * inputFormat 让整个输入机制复杂了很多。value 有以下输入路径:
67
- * 1. 传入的 form => inputFormat 处理
68
- * 2. updateForm => inputFormat 处理
69
- * 3. 但 content 中的 default 没法经过 inputFormat 处理,因为 inputFormat 要接受整个 value 作为参数
70
- * 4. 组件内部更新 value,不需要走 inputFormat
71
- */
72
- let value = reactive({}); // 表单数据对象
73
- let options = reactive({});
74
- let initValue = reactive({});
75
- let myelForm = ref();
76
- let methods = {};
77
- const customComponent = ref([]);
78
- let emit = defineEmits(["update:FormData"]);
79
- // 注入 element ui form 方法
80
- /**
81
- * element 相同,在 mounted 阶段存储 initValue
82
- * @see https://github.com/ElemeFE/element/blob/6ec5f8e900ff698cf30e9479d692784af836a108/packages/form/src/form-item.vue#L304
83
- */
84
- onMounted(async () => {
85
- initValue = _clonedeep(value);
86
- await nextTick();
87
- // 检查 myelForm 是否已经初始化
88
- if (myelForm && myelForm.value) {
89
- Object.keys(myelForm.value).forEach((item) => {
90
- // 检查属性是否存在于 methods 对象中
91
- if (myelForm.value[item] && !(item in methods)) {
92
- methods[item] = myelForm.value[item];
93
- }
94
- });
95
- }
96
- /**
97
- * 有些组件会 created 阶段更新初始值为合法值,这会触发 validate。目前已知的情况有:
98
- * - el-select 开启 multiple 时,会更新初始值 undefined 为 []
99
- * @hack
100
- */
101
- methods.clearValidate();
102
- });
103
-
104
- let props = defineProps({
105
- //表单项
106
- content: {
107
- type: Array,
108
- required: true,
109
- },
110
- // 禁用
111
- disabled: {
112
- type: [Boolean, Function],
113
- default: false,
114
- },
115
- //只读
116
- readonly: {
117
- type: Boolean,
118
- default: false,
119
- },
120
- /**
121
- * v-model 的值。传入后会优先使用
122
- */
123
- FormData: {
124
- type: Object,
125
- default: undefined,
126
- },
127
- });
128
- //兼容处理
129
- let innerContent = computed(() => transformContent(props.content));
130
- // 初始化默认值
131
- let setValueFromModel = () => {
132
- if (innerContent.length) return;
133
- /**
134
- * 没使用 v-model 时才从 default 采集数据
135
- * default 值没法考虑 inputFormat
136
- * 参考 value-format.md 的案例。那种情况下,default 该传什么?
137
- */
138
- let newValue = props.FormData
139
- ? transformInputValue(props.FormData, innerContent.value)
140
- : collect(innerContent.value, "default");
141
- correctValue(newValue, innerContent.value);
142
- if (!_isequal(value, newValue)) value = Object.assign(value, newValue);
143
- };
144
- // v-model初始化默认数据
145
- watch(
146
- () => props.FormData,
147
- (newForm) => {
148
- if (!newForm) return;
149
- setValueFromModel();
150
- },
151
- { immediate: true, deep: true }
152
- );
153
- // 初始化默认数据
154
- watch(
155
- innerContent,
156
- (newContent) => {
157
- try {
158
- if (!newContent) return;
159
-
160
- // 如果 content 没有变动 remote 的部分,这里需要保留之前 remote 注入的 options
161
- Object.assign(options, collect(newContent, "options"));
162
- setValueFromModel();
163
- } catch (error) {
164
- console.log(error);
165
- }
166
- },
167
- { immediate: true }
168
- );
169
-
170
- // v-model 传递值
171
- watch(value, (newValue, oldValue) => {
172
- try {
173
- if (!newValue) return;
174
- if (props.FormData) {
175
- let data = Object.assign(
176
- props.FormData,
177
- transformOutputValue(newValue, innerContent)
178
- );
179
- emit("update:FormData", data);
180
- }
181
- } catch (error) {
182
- console.log(error, "-----");
183
- }
184
- // deep: true, // updateValue 是全量更新,所以不用
185
- });
186
-
187
- /**
188
- * 更新表单数据
189
- * @param {String} options.id 表单ID
190
- * @param {All} options.value 表单数据
191
- */
192
- let updateValue = ({ id, value: v }) => {
193
- value[id] = v;
194
- };
195
- /**
196
- * 重置表单为初始值
197
- *
198
- * @public
199
- */
200
- let resetFields = async () => {
201
- /**
202
- * 之所以不用 el-form 的 resetFields 机制,有以下原因:
203
- * - el-form 的 resetFields 无视 el-form-renderer 的自定义组件
204
- * - el-form 的 resetFields 不会触发 input & change 事件,无法监听
205
- * - bug1: https://github.com/FEMessage/el-data-table/issues/176#issuecomment-587280825
206
- * - bug2:
207
- * 0. 建议先在监听器 watch.value 里 console.log(v.name, oldV.name)
208
- * 1. 打开 basic 示例
209
- * 2. label name 的输入框里输入 1,此时 log:'1' ''
210
- * 3. 点击 reset 按钮,此时 log 两条数据: '1' '1', '' ''
211
- * 4. 因为 _isequal(v, oldV),所以没有触发 v-model 更新
212
- */
213
- value = _clonedeep(initValue);
214
- await nextTick();
215
- methods.clearValidate();
216
- };
217
- /**
218
- * 当 strict 为 true 时,只返回设置的表单项的值, 过滤掉冗余字段, 更多请看 update-form 示例
219
- * @param {{strict: Boolean}} 默认 false
220
- * @return {object} key is item's id, value is item's value
221
- * @public
222
- */
223
- let getFormValue = ({ strict = false } = {}) => {
224
- return transformOutputValue(value, innerContent, { strict });
225
- };
226
- /**
227
- * update form values
228
- * @param {object} newValue - key is item's id, value is the new value
229
- * @public
230
- */
231
- let updateForm = (newValue) => {
232
- newValue = transformInputValue(newValue, innerContent);
233
- mergeValue(value, newValue, innerContent);
234
- };
235
- /**
236
- * update select options
237
- * @param {string} id<br>
238
- * @param {array} options
239
- * @public
240
- */
241
- let setOptions = (id, O) => {
242
- _set(options, id, O);
243
- options = Object.assign(options); // 设置之前不存在的 options 时需要重新设置响应式更新
244
- };
245
-
246
- /**
247
- * get custom component
248
- * @param {string} id<br>
249
- * @public
250
- */
251
- const getComponentById = (id) => {
252
- let content = [];
253
- props.content.forEach((item) => {
254
- if (item.type === GROUP) {
255
- const items = item.items.map((formItem) => {
256
- formItem.groupId = item.id;
257
- return formItem;
258
- });
259
- content.push(...items);
260
- } else {
261
- content.push(item);
262
- }
263
- });
264
- const itemContent = content.find((item) => item.id === id);
265
- if (!itemContent) {
266
- return undefined;
267
- }
268
- if (!itemContent.groupId) {
269
- return customComponent.value[id].customComponent;
270
- } else {
271
- const componentRef =
272
- customComponent.value[itemContent.groupId].customComponent;
273
- return componentRef[`formItem-${id}`].customComponent;
274
- }
275
- };
276
- provide("methods", methods);
277
- provide("updateForm", updateForm);
278
- provide("setOptions", setOptions);
279
- defineExpose({
280
- updateValue,
281
- resetFields,
282
- getFormValue,
283
- updateForm,
284
- setOptions,
285
- methods,
286
- getComponentById,
287
- });
288
- </script>
289
- <script>
290
- export default {
291
- name: "ElFormRenderer",
292
- };
293
- </script>
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>