iov-pro-components 0.0.3
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/.eslintignore +5 -0
- package/.eslintrc.js +192 -0
- package/.gitignore +3 -0
- package/README.md +4 -0
- package/babel.config.js +5 -0
- package/docs/.vuepress/config.js +169 -0
- package/docs/.vuepress/styles/index.styl +62 -0
- package/docs/.vuepress/styles/palette.styl +20 -0
- package/docs/.vuepress/theme/enhanceApp.js +100 -0
- package/docs/.vuepress/theme/index.js +3 -0
- package/docs/README.md +13 -0
- package/docs/components/description.md +519 -0
- package/docs/components/dialog-select.md +91 -0
- package/docs/components/display.md +36 -0
- package/docs/components/enums.md +33 -0
- package/docs/components/icon.md +406 -0
- package/docs/components/link-group.md +39 -0
- package/docs/components/page-detail.md +48 -0
- package/docs/components/page-module.md +51 -0
- package/docs/components/pro-form.md +958 -0
- package/docs/components/pro-table.md +683 -0
- package/docs/components/request.md +44 -0
- package/docs/components/search-table.md +963 -0
- package/docs/components/space.md +35 -0
- package/docs/components/sub-title.md +24 -0
- package/docs/components/submit-module.md +24 -0
- package/docs/template/add.md +124 -0
- package/docs/template/confirm.md +28 -0
- package/docs/template/detail.md +240 -0
- package/docs/template/dialog.md +339 -0
- package/docs/template/list.md +464 -0
- package/docs/template/tabs-mini.md +32 -0
- package/docs/template/tabs.md +32 -0
- package/jsconfig.json +19 -0
- package/lib/iov-pro-components.css +1 -0
- package/lib/iov-pro-components.min.js +7 -0
- package/lib/postcss.config.js +8 -0
- package/package.json +75 -0
- package/patches/vue-server-renderer+2.7.16.patch +13 -0
- package/rollup.config.mjs +79 -0
- package/src/App.vue +103 -0
- package/src/main.js +33 -0
- package/src/packages/column-tooltip/index.js +7 -0
- package/src/packages/column-tooltip/src/main.vue +127 -0
- package/src/packages/description/index.js +7 -0
- package/src/packages/description/src/main.vue +375 -0
- package/src/packages/description/src/text.vue +103 -0
- package/src/packages/dialog-select/index.js +7 -0
- package/src/packages/dialog-select/src/main.vue +308 -0
- package/src/packages/display/index.js +7 -0
- package/src/packages/display/src/main.vue +44 -0
- package/src/packages/enums/index.js +7 -0
- package/src/packages/enums/src/main.vue +23 -0
- package/src/packages/export/index.js +7 -0
- package/src/packages/export/src/main.vue +316 -0
- package/src/packages/fixed-button-group/index.js +7 -0
- package/src/packages/fixed-button-group/src/main.vue +104 -0
- package/src/packages/form/index.js +7 -0
- package/src/packages/form/src/collapse.vue +149 -0
- package/src/packages/form/src/main.vue +1190 -0
- package/src/packages/form-collapse/index.js +7 -0
- package/src/packages/index.js +86 -0
- package/src/packages/link-group/index.js +7 -0
- package/src/packages/link-group/src/main.vue +52 -0
- package/src/packages/page-detail/index.js +7 -0
- package/src/packages/page-detail/src/main.vue +123 -0
- package/src/packages/page-module/index.js +7 -0
- package/src/packages/page-module/src/main.vue +56 -0
- package/src/packages/preview/index.js +7 -0
- package/src/packages/preview/src/eval-image-viewer.js +50 -0
- package/src/packages/preview/src/image-viewer.vue +366 -0
- package/src/packages/preview/src/main.vue +97 -0
- package/src/packages/request/index.js +7 -0
- package/src/packages/request/src/main.vue +125 -0
- package/src/packages/search-table/index.js +7 -0
- package/src/packages/search-table/src/inner-tabs.vue +237 -0
- package/src/packages/search-table/src/main.vue +472 -0
- package/src/packages/search-table/src/outer-tabs.vue +45 -0
- package/src/packages/search-table-inner-tabs/index.js +7 -0
- package/src/packages/search-table-outer-tabs/index.js +7 -0
- package/src/packages/space/index.js +7 -0
- package/src/packages/space/src/main.vue +74 -0
- package/src/packages/sub-title/index.js +7 -0
- package/src/packages/sub-title/src/main.vue +70 -0
- package/src/packages/submit-module/index.js +7 -0
- package/src/packages/submit-module/src/main.vue +67 -0
- package/src/packages/table/index.js +7 -0
- package/src/packages/table/src/filter.vue +89 -0
- package/src/packages/table/src/main.vue +668 -0
- package/src/packages/table/src/search.vue +90 -0
- package/src/packages/table/src/sort.vue +118 -0
- package/src/packages/theme/index.scss +15 -0
- package/src/packages/theme/src/column-tooltip.scss +23 -0
- package/src/packages/theme/src/common/color.scss +134 -0
- package/src/packages/theme/src/description.scss +56 -0
- package/src/packages/theme/src/dialog-select.scss +32 -0
- package/src/packages/theme/src/fixed-button-group.scss +25 -0
- package/src/packages/theme/src/form.scss +11 -0
- package/src/packages/theme/src/link-group.scss +43 -0
- package/src/packages/theme/src/page-detail.scss +61 -0
- package/src/packages/theme/src/page-module.scss +46 -0
- package/src/packages/theme/src/preview.scss +67 -0
- package/src/packages/theme/src/search-table.scss +185 -0
- package/src/packages/theme/src/space.scss +12 -0
- package/src/packages/theme/src/sub-title.scss +47 -0
- package/src/packages/theme/src/submit-module.scss +13 -0
- package/src/packages/theme/src/table.scss +129 -0
- package/src/packages/theme/src/toolbar.scss +109 -0
- package/src/packages/toolbar/index.js +7 -0
- package/src/packages/toolbar/src/main.vue +126 -0
- package/src/packages/toolbar/src/setting.vue +217 -0
- package/src/packages/toolbar/src/style.vue +68 -0
- package/src/packages/toolbar/src/zoom.vue +65 -0
- package/src/router.js +83 -0
- package/src/utils/config-center.js +218 -0
- package/src/utils/function-eval.js +84 -0
- package/src/utils/index.js +104 -0
- package/src/views/column-tooltip.vue +37 -0
- package/src/views/components/OtherSelect.vue +18 -0
- package/src/views/description.vue +60 -0
- package/src/views/detail.vue +146 -0
- package/src/views/directive/number.js +82 -0
- package/src/views/enums.vue +22 -0
- package/src/views/export.vue +9 -0
- package/src/views/form-collapse.vue +185 -0
- package/src/views/form.vue +402 -0
- package/src/views/link-group.vue +16 -0
- package/src/views/preview.vue +33 -0
- package/src/views/request.vue +56 -0
- package/src/views/search-table.vue +297 -0
- package/src/views/table.vue +145 -0
- package/src/views/toolbar.vue +30 -0
- package/vue.config.js +22 -0
|
@@ -0,0 +1,1190 @@
|
|
|
1
|
+
<script>
|
|
2
|
+
import { getComponentNames, isElementUI, getFilteredDataType } from '@/utils/config-center'
|
|
3
|
+
import { toUpperCamelCase, splitColsToRow } from '@/utils'
|
|
4
|
+
import functionEval, { isEvalFunction } from '@/utils/function-eval'
|
|
5
|
+
import cloneDeep from 'lodash/cloneDeep'
|
|
6
|
+
import isEqual from 'lodash/isEqual'
|
|
7
|
+
import lodashGet from 'lodash/get'
|
|
8
|
+
import isObject from 'lodash/isObject'
|
|
9
|
+
import ProRequest from '@/packages/request'
|
|
10
|
+
import Space from '@/packages/space'
|
|
11
|
+
|
|
12
|
+
const isNil = val => [null, undefined].includes(val)
|
|
13
|
+
const isEmpty = val => [null, undefined, ''].includes(val)
|
|
14
|
+
|
|
15
|
+
// 当前允许的钩子函数
|
|
16
|
+
const HOOKS = {
|
|
17
|
+
INIT: 'init',
|
|
18
|
+
DO_LAYOUT: 'doLayout',
|
|
19
|
+
VALUE_CHANGE: 'valueChange',
|
|
20
|
+
OPTION_CHANGE: 'optionChange',
|
|
21
|
+
PROPS_CHANGE: 'propsChange'
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const DEFAULT_SPAN = 6
|
|
25
|
+
const DEFAULT_SPACE = 18
|
|
26
|
+
|
|
27
|
+
export default {
|
|
28
|
+
name: 'IovProForm',
|
|
29
|
+
inject: {
|
|
30
|
+
pageThis: { value: 'pageThis', default: null }
|
|
31
|
+
},
|
|
32
|
+
props: {
|
|
33
|
+
// 当前双向绑定的数据
|
|
34
|
+
value: {
|
|
35
|
+
type: Object,
|
|
36
|
+
default: () => ({})
|
|
37
|
+
},
|
|
38
|
+
// 外部数据源
|
|
39
|
+
dataSource: {
|
|
40
|
+
type: Object,
|
|
41
|
+
default: () => ({})
|
|
42
|
+
},
|
|
43
|
+
// 当前form-item配置
|
|
44
|
+
items: {
|
|
45
|
+
type: Array,
|
|
46
|
+
default: () => []
|
|
47
|
+
},
|
|
48
|
+
// 当前表单配置
|
|
49
|
+
config: {
|
|
50
|
+
type: Object,
|
|
51
|
+
default: () => ({
|
|
52
|
+
grid: false
|
|
53
|
+
})
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
data() {
|
|
57
|
+
return {
|
|
58
|
+
DEFAULT_SPAN,
|
|
59
|
+
// 当前表单数据
|
|
60
|
+
modelValue: {},
|
|
61
|
+
// 当前options请求的参数
|
|
62
|
+
optionParams: {},
|
|
63
|
+
// 被外部过滤后的数据
|
|
64
|
+
filteredData: {},
|
|
65
|
+
// 可以渲染的表单项配置
|
|
66
|
+
formItemsForRender: [],
|
|
67
|
+
// 上一次的缓存数据
|
|
68
|
+
prevState: {
|
|
69
|
+
value: cloneDeep(this.value || {}),
|
|
70
|
+
dataSource: cloneDeep(this.dataSource || {}),
|
|
71
|
+
optionParams: {},
|
|
72
|
+
modelValue: {}
|
|
73
|
+
},
|
|
74
|
+
// 需要触发的钩子函数
|
|
75
|
+
hooks: {
|
|
76
|
+
[HOOKS.INIT]: [],
|
|
77
|
+
[HOOKS.DO_LAYOUT]: [],
|
|
78
|
+
[HOOKS.VALUE_CHANGE]: [],
|
|
79
|
+
[HOOKS.OPTION_CHANGE]: [],
|
|
80
|
+
[HOOKS.PROPS_CHANGE]: []
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
computed: {
|
|
85
|
+
/**
|
|
86
|
+
* 表单的属性
|
|
87
|
+
* @time 2024-10-31 16:44:24
|
|
88
|
+
*/
|
|
89
|
+
formProps() {
|
|
90
|
+
return this.normalizeFormProps()
|
|
91
|
+
},
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* 当前所有的form-item集合
|
|
95
|
+
* @time 2024-11-04 17:23:02
|
|
96
|
+
*/
|
|
97
|
+
formItems() {
|
|
98
|
+
return this.normalizeFormItems(this.items)
|
|
99
|
+
}
|
|
100
|
+
},
|
|
101
|
+
watch: {
|
|
102
|
+
/**
|
|
103
|
+
* 如果表单数据的变化,则v-model双向绑定外部数据
|
|
104
|
+
* @time 2024-11-01 15:17:51
|
|
105
|
+
*/
|
|
106
|
+
modelValue: {
|
|
107
|
+
handler() {
|
|
108
|
+
// 当前值
|
|
109
|
+
const value = cloneDeep(this.value)
|
|
110
|
+
// 开始遍历数据
|
|
111
|
+
Object.keys(this.modelValue).forEach(key => {
|
|
112
|
+
// 获取字段名称
|
|
113
|
+
const fieldName = this.formItems.find(item => item.key === key)?.name
|
|
114
|
+
// 如果当前是数组
|
|
115
|
+
if (Array.isArray(fieldName)) {
|
|
116
|
+
// 当前数组的值
|
|
117
|
+
const modelValue = this.modelValue[key] || []
|
|
118
|
+
// 开始取值
|
|
119
|
+
fieldName.forEach((k, i) => {
|
|
120
|
+
// 直接赋值
|
|
121
|
+
value[k] = modelValue[i]
|
|
122
|
+
})
|
|
123
|
+
} else {
|
|
124
|
+
// 直接赋值
|
|
125
|
+
if (fieldName) {
|
|
126
|
+
value[fieldName] = this.modelValue[key]
|
|
127
|
+
} else {
|
|
128
|
+
value[key] = this.modelValue[key]
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
})
|
|
132
|
+
// 如果2个对象相等,则不处理
|
|
133
|
+
if (isEqual(this.prevState.value, value)) {
|
|
134
|
+
return
|
|
135
|
+
}
|
|
136
|
+
// 触发值改变钩子函数
|
|
137
|
+
this.triggerHooks(HOOKS.VALUE_CHANGE, value)
|
|
138
|
+
// 上一次value的值
|
|
139
|
+
const prevStateValue = this.prevState.value
|
|
140
|
+
// 记录当前双向绑定的数据(当前组件内部均使用此数据,减少render数量)
|
|
141
|
+
this.prevState.value = cloneDeep(value)
|
|
142
|
+
// 触发watch对应的方法,并且深拷贝一份数据
|
|
143
|
+
// 表单值改变后,触发对应的影响
|
|
144
|
+
this.triggerEffect()
|
|
145
|
+
// 开始双向绑定
|
|
146
|
+
this.$emit('input', value)
|
|
147
|
+
// 触发change,返回新老数据
|
|
148
|
+
this.$emit('change', value, prevStateValue)
|
|
149
|
+
},
|
|
150
|
+
deep: true
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 监听传入数据的变化
|
|
155
|
+
* @time 2024-11-13 16:23:35
|
|
156
|
+
*/
|
|
157
|
+
value: {
|
|
158
|
+
handler() {
|
|
159
|
+
// 当前值的key集合
|
|
160
|
+
const valueKeys = Object.keys(this.value)
|
|
161
|
+
const modelValueKeys = Object.keys(this.modelValue)
|
|
162
|
+
// 遍历当前传入的数据
|
|
163
|
+
valueKeys.forEach(key => {
|
|
164
|
+
// 取出当前表单项配置
|
|
165
|
+
const formItem = this.formItems.find(item => (Array.isArray(item.name) ? item.name.includes(key) : item.name === key))
|
|
166
|
+
// 如果当前是表单的字段
|
|
167
|
+
if (formItem) {
|
|
168
|
+
// 如果当前名称需要解构
|
|
169
|
+
if (Array.isArray(formItem.name)) {
|
|
170
|
+
// 当前key在数组中索引
|
|
171
|
+
const nameIndex = formItem.name.indexOf(key)
|
|
172
|
+
// 值发生改变
|
|
173
|
+
if (!isEqual(this.value[key], this.modelValue[formItem.key]?.[nameIndex])) {
|
|
174
|
+
// 如果字段值不存在
|
|
175
|
+
if (isEmpty(this.modelValue[formItem.key])) {
|
|
176
|
+
this.$set(this.modelValue, formItem.key, [])
|
|
177
|
+
}
|
|
178
|
+
// 设置值
|
|
179
|
+
this.$set(this.modelValue[formItem.key], nameIndex, this.value[key])
|
|
180
|
+
}
|
|
181
|
+
} else {
|
|
182
|
+
// 值发生改变
|
|
183
|
+
if (!isEqual(this.value[key], this.modelValue[formItem.key])) {
|
|
184
|
+
this.$set(this.modelValue, formItem.key, this.value[key])
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
// 值发生改变
|
|
189
|
+
if (!isEqual(this.value[key], this.modelValue[key])) {
|
|
190
|
+
this.$set(this.modelValue, key, this.value[key])
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
})
|
|
194
|
+
// 取出当前清空的数据
|
|
195
|
+
modelValueKeys.forEach(key => {
|
|
196
|
+
// 取出当前表单项配置
|
|
197
|
+
const formItem = this.formItems.find(item => item.key === key)
|
|
198
|
+
// 如果有配置项
|
|
199
|
+
if (formItem) {
|
|
200
|
+
// 如果表单项数据是数组
|
|
201
|
+
if (Array.isArray(formItem.name)) {
|
|
202
|
+
// 按照name获取字段值
|
|
203
|
+
let nextValue = formItem.name.map(name => this.value[name])
|
|
204
|
+
// 如果每个值都是空
|
|
205
|
+
if (nextValue.every(val => isEmpty(val))) {
|
|
206
|
+
nextValue = []
|
|
207
|
+
}
|
|
208
|
+
// 如果当前表单字段值和绑定的字段值不一致
|
|
209
|
+
if (!isEqual(this.modelValue[key], nextValue)) {
|
|
210
|
+
this.$set(this.modelValue, key, nextValue)
|
|
211
|
+
}
|
|
212
|
+
} else {
|
|
213
|
+
// 值被清空了
|
|
214
|
+
if (isNil(this.value[key])) {
|
|
215
|
+
this.clearFieldValue(formItem)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
} else {
|
|
219
|
+
// 如果当前表单字段值和绑定的字段值不一致
|
|
220
|
+
if (!isEqual(this.modelValue[key], this.value[key])) {
|
|
221
|
+
this.$set(this.modelValue, key, this.value[key])
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
})
|
|
225
|
+
// 深拷贝一份数据
|
|
226
|
+
this.prevState.modelValue = cloneDeep(this.modelValue)
|
|
227
|
+
},
|
|
228
|
+
deep: true
|
|
229
|
+
},
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* 监听数据源,如果数据源有变化,则需要出发options重新加载
|
|
233
|
+
* @time 2024-11-13 13:51:36
|
|
234
|
+
*/
|
|
235
|
+
dataSource: {
|
|
236
|
+
handler() {
|
|
237
|
+
// 数据源改变后,触发影响
|
|
238
|
+
this.triggerEffect()
|
|
239
|
+
// 深拷贝一份外部数据源数据
|
|
240
|
+
this.prevState.dataSource = cloneDeep(this.dataSource)
|
|
241
|
+
},
|
|
242
|
+
deep: true
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
created() {
|
|
246
|
+
// 当前转换函数
|
|
247
|
+
this.functionEval = functionEval(() => ({
|
|
248
|
+
model: this.modelValue,
|
|
249
|
+
dataSource: this.dataSource,
|
|
250
|
+
config: this.config,
|
|
251
|
+
$route: this.$route,
|
|
252
|
+
$router: this.$router,
|
|
253
|
+
formThis: this,
|
|
254
|
+
pageThis: this.pageThis || this.$parent
|
|
255
|
+
}))
|
|
256
|
+
},
|
|
257
|
+
mounted() {
|
|
258
|
+
// 触发首次加载的影响
|
|
259
|
+
this.triggerEffect()
|
|
260
|
+
// 触发loader
|
|
261
|
+
this.triggerHooks(HOOKS.INIT, this)
|
|
262
|
+
},
|
|
263
|
+
methods: {
|
|
264
|
+
/**
|
|
265
|
+
* 将表单项配置进行规范化
|
|
266
|
+
* @param {Array} items 当前所有表单项配置
|
|
267
|
+
* @time 2024-11-13 14:23:18
|
|
268
|
+
*/
|
|
269
|
+
normalizeFormItems(items) {
|
|
270
|
+
// 生成标准化的数据
|
|
271
|
+
const nomalizedItems = items.map(item => {
|
|
272
|
+
// 先深拷贝一份数据
|
|
273
|
+
const itemClone = cloneDeep(item)
|
|
274
|
+
// 如果有options方法
|
|
275
|
+
const { options } = itemClone
|
|
276
|
+
// 增加key字段
|
|
277
|
+
itemClone.key = Array.isArray(item.name) ? item.name.join(',') : item.name
|
|
278
|
+
// 将类型转换成大小写
|
|
279
|
+
itemClone.type = toUpperCamelCase(item.type)
|
|
280
|
+
// 新增的optionsCallback方法
|
|
281
|
+
if (!isEmpty(options)) {
|
|
282
|
+
// 如果当前options是函数
|
|
283
|
+
if (isEvalFunction(options)) {
|
|
284
|
+
itemClone.optionsCallback = () => this.functionEval(options)(this.prevState.value)
|
|
285
|
+
} else {
|
|
286
|
+
itemClone.optionsCallback = () => options
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
// 如果需要开启栅格系统
|
|
290
|
+
if (this.config.grid) {
|
|
291
|
+
itemClone.span = itemClone.span || this.config.span || DEFAULT_SPAN
|
|
292
|
+
}
|
|
293
|
+
// 如果当前有名称
|
|
294
|
+
if (Array.isArray(itemClone.name)) {
|
|
295
|
+
// 开始赋值
|
|
296
|
+
const nextValue = itemClone.name.map(name => this.prevState.value[name])
|
|
297
|
+
// 如果每个值都是空,则清空数据
|
|
298
|
+
if (nextValue.some(val => !isEmpty(val))) {
|
|
299
|
+
itemClone.value = nextValue
|
|
300
|
+
}
|
|
301
|
+
} else {
|
|
302
|
+
// 如果传入值非空
|
|
303
|
+
if (!isNil(this.prevState.value[itemClone.name])) {
|
|
304
|
+
itemClone.value = this.prevState.value[itemClone.name]
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// 如果有默认值
|
|
308
|
+
if (!isNil(itemClone.value)) {
|
|
309
|
+
if (isEvalFunction(itemClone.value)) {
|
|
310
|
+
itemClone.value = this.functionEval(itemClone.value)
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
// 返回数据
|
|
314
|
+
return itemClone
|
|
315
|
+
})
|
|
316
|
+
// 返回数据
|
|
317
|
+
return nomalizedItems
|
|
318
|
+
},
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* 标准化表单props属性
|
|
322
|
+
* @time 2024-11-13 14:26:03
|
|
323
|
+
*/
|
|
324
|
+
normalizeFormProps() {
|
|
325
|
+
// 需要过滤的属性key
|
|
326
|
+
const filterKeys = [
|
|
327
|
+
'grid',
|
|
328
|
+
'span',
|
|
329
|
+
'gutter'
|
|
330
|
+
]
|
|
331
|
+
// 默认配置
|
|
332
|
+
let defaultProps = {
|
|
333
|
+
size: 'small'
|
|
334
|
+
}
|
|
335
|
+
// 如果当前开启了栅格系统
|
|
336
|
+
if (this.config.grid) {
|
|
337
|
+
// 栅格系统系统,标题默认左对齐
|
|
338
|
+
defaultProps = {
|
|
339
|
+
...defaultProps,
|
|
340
|
+
labelPosition: 'left',
|
|
341
|
+
labelWidth: '80px'
|
|
342
|
+
}
|
|
343
|
+
} else {
|
|
344
|
+
// 未开启栅格系统,则标题默认上对齐
|
|
345
|
+
defaultProps = {
|
|
346
|
+
...defaultProps,
|
|
347
|
+
labelPosition: 'top'
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
return Object.keys(this.config)
|
|
351
|
+
.reduce(
|
|
352
|
+
(memo, key) => {
|
|
353
|
+
// 如果不在过滤名单内
|
|
354
|
+
if (!filterKeys.includes(key)) {
|
|
355
|
+
memo[key] = this.config[key]
|
|
356
|
+
}
|
|
357
|
+
return memo
|
|
358
|
+
}, defaultProps
|
|
359
|
+
)
|
|
360
|
+
},
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* 当表单值发生改变,触发对应的影响
|
|
364
|
+
* @time 2024-11-13 14:27:36
|
|
365
|
+
*/
|
|
366
|
+
triggerEffect() {
|
|
367
|
+
// 获取当前的form-items
|
|
368
|
+
const prevFormItems = this.formItemsForRender
|
|
369
|
+
// 重新生成表单items
|
|
370
|
+
this.doLayout()
|
|
371
|
+
// 初始化vif动态生成的默认值
|
|
372
|
+
this.initValue(prevFormItems)
|
|
373
|
+
// 重新加载options方法
|
|
374
|
+
this.triggerReloadOptions()
|
|
375
|
+
},
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* 初始化默认值
|
|
379
|
+
* @time 2024-11-13 14:45:52
|
|
380
|
+
*/
|
|
381
|
+
triggerInitDefaultValue(itemConfig) {
|
|
382
|
+
this.$set(
|
|
383
|
+
this.modelValue,
|
|
384
|
+
itemConfig.key,
|
|
385
|
+
itemConfig.value instanceof Function
|
|
386
|
+
? itemConfig.value(this.getOptions(itemConfig.key) || [])
|
|
387
|
+
: itemConfig.value
|
|
388
|
+
)
|
|
389
|
+
},
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* 触发一次钩子函数的执行
|
|
393
|
+
* @param {String} hookName 钩子函数名称
|
|
394
|
+
* @time 2024-11-14 11:01:58
|
|
395
|
+
*/
|
|
396
|
+
triggerHooks(hookName, ...props) {
|
|
397
|
+
// 获取hook名称集合
|
|
398
|
+
const hookNames = Object.values(HOOKS)
|
|
399
|
+
// 如果当前hook是合法的,则需要循环执行
|
|
400
|
+
if (hookNames.includes(hookName)) {
|
|
401
|
+
this.hooks[hookName].forEach(hook => hook(...props))
|
|
402
|
+
}
|
|
403
|
+
},
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* 添加一个钩子函数,用于在不同的时机对数据做处理
|
|
407
|
+
* @param {Array|Object} hooks 当前等待添加的钩子函数
|
|
408
|
+
* @time 2024-11-14 10:54:33
|
|
409
|
+
*/
|
|
410
|
+
addHooks(pendingHook) {
|
|
411
|
+
// 将当前hook转换成数组
|
|
412
|
+
const hooks = Array.isArray(pendingHook) ? pendingHook : [pendingHook]
|
|
413
|
+
// 获取hook名称集合
|
|
414
|
+
const hookNames = Object.values(HOOKS)
|
|
415
|
+
// 将hook循环放进待执行的hook列表中
|
|
416
|
+
hooks.forEach(hook => {
|
|
417
|
+
// 如果当前名称是合法的,则正常添加
|
|
418
|
+
if (hookNames.includes(hook.module)) {
|
|
419
|
+
this.hooks[hook.module].push(this.functionEval(hook.callback))
|
|
420
|
+
}
|
|
421
|
+
})
|
|
422
|
+
},
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* 获取当前已经改变的字段
|
|
426
|
+
* @time 2024-11-13 11:10:05
|
|
427
|
+
*/
|
|
428
|
+
getChangedFieldKeys() {
|
|
429
|
+
// 当前已经改变的字段keys
|
|
430
|
+
const formKeys = Object.keys({
|
|
431
|
+
...this.modelValue,
|
|
432
|
+
...this.prevState.modelValue
|
|
433
|
+
}).filter(key => !isEqual(this.modelValue[key], this.prevState.modelValue[key]))
|
|
434
|
+
// 当前已经改变的数据源
|
|
435
|
+
const dataSourceKeys = Object.keys({
|
|
436
|
+
...this.dataSource,
|
|
437
|
+
...this.prevState.dataSource
|
|
438
|
+
}).filter(key => !isEqual(this.dataSource[key], this.prevState.dataSource[key])).map(key => `dataSource.${key}`)
|
|
439
|
+
// 合并数据
|
|
440
|
+
return [...formKeys, ...dataSourceKeys]
|
|
441
|
+
},
|
|
442
|
+
|
|
443
|
+
/**
|
|
444
|
+
* 当依赖项发生变化时,重置字段的值
|
|
445
|
+
* 例如:某 item 从隐藏到显示,则需要初始化默认值;某 item 从隐藏到显示,则移除字段值;字段 watch 的值发生了改变,移除字段值;
|
|
446
|
+
* @time 2024-11-11 18:50:59
|
|
447
|
+
*/
|
|
448
|
+
initValue(prevFormItems = []) {
|
|
449
|
+
// 上一次的form-items的所有keys
|
|
450
|
+
const prevFormItemsKeys = prevFormItems.map(item => item.key)
|
|
451
|
+
// 当前的form-items的所有keys
|
|
452
|
+
const formItemsKeys = this.formItemsForRender.map(item => item.key)
|
|
453
|
+
// 当前已经改变的字段keys
|
|
454
|
+
const changedFieldKeys = this.getChangedFieldKeys()
|
|
455
|
+
|
|
456
|
+
// 循环数据
|
|
457
|
+
this.formItemsForRender.forEach(item => {
|
|
458
|
+
// 如果上一次无该字段,这次展示了
|
|
459
|
+
if (!prevFormItemsKeys.includes(item.key)) {
|
|
460
|
+
// 如果当前没有默认值,则初始化
|
|
461
|
+
if (isNil(item.value)) {
|
|
462
|
+
this.clearFieldValue(item)
|
|
463
|
+
} else {
|
|
464
|
+
// 初始化默认值
|
|
465
|
+
this.triggerInitDefaultValue(item)
|
|
466
|
+
}
|
|
467
|
+
} else {
|
|
468
|
+
// 如果当前watch的字段值发生了变化,则需要清空当前字段的值
|
|
469
|
+
if (item.watch && changedFieldKeys.some(key => item.watch.includes(key))) {
|
|
470
|
+
this.clearFieldValue(item)
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
})
|
|
474
|
+
|
|
475
|
+
// 取出当前被隐藏的字段,并清空隐藏字段的值
|
|
476
|
+
prevFormItems
|
|
477
|
+
.filter(item => !formItemsKeys.includes(item.key))
|
|
478
|
+
.forEach(item => {
|
|
479
|
+
// 清空隐藏字段的值
|
|
480
|
+
this.clearFieldValue(item)
|
|
481
|
+
// 清空上一次的请求参数(再次被显示时,可正常设置默认值)
|
|
482
|
+
this.prevState.optionParams[item.key] = null
|
|
483
|
+
})
|
|
484
|
+
},
|
|
485
|
+
|
|
486
|
+
/**
|
|
487
|
+
* 重新加载options方法
|
|
488
|
+
* @time 2024-11-12 14:53:54
|
|
489
|
+
*/
|
|
490
|
+
triggerReloadOptions() {
|
|
491
|
+
// 取到所有有options的数据
|
|
492
|
+
this.formItemsForRender
|
|
493
|
+
// 过滤掉没有options的配置
|
|
494
|
+
.filter(item => !!item.optionsCallback)
|
|
495
|
+
// 当前数据
|
|
496
|
+
.forEach(item => {
|
|
497
|
+
// 当前请求参数
|
|
498
|
+
const optionParams = this.getWatchValue(item)
|
|
499
|
+
// 如果当前请求参数发生了变化
|
|
500
|
+
if (!isEqual(optionParams, this.optionParams[item.key])) {
|
|
501
|
+
this.$set(this.optionParams, item.key, optionParams)
|
|
502
|
+
}
|
|
503
|
+
})
|
|
504
|
+
},
|
|
505
|
+
|
|
506
|
+
/**
|
|
507
|
+
* 获取watch字段对应的值
|
|
508
|
+
* @param {Object} item 参数说明
|
|
509
|
+
* @time 2024-11-13 15:13:15
|
|
510
|
+
*/
|
|
511
|
+
getWatchValue(item) {
|
|
512
|
+
// 如果没有需要监听的字段
|
|
513
|
+
if (!item.watch || item.watch.length === 0) {
|
|
514
|
+
return {}
|
|
515
|
+
}
|
|
516
|
+
// 生成监听字段对应的值
|
|
517
|
+
return item.watch.reduce((memo, key) => {
|
|
518
|
+
// 如果需要从其他字段中取值
|
|
519
|
+
memo[key] = key.indexOf('.') > 0
|
|
520
|
+
? lodashGet(this, key)
|
|
521
|
+
: this.prevState.value[key]
|
|
522
|
+
return memo
|
|
523
|
+
}, {})
|
|
524
|
+
},
|
|
525
|
+
|
|
526
|
+
/**
|
|
527
|
+
* 当前options的数据加载完成,设置初始化值
|
|
528
|
+
* @param {Object} itemConfig 当前表单项的配置信息
|
|
529
|
+
* @param {Array} options 当前加载的下拉数据
|
|
530
|
+
* @time 2024-11-12 15:50:17
|
|
531
|
+
*/
|
|
532
|
+
onOptionsLoaded(itemConfig, options) {
|
|
533
|
+
// 如果当前请求完接口了,且有options配置,且value是函数
|
|
534
|
+
if (
|
|
535
|
+
isEmpty(this.prevState.optionParams[itemConfig.key]) &&
|
|
536
|
+
itemConfig.value &&
|
|
537
|
+
itemConfig.optionsCallback
|
|
538
|
+
) {
|
|
539
|
+
this.triggerInitDefaultValue(itemConfig)
|
|
540
|
+
}
|
|
541
|
+
// 触发options加载完成
|
|
542
|
+
this.triggerHooks(HOOKS.OPTION_CHANGE, cloneDeep(itemConfig), options)
|
|
543
|
+
// 上一次的options的请求参数
|
|
544
|
+
this.prevState.optionParams[itemConfig.key] = cloneDeep(this.optionParams[itemConfig.key])
|
|
545
|
+
},
|
|
546
|
+
|
|
547
|
+
/**
|
|
548
|
+
* 清空字段的值
|
|
549
|
+
* @param {Object} field 当前所有字段
|
|
550
|
+
* @time 2024-11-05 15:45:21
|
|
551
|
+
*/
|
|
552
|
+
clearFieldValue(field) {
|
|
553
|
+
// 当前值
|
|
554
|
+
let value
|
|
555
|
+
// 根据类型判断
|
|
556
|
+
switch (field.type) {
|
|
557
|
+
case 'Select': // 如果当前是下拉
|
|
558
|
+
// 如果当前是多选
|
|
559
|
+
if (field.props?.multiple) {
|
|
560
|
+
value = []
|
|
561
|
+
}
|
|
562
|
+
break
|
|
563
|
+
case 'Checkbox': // 如果当前是多选
|
|
564
|
+
value = []
|
|
565
|
+
break
|
|
566
|
+
case 'Cascader': // 如果当前是级联
|
|
567
|
+
value = []
|
|
568
|
+
break
|
|
569
|
+
case 'TimePicker': // 如果当前是时间选择
|
|
570
|
+
// 如果当前是范围选择
|
|
571
|
+
if (field.props?.isRange) {
|
|
572
|
+
value = []
|
|
573
|
+
}
|
|
574
|
+
break
|
|
575
|
+
case 'DatePicker': // 如果当前是日期选择
|
|
576
|
+
// 如果是范围选择器
|
|
577
|
+
if (['daterange', 'monthrange', 'datetimerange'].includes(field.props?.type)) {
|
|
578
|
+
value = []
|
|
579
|
+
}
|
|
580
|
+
break
|
|
581
|
+
case 'Upload': // 如果当前是上传组件
|
|
582
|
+
// 如果是多选
|
|
583
|
+
if (field.props?.multiple) {
|
|
584
|
+
value = []
|
|
585
|
+
}
|
|
586
|
+
break
|
|
587
|
+
}
|
|
588
|
+
// 开始设置数据
|
|
589
|
+
this.$set(this.modelValue, field.key, value)
|
|
590
|
+
},
|
|
591
|
+
|
|
592
|
+
/**
|
|
593
|
+
* 重新生成可渲染的表单项配置数据
|
|
594
|
+
* @time 2024-11-05 16:43:54
|
|
595
|
+
*/
|
|
596
|
+
doLayout() {
|
|
597
|
+
// 生成当前需要展示的表单项
|
|
598
|
+
const formItemsForRender = this.formItems
|
|
599
|
+
.filter(this.vif.bind(this))
|
|
600
|
+
.filter(item => {
|
|
601
|
+
// 如果没有名称,则给出提示
|
|
602
|
+
if (!item.name) {
|
|
603
|
+
console.warn(`表单项${item.label}中的name属性必填,请检查`)
|
|
604
|
+
}
|
|
605
|
+
return !!item.name
|
|
606
|
+
})
|
|
607
|
+
// 当前表单项的key集合
|
|
608
|
+
const formItemsKeys = this.formItemsForRender.map(item => item.key)
|
|
609
|
+
// 下一次渲染的表单项key集合
|
|
610
|
+
const nextFormItemsKeys = formItemsForRender.map(item => item.key)
|
|
611
|
+
// 如果重新生成的formItems和之前一样,则不赋值,防止频繁render
|
|
612
|
+
if (isEqual(formItemsKeys, nextFormItemsKeys)) {
|
|
613
|
+
return
|
|
614
|
+
}
|
|
615
|
+
// 触发钩子函数
|
|
616
|
+
this.triggerHooks(HOOKS.DO_LAYOUT, formItemsForRender)
|
|
617
|
+
// 开始触发render渲染
|
|
618
|
+
this.formItemsForRender = formItemsForRender
|
|
619
|
+
},
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* 判断当前表单项是否需要渲染
|
|
623
|
+
* @param {Object} itemConfig 表单项配置
|
|
624
|
+
* @time 2024-10-31 16:56:03
|
|
625
|
+
*/
|
|
626
|
+
vif(itemConfig) {
|
|
627
|
+
// 取出当前this
|
|
628
|
+
const formThis = this
|
|
629
|
+
// 是否展示
|
|
630
|
+
let display = true
|
|
631
|
+
// 如果当前是boolean类型
|
|
632
|
+
if (typeof itemConfig.vif === 'boolean') {
|
|
633
|
+
display = itemConfig.vif
|
|
634
|
+
}
|
|
635
|
+
// 如果当前是函数
|
|
636
|
+
if (isEvalFunction(itemConfig.vif)) {
|
|
637
|
+
// 执行函数,获取展示状态
|
|
638
|
+
display = formThis.functionEval(itemConfig.vif)(formThis.prevState.value)
|
|
639
|
+
}
|
|
640
|
+
// 返回状态
|
|
641
|
+
return display
|
|
642
|
+
},
|
|
643
|
+
|
|
644
|
+
/**
|
|
645
|
+
* 返回form-item的props属性
|
|
646
|
+
* @param {Object} props 当前所有属性值
|
|
647
|
+
* @time 2024-10-31 17:10:44
|
|
648
|
+
*/
|
|
649
|
+
formItemProps(props) {
|
|
650
|
+
// TODO:按照element和antdesign的不同进行配置
|
|
651
|
+
if (isElementUI()) {
|
|
652
|
+
// 表单items的属性
|
|
653
|
+
const FORM_ITEM_PROP_KEYS = [
|
|
654
|
+
'props',
|
|
655
|
+
'label',
|
|
656
|
+
'labelWidth',
|
|
657
|
+
'required',
|
|
658
|
+
'rules',
|
|
659
|
+
'error',
|
|
660
|
+
'showMessage',
|
|
661
|
+
'inlineMessage',
|
|
662
|
+
'size'
|
|
663
|
+
]
|
|
664
|
+
return Object.keys(props).reduce(
|
|
665
|
+
(memo, propKey) => {
|
|
666
|
+
// 如果当前有在item的属性内
|
|
667
|
+
if (FORM_ITEM_PROP_KEYS.includes(propKey)) {
|
|
668
|
+
// 如果当前属性是函数
|
|
669
|
+
if (isEvalFunction(props[propKey])) {
|
|
670
|
+
// 当前4个属性可以通过函数的方式获取值
|
|
671
|
+
if (['props', 'rules', 'label', 'labelWidth'].includes(propKey)) {
|
|
672
|
+
memo[propKey] = this.functionEval(props[propKey])(this.prevState.value)
|
|
673
|
+
}
|
|
674
|
+
} else {
|
|
675
|
+
memo[propKey] = props[propKey]
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
return memo
|
|
679
|
+
}, {}
|
|
680
|
+
)
|
|
681
|
+
}
|
|
682
|
+
},
|
|
683
|
+
|
|
684
|
+
/**
|
|
685
|
+
* 通过指令转换
|
|
686
|
+
* @param {String} value 当前值
|
|
687
|
+
* @param {Array|String} directive 当前指令
|
|
688
|
+
* @time 2025-05-08 09:32:40
|
|
689
|
+
*/
|
|
690
|
+
transformValueByDirective(value, directive) {
|
|
691
|
+
// 如果当前未输入指令
|
|
692
|
+
if ([undefined, null, ''].includes(directive)) {
|
|
693
|
+
return value
|
|
694
|
+
}
|
|
695
|
+
// 将指令转换成数组
|
|
696
|
+
const normalizedDirective = Array.isArray(directive)
|
|
697
|
+
? directive
|
|
698
|
+
: [directive]
|
|
699
|
+
// 转换后的值
|
|
700
|
+
let formattedValue = value
|
|
701
|
+
// 遍历指令
|
|
702
|
+
normalizedDirective.forEach(d => {
|
|
703
|
+
switch (d) {
|
|
704
|
+
case 'model.trim':
|
|
705
|
+
formattedValue = formattedValue.trim()
|
|
706
|
+
break
|
|
707
|
+
case 'model.number':
|
|
708
|
+
formattedValue = Number(formattedValue)
|
|
709
|
+
break
|
|
710
|
+
}
|
|
711
|
+
})
|
|
712
|
+
// 返回转换后的数据
|
|
713
|
+
return formattedValue
|
|
714
|
+
},
|
|
715
|
+
|
|
716
|
+
/**
|
|
717
|
+
* 绑定事件
|
|
718
|
+
* @param {Object} event 当前事件
|
|
719
|
+
* @time 2024-10-31 18:34:58
|
|
720
|
+
*/
|
|
721
|
+
bindEvent(itemConfig) {
|
|
722
|
+
// 取出事件key
|
|
723
|
+
const key = itemConfig.key
|
|
724
|
+
// 当前监听的事件
|
|
725
|
+
const events = itemConfig.on
|
|
726
|
+
// 当前表单的this
|
|
727
|
+
const formThis = this
|
|
728
|
+
// 输入事件
|
|
729
|
+
const inputEvent = {
|
|
730
|
+
/**
|
|
731
|
+
* 设置input方法,便于双向绑定实现
|
|
732
|
+
* @time 2024-11-01 16:01:43
|
|
733
|
+
*/
|
|
734
|
+
input(value) {
|
|
735
|
+
// 开始双向绑定
|
|
736
|
+
formThis.$set(formThis.modelValue, key, formThis.transformValueByDirective(value, itemConfig.directive))
|
|
737
|
+
// 清空所有的过滤后的数据
|
|
738
|
+
Object.keys(formThis.filteredData).forEach(type => {
|
|
739
|
+
// 如果当前是过滤后的数据
|
|
740
|
+
if (formThis.filteredData[type]) {
|
|
741
|
+
formThis.$set(formThis.filteredData[type], key, null)
|
|
742
|
+
}
|
|
743
|
+
})
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
// 如果当前是空,则设置input方法
|
|
747
|
+
if ([null, undefined].includes(events)) {
|
|
748
|
+
return inputEvent
|
|
749
|
+
}
|
|
750
|
+
return Object.keys(events).reduce(
|
|
751
|
+
(memo, eventKey) => {
|
|
752
|
+
// 将函数使用functionEval包裹
|
|
753
|
+
memo[eventKey] = (...args) => {
|
|
754
|
+
// 如果当前抛出了input事件,则需要双向绑定
|
|
755
|
+
if (eventKey === 'input') {
|
|
756
|
+
// 开始双向绑定
|
|
757
|
+
inputEvent.input(args[0])
|
|
758
|
+
}
|
|
759
|
+
// 执行函数
|
|
760
|
+
this.$nextTick(() => formThis.functionEval(events[eventKey])(...args))
|
|
761
|
+
}
|
|
762
|
+
// 返回当前对象
|
|
763
|
+
return memo
|
|
764
|
+
}, { input: inputEvent.input }
|
|
765
|
+
)
|
|
766
|
+
},
|
|
767
|
+
|
|
768
|
+
/**
|
|
769
|
+
* 获取options的值
|
|
770
|
+
* @param {Array | String} keys 当前所有字段的keys
|
|
771
|
+
* @time 2024-11-13 09:46:01
|
|
772
|
+
*/
|
|
773
|
+
getOptions(keys) {
|
|
774
|
+
// 当前字段的keys
|
|
775
|
+
const fieldKeys = keys
|
|
776
|
+
? Array.isArray(keys) ? keys : [keys]
|
|
777
|
+
: this.formItemsForRender.map(item => item.key)
|
|
778
|
+
// 当前options的数据
|
|
779
|
+
const options = {}
|
|
780
|
+
// 取出需要的数据
|
|
781
|
+
fieldKeys.forEach(key => {
|
|
782
|
+
// 如果api组件存在,则获取接口响应的数据
|
|
783
|
+
if (this.$refs[`${key}Api`]) {
|
|
784
|
+
options[key] = cloneDeep(this.$refs[`${key}Api`].respData)
|
|
785
|
+
}
|
|
786
|
+
})
|
|
787
|
+
// 获取options的所有数据
|
|
788
|
+
return fieldKeys.length === 1 ? options[fieldKeys[0]] : options
|
|
789
|
+
},
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* 触发表单校验,并返回校验结果
|
|
793
|
+
* @time 2024-11-12 19:25:15
|
|
794
|
+
*/
|
|
795
|
+
async validate() {
|
|
796
|
+
await this.$refs.form.validate()
|
|
797
|
+
},
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* 清空表单的校验
|
|
801
|
+
* @time 2024-11-12 19:30:27
|
|
802
|
+
*/
|
|
803
|
+
clearValidate(props) {
|
|
804
|
+
this.$refs.form.clearValidate(props)
|
|
805
|
+
},
|
|
806
|
+
|
|
807
|
+
/**
|
|
808
|
+
* 对部分表单字段进行校验
|
|
809
|
+
* @param {Array|String} props 需要校验的字段
|
|
810
|
+
* @time 2025-05-06 14:06:10
|
|
811
|
+
*/
|
|
812
|
+
validateField(props) {
|
|
813
|
+
this.$refs.form.validateField(props)
|
|
814
|
+
},
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* 重置表单
|
|
818
|
+
* @time 2024-11-12 19:41:58
|
|
819
|
+
*/
|
|
820
|
+
resetFields() {
|
|
821
|
+
// 清空上一次的options的请求参数
|
|
822
|
+
this.prevState.optionParams = {}
|
|
823
|
+
// 初始化默认值
|
|
824
|
+
this.initValue()
|
|
825
|
+
// 清空校验
|
|
826
|
+
this.$nextTick(this.clearValidate.bind(this))
|
|
827
|
+
},
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
* 获取表单组件的props
|
|
831
|
+
* @param {Object} props 当前参数
|
|
832
|
+
* @time 2024-12-09 19:23:56
|
|
833
|
+
*/
|
|
834
|
+
evalComponentProps(itemConfig, props) {
|
|
835
|
+
// 如果没有属性
|
|
836
|
+
if (!props) {
|
|
837
|
+
return {}
|
|
838
|
+
}
|
|
839
|
+
// 返回属性
|
|
840
|
+
return Object.keys(props).reduce((memo, propKey) => {
|
|
841
|
+
// 如果当前是需要执行的数据
|
|
842
|
+
if (isEvalFunction(props[propKey])) {
|
|
843
|
+
memo[propKey] = async query => {
|
|
844
|
+
// 得到结果
|
|
845
|
+
const evalResult = await functionEval(() => ({
|
|
846
|
+
model: this.modelValue,
|
|
847
|
+
dataSource: this.dataSource,
|
|
848
|
+
config: this.config,
|
|
849
|
+
formThis: this,
|
|
850
|
+
pageThis: this.pageThis || this.$parent,
|
|
851
|
+
options: this.getOptions(itemConfig.key)
|
|
852
|
+
}))(props[propKey])(query)
|
|
853
|
+
// 过滤的key
|
|
854
|
+
const filteredDataKey = getFilteredDataType(itemConfig.type, propKey)
|
|
855
|
+
// 如果当前是remoteMethod属性,则需要覆盖
|
|
856
|
+
if (!isEmpty(filteredDataKey)) {
|
|
857
|
+
// 如果属性不存在
|
|
858
|
+
if (!this.filteredData[filteredDataKey]) {
|
|
859
|
+
this.$set(this.filteredData, filteredDataKey, {})
|
|
860
|
+
}
|
|
861
|
+
// 设置值
|
|
862
|
+
this.$set(this.filteredData[filteredDataKey], itemConfig.key, evalResult)
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
} else {
|
|
866
|
+
memo[propKey] = props[propKey]
|
|
867
|
+
}
|
|
868
|
+
return memo
|
|
869
|
+
}, {})
|
|
870
|
+
},
|
|
871
|
+
|
|
872
|
+
/**
|
|
873
|
+
* 获取组件的指令
|
|
874
|
+
* @param {Array|String|Object} directive 指令集合
|
|
875
|
+
* @time 2025-05-08 14:55:33
|
|
876
|
+
*/
|
|
877
|
+
getComponentDirective(directive) {
|
|
878
|
+
// 如果没有指令
|
|
879
|
+
if ([null, undefined, ''].includes(directive)) {
|
|
880
|
+
return []
|
|
881
|
+
}
|
|
882
|
+
// 将指令转换成数组
|
|
883
|
+
const normalizedDirective = Array.isArray(directive)
|
|
884
|
+
? directive
|
|
885
|
+
: [directive]
|
|
886
|
+
// 转换成指令标准写法
|
|
887
|
+
return normalizedDirective
|
|
888
|
+
.filter(d => isObject(d))
|
|
889
|
+
.map(d => ({ name: d.name, value: d.value, modifiers: {}}))
|
|
890
|
+
}
|
|
891
|
+
},
|
|
892
|
+
render(h) {
|
|
893
|
+
// 获取表单组件
|
|
894
|
+
const [
|
|
895
|
+
Form,
|
|
896
|
+
FormItem,
|
|
897
|
+
Row,
|
|
898
|
+
Col
|
|
899
|
+
] = getComponentNames(['form', 'form-item', 'row', 'col'])
|
|
900
|
+
|
|
901
|
+
// 绑定事件方法
|
|
902
|
+
const bindEvent = this.bindEvent.bind(this)
|
|
903
|
+
|
|
904
|
+
/**
|
|
905
|
+
* 从props中提取dom的属性
|
|
906
|
+
* @param {Object} props 当前传递的props
|
|
907
|
+
* @time 2025-04-01 09:54:39
|
|
908
|
+
*/
|
|
909
|
+
const getFormAttrs = (props) => {
|
|
910
|
+
// 如果没有配置props
|
|
911
|
+
if (!props) {
|
|
912
|
+
return {}
|
|
913
|
+
}
|
|
914
|
+
// 获取dom真实属性
|
|
915
|
+
const DOM_ATTR_KEYS = [
|
|
916
|
+
'id',
|
|
917
|
+
'name',
|
|
918
|
+
'placeholder',
|
|
919
|
+
'autocomplete',
|
|
920
|
+
'maxlength',
|
|
921
|
+
'minlength',
|
|
922
|
+
'max',
|
|
923
|
+
'min',
|
|
924
|
+
'step',
|
|
925
|
+
'autofocus',
|
|
926
|
+
'form'
|
|
927
|
+
]
|
|
928
|
+
return Object.keys(props)
|
|
929
|
+
.filter(propKey => DOM_ATTR_KEYS.includes(propKey))
|
|
930
|
+
.reduce((memo, propKey) => {
|
|
931
|
+
memo[propKey] = props[propKey]
|
|
932
|
+
return memo
|
|
933
|
+
}, {})
|
|
934
|
+
}
|
|
935
|
+
|
|
936
|
+
/**
|
|
937
|
+
* 获取表单输入组件
|
|
938
|
+
* @param {Object} itemConfig 表单项配置
|
|
939
|
+
* @time 2024-10-31 17:41:10
|
|
940
|
+
*/
|
|
941
|
+
const formInputComp = (itemConfig, formItemsProps, options) => {
|
|
942
|
+
// 下拉组件,单选组件,多选组件
|
|
943
|
+
const [
|
|
944
|
+
Option,
|
|
945
|
+
RadioGroup,
|
|
946
|
+
CheckboxGroup,
|
|
947
|
+
ComponentType
|
|
948
|
+
] = getComponentNames(['option', 'radio-group', 'checkbox-group', itemConfig.type])
|
|
949
|
+
// 当前所需的组件
|
|
950
|
+
let comp = null
|
|
951
|
+
// 表单输入类型组件的属性
|
|
952
|
+
const componentProps = {
|
|
953
|
+
// 默认展示清空操作
|
|
954
|
+
clearable: true,
|
|
955
|
+
...(
|
|
956
|
+
['DatePicker'].includes(itemConfig.type)
|
|
957
|
+
? {
|
|
958
|
+
valueFormat: 'yyyy-MM-dd HH:mm:ss',
|
|
959
|
+
defaultTime: ['00:00:00', '23:59:59'],
|
|
960
|
+
startPlaceholder: '开始日期',
|
|
961
|
+
endPlaceholder: '结束日期'
|
|
962
|
+
}
|
|
963
|
+
: {}
|
|
964
|
+
),
|
|
965
|
+
...this.evalComponentProps(itemConfig, formItemsProps.props)
|
|
966
|
+
}
|
|
967
|
+
// 当前组件的插槽
|
|
968
|
+
const componentSlots = itemConfig.slots?.component
|
|
969
|
+
// 等待渲染的插槽
|
|
970
|
+
const renderComponentSlots = componentSlots
|
|
971
|
+
? Object.keys(componentSlots).reduce((memo, key) => {
|
|
972
|
+
// 如果当前有插槽
|
|
973
|
+
if (this.$scopedSlots[componentSlots[key]]) {
|
|
974
|
+
memo.push(<span slot={key}>{this.$scopedSlots[componentSlots[key]]()}</span>)
|
|
975
|
+
}
|
|
976
|
+
return memo
|
|
977
|
+
}, [])
|
|
978
|
+
: null
|
|
979
|
+
// 过滤的数据
|
|
980
|
+
const filterOptionsData = this.filteredData.options?.[itemConfig.key]
|
|
981
|
+
// 下拉数据
|
|
982
|
+
const optionsData = filterOptionsData || options.data
|
|
983
|
+
// 插槽名称
|
|
984
|
+
const slotName = itemConfig.slotName
|
|
985
|
+
? itemConfig.slotName
|
|
986
|
+
: Array.isArray(itemConfig.name)
|
|
987
|
+
? itemConfig.name.join(',')
|
|
988
|
+
: itemConfig.name
|
|
989
|
+
// 如果当前是下拉组件
|
|
990
|
+
switch (itemConfig.type) {
|
|
991
|
+
case 'Slot': // 如果是插槽组件
|
|
992
|
+
comp = this.$slots[slotName]
|
|
993
|
+
break
|
|
994
|
+
case 'Select': // 如果是下拉组件
|
|
995
|
+
comp = (
|
|
996
|
+
<ComponentType
|
|
997
|
+
value={this.modelValue[itemConfig.key]}
|
|
998
|
+
props={{
|
|
999
|
+
...componentProps,
|
|
1000
|
+
loading: options.loading
|
|
1001
|
+
}}
|
|
1002
|
+
attrs={{
|
|
1003
|
+
...getFormAttrs(componentProps),
|
|
1004
|
+
placeholder: componentProps.placeholder || `请选择${formItemsProps.label}`
|
|
1005
|
+
}}
|
|
1006
|
+
class={componentProps.class}
|
|
1007
|
+
style={componentProps.style}
|
|
1008
|
+
on={bindEvent(itemConfig)}
|
|
1009
|
+
>
|
|
1010
|
+
{renderComponentSlots}
|
|
1011
|
+
{ // 循环options的数据
|
|
1012
|
+
optionsData?.map(option => (
|
|
1013
|
+
<Option key={option.value} label={option.label} value={option.value} />
|
|
1014
|
+
))
|
|
1015
|
+
}
|
|
1016
|
+
</ComponentType>
|
|
1017
|
+
)
|
|
1018
|
+
break
|
|
1019
|
+
case 'Radio': // 如果是单选
|
|
1020
|
+
case 'RadioButton': // 如果是单选按钮
|
|
1021
|
+
comp = (
|
|
1022
|
+
<RadioGroup
|
|
1023
|
+
value={this.modelValue[itemConfig.key]}
|
|
1024
|
+
props={componentProps}
|
|
1025
|
+
attrs={getFormAttrs(componentProps)}
|
|
1026
|
+
class={componentProps.class}
|
|
1027
|
+
style={componentProps.style}
|
|
1028
|
+
on={bindEvent(itemConfig)}
|
|
1029
|
+
>
|
|
1030
|
+
{ // 循环options的数据
|
|
1031
|
+
options.data?.map(option => (
|
|
1032
|
+
<ComponentType key={option.value} label={option.value}>{option.label}</ComponentType>
|
|
1033
|
+
))
|
|
1034
|
+
}
|
|
1035
|
+
</RadioGroup>
|
|
1036
|
+
)
|
|
1037
|
+
break
|
|
1038
|
+
case 'Checkbox': // 如果是多选
|
|
1039
|
+
case 'CheckboxButton': // 如果是多选按钮
|
|
1040
|
+
comp = (
|
|
1041
|
+
<CheckboxGroup
|
|
1042
|
+
value={this.modelValue[itemConfig.key]}
|
|
1043
|
+
props={componentProps}
|
|
1044
|
+
attrs={getFormAttrs(componentProps)}
|
|
1045
|
+
class={componentProps.class}
|
|
1046
|
+
style={componentProps.style}
|
|
1047
|
+
on={bindEvent(itemConfig)}
|
|
1048
|
+
>
|
|
1049
|
+
{ // 循环options的数据
|
|
1050
|
+
options.data?.map(option => (
|
|
1051
|
+
<ComponentType key={option.value} label={option.value}>{option.label}</ComponentType>
|
|
1052
|
+
))
|
|
1053
|
+
}
|
|
1054
|
+
</CheckboxGroup>
|
|
1055
|
+
)
|
|
1056
|
+
break
|
|
1057
|
+
default: // 其他组件
|
|
1058
|
+
// 设置默认的placeholder
|
|
1059
|
+
comp = h(
|
|
1060
|
+
ComponentType,
|
|
1061
|
+
{
|
|
1062
|
+
props: {
|
|
1063
|
+
...componentProps,
|
|
1064
|
+
options: options.data || [],
|
|
1065
|
+
value: this.modelValue[itemConfig.key]
|
|
1066
|
+
},
|
|
1067
|
+
attrs: {
|
|
1068
|
+
...getFormAttrs(componentProps),
|
|
1069
|
+
placeholder: componentProps.placeholder || `请${['Input', 'InputNumber'].includes(itemConfig.type) ? '输入' : '选择'}${formItemsProps.label}`
|
|
1070
|
+
},
|
|
1071
|
+
directives: this.getComponentDirective(itemConfig.directive),
|
|
1072
|
+
class: componentProps.class,
|
|
1073
|
+
style: componentProps.style,
|
|
1074
|
+
on: bindEvent(itemConfig)
|
|
1075
|
+
},
|
|
1076
|
+
renderComponentSlots
|
|
1077
|
+
)
|
|
1078
|
+
break
|
|
1079
|
+
}
|
|
1080
|
+
return comp
|
|
1081
|
+
}
|
|
1082
|
+
|
|
1083
|
+
// 等待渲染的组件
|
|
1084
|
+
let FormItems = this.formItemsForRender
|
|
1085
|
+
.map((item) => {
|
|
1086
|
+
// 当前表单项的props
|
|
1087
|
+
const formItemProps = this.formItemProps(item)
|
|
1088
|
+
// 表单配置的slots
|
|
1089
|
+
const formItemSlots = item.slots?.formItem
|
|
1090
|
+
// 当前表单项配置
|
|
1091
|
+
let ItemComp = (
|
|
1092
|
+
<FormItem
|
|
1093
|
+
key={item.key}
|
|
1094
|
+
props={{
|
|
1095
|
+
...formItemProps,
|
|
1096
|
+
prop: item.key
|
|
1097
|
+
}}
|
|
1098
|
+
scopedSlots={
|
|
1099
|
+
formItemSlots
|
|
1100
|
+
? Object.keys(formItemSlots).reduce((memo, key) => {
|
|
1101
|
+
// 如果当前有插槽内容,则直接渲染
|
|
1102
|
+
if (this.$scopedSlots[formItemSlots[key]]) {
|
|
1103
|
+
memo[key] = this.$scopedSlots[formItemSlots[key]]
|
|
1104
|
+
}
|
|
1105
|
+
return memo
|
|
1106
|
+
}, {})
|
|
1107
|
+
: {}
|
|
1108
|
+
}
|
|
1109
|
+
class='iov-pro-form__form-item override important'
|
|
1110
|
+
>
|
|
1111
|
+
|
|
1112
|
+
{ // 如果需要加载options
|
|
1113
|
+
item.optionsCallback
|
|
1114
|
+
? (
|
|
1115
|
+
<ProRequest
|
|
1116
|
+
ref={`${item.key}Api`}
|
|
1117
|
+
api={item.optionsCallback}
|
|
1118
|
+
params={this.optionParams[item.key]}
|
|
1119
|
+
scopedSlots={{
|
|
1120
|
+
default: options => formInputComp(item, formItemProps || {}, options)
|
|
1121
|
+
}}
|
|
1122
|
+
onLoaded={this.onOptionsLoaded.bind(this, item)}
|
|
1123
|
+
/>
|
|
1124
|
+
)
|
|
1125
|
+
: formInputComp(item, formItemProps, { data: [], loading: false })
|
|
1126
|
+
}
|
|
1127
|
+
{}
|
|
1128
|
+
</FormItem>
|
|
1129
|
+
)
|
|
1130
|
+
// 如果当前需要栅格系统
|
|
1131
|
+
if (this.config.grid) {
|
|
1132
|
+
ItemComp = (
|
|
1133
|
+
<Col
|
|
1134
|
+
key={item.key}
|
|
1135
|
+
span={item.span}
|
|
1136
|
+
>{ItemComp}</Col>
|
|
1137
|
+
)
|
|
1138
|
+
}
|
|
1139
|
+
return ItemComp
|
|
1140
|
+
})
|
|
1141
|
+
|
|
1142
|
+
// 如果有前追加的项目
|
|
1143
|
+
if (this.$scopedSlots.prepend) {
|
|
1144
|
+
FormItems = [
|
|
1145
|
+
...this.$scopedSlots.prepend(),
|
|
1146
|
+
...FormItems
|
|
1147
|
+
]
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// 如果有后追加的项目
|
|
1151
|
+
if (this.$scopedSlots.append) {
|
|
1152
|
+
// 当前追加的表单项item
|
|
1153
|
+
// 将其塞入最后
|
|
1154
|
+
FormItems.push(...this.$scopedSlots.append())
|
|
1155
|
+
}
|
|
1156
|
+
|
|
1157
|
+
// 如果需要栅格系统,则需要在外层包裹一层row
|
|
1158
|
+
if (this.config.grid) {
|
|
1159
|
+
// 将表单项拆分为行
|
|
1160
|
+
const FormItemsInRows = splitColsToRow(FormItems, 'componentOptions.propsData.span')
|
|
1161
|
+
FormItems = (
|
|
1162
|
+
<Space space={this.config.space || DEFAULT_SPACE}>
|
|
1163
|
+
{ // 遍历出当前栅格的行
|
|
1164
|
+
FormItemsInRows.map(row => <Row gutter={this.config.gutter}>{row}</Row>)
|
|
1165
|
+
}
|
|
1166
|
+
</Space>
|
|
1167
|
+
)
|
|
1168
|
+
} else {
|
|
1169
|
+
FormItems = (
|
|
1170
|
+
<Space space={this.config.space || DEFAULT_SPACE}>
|
|
1171
|
+
{FormItems}
|
|
1172
|
+
</Space>
|
|
1173
|
+
)
|
|
1174
|
+
}
|
|
1175
|
+
return (
|
|
1176
|
+
<Form
|
|
1177
|
+
ref='form'
|
|
1178
|
+
class='iov-pro-form'
|
|
1179
|
+
props={{
|
|
1180
|
+
...this.formProps,
|
|
1181
|
+
model: this.modelValue
|
|
1182
|
+
}}
|
|
1183
|
+
onSubmit_native_prevent
|
|
1184
|
+
>
|
|
1185
|
+
{ FormItems }
|
|
1186
|
+
</Form>
|
|
1187
|
+
)
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
</script>
|