el-plus-crud 0.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.
Files changed (86) hide show
  1. package/.eslintignore +18 -0
  2. package/.eslintrc.js +78 -0
  3. package/.lintstagedrc +3 -0
  4. package/.prettierrc.js +39 -0
  5. package/CHANGELOG.md +5 -0
  6. package/LICENSE +21 -0
  7. package/README.md +15 -0
  8. package/example/App.vue +79 -0
  9. package/example/assets/vue.svg +1 -0
  10. package/example/main.js +18 -0
  11. package/example/style.css +5 -0
  12. package/index.html +13 -0
  13. package/lib/components/el-plus-form/ElPlusForm.vue +744 -0
  14. package/lib/components/el-plus-form/ElPlusFormDialog.vue +107 -0
  15. package/lib/components/el-plus-form/components/ElPlusFormAutocomplete.vue +49 -0
  16. package/lib/components/el-plus-form/components/ElPlusFormBtn.vue +96 -0
  17. package/lib/components/el-plus-form/components/ElPlusFormBtns.vue +133 -0
  18. package/lib/components/el-plus-form/components/ElPlusFormCascader.vue +38 -0
  19. package/lib/components/el-plus-form/components/ElPlusFormCascaderPanel.vue +51 -0
  20. package/lib/components/el-plus-form/components/ElPlusFormCheckbox.vue +42 -0
  21. package/lib/components/el-plus-form/components/ElPlusFormCheckboxButton.vue +42 -0
  22. package/lib/components/el-plus-form/components/ElPlusFormColor.vue +38 -0
  23. package/lib/components/el-plus-form/components/ElPlusFormDate.vue +37 -0
  24. package/lib/components/el-plus-form/components/ElPlusFormDaterange.vue +44 -0
  25. package/lib/components/el-plus-form/components/ElPlusFormDatetime.vue +38 -0
  26. package/lib/components/el-plus-form/components/ElPlusFormFile.vue +47 -0
  27. package/lib/components/el-plus-form/components/ElPlusFormImage.vue +113 -0
  28. package/lib/components/el-plus-form/components/ElPlusFormInput.vue +56 -0
  29. package/lib/components/el-plus-form/components/ElPlusFormLink.vue +271 -0
  30. package/lib/components/el-plus-form/components/ElPlusFormNbinput.vue +51 -0
  31. package/lib/components/el-plus-form/components/ElPlusFormNumber.vue +107 -0
  32. package/lib/components/el-plus-form/components/ElPlusFormPassword.vue +39 -0
  33. package/lib/components/el-plus-form/components/ElPlusFormRadio.vue +42 -0
  34. package/lib/components/el-plus-form/components/ElPlusFormRate.vue +38 -0
  35. package/lib/components/el-plus-form/components/ElPlusFormSelect.vue +149 -0
  36. package/lib/components/el-plus-form/components/ElPlusFormSlider.vue +38 -0
  37. package/lib/components/el-plus-form/components/ElPlusFormStatus.vue +67 -0
  38. package/lib/components/el-plus-form/components/ElPlusFormSwitch.vue +38 -0
  39. package/lib/components/el-plus-form/components/ElPlusFormTag.vue +78 -0
  40. package/lib/components/el-plus-form/components/ElPlusFormText.vue +115 -0
  41. package/lib/components/el-plus-form/components/ElPlusFormTextarea.vue +45 -0
  42. package/lib/components/el-plus-form/components/ElPlusFormTransfer.vue +44 -0
  43. package/lib/components/el-plus-form/components/ElPlusFormTree.vue +53 -0
  44. package/lib/components/el-plus-form/components/ElPlusFormTreeSelect.vue +36 -0
  45. package/lib/components/el-plus-form/components/components/IconSelectorList.vue +92 -0
  46. package/lib/components/el-plus-form/components/components/file-icons/FileIcons.vue +135 -0
  47. package/lib/components/el-plus-form/components/components/file-icons/data/index.ts +27 -0
  48. package/lib/components/el-plus-form/components/components/file-icons/images/doc.svg +13 -0
  49. package/lib/components/el-plus-form/components/components/file-icons/images/file.svg +19 -0
  50. package/lib/components/el-plus-form/components/components/file-icons/images/jpg.svg +14 -0
  51. package/lib/components/el-plus-form/components/components/file-icons/images/pdf.svg +13 -0
  52. package/lib/components/el-plus-form/components/components/file-icons/images/png.svg +13 -0
  53. package/lib/components/el-plus-form/components/components/file-icons/images/ppt.svg +13 -0
  54. package/lib/components/el-plus-form/components/components/file-icons/images/xls.svg +13 -0
  55. package/lib/components/el-plus-form/components/index.ts +17 -0
  56. package/lib/components/el-plus-form/data/file.ts +74 -0
  57. package/lib/components/el-plus-form/images/icon/excel.png +0 -0
  58. package/lib/components/el-plus-form/images/icon/file.png +0 -0
  59. package/lib/components/el-plus-form/images/icon/pdf.png +0 -0
  60. package/lib/components/el-plus-form/images/icon/ppt.png +0 -0
  61. package/lib/components/el-plus-form/images/icon/txt.png +0 -0
  62. package/lib/components/el-plus-form/images/icon/word.png +0 -0
  63. package/lib/components/el-plus-form/images/icon/zip.png +0 -0
  64. package/lib/components/el-plus-form/mixins/index.ts +113 -0
  65. package/lib/components/el-plus-form/util/index.ts +266 -0
  66. package/lib/components/el-plus-form/util/validate.ts +310 -0
  67. package/lib/components/el-plus-table/ElPlusTable.vue +723 -0
  68. package/lib/components/el-plus-table/components/columnItem.vue +185 -0
  69. package/lib/components/el-plus-table/components/header.vue +185 -0
  70. package/lib/components/el-plus-table/components/settingColumn.vue +168 -0
  71. package/lib/components/el-plus-table/components/statisticInfo.vue +47 -0
  72. package/lib/components/el-plus-table/util/index.ts +123 -0
  73. package/lib/config/form.ts +12 -0
  74. package/lib/config/index.ts +9 -0
  75. package/lib/index.ts +29 -0
  76. package/package.json +65 -0
  77. package/public/vite.svg +1 -0
  78. package/tsconfig.json +68 -0
  79. package/types/axios.d.ts +13 -0
  80. package/types/formList.d.ts +365 -0
  81. package/types/global.d.ts +145 -0
  82. package/types/layout.d.ts +57 -0
  83. package/types/mitt.d.ts +38 -0
  84. package/types/pinia.d.ts +149 -0
  85. package/types/views.d.ts +329 -0
  86. package/vite.config.ts +68 -0
