el-form-renderer-vue3 1.1.2 → 2.0.2
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/LICENSE +21 -0
- package/README.en.md +36 -0
- package/README.md +11 -15
- package/dist/el-form-renderer-vue3.cjs.js +6 -0
- package/dist/el-form-renderer-vue3.es.js +4375 -0
- package/dist/el-form-renderer-vue3.iife.js +6 -0
- package/dist/el-form-renderer-vue3.umd.js +6 -0
- package/dist/qq.jpg +0 -0
- package/package.json +136 -8
- package/el-form-renderer-vue3.es.js +0 -4041
- package/el-form-renderer-vue3.umd.js +0 -5
- package/femessage/components/render-form-group.vue +0 -46
- package/femessage/components/render-form-item.vue +0 -335
- package/femessage/el-form-renderer.vue +0 -313
- package/femessage/util/CustomComponent.js +0 -28
- package/femessage/util/VNode.js +0 -9
- package/femessage/util/enable-when.js +0 -26
- package/femessage/util/keys.js +0 -3
- package/femessage/util/ployfill.js +0 -14
- package/femessage/util/transform-content.js +0 -47
- package/femessage/util/utils.js +0 -126
- /package/{vite.svg → dist/vite.svg} +0 -0
|
@@ -1,313 +0,0 @@
|
|
|
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 = () => {
|
|
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
|
-
value[key]=undefined
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
|
|
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 +0,0 @@
|
|
|
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;
|
package/femessage/util/VNode.js
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import _get from "lodash.get";
|
|
2
|
-
import _has from "lodash.has";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* 处理 enableWhen
|
|
6
|
-
*
|
|
7
|
-
* 与条件: 简单依赖关系存在2种情况:简单对象 || 字符串
|
|
8
|
-
* 或条件: 即使用 [] 包裹所有与条件 enableWhen: [{ a: 1 }, { a: 2 }]
|
|
9
|
-
*/
|
|
10
|
-
export default function getEnableWhenStatus(enableWhen, value) {
|
|
11
|
-
if (!enableWhen) return true;
|
|
12
|
-
// 处理一个与条件
|
|
13
|
-
const handleCondition = (condition) => {
|
|
14
|
-
// 简单字符串(ID), 只要有值即为true
|
|
15
|
-
if (typeof condition === "string") return _has(value, condition);
|
|
16
|
-
// 简单对象判断: 是否所有依赖条件都通过
|
|
17
|
-
return Object.keys(condition).every((path) => {
|
|
18
|
-
const v = _get(value, path);
|
|
19
|
-
return v !== undefined && v === condition[path];
|
|
20
|
-
});
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
return Array.isArray(enableWhen)
|
|
24
|
-
? enableWhen.some(handleCondition)
|
|
25
|
-
: handleCondition(enableWhen);
|
|
26
|
-
}
|
package/femessage/util/keys.js
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
//去除谷歌浏览器的scroll、wheel等事件警告
|
|
2
|
-
(function () {
|
|
3
|
-
if (typeof EventTarget !== "undefined") {
|
|
4
|
-
let func = EventTarget.prototype.addEventListener;
|
|
5
|
-
EventTarget.prototype.addEventListener = function (type, fn, capture) {
|
|
6
|
-
this.func = func;
|
|
7
|
-
if (typeof capture !== "boolean") {
|
|
8
|
-
capture = capture || {};
|
|
9
|
-
capture.passive = false;
|
|
10
|
-
}
|
|
11
|
-
this.func(type, fn, capture);
|
|
12
|
-
};
|
|
13
|
-
}
|
|
14
|
-
})();
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import _kebabcase from "lodash.kebabcase";
|
|
2
|
-
export default function transformContent(content) {
|
|
3
|
-
return content.map(({ ...item }) => {
|
|
4
|
-
if (item.type === "group") {
|
|
5
|
-
item.items = transformContent(item.items);
|
|
6
|
-
} else {
|
|
7
|
-
removeDollarInKey(item);
|
|
8
|
-
setItemId(item);
|
|
9
|
-
extractRulesFromComponent(item);
|
|
10
|
-
// 有些旧写法是 checkboxGroup & radioGroup
|
|
11
|
-
// 转换字符串string为kebab case.(foo-bar)
|
|
12
|
-
item.type = _kebabcase(item.type);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
return item;
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
// 兼容旧写法:$id、$name
|
|
19
|
-
function removeDollarInKey(item) {
|
|
20
|
-
Object.keys(item)
|
|
21
|
-
// 首先检查属性名是否以"$"开头,然后检查去掉"$"后的属性名是否不在对象item中。
|
|
22
|
-
.filter((k) => k.startsWith("$") && !(k.slice(1) in item))
|
|
23
|
-
// 将对象item中的k属性的值赋给item[k.slice(1)],然后删除原来的k属性,以实现将"$"去除的操作。
|
|
24
|
-
.forEach((k) => ((item[k.slice(1)] = item[k]), delete item[k]));
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// 为一个对象 item 设置一个 id 属性,但只有在 item 的 id 属性不存在时才会执行。
|
|
28
|
-
function setItemId(item) {
|
|
29
|
-
if (item.id) return;
|
|
30
|
-
// name 是符合表单项直觉的命名; prop 是为了与 element 的 table 的 columns 匹配
|
|
31
|
-
item.id = item.name || item.prop;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// 其作用是从给定的 item 参数中提取规则并将其添加到 item.rules 属性中
|
|
35
|
-
export function extractRulesFromComponent(item) {
|
|
36
|
-
// 是否覆盖自定义组件内置的校验规则;(overrideRules:true不校验组件内规则)
|
|
37
|
-
if (item.overrideRules) return;
|
|
38
|
-
const { component } = item;
|
|
39
|
-
|
|
40
|
-
// 使用全局注册的组件暂时无法处理(处理自定义组件内的rules)
|
|
41
|
-
if (!component || typeof component === "string") return;
|
|
42
|
-
const { rules = [] } = component;
|
|
43
|
-
item.rules = [
|
|
44
|
-
...(item.rules || []),
|
|
45
|
-
...(typeof rules === "function" ? rules(item) : rules),
|
|
46
|
-
];
|
|
47
|
-
}
|
package/femessage/util/utils.js
DELETED
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
import _isplainobject from "lodash.isplainobject";
|
|
2
|
-
// var pairs = [
|
|
3
|
-
// ["a", 1],
|
|
4
|
-
// ["b", 2],
|
|
5
|
-
// ["c", 3],
|
|
6
|
-
// ];==》{ 'a': 1, 'b': 2, 'c': 3 }
|
|
7
|
-
import _frompairs from "lodash.frompairs";
|
|
8
|
-
// collect 函数接受两个参数,content 和 key,其中 content 是一个包含嵌套数据的数组,key 是要收集的键的名称。(key:options| defult)
|
|
9
|
-
export function collect(content, key) {
|
|
10
|
-
return _frompairs(
|
|
11
|
-
content
|
|
12
|
-
|
|
13
|
-
// 使用 map 函数对 content 数组进行映射操作。对每个数组中的元素(item)进行处理,创建一个新的对象,该对象包含三个属性:
|
|
14
|
-
.map((item) => ({
|
|
15
|
-
id: item.id,
|
|
16
|
-
type: item.type,
|
|
17
|
-
value: item.type === "group" ? collect(item.items, key) : item[key],
|
|
18
|
-
}))
|
|
19
|
-
// 函数对上一步生成的对象数组进行过滤。只保留那些满足以下条件的对象:(带有optios数据的)
|
|
20
|
-
.filter(
|
|
21
|
-
({ type, value }) =>
|
|
22
|
-
value !== undefined || (type === "group" && Object.keys(value).length)
|
|
23
|
-
)
|
|
24
|
-
// 返回 [select(id),optons]
|
|
25
|
-
.map(({ id, value }) => [id, value])
|
|
26
|
-
);
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* 根据 content 中的 outputFormat 来处理 value;
|
|
30
|
-
* 如果 outputFormat 处理后的值是对象类型,会覆盖(Object.assign)到 value 上
|
|
31
|
-
*/
|
|
32
|
-
|
|
33
|
-
// value 是待处理的值,content 是一个数组,strict 是一个可选的参数,默认为 false。(当 strict 为 true 时,只返回设置的表单项的值, 过滤掉冗余字段, )
|
|
34
|
-
export function transformOutputValue(value, content, { strict = false } = {}) {
|
|
35
|
-
// 参数的值创建一个新的对象 newVal,如果 strict 为 true,则创建一个空对象,否则创建一个与输入 value 一样的对象的副本。
|
|
36
|
-
const newVal = strict ? {} : { ...value };
|
|
37
|
-
|
|
38
|
-
Object.keys(value).forEach((id) => {
|
|
39
|
-
// 找出表单项
|
|
40
|
-
const item = content.value.find((item) => item.id === id);
|
|
41
|
-
if (!item) return;
|
|
42
|
-
// 除去多选
|
|
43
|
-
if (item.type !== "group") {
|
|
44
|
-
// 用于处理输出值,参数为对应组件返回值
|
|
45
|
-
// 如果处理后的值是对象类型,会覆盖(Object.assign)到整个表单的值上
|
|
46
|
-
if (item.outputFormat) {
|
|
47
|
-
const v = item.outputFormat(value[id]);
|
|
48
|
-
// REVIEW: 仅根据 format 后的类型来判断赋值形式,有些隐晦
|
|
49
|
-
// (boolean): 如果 value 为一个普通对象,那么返回 true,否则返回 false(({ 'x': 0, 'y': 0 }); (Object.create(null));)
|
|
50
|
-
if (_isplainobject(v)) Object.assign(newVal, v);
|
|
51
|
-
else newVal[id] = v;
|
|
52
|
-
} else {
|
|
53
|
-
// 如果 item 没有 outputFormat 属性,直接将 value[id] 赋值给 newVal[id]。
|
|
54
|
-
newVal[id] = value[id];
|
|
55
|
-
}
|
|
56
|
-
} else {
|
|
57
|
-
// 多选递归处理
|
|
58
|
-
newVal[id] = transformOutputValue(value[id], item.items, { strict });
|
|
59
|
-
}
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
return newVal;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/**
|
|
66
|
-
* 根据 content 中的 inputFormat 来处理 value
|
|
67
|
-
* inputFormat 接受的是当前层级的 value
|
|
68
|
-
* 复杂点在于,不管传入的 value 是否包含某表单项的 key,所有使用了 inputFormat 的项都有可能在这次 update 中被更新
|
|
69
|
-
*/
|
|
70
|
-
export function transformInputValue(value, content) {
|
|
71
|
-
// 首先,创建了一个名为 newVal 的新对象,它是 value 的副本,以便在不修改原始数据的情况下进行操作。
|
|
72
|
-
const newVal = { ...value };
|
|
73
|
-
const processItem = (item) => {
|
|
74
|
-
const { id } = item;
|
|
75
|
-
if (item.inputFormat) {
|
|
76
|
-
// 对于每个 item,它检查是否存在 inputFormat 属性。如果存在,它将调用 item.inputFormat(value) 来处理 value,
|
|
77
|
-
// 然后将处理后的结果赋值给 newVal 的相应属性(根据 id 来确定属性名),但仅在处理后的值不为 undefined 时才会进行赋值。
|
|
78
|
-
const v = item.inputFormat(value);
|
|
79
|
-
if (v !== undefined) newVal[id] = v;
|
|
80
|
-
} else if (id in value) {
|
|
81
|
-
// 如果 item 没有 inputFormat 属性,它会检查是否 value 中存在与 item 的 id 相匹配的属性。如果存在,它会将该属性的值赋值给 newVal 的相应属性。
|
|
82
|
-
if (item.type !== "group") {
|
|
83
|
-
newVal[id] = value[id];
|
|
84
|
-
} else {
|
|
85
|
-
// 如果 item 的类型是 "group",则会递归调用 transformInputValue 函数来处理嵌套的对象。
|
|
86
|
-
newVal[id] = transformInputValue(value[id], item.items);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
};
|
|
90
|
-
// 判断响应式数据的类型
|
|
91
|
-
const itemsToProcess = content.value || content;
|
|
92
|
-
itemsToProcess.forEach(processItem);
|
|
93
|
-
return newVal;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// 对 group checkbox-group初始化值修正默认为空数组
|
|
97
|
-
export function correctValue(value, content) {
|
|
98
|
-
content.forEach(({ type, id, items }) => {
|
|
99
|
-
switch (type) {
|
|
100
|
-
case "group":
|
|
101
|
-
if (!(id in value)) value[id] = {};
|
|
102
|
-
correctValue(value[id], items);
|
|
103
|
-
break;
|
|
104
|
-
case "checkbox-group":
|
|
105
|
-
if (!(id in value)) value[id] = [];
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* 递归合并 oldV & newV,策略如下:
|
|
112
|
-
* 1. 如果该项的 type 不是 GROUP,直接覆盖合并到 oldV
|
|
113
|
-
* 2. 如果是,则递归执行步骤 1
|
|
114
|
-
*/
|
|
115
|
-
export function mergeValue(oldV, newV, content) {
|
|
116
|
-
// 遍历 newV 对象的所有属性
|
|
117
|
-
Object.keys(newV).forEach((k) => {
|
|
118
|
-
// 对于每个属性 k,首先尝试在 content 数组中查找具有相同 id 值的项,如果找不到则使用一个空对象。
|
|
119
|
-
const item = content.value.find((item) => item.id === k) || {};
|
|
120
|
-
// 如果不是 "group" 类型, 就直接将 newV 中的属性值覆盖到 oldV 中的对应属性上,实现合并。
|
|
121
|
-
if (item.type !== "group") oldV[k] = newV[k];
|
|
122
|
-
// 如果项的类型是 "group",则递归调用这个函数 mergeValue,以进一步合并 oldV[k] 和 newV[k],并传入该项的子项数组 item.items 作为 content 参数。
|
|
123
|
-
else mergeValue(oldV[k], newV[k], item.items);
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
export function noop() {}
|
|
File without changes
|