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.
Files changed (133) hide show
  1. package/.eslintignore +5 -0
  2. package/.eslintrc.js +192 -0
  3. package/.gitignore +3 -0
  4. package/README.md +4 -0
  5. package/babel.config.js +5 -0
  6. package/docs/.vuepress/config.js +169 -0
  7. package/docs/.vuepress/styles/index.styl +62 -0
  8. package/docs/.vuepress/styles/palette.styl +20 -0
  9. package/docs/.vuepress/theme/enhanceApp.js +100 -0
  10. package/docs/.vuepress/theme/index.js +3 -0
  11. package/docs/README.md +13 -0
  12. package/docs/components/description.md +519 -0
  13. package/docs/components/dialog-select.md +91 -0
  14. package/docs/components/display.md +36 -0
  15. package/docs/components/enums.md +33 -0
  16. package/docs/components/icon.md +406 -0
  17. package/docs/components/link-group.md +39 -0
  18. package/docs/components/page-detail.md +48 -0
  19. package/docs/components/page-module.md +51 -0
  20. package/docs/components/pro-form.md +958 -0
  21. package/docs/components/pro-table.md +683 -0
  22. package/docs/components/request.md +44 -0
  23. package/docs/components/search-table.md +963 -0
  24. package/docs/components/space.md +35 -0
  25. package/docs/components/sub-title.md +24 -0
  26. package/docs/components/submit-module.md +24 -0
  27. package/docs/template/add.md +124 -0
  28. package/docs/template/confirm.md +28 -0
  29. package/docs/template/detail.md +240 -0
  30. package/docs/template/dialog.md +339 -0
  31. package/docs/template/list.md +464 -0
  32. package/docs/template/tabs-mini.md +32 -0
  33. package/docs/template/tabs.md +32 -0
  34. package/jsconfig.json +19 -0
  35. package/lib/iov-pro-components.css +1 -0
  36. package/lib/iov-pro-components.min.js +7 -0
  37. package/lib/postcss.config.js +8 -0
  38. package/package.json +75 -0
  39. package/patches/vue-server-renderer+2.7.16.patch +13 -0
  40. package/rollup.config.mjs +79 -0
  41. package/src/App.vue +103 -0
  42. package/src/main.js +33 -0
  43. package/src/packages/column-tooltip/index.js +7 -0
  44. package/src/packages/column-tooltip/src/main.vue +127 -0
  45. package/src/packages/description/index.js +7 -0
  46. package/src/packages/description/src/main.vue +375 -0
  47. package/src/packages/description/src/text.vue +103 -0
  48. package/src/packages/dialog-select/index.js +7 -0
  49. package/src/packages/dialog-select/src/main.vue +308 -0
  50. package/src/packages/display/index.js +7 -0
  51. package/src/packages/display/src/main.vue +44 -0
  52. package/src/packages/enums/index.js +7 -0
  53. package/src/packages/enums/src/main.vue +23 -0
  54. package/src/packages/export/index.js +7 -0
  55. package/src/packages/export/src/main.vue +316 -0
  56. package/src/packages/fixed-button-group/index.js +7 -0
  57. package/src/packages/fixed-button-group/src/main.vue +104 -0
  58. package/src/packages/form/index.js +7 -0
  59. package/src/packages/form/src/collapse.vue +149 -0
  60. package/src/packages/form/src/main.vue +1190 -0
  61. package/src/packages/form-collapse/index.js +7 -0
  62. package/src/packages/index.js +86 -0
  63. package/src/packages/link-group/index.js +7 -0
  64. package/src/packages/link-group/src/main.vue +52 -0
  65. package/src/packages/page-detail/index.js +7 -0
  66. package/src/packages/page-detail/src/main.vue +123 -0
  67. package/src/packages/page-module/index.js +7 -0
  68. package/src/packages/page-module/src/main.vue +56 -0
  69. package/src/packages/preview/index.js +7 -0
  70. package/src/packages/preview/src/eval-image-viewer.js +50 -0
  71. package/src/packages/preview/src/image-viewer.vue +366 -0
  72. package/src/packages/preview/src/main.vue +97 -0
  73. package/src/packages/request/index.js +7 -0
  74. package/src/packages/request/src/main.vue +125 -0
  75. package/src/packages/search-table/index.js +7 -0
  76. package/src/packages/search-table/src/inner-tabs.vue +237 -0
  77. package/src/packages/search-table/src/main.vue +472 -0
  78. package/src/packages/search-table/src/outer-tabs.vue +45 -0
  79. package/src/packages/search-table-inner-tabs/index.js +7 -0
  80. package/src/packages/search-table-outer-tabs/index.js +7 -0
  81. package/src/packages/space/index.js +7 -0
  82. package/src/packages/space/src/main.vue +74 -0
  83. package/src/packages/sub-title/index.js +7 -0
  84. package/src/packages/sub-title/src/main.vue +70 -0
  85. package/src/packages/submit-module/index.js +7 -0
  86. package/src/packages/submit-module/src/main.vue +67 -0
  87. package/src/packages/table/index.js +7 -0
  88. package/src/packages/table/src/filter.vue +89 -0
  89. package/src/packages/table/src/main.vue +668 -0
  90. package/src/packages/table/src/search.vue +90 -0
  91. package/src/packages/table/src/sort.vue +118 -0
  92. package/src/packages/theme/index.scss +15 -0
  93. package/src/packages/theme/src/column-tooltip.scss +23 -0
  94. package/src/packages/theme/src/common/color.scss +134 -0
  95. package/src/packages/theme/src/description.scss +56 -0
  96. package/src/packages/theme/src/dialog-select.scss +32 -0
  97. package/src/packages/theme/src/fixed-button-group.scss +25 -0
  98. package/src/packages/theme/src/form.scss +11 -0
  99. package/src/packages/theme/src/link-group.scss +43 -0
  100. package/src/packages/theme/src/page-detail.scss +61 -0
  101. package/src/packages/theme/src/page-module.scss +46 -0
  102. package/src/packages/theme/src/preview.scss +67 -0
  103. package/src/packages/theme/src/search-table.scss +185 -0
  104. package/src/packages/theme/src/space.scss +12 -0
  105. package/src/packages/theme/src/sub-title.scss +47 -0
  106. package/src/packages/theme/src/submit-module.scss +13 -0
  107. package/src/packages/theme/src/table.scss +129 -0
  108. package/src/packages/theme/src/toolbar.scss +109 -0
  109. package/src/packages/toolbar/index.js +7 -0
  110. package/src/packages/toolbar/src/main.vue +126 -0
  111. package/src/packages/toolbar/src/setting.vue +217 -0
  112. package/src/packages/toolbar/src/style.vue +68 -0
  113. package/src/packages/toolbar/src/zoom.vue +65 -0
  114. package/src/router.js +83 -0
  115. package/src/utils/config-center.js +218 -0
  116. package/src/utils/function-eval.js +84 -0
  117. package/src/utils/index.js +104 -0
  118. package/src/views/column-tooltip.vue +37 -0
  119. package/src/views/components/OtherSelect.vue +18 -0
  120. package/src/views/description.vue +60 -0
  121. package/src/views/detail.vue +146 -0
  122. package/src/views/directive/number.js +82 -0
  123. package/src/views/enums.vue +22 -0
  124. package/src/views/export.vue +9 -0
  125. package/src/views/form-collapse.vue +185 -0
  126. package/src/views/form.vue +402 -0
  127. package/src/views/link-group.vue +16 -0
  128. package/src/views/preview.vue +33 -0
  129. package/src/views/request.vue +56 -0
  130. package/src/views/search-table.vue +297 -0
  131. package/src/views/table.vue +145 -0
  132. package/src/views/toolbar.vue +30 -0
  133. 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>