@@ -0,0 +1,744 @@
1
+ <template>
2
+ <div :style="formLayout">
3
+ <slot name="top" :formData="props.modelValue"></slot>
4
+ <div :style="{ display: 'flex', justifyContent: isDialog ? 'center' : '' }">
5
+ <el-form class="el-plus-form-panel" ref="refElPlusForm" :model="props.modelValue" @submit.prevent="handleSubmitForm" v-bind="computedFormAttrs">
6
+ <el-row :gutter="10" v-for="(formList, index) in attrMapToTableList" :key="index" :style="{ marginRight: isTable ? '20px' : 0 }">
7
+ <el-col v-for="(formItem, y) in formList" :key="index + '-' + y + '-' + formItem.field" :xs="24" :sm="24" :md="formItem.colspan && formItem.colspan >= column ? 24 : column >= 2 ? 12 : 24" :lg="formItem.colspan && formItem.colspan >= column ? 24 : Math.floor((24 / column) * (formItem.colspan || 1))" :xl="formItem.colspan && formItem.colspan >= column ? 24 : Math.floor((24 / column) * (formItem.colspan || 1))">
8
+ <div v-if="formItem._vif" class="el-plus-form-column-panel" :style="{ 'justify-content': isTable ? 'flex-end' : 'flex-start' }">
9
+ <el-form-item style="min-height: 40px" :label="showLabel && formItem.showLabel !== false ? formItem._label : null" :label-width="formItem.labelWidth || labelWidth || (isDialog ? '100px' : '120px')" :prop="formItem.field" :style="{ width: formItem._attrs?.width || formItem.width || (isTable ? '150px' : '100%') }">
10
+ <component style="min-width: 80px; width: 100%" :is="formItem._type" :formData="props.modelValue" :disabled="formItem._disabled ?? disabled ?? false" :readonly="readonly ?? false" v-bind="formItem._attrs" :desc="formItem" :ref="setComponentRef" :field="formItem.field" v-model="props.modelValue[formItem.field || '']" :isTable="isTable" @validateThis="() => handelValidateThis(formItem.field || '')" />
11
+ <div class="el-plus-form-tip" v-if="formItem._tip" v-html="formItem._tip" />
12
+ </el-form-item>
13
+ </div>
14
+ </el-col>
15
+ </el-row>
16
+ <slot name="row"></slot>
17
+ </el-form>
18
+ </div>
19
+ <slot name="default"></slot>
20
+ <el-form>
21
+ <slot :data="props.modelValue" name="form-btn">
22
+ <div v-if="btnList && btnList.length > 0" style="display: flex; justify-content: center">
23
+ <template v-for="(btn, index) of btnList" :key="index">
24
+ <ElPlusFormBtn v-bind="btn"></ElPlusFormBtn>
25
+ </template>
26
+ </div>
27
+ </slot>
28
+ <slot name="bottom" :formData="props.modelValue"></slot>
29
+ </el-form>
30
+ <slot name="form-footer" :formData="props.modelValue"></slot>
31
+ </div>
32
+ </template>
33
+ <script lang="ts">
34
+ export default {
35
+ name: 'ElPlusForm',
36
+ inheritAttrs: false,
37
+ customOptions: {}
38
+ }
39
+ </script>
40
+ <script lang="ts" setup>
41
+ import { ref, computed, useAttrs, nextTick, onMounted, watch, inject } from 'vue'
42
+ import { throttle } from 'lodash'
43
+ import { castArray, isMobile } from './util'
44
+ import * as validates from './util/validate'
45
+ import { typeList } from './components/index'
46
+ import ElPlusFormBtn from './components/ElPlusFormBtn.vue'
47
+ import { cloneDeep } from 'lodash'
48
+
49
+ // IFormProps定义
50
+ export interface IFormProps {
51
+ // 表单描述
52
+ formDesc?: IFormDesc | null
53
+ // 表单数据
54
+ modelValue?: { [key: string]: any } | {}
55
+ // 表单自身属性
56
+ formAttrs?: { [key: string]: any }
57
+ // 校检规则
58
+ rules?: { [key: string]: any } | null
59
+ // 提交状态
60
+ isLoading?: boolean
61
+ // 提交函数
62
+ requestFn?: Function | null
63
+ // 表单更新的函数
64
+ updateFn?: Function | null
65
+ // 是否显示底部按钮组
66
+ showBtns?: boolean
67
+ // 是否显示submit按钮
68
+ showSubmit?: boolean
69
+ // 提交按钮文本
70
+ submitBtnText?: string
71
+ // 是否显示 cancel 取消按钮
72
+ // 默认值: isDialog = true 时, 默认值为 true
73
+ showCancel?: boolean
74
+ // 取消按钮文本
75
+ cancelBtnText?: string
76
+ // 是否显示重置按钮
77
+ showReset?: boolean
78
+ // 重置按钮文本
79
+ resetBtnText?: string
80
+ // 是否显示label
81
+ showLabel?: boolean
82
+ // label宽度
83
+ labelWidth?: number | string
84
+ // 全局禁用表单
85
+ disabled?: boolean
86
+ // 全局的readonly
87
+ readonly?: boolean
88
+ // 是否为弹窗
89
+ isDialog?: boolean
90
+ // options 的请求方法
91
+ optionsFn?: Function | null
92
+ // 表单全局size
93
+ size?: string
94
+ // 表单列 默认1
95
+ column?: number
96
+ // 是否是列表头部的表单
97
+ isTable?: boolean
98
+ // 唯一标识符。默认为id
99
+ idKey?: string
100
+ // 最大宽度
101
+ maxWidth?: string
102
+ // 比如 beforeValidate, beforeRequest, success, requestError, requestEnd
103
+ // 其他钩子 直接放到attrs里面去了
104
+ }
105
+
106
+ // 定义全局的format对象
107
+ const elPlusFormFormat = inject('format') as any
108
+
109
+ const emits = defineEmits(['request', 'reset', 'cancel'])
110
+ const props = withDefaults(defineProps<IFormProps>(), {
111
+ // 表单描述
112
+ formDesc: null,
113
+ // 表单数据
114
+ modelValue: () => {
115
+ return {}
116
+ },
117
+ // 表单自身属性
118
+ formAttrs: () => {
119
+ return {}
120
+ },
121
+ // 校检规则
122
+ rules: null,
123
+ // 提交状态
124
+ isLoading: false,
125
+ // 提交函数
126
+ requestFn: null,
127
+ // 表单更新的函数
128
+ updateFn: null,
129
+ // 是否显示按钮
130
+ showBtns: true,
131
+ // 是否显示submit按钮
132
+ showSubmit: true,
133
+ // 提交按钮文本
134
+ submitBtnText: '提交',
135
+ // 是否显示 cancel 取消按钮
136
+ // 默认值: isDialog = true 时, 默认值为 true
137
+ showCancel: false,
138
+ // 取消按钮
139
+ cancelBtnText: '取消',
140
+ // 是否显示重置按钮
141
+ showReset: true,
142
+ // 重置按钮
143
+ resetBtnText: '重置',
144
+ // 是否显示标签
145
+ showLabel: true,
146
+ // 标签宽度
147
+ labelWidth: '',
148
+ // 全局禁用表单
149
+ disabled: false,
150
+ // 全局的readonly
151
+ readonly: false,
152
+ // 是否为弹窗
153
+ isDialog: false,
154
+ // options 的请求方法
155
+ optionsFn: null,
156
+ // 表单全局size
157
+ size: 'small',
158
+ // 表单列 默认1
159
+ column: 1,
160
+ // 是否是列表头部的表单
161
+ isTable: false,
162
+ // 唯一标识符。默认为id
163
+ idKey: 'id'
164
+ // 其他钩子 直接放到attrs里面去了
165
+ // 比如 beforeValidate, beforeRequest, success, requestError, requestEnd
166
+ })
167
+
168
+ const refElPlusForm = ref()
169
+
170
+ // 是否开启全局监听
171
+ const isOpenListen = ref(false)
172
+
173
+ const tempAttr = useAttrs()
174
+
175
+ // 每个表单项的ref
176
+ const fieldRefs_ = ref([] as Array<any>)
177
+
178
+ // loading
179
+ const innerIsLoading = ref(false)
180
+
181
+ let oldFormData = null as any
182
+
183
+ // 整体的布局方式
184
+ const formLayout = computed(() => ({ display: 'flex', flexDirection: props.isTable ? 'row' : 'column' })) as any
185
+
186
+ // 表单的Attrs
187
+ const computedFormAttrs = computed(() => {
188
+ return {
189
+ scrollToError: true,
190
+ ...props.formAttrs,
191
+ labelWidth: props.labelWidth === 'auto' ? (props.isDialog ? '100px' : '120px') : parseInt(props.labelWidth + '') + 'px',
192
+ // validateOnRuleChange: false,
193
+ disabled: props.disabled || innerIsLoading.value,
194
+ rules: computedRules,
195
+ labelPosition: isMobile() ? 'top' : 'right',
196
+ style: {
197
+ width: props.maxWidth || props.isTable ? '100%' : props.isDialog ? '80%' : '1000px',
198
+ paddingRight: props.isTable ? '0' : '20px'
199
+ }
200
+ }
201
+ })
202
+
203
+ // 合并校验规则
204
+ const computedRules = computed(() => {
205
+ // 首先拿到表单总体传入的rules
206
+ const tempRules = props.rules || []
207
+ // 遍历属性描述对象,看看里面有没有校验规则
208
+ if (props.formDesc) {
209
+ Object.keys(props.formDesc).map((field: any) => {
210
+ if (!tempRules[field]) tempRules[field] = []
211
+ if (props.formDesc) {
212
+ if (props.formDesc[field].rules) {
213
+ if (typeof props.formDesc[field].rules === 'string') {
214
+ // string的话,就行默认校验规则中取
215
+ tempRules[field].push(...(validates as any)[props.formDesc[field].rules as string])
216
+ } else {
217
+ // 查看总体规则中是否已经含有了该属性的校验
218
+ castArray(props.formDesc[field].rules).map((item: any) => {
219
+ tempRules[field].push(item)
220
+ })
221
+ }
222
+ } else if (props.formDesc[field].required || props.formDesc[field].require) {
223
+ // 如果直接指定 required || require,手动添加校验信息
224
+ //
225
+ let rules = 'notAllBlank'
226
+ switch (props.formDesc[field].type) {
227
+ case 'upload':
228
+ case 'select':
229
+ case 'password':
230
+ case 'textarea':
231
+ rules = props.formDesc[field].type
232
+ break
233
+ case 'cascader':
234
+ case 'tselect':
235
+ case 'linkuser':
236
+ case 'radio':
237
+ rules = 'select'
238
+ break
239
+ }
240
+ tempRules[field].push(...(validates as any)[rules])
241
+ }
242
+ }
243
+ })
244
+ }
245
+ return tempRules
246
+ })
247
+
248
+ // 属性对象转数组
249
+ const attrMapToTableList = computed(() => {
250
+ const formLayoutRows = [] as Array<Array<IFormDescItem>>
251
+ if (props.formDesc) {
252
+ let tempData = [] as Array<IFormDescItem>
253
+ for (const key in props.formDesc) {
254
+ tempData.push({ ...props.formDesc[key], field: key })
255
+ }
256
+ // 这里处理一下layout的布局-渲染
257
+ let rowItemList = [] as Array<IFormDescItem>
258
+ let tempCount = 0
259
+ tempData.map((item) => {
260
+ if (item._vif || item.isBlank) {
261
+ rowItemList.push(item)
262
+ tempCount++
263
+ if (item.colspan) {
264
+ tempCount += item.colspan - 1
265
+ }
266
+ if (tempCount >= props.column) {
267
+ // 一行结束
268
+ formLayoutRows.push(rowItemList)
269
+ rowItemList = [] as Array<IFormDescItem>
270
+ tempCount = 0
271
+ return
272
+ }
273
+ }
274
+ })
275
+ if (rowItemList.length > 0) {
276
+ formLayoutRows.push(rowItemList)
277
+ }
278
+ }
279
+ return formLayoutRows
280
+ })
281
+
282
+ // 整体初始化属性
283
+ const initFormAttrs = throttle(() => {
284
+ if (props.formDesc) {
285
+ Object.keys(props.formDesc).forEach((field) => {
286
+ if (props.formDesc) {
287
+ const formItem = props.formDesc[field]
288
+ if (formItem && formItem.type) {
289
+ // 设置 _type
290
+ formItem._type = typeList.includes(formItem.type.toLowerCase()) ? 'el-plus-form-' + formItem.type : formItem.type
291
+ // 触发 v-if 显示 / 隐藏 设置_vif
292
+ formItem._vif = handelKeyValue(formItem, 'vif', field, !formItem.isBlank ?? true)
293
+ // 触发 disabled 禁用 / 启用 设置_disabled
294
+ formItem._disabled = handelKeyValue(formItem, 'disabled', field, props.disabled ?? false)
295
+ // 动态属性 attrs
296
+ const tempAttr = {} as any
297
+ // if ((typeof formItem.attrs === 'object' && formItem.attrs?.options) || formItem.options) {
298
+ // tempAttr.options = (formItem.attrs as any)?.options || formItem.options
299
+ // }
300
+ formItem._attrs = Object.assign({}, handelKeyValue(formItem, 'attrs', field), tempAttr)
301
+ // 动态options
302
+ // formItem._options = handelKeyValue(formItem, 'options', field)
303
+ // 动态 _label
304
+ formItem._label = handelKeyValue(formItem, 'label', field)
305
+ // 动态 prop
306
+ // formItem._prop = handelKeyValue(formItem, 'prop', field);
307
+ // 动态 _tip
308
+ formItem._tip = handelKeyValue(formItem, 'tip', field)
309
+ // 单独处理下上传
310
+ if (!formItem._tip && !formItem.noTip && formItem.type === 'upload') {
311
+ formItem._tip = `最多上传${formItem.multiple ? formItem.limit || 20 : 1}${formItem.upType === 'file' ? '个文件' : '张图片'}`
312
+ }
313
+ // 这里初始化一下默认值
314
+ if (formItem.default !== undefined && formItem.default !== null && props.modelValue[field] === undefined) {
315
+ props.modelValue[field] = formItem.default
316
+ }
317
+ // 这里格式化一下数据
318
+ if (formItem.format) {
319
+ if (typeof formItem.format === 'string') {
320
+ props.modelValue[field] = elPlusFormFormat[formItem.format](props.modelValue[field], props.modelValue)
321
+ } else if (typeof formItem.format === 'function') {
322
+ props.modelValue[field] = (formItem as any).format(props.modelValue[field], props.modelValue)
323
+ } else {
324
+ // console.log('未知的格式化类型:', formItem.format)
325
+ }
326
+ }
327
+ }
328
+ }
329
+ })
330
+ }
331
+ }, 500)
332
+
333
+ // 表单底部按钮
334
+ const btnList = computed(() => {
335
+ let btnList_ = [] as any[]
336
+ if (!props.showBtns) {
337
+ return btnList_
338
+ }
339
+ // 取消按钮
340
+ if (props.showCancel) {
341
+ btnList_.push({
342
+ field: '_reset_btn',
343
+ desc: {
344
+ label: props.cancelBtnText || '取消',
345
+ size: props.size,
346
+ disabled: innerIsLoading.value,
347
+ on: { click: () => emits('cancel') }
348
+ }
349
+ })
350
+ }
351
+ // 重置按钮
352
+ if (props.showReset) {
353
+ btnList_.push({
354
+ field: '_reset_btn',
355
+ desc: {
356
+ label: props.resetBtnText || '重置',
357
+ confirm: `确定要${props.resetBtnText || '重置'}?`,
358
+ size: props.size,
359
+ disabled: innerIsLoading.value,
360
+ on: { click: reset }
361
+ }
362
+ })
363
+ }
364
+ // 提交按钮
365
+ if (props.showSubmit) {
366
+ btnList_.push({
367
+ field: '_reset_btn',
368
+ desc: {
369
+ label: props.submitBtnText || '提交',
370
+ size: props.size,
371
+ type: 'primary',
372
+ loading: props.isLoading || innerIsLoading.value,
373
+ on: { click: handleSubmitForm }
374
+ }
375
+ })
376
+ }
377
+ return btnList_
378
+ })
379
+
380
+ // 处理属性
381
+ const handelKeyValue = (formItem: IFormDescItem, key: string, field: string, defVal: number | boolean | string | null = null) => {
382
+ if (typeof formItem[key] === 'function') {
383
+ // 如果有方法类型的判断,则需要启用动态监测
384
+ isOpenListen.value = true
385
+ return formItem[key](props.modelValue, props.modelValue[field])
386
+ } else if (typeof formItem[key] === 'boolean') {
387
+ return formItem[key] ?? false
388
+ } else if (typeof formItem[key] === 'string') {
389
+ return formItem[key] ?? ''
390
+ } else {
391
+ return formItem[key] ?? defVal
392
+ }
393
+ }
394
+
395
+ // 验证表单
396
+ const validateForm = () => {
397
+ return new Promise((resolve: any, reject: any) => {
398
+ if (computedRules.value) {
399
+ // 当传递了验证规则
400
+ refElPlusForm.value.validate((valid: Boolean, invalidFields: any) => {
401
+ if (valid) {
402
+ // 验证通过
403
+ resolve()
404
+ } else {
405
+ // 显示错误
406
+ reject(invalidFields)
407
+ }
408
+ })
409
+ } else {
410
+ resolve()
411
+ }
412
+ })
413
+ }
414
+
415
+ // 设置子组件的ref-重置form的时候需要用到
416
+ const setComponentRef = (el: any) => {
417
+ if (!el) return
418
+ if (fieldRefs_.value.filter((item: any) => item.field === el.field).length <= 0) {
419
+ fieldRefs_.value.push(el)
420
+ }
421
+ }
422
+
423
+ /**
424
+ * 处理单个表单值
425
+ * @param val
426
+ */
427
+ const handelValToForm = (desc: IFormDescItem, field: string, val: any) => {
428
+ const result = {} as { [key: string]: any }
429
+ if (!desc) return { [field]: val }
430
+ if (!desc._vif || val === undefined || val === null) return result
431
+ if (desc.type === 'category') {
432
+ if (val === null) val = []
433
+ // 如果是直接显示结果
434
+ if (desc.checkStrictly === true) {
435
+ result.categoryId = val[2] ?? val[1] ?? val[0] ?? ''
436
+ } else {
437
+ for (let i = 0; i < 3; i++) {
438
+ result['categoryId' + (i + 1)] = val[i] ?? ''
439
+ }
440
+ }
441
+ } else if (desc.type === 'area') {
442
+ if (val === null) val = [null, null, null, null]
443
+ const [pid, cid, zid, sid] = val
444
+ if (desc.checkStrictly) {
445
+ result[field] = sid || zid || cid || pid || null
446
+ } else {
447
+ result.provinceId = pid || -1
448
+ result.cityId = cid || -1
449
+ result.zoneId = zid || -1
450
+ result.streetId = sid || -1
451
+ }
452
+ } else if (desc.type === 'daterange') {
453
+ if (val && val.length === 2) {
454
+ result.startTime = val[0]
455
+ if (typeof val[1] === 'string') {
456
+ result.endTime = new Date(val[1]).getTime()
457
+ } else {
458
+ result.endTime = val[1]
459
+ }
460
+ result.endTime = result.endTime + (24 * 60 * 60 - 1) * 1000
461
+ // 再处理一下时间戳
462
+ result.startTime = elPlusFormFormat.time(result.startTime, 3)
463
+ result.endTime = elPlusFormFormat.time(result.endTime, 3)
464
+ }
465
+ } else if (desc.type === 'linkuser') {
466
+ const [userIds, deptIds, userNames, deptNames] = val
467
+ result.userIds = userIds
468
+ result.deptIds = deptIds
469
+ result.userNames = userNames
470
+ result.deptNames = deptNames
471
+ } else {
472
+ // 这里处理下通用表单的数据类型
473
+ switch (desc.type) {
474
+ case 'checkbox':
475
+ if (Array.isArray(val)) {
476
+ val = val.join(',')
477
+ }
478
+ break
479
+ }
480
+ result[field] = val
481
+ }
482
+ return result
483
+ }
484
+
485
+ /**
486
+ * 获取处理过后的表单数据
487
+ */
488
+ const getFormData = () => {
489
+ const tempData = {} as { [key: string]: any }
490
+ if (props.formDesc) {
491
+ // 循环获取表单数据
492
+ Object.keys(props.modelValue).map((key) => {
493
+ if (['provinceId', 'cityId', 'zoneId', 'streetId'].indexOf(key) >= 0) return
494
+ if (props.formDesc) {
495
+ Object.assign(tempData, handelValToForm(props.formDesc[key], key, props.modelValue[key]))
496
+ }
497
+ })
498
+ }
499
+ return tempData
500
+ }
501
+
502
+ /**
503
+ * 刷新验证码图片
504
+ */
505
+ const changeValidImg = () => {
506
+ fieldRefs_.value.map((item) => {
507
+ if (item.CID) {
508
+ item.changeValidImg()
509
+ }
510
+ })
511
+ }
512
+
513
+ /**
514
+ * 提交表单
515
+ */
516
+ const handleSubmitForm = async () => {
517
+ let postData = getFormData()
518
+ try {
519
+ // 校验表单事件处理
520
+ if (tempAttr.beforeValidate) {
521
+ const isPass = await (tempAttr.beforeValidate as Function)(postData)
522
+ if (isPass === false) return
523
+ }
524
+
525
+ await validateForm()
526
+ for (const field in postData) {
527
+ // 去除下划线开头的参数
528
+ if (field.indexOf('_') === 0) {
529
+ delete postData[field]
530
+ continue
531
+ }
532
+ if (props.formDesc) {
533
+ const formItem = props.formDesc[field]
534
+ // valueFormatter的处理
535
+ if (formItem && formItem.valueFormat) {
536
+ postData[field] = formItem.valueFormat(postData[field], postData)
537
+ }
538
+ // 处理带有true或者false值
539
+ if (postData[field] === true || postData[field] === false) {
540
+ postData[field] = +postData[field]
541
+ }
542
+ }
543
+ }
544
+
545
+ // 提交数据前的通知
546
+ if (tempAttr.beforeRequest) {
547
+ const beforeRequestData = await (tempAttr.beforeRequest as Function)(postData)
548
+ if (beforeRequestData === false) return
549
+ if (typeof beforeRequestData === 'object') {
550
+ postData = beforeRequestData
551
+ }
552
+ }
553
+ if (props.requestFn) {
554
+ // 在内部调用请求
555
+ if (innerIsLoading.value) return
556
+ innerIsLoading.value = true
557
+ try {
558
+ let response = {}
559
+ if (props.updateFn && postData && (postData as any)[props.idKey]) {
560
+ response = await props.updateFn(postData)
561
+ } else {
562
+ response = await props.requestFn(postData)
563
+ }
564
+ nextTick(() => {
565
+ if (tempAttr.success && typeof tempAttr.success === 'function') {
566
+ tempAttr.success({ response, formData: props.modelValue, callback: () => (innerIsLoading.value = false) } as IFormBack)
567
+ reset()
568
+ }
569
+ })
570
+ } catch (error) {
571
+ // 如果用户有处理异常的方法了
572
+ if (tempAttr.requestError && typeof tempAttr.requestError === 'function') {
573
+ tempAttr.requestError(error)
574
+ } else {
575
+ // 处理异常情况
576
+ if (error instanceof Error) {
577
+ // 返回的是Error类型, 则进行解析
578
+ try {
579
+ const msg = JSON.parse(error.message)
580
+ if (msg instanceof Object) {
581
+ // innerFormError.value = msg;
582
+ }
583
+ // eslint-disable-next-line
584
+ } catch {}
585
+ } else if (error instanceof Object) {
586
+ // 返回的是对象类型, 则直接设置
587
+ // innerFormError = error;
588
+ }
589
+ if (tempAttr.requestError && typeof tempAttr.requestError === 'function') {
590
+ tempAttr.requestError()
591
+ }
592
+ }
593
+ // 报错了这里恢复
594
+ innerIsLoading.value = false
595
+ } finally {
596
+ if (!props.isDialog) {
597
+ innerIsLoading.value = false
598
+ }
599
+ if (tempAttr.requestEnd && typeof tempAttr.requestEnd === 'function') {
600
+ tempAttr.requestEnd()
601
+ }
602
+ }
603
+ } else {
604
+ // 在外部用户自己处理请求
605
+ if (props.isLoading) return
606
+ emits('request', postData)
607
+ }
608
+ } catch (error) {
609
+ // console.log('error: ', error)
610
+ }
611
+ }
612
+
613
+ /**
614
+ * 重置,并清除校验
615
+ */
616
+ const reset = () => {
617
+ // refElPlusForm.value.resetFields();
618
+ // 重置
619
+ Object.keys(props.modelValue).map((key) => {
620
+ props.modelValue[key] = oldFormData ? oldFormData[key] : (props.formDesc && props.formDesc[key] && props.formDesc[key].default) ?? null
621
+ })
622
+ setTimeout(() => {
623
+ // 清空校验
624
+ clearValid()
625
+ }, 100)
626
+ // 通知外部
627
+ emits('reset')
628
+ }
629
+
630
+ /**
631
+ * 清空校验
632
+ */
633
+ const clearValid = () => {
634
+ refElPlusForm.value?.clearValidate()
635
+ }
636
+
637
+ /**
638
+ * 清理
639
+ */
640
+ const clear = () => {
641
+ // 这里进行数据清空
642
+ if (props.modelValue) {
643
+ Object.keys(props.modelValue).map((key) => {
644
+ props.modelValue[key] = (props.formDesc && props.formDesc[key] && props.formDesc[key].default) ?? null
645
+ })
646
+ }
647
+ // 重置校验
648
+ nextTick(() => {
649
+ clearValid()
650
+ })
651
+ }
652
+
653
+ /**
654
+ * 单独校验一下字段
655
+ * @param field
656
+ */
657
+ function handelValidateThis(field: string) {
658
+ refElPlusForm.value.validateField(field, (err: any) => {
659
+ // console.log('err: ', err)
660
+ })
661
+ }
662
+
663
+ // 监听Desc改变
664
+ watch(
665
+ () => props.formDesc,
666
+ (formDesc) => {
667
+ if (formDesc) {
668
+ // 检查联动
669
+ initFormAttrs()
670
+ }
671
+ }
672
+ )
673
+
674
+ // 深度监听data改变
675
+ watch(
676
+ () => props.modelValue,
677
+ (data, oldData) => {
678
+ if (isOpenListen.value && data) {
679
+ // 检查联动
680
+ initFormAttrs()
681
+ nextTick(() => {
682
+ setTimeout(() => {
683
+ if (!oldFormData) {
684
+ oldFormData = cloneDeep(props.modelValue)
685
+ }
686
+ }, 100)
687
+ })
688
+ }
689
+ },
690
+ { deep: true }
691
+ )
692
+
693
+ onMounted(async () => {
694
+ // 检查联动
695
+ if (!isOpenListen.value) {
696
+ initFormAttrs()
697
+ }
698
+ nextTick(() => {
699
+ setTimeout(() => {
700
+ clearValid()
701
+ setTimeout(() => {
702
+ if (!oldFormData) {
703
+ oldFormData = cloneDeep(props.modelValue)
704
+ }
705
+ }, 100)
706
+ }, 100)
707
+ })
708
+ })
709
+
710
+ // 暴露对外方法
711
+ defineExpose({ submit: handleSubmitForm, getData: getFormData, validate: validateForm, reset, clearValid, clear, changeValidImg })
712
+ </script>
713
+ <style lang="scss">
714
+ .el-plus-form-panel {
715
+ .el-plus-form--inline .el-plus-form-btns {
716
+ width: auto;
717
+ }
718
+
719
+ .el-form--inline .el-form-item {
720
+ margin-right: 12px !important;
721
+ }
722
+
723
+ .el-plus-form-col--break {
724
+ clear: both;
725
+ }
726
+
727
+ .el-plus-form-tip {
728
+ color: #909399;
729
+ line-height: 1.5em;
730
+ margin-top: 3px;
731
+ }
732
+
733
+ .el-plus-form-tip code {
734
+ padding: 2px 4px;
735
+ font-size: 90%;
736
+ color: #c7254e;
737
+ background-color: #f9f2f4;
738
+ border-radius: 4px;
739
+ }
740
+ .el-form-item--default {
741
+ margin-bottom: 18px !important;
742
+ }
743
+ }
744
+ </style>