Commit 84c9d78fa7feffe3430b7c85dc97383cd7193ba1
1 parent
e2333642
refactor(form): code optimization and reconstruction
Showing
17 changed files
with
676 additions
and
433 deletions
CHANGELOG.zh_CN.md
1 | 1 | ## Wip |
2 | 2 | |
3 | +### ✨ Features | |
4 | + | |
5 | +- 表单组件现在支持直接传入 model 直接进行 set 操作,参考**组件->弹窗扩展->打开弹窗并传递数据** | |
6 | + | |
7 | +- modal 的 useModalInner 现在支持传入回调函数,用于接收外部`transferModalData`传进来的值, | |
8 | + - 用于处理打开弹窗对表单等组件的设置值。参考**组件->弹窗扩展->打开弹窗并传递数据** | |
9 | + - `receiveModalDataRef`这个值暂时保留。尽量少用。后续可能会删除。 | |
10 | + | |
11 | +### ✨ Refactor | |
12 | + | |
13 | +- 表单代码优化重构 | |
14 | + | |
3 | 15 | ### 🎫 Chores |
4 | 16 | |
5 | 17 | - 添加部分注释 |
... | ... | @@ -10,6 +22,7 @@ |
10 | 22 | |
11 | 23 | - 修复本地代理 post 接口到 https 地址超时错误 |
12 | 24 | - 修复 modal 在不显示 footer 的时候全屏高度计算问题 |
25 | +- 修复表单重置未删除校验信息错误 | |
13 | 26 | |
14 | 27 | ## 2.0.0-rc.6 (2020-10-28) |
15 | 28 | ... | ... |
src/components/Form/src/BasicForm.vue
... | ... | @@ -25,6 +25,8 @@ |
25 | 25 | <script lang="ts"> |
26 | 26 | import type { FormActionType, FormProps, FormSchema } from './types/form'; |
27 | 27 | import type { Form as FormType, ValidateFields } from 'ant-design-vue/types/form/form'; |
28 | + import type { AdvanceState } from './types/hooks'; | |
29 | + import type { Ref } from 'vue'; | |
28 | 30 | |
29 | 31 | import { |
30 | 32 | defineComponent, |
... | ... | @@ -32,27 +34,22 @@ |
32 | 34 | ref, |
33 | 35 | computed, |
34 | 36 | unref, |
35 | - toRaw, | |
36 | - watch, | |
37 | 37 | toRef, |
38 | 38 | onMounted, |
39 | + watchEffect, | |
39 | 40 | } from 'vue'; |
40 | 41 | import { Form, Row } from 'ant-design-vue'; |
41 | 42 | import FormItem from './FormItem'; |
42 | 43 | import { basicProps } from './props'; |
43 | - import { deepMerge, unique } from '/@/utils'; | |
44 | + import { deepMerge } from '/@/utils'; | |
44 | 45 | import FormAction from './FormAction'; |
45 | 46 | |
46 | 47 | import { dateItemType } from './helper'; |
47 | 48 | import moment from 'moment'; |
48 | - import { isArray, isBoolean, isFunction, isNumber, isObject, isString } from '/@/utils/is'; | |
49 | 49 | import { cloneDeep } from 'lodash-es'; |
50 | - import { useBreakpoint } from '/@/hooks/event/useBreakpoint'; | |
51 | - // import { useThrottle } from '/@/hooks/core/useThrottle'; | |
52 | 50 | import { useFormValues } from './hooks/useFormValues'; |
53 | - import type { ColEx } from './types'; | |
54 | - import { NamePath } from 'ant-design-vue/types/form/form-item'; | |
55 | - const BASIC_COL_LEN = 24; | |
51 | + import useAdvanced from './hooks/useAdvanced'; | |
52 | + import { useFormAction } from './hooks/useFormAction'; | |
56 | 53 | |
57 | 54 | export default defineComponent({ |
58 | 55 | name: 'BasicForm', |
... | ... | @@ -61,13 +58,20 @@ |
61 | 58 | props: basicProps, |
62 | 59 | emits: ['advanced-change', 'reset', 'submit', 'register'], |
63 | 60 | setup(props, { emit }) { |
64 | - let formModel = reactive({}); | |
65 | - const advanceState = reactive({ | |
61 | + const formModel = reactive({}); | |
62 | + | |
63 | + const actionState = reactive({ | |
64 | + resetAction: {}, | |
65 | + submitAction: {}, | |
66 | + }); | |
67 | + | |
68 | + const advanceState = reactive<AdvanceState>({ | |
66 | 69 | isAdvanced: true, |
67 | 70 | hideAdvanceBtn: false, |
68 | 71 | isLoad: false, |
69 | 72 | actionSpan: 6, |
70 | 73 | }); |
74 | + | |
71 | 75 | const defaultValueRef = ref<any>({}); |
72 | 76 | const propsRef = ref<Partial<FormProps>>({}); |
73 | 77 | const schemaRef = ref<FormSchema[] | null>(null); |
... | ... | @@ -78,50 +82,24 @@ |
78 | 82 | return deepMerge(cloneDeep(props), unref(propsRef)); |
79 | 83 | } |
80 | 84 | ); |
85 | + | |
81 | 86 | // 获取表单基本配置 |
82 | 87 | const getProps = computed( |
83 | 88 | (): FormProps => { |
84 | - const resetAction = { | |
85 | - onClick: resetFields, | |
86 | - }; | |
87 | - const submitAction = { | |
88 | - onClick: handleSubmit, | |
89 | - }; | |
90 | 89 | return { |
91 | 90 | ...unref(getMergePropsRef), |
92 | 91 | resetButtonOptions: deepMerge( |
93 | - resetAction, | |
92 | + actionState.resetAction, | |
94 | 93 | unref(getMergePropsRef).resetButtonOptions || {} |
95 | - ) as any, | |
94 | + ), | |
96 | 95 | submitButtonOptions: deepMerge( |
97 | - submitAction, | |
96 | + actionState.submitAction, | |
98 | 97 | unref(getMergePropsRef).submitButtonOptions || {} |
99 | - ) as any, | |
98 | + ), | |
100 | 99 | }; |
101 | 100 | } |
102 | 101 | ); |
103 | 102 | |
104 | - const getActionPropsRef = computed(() => { | |
105 | - const { | |
106 | - resetButtonOptions, | |
107 | - submitButtonOptions, | |
108 | - showActionButtonGroup, | |
109 | - showResetButton, | |
110 | - showSubmitButton, | |
111 | - showAdvancedButton, | |
112 | - actionColOptions, | |
113 | - } = unref(getProps); | |
114 | - return { | |
115 | - resetButtonOptions, | |
116 | - submitButtonOptions, | |
117 | - show: showActionButtonGroup, | |
118 | - showResetButton, | |
119 | - showSubmitButton, | |
120 | - showAdvancedButton, | |
121 | - actionColOptions, | |
122 | - }; | |
123 | - }); | |
124 | - | |
125 | 103 | const getSchema = computed((): FormSchema[] => { |
126 | 104 | const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any); |
127 | 105 | for (const schema of schemas) { |
... | ... | @@ -133,305 +111,51 @@ |
133 | 111 | return schemas as FormSchema[]; |
134 | 112 | }); |
135 | 113 | |
136 | - const getEmptySpanRef = computed((): number => { | |
137 | - if (!advanceState.isAdvanced) { | |
138 | - return 0; | |
139 | - } | |
140 | - const emptySpan = unref(getMergePropsRef).emptySpan || 0; | |
141 | - | |
142 | - if (isNumber(emptySpan)) { | |
143 | - return emptySpan; | |
144 | - } | |
145 | - if (isObject(emptySpan)) { | |
146 | - const { span = 0 } = emptySpan; | |
147 | - const screen = unref(screenRef) as string; | |
148 | - | |
149 | - const screenSpan = (emptySpan as any)[screen.toLowerCase()]; | |
150 | - return screenSpan || span || 0; | |
151 | - } | |
152 | - return 0; | |
114 | + const { getActionPropsRef, handleToggleAdvanced } = useAdvanced({ | |
115 | + advanceState, | |
116 | + emit, | |
117 | + getMergePropsRef, | |
118 | + getProps, | |
119 | + getSchema, | |
120 | + formModel, | |
121 | + defaultValueRef, | |
153 | 122 | }); |
154 | 123 | |
155 | - const { realWidthRef, screenEnum, screenRef } = useBreakpoint(); | |
156 | - // const [throttleUpdateAdvanced] = useThrottle(updateAdvanced, 30, { immediate: true }); | |
157 | - watch( | |
158 | - [() => unref(getSchema), () => advanceState.isAdvanced, () => unref(realWidthRef)], | |
159 | - () => { | |
160 | - const { showAdvancedButton } = unref(getProps); | |
161 | - if (showAdvancedButton) { | |
162 | - updateAdvanced(); | |
163 | - } | |
164 | - }, | |
165 | - { immediate: true } | |
166 | - ); | |
167 | - | |
168 | - function initDefault() { | |
169 | - const schemas = unref(getSchema); | |
170 | - const obj: any = {}; | |
171 | - schemas.forEach((item) => { | |
172 | - if (item.defaultValue) { | |
173 | - obj[item.field] = item.defaultValue; | |
174 | - (formModel as any)[item.field] = item.defaultValue; | |
175 | - } | |
176 | - }); | |
177 | - defaultValueRef.value = obj; | |
178 | - } | |
179 | - | |
180 | - function updateAdvanced() { | |
181 | - let itemColSum = 0; | |
182 | - let realItemColSum = 0; | |
183 | - for (const schema of unref(getSchema)) { | |
184 | - const { show, colProps } = schema; | |
185 | - let isShow = true; | |
186 | - | |
187 | - if (isBoolean(show)) { | |
188 | - isShow = show; | |
189 | - } | |
190 | - | |
191 | - if (isFunction(show)) { | |
192 | - isShow = show({ | |
193 | - schema: schema, | |
194 | - model: formModel, | |
195 | - field: schema.field, | |
196 | - values: { | |
197 | - ...unref(defaultValueRef), | |
198 | - ...formModel, | |
199 | - }, | |
200 | - }); | |
201 | - } | |
202 | - if (isShow && colProps) { | |
203 | - const { itemColSum: sum, isAdvanced } = getAdvanced(colProps, itemColSum); | |
204 | - | |
205 | - itemColSum = sum || 0; | |
206 | - if (isAdvanced) { | |
207 | - realItemColSum = itemColSum; | |
208 | - } | |
209 | - schema.isAdvanced = isAdvanced; | |
210 | - } | |
211 | - } | |
212 | - advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpanRef); | |
213 | - getAdvanced( | |
214 | - unref(getActionPropsRef).actionColOptions || { span: BASIC_COL_LEN }, | |
215 | - itemColSum, | |
216 | - true | |
217 | - ); | |
218 | - emit('advanced-change'); | |
219 | - } | |
220 | - function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false) { | |
221 | - const width = unref(realWidthRef); | |
222 | - | |
223 | - const mdWidth = | |
224 | - parseInt(itemCol.md as string) || | |
225 | - parseInt(itemCol.xs as string) || | |
226 | - parseInt(itemCol.sm as string) || | |
227 | - (itemCol.span as number) || | |
228 | - BASIC_COL_LEN; | |
229 | - const lgWidth = parseInt(itemCol.lg as string) || mdWidth; | |
230 | - const xlWidth = parseInt(itemCol.xl as string) || lgWidth; | |
231 | - const xxlWidth = parseInt(itemCol.xxl as string) || xlWidth; | |
232 | - if (width <= screenEnum.LG) { | |
233 | - itemColSum += mdWidth; | |
234 | - } else if (width < screenEnum.XL) { | |
235 | - itemColSum += lgWidth; | |
236 | - } else if (width < screenEnum.XXL) { | |
237 | - itemColSum += xlWidth; | |
238 | - } else { | |
239 | - itemColSum += xxlWidth; | |
240 | - } | |
241 | - if (isLastAction) { | |
242 | - advanceState.hideAdvanceBtn = false; | |
243 | - if (itemColSum <= BASIC_COL_LEN * 2) { | |
244 | - // 小于等于2行时,不显示收起展开按钮 | |
245 | - advanceState.hideAdvanceBtn = true; | |
246 | - advanceState.isAdvanced = true; | |
247 | - } else if ( | |
248 | - itemColSum > BASIC_COL_LEN * 2 && | |
249 | - itemColSum <= BASIC_COL_LEN * (props.autoAdvancedLine || 3) | |
250 | - ) { | |
251 | - advanceState.hideAdvanceBtn = false; | |
252 | - | |
253 | - // 大于3行默认收起 | |
254 | - } else if (!advanceState.isLoad) { | |
255 | - advanceState.isLoad = true; | |
256 | - advanceState.isAdvanced = !advanceState.isAdvanced; | |
257 | - } | |
258 | - return { isAdvanced: advanceState.isAdvanced, itemColSum }; | |
259 | - } | |
260 | - if (itemColSum > BASIC_COL_LEN) { | |
261 | - return { isAdvanced: advanceState.isAdvanced, itemColSum }; | |
262 | - } else { | |
263 | - // 第一行始终显示 | |
264 | - return { isAdvanced: true, itemColSum }; | |
265 | - } | |
266 | - } | |
267 | - | |
268 | - async function resetFields(): Promise<any> { | |
269 | - const { resetFunc, submitOnReset } = unref(getProps); | |
270 | - resetFunc && isFunction(resetFunc) && (await resetFunc()); | |
271 | - const formEl = unref(formElRef); | |
272 | - if (!formEl) return; | |
273 | - Object.keys(formModel).forEach((key) => { | |
274 | - (formModel as any)[key] = defaultValueRef.value[key]; | |
275 | - }); | |
276 | - // const values = formEl.resetFields(); | |
277 | - emit('reset', toRaw(formModel)); | |
278 | - // return values; | |
279 | - submitOnReset && handleSubmit(); | |
280 | - } | |
281 | - | |
282 | - /** | |
283 | - * @description: 设置表单值 | |
284 | - */ | |
285 | - async function setFieldsValue(values: any): Promise<void> { | |
286 | - const fields = unref(getSchema) | |
287 | - .map((item) => item.field) | |
288 | - .filter(Boolean); | |
289 | - const formEl = unref(formElRef); | |
290 | - Object.keys(values).forEach((key) => { | |
291 | - const element = values[key]; | |
292 | - if (fields.includes(key) && element !== undefined && element !== null) { | |
293 | - // 时间 | |
294 | - if (itemIsDateType(key)) { | |
295 | - if (Array.isArray(element)) { | |
296 | - const arr: any[] = []; | |
297 | - for (const ele of element) { | |
298 | - arr.push(moment(ele)); | |
299 | - } | |
300 | - (formModel as any)[key] = arr; | |
301 | - } else { | |
302 | - (formModel as any)[key] = moment(element); | |
303 | - } | |
304 | - } else { | |
305 | - (formModel as any)[key] = element; | |
306 | - } | |
307 | - if (formEl) { | |
308 | - formEl.validateFields([key]); | |
309 | - } | |
310 | - } | |
311 | - }); | |
312 | - } | |
313 | - | |
314 | - /** | |
315 | - * @description: 表单提交 | |
316 | - */ | |
317 | - async function handleSubmit(e?: Event): Promise<void> { | |
318 | - e && e.preventDefault(); | |
319 | - const { submitFunc } = unref(getProps); | |
320 | - if (submitFunc && isFunction(submitFunc)) { | |
321 | - await submitFunc(); | |
322 | - return; | |
323 | - } | |
324 | - const formEl = unref(formElRef); | |
325 | - if (!formEl) return; | |
326 | - try { | |
327 | - const values = await formEl.validate(); | |
328 | - const res = handleFormValues(values); | |
329 | - emit('submit', res); | |
330 | - } catch (error) {} | |
331 | - } | |
332 | - | |
333 | - /** | |
334 | - * @description: 根据字段名删除 | |
335 | - */ | |
336 | - function removeSchemaByFiled(fields: string | string[]): void { | |
337 | - const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); | |
338 | - if (!fields) { | |
339 | - return; | |
340 | - } | |
341 | - let fieldList: string[] = fields as string[]; | |
342 | - if (isString(fields)) { | |
343 | - fieldList = [fields]; | |
344 | - } | |
345 | - for (const field of fieldList) { | |
346 | - _removeSchemaByFiled(field, schemaList); | |
347 | - } | |
348 | - schemaRef.value = schemaList as any; | |
349 | - } | |
350 | - | |
351 | - /** | |
352 | - * @description: 根据字段名删除 | |
353 | - */ | |
354 | - function _removeSchemaByFiled(field: string, schemaList: FormSchema[]): void { | |
355 | - if (isString(field)) { | |
356 | - const index = schemaList.findIndex((schema) => schema.field === field); | |
357 | - if (index !== -1) { | |
358 | - schemaList.splice(index, 1); | |
359 | - } | |
360 | - } | |
361 | - } | |
362 | - | |
363 | - /** | |
364 | - * @description: 往某个字段后面插入,如果没有插入最后一个 | |
365 | - */ | |
366 | - function appendSchemaByField(schema: FormSchema, prefixField?: string) { | |
367 | - const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); | |
368 | - | |
369 | - const index = schemaList.findIndex((schema) => schema.field === prefixField); | |
370 | - const hasInList = schemaList.find((item) => item.field === schema.field); | |
371 | - | |
372 | - if (hasInList) { | |
373 | - return; | |
374 | - } | |
375 | - if (!prefixField || index === -1) { | |
376 | - schemaList.push(schema); | |
377 | - schemaRef.value = schemaList as any; | |
378 | - return; | |
379 | - } | |
380 | - if (index !== -1) { | |
381 | - schemaList.splice(index + 1, 0, schema); | |
382 | - } | |
383 | - schemaRef.value = schemaList as any; | |
384 | - } | |
385 | - | |
386 | - function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) { | |
387 | - let updateData: Partial<FormSchema>[] = []; | |
388 | - if (isObject(data)) { | |
389 | - updateData.push(data as FormSchema); | |
390 | - } | |
391 | - if (isArray(data)) { | |
392 | - updateData = [...data]; | |
393 | - } | |
394 | - const hasField = updateData.every((item) => Reflect.has(item, 'field') && item.field); | |
395 | - if (!hasField) { | |
396 | - throw new Error('Must pass in the `field` field!'); | |
397 | - } | |
398 | - const schema: FormSchema[] = []; | |
399 | - updateData.forEach((item) => { | |
400 | - unref(getSchema).forEach((val) => { | |
401 | - if (val.field === item.field) { | |
402 | - const newScheam = deepMerge(val, item); | |
403 | - schema.push(newScheam as FormSchema); | |
404 | - } else { | |
405 | - schema.push(val); | |
406 | - } | |
407 | - }); | |
408 | - }); | |
409 | - schemaRef.value = unique(schema, 'field') as any; | |
410 | - } | |
411 | - | |
412 | - function handleToggleAdvanced() { | |
413 | - advanceState.isAdvanced = !advanceState.isAdvanced; | |
414 | - } | |
415 | - | |
416 | - const handleFormValues = useFormValues( | |
417 | - toRef(props, 'transformDateFunc'), | |
418 | - toRef(props, 'fieldMapToTime') | |
419 | - ); | |
124 | + const { handleFormValues, initDefault } = useFormValues({ | |
125 | + transformDateFuncRef: toRef(props, 'transformDateFunc') as Ref<Fn<any>>, | |
126 | + fieldMapToTimeRef: toRef(props, 'fieldMapToTime'), | |
127 | + defaultValueRef, | |
128 | + getSchema, | |
129 | + formModel, | |
130 | + }); | |
420 | 131 | |
421 | - function getFieldsValue(): any { | |
422 | - const formEl = unref(formElRef); | |
423 | - if (!formEl) return; | |
424 | - return handleFormValues(toRaw(unref(formModel))); | |
425 | - } | |
132 | + const { | |
133 | + // handleSubmit, | |
134 | + setFieldsValue, | |
135 | + clearValidate, | |
136 | + validate, | |
137 | + validateFields, | |
138 | + getFieldsValue, | |
139 | + updateSchema, | |
140 | + appendSchemaByField, | |
141 | + removeSchemaByFiled, | |
142 | + resetFields, | |
143 | + } = useFormAction({ | |
144 | + emit, | |
145 | + getProps, | |
146 | + formModel, | |
147 | + getSchema, | |
148 | + defaultValueRef, | |
149 | + formElRef: formElRef as any, | |
150 | + schemaRef: schemaRef as any, | |
151 | + handleFormValues, | |
152 | + actionState, | |
153 | + }); | |
426 | 154 | |
427 | - /** | |
428 | - * @description: 是否是时间 | |
429 | - */ | |
430 | - function itemIsDateType(key: string) { | |
431 | - return unref(getSchema).some((item) => { | |
432 | - return item.field === key ? dateItemType.includes(item.component!) : false; | |
433 | - }); | |
434 | - } | |
155 | + watchEffect(() => { | |
156 | + if (!unref(getMergePropsRef).model) return; | |
157 | + setFieldsValue(unref(getMergePropsRef).model); | |
158 | + }); | |
435 | 159 | |
436 | 160 | /** |
437 | 161 | * @description:设置表单 |
... | ... | @@ -441,21 +165,6 @@ |
441 | 165 | propsRef.value = mergeProps; |
442 | 166 | } |
443 | 167 | |
444 | - function validateFields(nameList?: NamePath[] | undefined) { | |
445 | - if (!formElRef.value) return; | |
446 | - return formElRef.value.validateFields(nameList); | |
447 | - } | |
448 | - | |
449 | - function validate(nameList?: NamePath[] | undefined) { | |
450 | - if (!formElRef.value) return; | |
451 | - return formElRef.value.validate(nameList); | |
452 | - } | |
453 | - | |
454 | - function clearValidate(name: string | string[]) { | |
455 | - if (!formElRef.value) return; | |
456 | - formElRef.value.clearValidate(name); | |
457 | - } | |
458 | - | |
459 | 168 | const methods: Partial<FormActionType> = { |
460 | 169 | getFieldsValue, |
461 | 170 | setFieldsValue, | ... | ... |
src/components/Form/src/FormAction.tsx
... | ... | @@ -57,6 +57,7 @@ export default defineComponent({ |
57 | 57 | ...props.resetButtonOptions, |
58 | 58 | }; |
59 | 59 | }); |
60 | + | |
60 | 61 | const getSubmitBtnOptionsRef = computed(() => { |
61 | 62 | return { |
62 | 63 | text: '查询', |
... | ... | @@ -80,10 +81,12 @@ export default defineComponent({ |
80 | 81 | function toggleAdvanced() { |
81 | 82 | emit('toggle-advanced'); |
82 | 83 | } |
84 | + | |
83 | 85 | return () => { |
84 | 86 | if (!props.show) { |
85 | 87 | return; |
86 | 88 | } |
89 | + | |
87 | 90 | const { |
88 | 91 | showAdvancedButton, |
89 | 92 | hideAdvanceBtn, |
... | ... | @@ -91,50 +94,49 @@ export default defineComponent({ |
91 | 94 | showResetButton, |
92 | 95 | showSubmitButton, |
93 | 96 | } = props; |
97 | + | |
94 | 98 | return ( |
95 | - <> | |
96 | - <Col {...unref(actionColOpt)} style={{ textAlign: 'right' }}> | |
97 | - {() => ( | |
98 | - <Form.Item> | |
99 | - {() => ( | |
100 | - <> | |
101 | - {getSlot(slots, 'advanceBefore')} | |
102 | - {showAdvancedButton && !hideAdvanceBtn && ( | |
103 | - <Button type="default" class="mr-2" onClick={toggleAdvanced}> | |
104 | - {() => ( | |
105 | - <> | |
106 | - {isAdvanced ? '收起' : '展开'} | |
107 | - {isAdvanced ? ( | |
108 | - <UpOutlined class="advanced-icon" /> | |
109 | - ) : ( | |
110 | - <DownOutlined class="advanced-icon" /> | |
111 | - )} | |
112 | - </> | |
113 | - )} | |
114 | - </Button> | |
115 | - )} | |
99 | + <Col {...unref(actionColOpt)} style={{ textAlign: 'right' }}> | |
100 | + {() => ( | |
101 | + <Form.Item> | |
102 | + {() => ( | |
103 | + <> | |
104 | + {getSlot(slots, 'advanceBefore')} | |
105 | + {showAdvancedButton && !hideAdvanceBtn && ( | |
106 | + <Button type="default" class="mr-2" onClick={toggleAdvanced}> | |
107 | + {() => ( | |
108 | + <> | |
109 | + {isAdvanced ? '收起' : '展开'} | |
110 | + {isAdvanced ? ( | |
111 | + <UpOutlined class="advanced-icon" /> | |
112 | + ) : ( | |
113 | + <DownOutlined class="advanced-icon" /> | |
114 | + )} | |
115 | + </> | |
116 | + )} | |
117 | + </Button> | |
118 | + )} | |
116 | 119 | |
117 | - {getSlot(slots, 'resetBefore')} | |
118 | - {showResetButton && ( | |
119 | - <Button type="default" class="mr-2" {...unref(getResetBtnOptionsRef)}> | |
120 | - {() => unref(getResetBtnOptionsRef).text} | |
121 | - </Button> | |
122 | - )} | |
120 | + {getSlot(slots, 'resetBefore')} | |
121 | + {showResetButton && ( | |
122 | + <Button type="default" class="mr-2" {...unref(getResetBtnOptionsRef)}> | |
123 | + {() => unref(getResetBtnOptionsRef).text} | |
124 | + </Button> | |
125 | + )} | |
123 | 126 | |
124 | - {getSlot(slots, 'submitBefore')} | |
125 | - {showSubmitButton && ( | |
126 | - <Button type="primary" {...unref(getSubmitBtnOptionsRef)}> | |
127 | - {() => unref(getSubmitBtnOptionsRef).text} | |
128 | - </Button> | |
129 | - )} | |
127 | + {getSlot(slots, 'submitBefore')} | |
128 | + {showSubmitButton && ( | |
129 | + <Button type="primary" {...unref(getSubmitBtnOptionsRef)}> | |
130 | + {() => unref(getSubmitBtnOptionsRef).text} | |
131 | + </Button> | |
132 | + )} | |
130 | 133 | |
131 | - {getSlot(slots, 'submitAfter')} | |
132 | - </> | |
133 | - )} | |
134 | - </Form.Item> | |
135 | - )} | |
136 | - </Col> | |
137 | - </> | |
134 | + {getSlot(slots, 'submitAfter')} | |
135 | + </> | |
136 | + )} | |
137 | + </Form.Item> | |
138 | + )} | |
139 | + </Col> | |
138 | 140 | ); |
139 | 141 | }; |
140 | 142 | }, | ... | ... |
src/components/Form/src/FormItem.tsx
1 | +import type { ValidationRule } from 'ant-design-vue/types/form/form'; | |
2 | +import type { PropType } from 'vue'; | |
3 | +import type { FormProps } from './types/form'; | |
4 | +import type { FormSchema } from './types/form'; | |
5 | + | |
1 | 6 | import { defineComponent, computed, unref, toRef } from 'vue'; |
2 | 7 | import { Form, Col } from 'ant-design-vue'; |
3 | 8 | import { componentMap } from './componentMap'; |
9 | +import { BasicHelp } from '/@/components/Basic'; | |
4 | 10 | |
5 | -import type { PropType } from 'vue'; | |
6 | -import type { FormProps } from './types/form'; | |
7 | -import type { FormSchema } from './types/form'; | |
8 | 11 | import { isBoolean, isFunction } from '/@/utils/is'; |
9 | -import { useItemLabelWidth } from './hooks/useLabelWidth'; | |
10 | 12 | import { getSlot } from '/@/utils/helper/tsxHelper'; |
11 | -import { BasicHelp } from '/@/components/Basic'; | |
12 | 13 | import { createPlaceholderMessage } from './helper'; |
13 | 14 | import { upperFirst, cloneDeep } from 'lodash-es'; |
14 | -import { ValidationRule } from 'ant-design-vue/types/form/form'; | |
15 | + | |
16 | +import { useItemLabelWidth } from './hooks/useLabelWidth'; | |
17 | + | |
15 | 18 | export default defineComponent({ |
16 | 19 | name: 'BasicFormItem', |
17 | 20 | inheritAttrs: false, |
... | ... | @@ -50,6 +53,7 @@ export default defineComponent({ |
50 | 53 | schema: schema, |
51 | 54 | }; |
52 | 55 | }); |
56 | + | |
53 | 57 | const getShowRef = computed(() => { |
54 | 58 | const { show, ifShow, isAdvanced } = props.schema; |
55 | 59 | const { showAdvancedButton } = props.formProps; |
... | ... | @@ -226,6 +230,7 @@ export default defineComponent({ |
226 | 230 | </span> |
227 | 231 | ); |
228 | 232 | } |
233 | + | |
229 | 234 | function renderItem() { |
230 | 235 | const { itemProps, slot, render, field } = props.schema; |
231 | 236 | const { labelCol, wrapperCol } = unref(itemLabelWidthRef); |
... | ... | @@ -255,11 +260,8 @@ export default defineComponent({ |
255 | 260 | const { colProps = {}, colSlot, renderColContent, component } = props.schema; |
256 | 261 | if (!componentMap.has(component)) return null; |
257 | 262 | const { baseColProps = {} } = props.formProps; |
258 | - | |
259 | 263 | const realColProps = { ...baseColProps, ...colProps }; |
260 | - | |
261 | 264 | const { isIfShow, isShow } = unref(getShowRef); |
262 | - | |
263 | 265 | const getContent = () => { |
264 | 266 | return colSlot |
265 | 267 | ? getSlot(slots, colSlot) | ... | ... |
src/components/Form/src/helper.ts
1 | -import { ComponentType } from './types/index'; | |
1 | +import type { ComponentType } from './types/index'; | |
2 | + | |
2 | 3 | /** |
3 | 4 | * @description: 生成placeholder |
4 | 5 | */ |
... | ... | @@ -21,9 +22,11 @@ export function createPlaceholderMessage(component: ComponentType) { |
21 | 22 | } |
22 | 23 | return ''; |
23 | 24 | } |
25 | + | |
24 | 26 | function genType() { |
25 | 27 | return ['DatePicker', 'MonthPicker', 'RangePicker', 'WeekPicker', 'TimePicker']; |
26 | 28 | } |
29 | + | |
27 | 30 | /** |
28 | 31 | * 时间字段 |
29 | 32 | */ | ... | ... |
src/components/Form/src/hooks/useAdvanced.ts
0 → 100644
1 | +import type { ColEx } from '../types'; | |
2 | +import type { AdvanceState } from '../types/hooks'; | |
3 | +import type { ComputedRef, Ref } from 'vue'; | |
4 | +import type { FormProps, FormSchema } from '../types/form'; | |
5 | + | |
6 | +import { computed, unref, watch } from 'vue'; | |
7 | +import { isBoolean, isFunction, isNumber, isObject } from '/@/utils/is'; | |
8 | + | |
9 | +import { useBreakpoint } from '/@/hooks/event/useBreakpoint'; | |
10 | + | |
11 | +const BASIC_COL_LEN = 24; | |
12 | + | |
13 | +interface UseAdvancedContext { | |
14 | + advanceState: AdvanceState; | |
15 | + emit: EmitType; | |
16 | + getMergePropsRef: ComputedRef<FormProps>; | |
17 | + getProps: ComputedRef<FormProps>; | |
18 | + getSchema: ComputedRef<FormSchema[]>; | |
19 | + formModel: any; | |
20 | + defaultValueRef: Ref<any>; | |
21 | +} | |
22 | + | |
23 | +export default function ({ | |
24 | + advanceState, | |
25 | + emit, | |
26 | + getMergePropsRef, | |
27 | + getProps, | |
28 | + getSchema, | |
29 | + formModel, | |
30 | + defaultValueRef, | |
31 | +}: UseAdvancedContext) { | |
32 | + const { realWidthRef, screenEnum, screenRef } = useBreakpoint(); | |
33 | + const getEmptySpanRef = computed((): number => { | |
34 | + if (!advanceState.isAdvanced) { | |
35 | + return 0; | |
36 | + } | |
37 | + const emptySpan = unref(getMergePropsRef).emptySpan || 0; | |
38 | + | |
39 | + if (isNumber(emptySpan)) { | |
40 | + return emptySpan; | |
41 | + } | |
42 | + if (isObject(emptySpan)) { | |
43 | + const { span = 0 } = emptySpan; | |
44 | + const screen = unref(screenRef) as string; | |
45 | + | |
46 | + const screenSpan = (emptySpan as any)[screen.toLowerCase()]; | |
47 | + return screenSpan || span || 0; | |
48 | + } | |
49 | + return 0; | |
50 | + }); | |
51 | + | |
52 | + const getActionPropsRef = computed(() => { | |
53 | + const { | |
54 | + resetButtonOptions, | |
55 | + submitButtonOptions, | |
56 | + showActionButtonGroup, | |
57 | + showResetButton, | |
58 | + showSubmitButton, | |
59 | + showAdvancedButton, | |
60 | + actionColOptions, | |
61 | + } = unref(getProps); | |
62 | + return { | |
63 | + resetButtonOptions, | |
64 | + submitButtonOptions, | |
65 | + show: showActionButtonGroup, | |
66 | + showResetButton, | |
67 | + showSubmitButton, | |
68 | + showAdvancedButton, | |
69 | + actionColOptions, | |
70 | + }; | |
71 | + }); | |
72 | + watch( | |
73 | + [() => unref(getSchema), () => advanceState.isAdvanced, () => unref(realWidthRef)], | |
74 | + () => { | |
75 | + const { showAdvancedButton } = unref(getProps); | |
76 | + if (showAdvancedButton) { | |
77 | + updateAdvanced(); | |
78 | + } | |
79 | + }, | |
80 | + { immediate: true } | |
81 | + ); | |
82 | + | |
83 | + function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false) { | |
84 | + const width = unref(realWidthRef); | |
85 | + | |
86 | + const mdWidth = | |
87 | + parseInt(itemCol.md as string) || | |
88 | + parseInt(itemCol.xs as string) || | |
89 | + parseInt(itemCol.sm as string) || | |
90 | + (itemCol.span as number) || | |
91 | + BASIC_COL_LEN; | |
92 | + const lgWidth = parseInt(itemCol.lg as string) || mdWidth; | |
93 | + const xlWidth = parseInt(itemCol.xl as string) || lgWidth; | |
94 | + const xxlWidth = parseInt(itemCol.xxl as string) || xlWidth; | |
95 | + if (width <= screenEnum.LG) { | |
96 | + itemColSum += mdWidth; | |
97 | + } else if (width < screenEnum.XL) { | |
98 | + itemColSum += lgWidth; | |
99 | + } else if (width < screenEnum.XXL) { | |
100 | + itemColSum += xlWidth; | |
101 | + } else { | |
102 | + itemColSum += xxlWidth; | |
103 | + } | |
104 | + if (isLastAction) { | |
105 | + advanceState.hideAdvanceBtn = false; | |
106 | + if (itemColSum <= BASIC_COL_LEN * 2) { | |
107 | + // 小于等于2行时,不显示收起展开按钮 | |
108 | + advanceState.hideAdvanceBtn = true; | |
109 | + advanceState.isAdvanced = true; | |
110 | + } else if ( | |
111 | + itemColSum > BASIC_COL_LEN * 2 && | |
112 | + itemColSum <= BASIC_COL_LEN * (unref(getMergePropsRef).autoAdvancedLine || 3) | |
113 | + ) { | |
114 | + advanceState.hideAdvanceBtn = false; | |
115 | + | |
116 | + // 大于3行默认收起 | |
117 | + } else if (!advanceState.isLoad) { | |
118 | + advanceState.isLoad = true; | |
119 | + advanceState.isAdvanced = !advanceState.isAdvanced; | |
120 | + } | |
121 | + return { isAdvanced: advanceState.isAdvanced, itemColSum }; | |
122 | + } | |
123 | + if (itemColSum > BASIC_COL_LEN) { | |
124 | + return { isAdvanced: advanceState.isAdvanced, itemColSum }; | |
125 | + } else { | |
126 | + // 第一行始终显示 | |
127 | + return { isAdvanced: true, itemColSum }; | |
128 | + } | |
129 | + } | |
130 | + | |
131 | + function updateAdvanced() { | |
132 | + let itemColSum = 0; | |
133 | + let realItemColSum = 0; | |
134 | + for (const schema of unref(getSchema)) { | |
135 | + const { show, colProps } = schema; | |
136 | + let isShow = true; | |
137 | + | |
138 | + if (isBoolean(show)) { | |
139 | + isShow = show; | |
140 | + } | |
141 | + | |
142 | + if (isFunction(show)) { | |
143 | + isShow = show({ | |
144 | + schema: schema, | |
145 | + model: formModel, | |
146 | + field: schema.field, | |
147 | + values: { | |
148 | + ...unref(defaultValueRef), | |
149 | + ...formModel, | |
150 | + }, | |
151 | + }); | |
152 | + } | |
153 | + | |
154 | + if (isShow && colProps) { | |
155 | + const { itemColSum: sum, isAdvanced } = getAdvanced(colProps, itemColSum); | |
156 | + | |
157 | + itemColSum = sum || 0; | |
158 | + if (isAdvanced) { | |
159 | + realItemColSum = itemColSum; | |
160 | + } | |
161 | + schema.isAdvanced = isAdvanced; | |
162 | + } | |
163 | + } | |
164 | + | |
165 | + advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpanRef); | |
166 | + | |
167 | + getAdvanced( | |
168 | + unref(getActionPropsRef).actionColOptions || { span: BASIC_COL_LEN }, | |
169 | + itemColSum, | |
170 | + true | |
171 | + ); | |
172 | + emit('advanced-change'); | |
173 | + } | |
174 | + | |
175 | + function handleToggleAdvanced() { | |
176 | + advanceState.isAdvanced = !advanceState.isAdvanced; | |
177 | + } | |
178 | + return { getActionPropsRef, handleToggleAdvanced }; | |
179 | +} | ... | ... |
src/components/Form/src/hooks/useComponentRegister.ts
1 | +import type { ComponentType } from '../types/index'; | |
1 | 2 | import { tryOnUnmounted } from '/@/utils/helper/vueHelper'; |
2 | 3 | import { add, del } from '../componentMap'; |
3 | - | |
4 | -import { ComponentType } from '../types/index'; | |
5 | 4 | export function useComponentRegister(compName: ComponentType, comp: any) { |
6 | 5 | add(compName, comp); |
7 | 6 | tryOnUnmounted(() => { | ... | ... |
src/components/Form/src/hooks/useForm.ts
1 | 1 | import { ref, onUnmounted, unref } from 'vue'; |
2 | 2 | |
3 | 3 | import { isInSetup } from '/@/utils/helper/vueHelper'; |
4 | +import { isProdMode } from '/@/utils/env'; | |
4 | 5 | |
5 | 6 | import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form'; |
6 | -import { isProdMode } from '/@/utils/env'; | |
7 | 7 | import type { NamePath } from 'ant-design-vue/types/form/form-item'; |
8 | 8 | import type { ValidateFields } from 'ant-design-vue/types/form/form'; |
9 | 9 | |
... | ... | @@ -11,6 +11,7 @@ export function useForm(props?: Partial<FormProps>): UseFormReturnType { |
11 | 11 | isInSetup(); |
12 | 12 | const formRef = ref<FormActionType | null>(null); |
13 | 13 | const loadedRef = ref<boolean | null>(false); |
14 | + | |
14 | 15 | function getForm() { |
15 | 16 | const form = unref(formRef); |
16 | 17 | if (!form) { | ... | ... |
src/components/Form/src/hooks/useFormAction.ts
0 → 100644
1 | +import type { ComputedRef, Ref } from 'vue'; | |
2 | +import type { FormProps, FormSchema } from '../types/form'; | |
3 | +import type { Form as FormType } from 'ant-design-vue/types/form/form'; | |
4 | +import type { NamePath } from 'ant-design-vue/types/form/form-item'; | |
5 | + | |
6 | +import { unref, toRaw } from 'vue'; | |
7 | + | |
8 | +import { isArray, isFunction, isObject, isString } from '/@/utils/is'; | |
9 | +import { deepMerge, unique } from '/@/utils'; | |
10 | +import { dateItemType } from '../helper'; | |
11 | +import moment from 'moment'; | |
12 | +import { cloneDeep } from 'lodash-es'; | |
13 | + | |
14 | +interface UseFormActionContext { | |
15 | + emit: EmitType; | |
16 | + getProps: ComputedRef<FormProps>; | |
17 | + getSchema: ComputedRef<FormSchema[]>; | |
18 | + formModel: any; | |
19 | + defaultValueRef: Ref<any>; | |
20 | + formElRef: Ref<FormType>; | |
21 | + schemaRef: Ref<FormSchema[]>; | |
22 | + handleFormValues: Fn; | |
23 | + actionState: { | |
24 | + resetAction: any; | |
25 | + submitAction: any; | |
26 | + }; | |
27 | +} | |
28 | +export function useFormAction({ | |
29 | + emit, | |
30 | + getProps, | |
31 | + formModel, | |
32 | + getSchema, | |
33 | + defaultValueRef, | |
34 | + formElRef, | |
35 | + schemaRef, | |
36 | + handleFormValues, | |
37 | + actionState, | |
38 | +}: UseFormActionContext) { | |
39 | + async function resetFields(): Promise<any> { | |
40 | + const { resetFunc, submitOnReset } = unref(getProps); | |
41 | + resetFunc && isFunction(resetFunc) && (await resetFunc()); | |
42 | + const formEl = unref(formElRef); | |
43 | + if (!formEl) return; | |
44 | + Object.keys(formModel).forEach((key) => { | |
45 | + (formModel as any)[key] = defaultValueRef.value[key]; | |
46 | + }); | |
47 | + // @ts-ignore | |
48 | + // TODO 官方组件库类型定义错误,可以不传参数 | |
49 | + formEl.clearValidate(); | |
50 | + emit('reset', toRaw(formModel)); | |
51 | + // return values; | |
52 | + submitOnReset && handleSubmit(); | |
53 | + } | |
54 | + | |
55 | + /** | |
56 | + * @description: 设置表单值 | |
57 | + */ | |
58 | + async function setFieldsValue(values: any): Promise<void> { | |
59 | + const fields = unref(getSchema) | |
60 | + .map((item) => item.field) | |
61 | + .filter(Boolean); | |
62 | + const formEl = unref(formElRef); | |
63 | + Object.keys(values).forEach((key) => { | |
64 | + const element = values[key]; | |
65 | + if (fields.includes(key) && element !== undefined && element !== null) { | |
66 | + // 时间 | |
67 | + if (itemIsDateType(key)) { | |
68 | + if (Array.isArray(element)) { | |
69 | + const arr: any[] = []; | |
70 | + for (const ele of element) { | |
71 | + arr.push(moment(ele)); | |
72 | + } | |
73 | + (formModel as any)[key] = arr; | |
74 | + } else { | |
75 | + (formModel as any)[key] = moment(element); | |
76 | + } | |
77 | + } else { | |
78 | + (formModel as any)[key] = element; | |
79 | + } | |
80 | + if (formEl) { | |
81 | + formEl.validateFields([key]); | |
82 | + } | |
83 | + } | |
84 | + }); | |
85 | + } | |
86 | + /** | |
87 | + * @description: 根据字段名删除 | |
88 | + */ | |
89 | + function removeSchemaByFiled(fields: string | string[]): void { | |
90 | + const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); | |
91 | + if (!fields) { | |
92 | + return; | |
93 | + } | |
94 | + let fieldList: string[] = fields as string[]; | |
95 | + if (isString(fields)) { | |
96 | + fieldList = [fields]; | |
97 | + } | |
98 | + for (const field of fieldList) { | |
99 | + _removeSchemaByFiled(field, schemaList); | |
100 | + } | |
101 | + schemaRef.value = schemaList as any; | |
102 | + } | |
103 | + | |
104 | + /** | |
105 | + * @description: 根据字段名删除 | |
106 | + */ | |
107 | + function _removeSchemaByFiled(field: string, schemaList: FormSchema[]): void { | |
108 | + if (isString(field)) { | |
109 | + const index = schemaList.findIndex((schema) => schema.field === field); | |
110 | + if (index !== -1) { | |
111 | + schemaList.splice(index, 1); | |
112 | + } | |
113 | + } | |
114 | + } | |
115 | + | |
116 | + /** | |
117 | + * @description: 往某个字段后面插入,如果没有插入最后一个 | |
118 | + */ | |
119 | + function appendSchemaByField(schema: FormSchema, prefixField?: string) { | |
120 | + const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); | |
121 | + | |
122 | + const index = schemaList.findIndex((schema) => schema.field === prefixField); | |
123 | + const hasInList = schemaList.find((item) => item.field === schema.field); | |
124 | + | |
125 | + if (hasInList) { | |
126 | + return; | |
127 | + } | |
128 | + if (!prefixField || index === -1) { | |
129 | + schemaList.push(schema); | |
130 | + schemaRef.value = schemaList as any; | |
131 | + return; | |
132 | + } | |
133 | + if (index !== -1) { | |
134 | + schemaList.splice(index + 1, 0, schema); | |
135 | + } | |
136 | + schemaRef.value = schemaList as any; | |
137 | + } | |
138 | + | |
139 | + function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) { | |
140 | + let updateData: Partial<FormSchema>[] = []; | |
141 | + if (isObject(data)) { | |
142 | + updateData.push(data as FormSchema); | |
143 | + } | |
144 | + if (isArray(data)) { | |
145 | + updateData = [...data]; | |
146 | + } | |
147 | + const hasField = updateData.every((item) => Reflect.has(item, 'field') && item.field); | |
148 | + if (!hasField) { | |
149 | + throw new Error('Must pass in the `field` field!'); | |
150 | + } | |
151 | + const schema: FormSchema[] = []; | |
152 | + updateData.forEach((item) => { | |
153 | + unref(getSchema).forEach((val) => { | |
154 | + if (val.field === item.field) { | |
155 | + const newScheam = deepMerge(val, item); | |
156 | + schema.push(newScheam as FormSchema); | |
157 | + } else { | |
158 | + schema.push(val); | |
159 | + } | |
160 | + }); | |
161 | + }); | |
162 | + schemaRef.value = unique(schema, 'field') as any; | |
163 | + } | |
164 | + | |
165 | + function getFieldsValue(): any { | |
166 | + const formEl = unref(formElRef); | |
167 | + if (!formEl) return; | |
168 | + return handleFormValues(toRaw(unref(formModel))); | |
169 | + } | |
170 | + | |
171 | + /** | |
172 | + * @description: 是否是时间 | |
173 | + */ | |
174 | + function itemIsDateType(key: string) { | |
175 | + return unref(getSchema).some((item) => { | |
176 | + return item.field === key ? dateItemType.includes(item.component!) : false; | |
177 | + }); | |
178 | + } | |
179 | + | |
180 | + function validateFields(nameList?: NamePath[] | undefined) { | |
181 | + if (!formElRef.value) return; | |
182 | + return formElRef.value.validateFields(nameList); | |
183 | + } | |
184 | + | |
185 | + function validate(nameList?: NamePath[] | undefined) { | |
186 | + if (!formElRef.value) return; | |
187 | + return formElRef.value.validate(nameList); | |
188 | + } | |
189 | + | |
190 | + function clearValidate(name: string | string[]) { | |
191 | + if (!formElRef.value) return; | |
192 | + formElRef.value.clearValidate(name); | |
193 | + } | |
194 | + | |
195 | + /** | |
196 | + * @description: 表单提交 | |
197 | + */ | |
198 | + async function handleSubmit(e?: Event): Promise<void> { | |
199 | + e && e.preventDefault(); | |
200 | + const { submitFunc } = unref(getProps); | |
201 | + if (submitFunc && isFunction(submitFunc)) { | |
202 | + await submitFunc(); | |
203 | + return; | |
204 | + } | |
205 | + const formEl = unref(formElRef); | |
206 | + if (!formEl) return; | |
207 | + try { | |
208 | + const values = await formEl.validate(); | |
209 | + const res = handleFormValues(values); | |
210 | + emit('submit', res); | |
211 | + } catch (error) {} | |
212 | + } | |
213 | + actionState.resetAction = { | |
214 | + onClick: resetFields, | |
215 | + }; | |
216 | + | |
217 | + actionState.submitAction = { | |
218 | + onClick: handleSubmit, | |
219 | + }; | |
220 | + | |
221 | + return { | |
222 | + handleSubmit, | |
223 | + clearValidate, | |
224 | + validate, | |
225 | + validateFields, | |
226 | + getFieldsValue, | |
227 | + updateSchema, | |
228 | + appendSchemaByField, | |
229 | + removeSchemaByFiled, | |
230 | + resetFields, | |
231 | + setFieldsValue, | |
232 | + }; | |
233 | +} | ... | ... |
src/components/Form/src/hooks/useFormValues.ts
1 | 1 | import { isArray, isFunction, isObject, isString } from '/@/utils/is'; |
2 | 2 | import moment from 'moment'; |
3 | 3 | import { unref } from 'vue'; |
4 | -import type { Ref } from 'vue'; | |
5 | -import type { FieldMapToTime } from '../types/form'; | |
4 | +import type { Ref, ComputedRef } from 'vue'; | |
5 | +import type { FieldMapToTime, FormSchema } from '../types/form'; | |
6 | 6 | |
7 | -export function useFormValues( | |
8 | - transformDateFuncRef: Ref<Fn>, | |
9 | - fieldMapToTimeRef: Ref<FieldMapToTime> | |
10 | -) { | |
7 | +interface UseFormValuesContext { | |
8 | + transformDateFuncRef: Ref<Fn>; | |
9 | + fieldMapToTimeRef: Ref<FieldMapToTime>; | |
10 | + defaultValueRef: Ref<any>; | |
11 | + getSchema: ComputedRef<FormSchema[]>; | |
12 | + formModel: any; | |
13 | +} | |
14 | +export function useFormValues({ | |
15 | + transformDateFuncRef, | |
16 | + fieldMapToTimeRef, | |
17 | + defaultValueRef, | |
18 | + getSchema, | |
19 | + formModel, | |
20 | +}: UseFormValuesContext) { | |
11 | 21 | // 处理表单值 |
12 | 22 | function handleFormValues(values: any) { |
13 | 23 | if (!isObject(values)) { |
... | ... | @@ -35,6 +45,7 @@ export function useFormValues( |
35 | 45 | } |
36 | 46 | return handleRangeTimeValue(resMap); |
37 | 47 | } |
48 | + | |
38 | 49 | /** |
39 | 50 | * @description: 处理时间区间参数 |
40 | 51 | */ |
... | ... | @@ -58,5 +69,18 @@ export function useFormValues( |
58 | 69 | |
59 | 70 | return values; |
60 | 71 | } |
61 | - return handleFormValues; | |
72 | + | |
73 | + function initDefault() { | |
74 | + const schemas = unref(getSchema); | |
75 | + const obj: any = {}; | |
76 | + schemas.forEach((item) => { | |
77 | + if (item.defaultValue) { | |
78 | + obj[item.field] = item.defaultValue; | |
79 | + (formModel as any)[item.field] = item.defaultValue; | |
80 | + } | |
81 | + }); | |
82 | + defaultValueRef.value = obj; | |
83 | + } | |
84 | + | |
85 | + return { handleFormValues, initDefault }; | |
62 | 86 | } | ... | ... |
src/components/Form/src/props.ts
... | ... | @@ -3,6 +3,10 @@ import type { PropType } from 'vue'; |
3 | 3 | import type { ColEx } from './types'; |
4 | 4 | |
5 | 5 | export const basicProps = { |
6 | + model: { | |
7 | + type: Object as PropType<any>, | |
8 | + default: {}, | |
9 | + }, | |
6 | 10 | // 标签宽度 固定宽度 |
7 | 11 | labelWidth: { |
8 | 12 | type: [Number, String] as PropType<number | string>, | ... | ... |
src/components/Form/src/types/form.ts
... | ... | @@ -35,6 +35,8 @@ export type RegisterFn = (formInstance: FormActionType) => void; |
35 | 35 | export type UseFormReturnType = [RegisterFn, FormActionType]; |
36 | 36 | |
37 | 37 | export interface FormProps { |
38 | + // 表单值 | |
39 | + model?: any; | |
38 | 40 | // 整个表单所有项宽度 |
39 | 41 | labelWidth?: number | string; |
40 | 42 | // 重置时提交 | ... | ... |
src/components/Form/src/types/hooks.ts
0 → 100644
src/components/Modal/src/ModalWrapper.tsx
... | ... | @@ -15,13 +15,12 @@ import { |
15 | 15 | import { Spin } from 'ant-design-vue'; |
16 | 16 | |
17 | 17 | import { useWindowSizeFn } from '/@/hooks/event/useWindowSize'; |
18 | -// import { useTimeout } from '/@/hooks/core/useTimeout'; | |
18 | +import { useTimeout } from '/@/hooks/core/useTimeout'; | |
19 | 19 | |
20 | 20 | import { getSlot } from '/@/utils/helper/tsxHelper'; |
21 | 21 | import { useElResize } from '/@/hooks/event/useElResize'; |
22 | 22 | export default defineComponent({ |
23 | 23 | name: 'ModalWrapper', |
24 | - emits: ['heightChange', 'getExtHeight'], | |
25 | 24 | props: { |
26 | 25 | loading: { |
27 | 26 | type: Boolean as PropType<boolean>, |
... | ... | @@ -52,6 +51,7 @@ export default defineComponent({ |
52 | 51 | default: false, |
53 | 52 | }, |
54 | 53 | }, |
54 | + emits: ['heightChange', 'getExtHeight'], | |
55 | 55 | setup(props: ModalWrapperProps, { slots, emit }) { |
56 | 56 | const wrapperRef = ref<HTMLElement | null>(null); |
57 | 57 | const spinRef = ref<any>(null); |
... | ... | @@ -66,7 +66,7 @@ export default defineComponent({ |
66 | 66 | }); |
67 | 67 | |
68 | 68 | // 重试次数 |
69 | - // let tryCount = 0; | |
69 | + let tryCount = 0; | |
70 | 70 | let stopElResizeFn: Fn = () => {}; |
71 | 71 | |
72 | 72 | watchEffect(() => { |
... | ... | @@ -123,17 +123,17 @@ export default defineComponent({ |
123 | 123 | } |
124 | 124 | await nextTick(); |
125 | 125 | const spinEl = unref(spinRef); |
126 | - // if (!spinEl) { | |
127 | - // useTimeout(() => { | |
128 | - // // retry | |
129 | - // if (tryCount < 3) { | |
130 | - // setModalHeight(); | |
131 | - // } | |
132 | - // tryCount++; | |
133 | - // }, 10); | |
134 | - // return; | |
135 | - // } | |
136 | - // tryCount = 0; | |
126 | + if (!spinEl) { | |
127 | + useTimeout(() => { | |
128 | + // retry | |
129 | + if (tryCount < 3) { | |
130 | + setModalHeight(); | |
131 | + } | |
132 | + tryCount++; | |
133 | + }, 10); | |
134 | + return; | |
135 | + } | |
136 | + tryCount = 0; | |
137 | 137 | |
138 | 138 | const spinContainerEl = spinEl.$el.querySelector('.ant-spin-container') as HTMLElement; |
139 | 139 | if (!spinContainerEl) return; |
... | ... | @@ -142,7 +142,7 @@ export default defineComponent({ |
142 | 142 | |
143 | 143 | if (props.fullScreen) { |
144 | 144 | realHeightRef.value = |
145 | - window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 26; | |
145 | + window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 6; | |
146 | 146 | } else { |
147 | 147 | realHeightRef.value = realHeight > maxHeight ? maxHeight : realHeight + 16 + 30; |
148 | 148 | } | ... | ... |
src/components/Modal/src/useModal.ts
... | ... | @@ -5,8 +5,9 @@ import type { |
5 | 5 | ReturnMethods, |
6 | 6 | UseModalInnerReturnType, |
7 | 7 | } from './types'; |
8 | -import { ref, onUnmounted, unref, getCurrentInstance, reactive, computed } from 'vue'; | |
8 | +import { ref, onUnmounted, unref, getCurrentInstance, reactive, computed, watchEffect } from 'vue'; | |
9 | 9 | import { isProdMode } from '/@/utils/env'; |
10 | +import { isFunction } from '/@/utils/is'; | |
10 | 11 | const dataTransferRef = reactive<any>({}); |
11 | 12 | |
12 | 13 | /** |
... | ... | @@ -58,7 +59,7 @@ export function useModal(): UseModalReturnType { |
58 | 59 | return [register, methods]; |
59 | 60 | } |
60 | 61 | |
61 | -export const useModalInner = (): UseModalInnerReturnType => { | |
62 | +export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => { | |
62 | 63 | const modalInstanceRef = ref<ModalMethods | null>(null); |
63 | 64 | const currentInstall = getCurrentInstance(); |
64 | 65 | const uidRef = ref<string>(''); |
... | ... | @@ -81,6 +82,13 @@ export const useModalInner = (): UseModalInnerReturnType => { |
81 | 82 | currentInstall.emit('register', modalInstance); |
82 | 83 | }; |
83 | 84 | |
85 | + watchEffect(() => { | |
86 | + const data = dataTransferRef[unref(uidRef)]; | |
87 | + if (!data) return; | |
88 | + if (!callbackFn || !isFunction(callbackFn)) return; | |
89 | + callbackFn(data); | |
90 | + }); | |
91 | + | |
84 | 92 | return [ |
85 | 93 | register, |
86 | 94 | { | ... | ... |
src/views/demo/comp/modal/Modal4.vue
1 | 1 | <template> |
2 | 2 | <BasicModal v-bind="$attrs" @register="register" title="Modal Title"> |
3 | 3 | <p class="h-20">外部传递数据: {{ receiveModalDataRef }}</p> |
4 | + <BasicForm @register="registerForm" :model="model" /> | |
4 | 5 | </BasicModal> |
5 | 6 | </template> |
6 | 7 | <script lang="ts"> |
7 | - import { defineComponent } from 'vue'; | |
8 | + import { defineComponent, nextTick, ref } from 'vue'; | |
8 | 9 | import { BasicModal, useModalInner } from '/@/components/Modal'; |
10 | + import { BasicForm, FormSchema, useForm } from '/@/components/Form/index'; | |
11 | + const schemas: FormSchema[] = [ | |
12 | + { | |
13 | + field: 'field1', | |
14 | + component: 'Input', | |
15 | + label: '字段1', | |
16 | + colProps: { | |
17 | + span: 12, | |
18 | + }, | |
19 | + defaultValue: '111', | |
20 | + }, | |
21 | + { | |
22 | + field: 'field2', | |
23 | + component: 'Input', | |
24 | + label: '字段2', | |
25 | + colProps: { | |
26 | + span: 12, | |
27 | + }, | |
28 | + }, | |
29 | + ]; | |
9 | 30 | export default defineComponent({ |
10 | - components: { BasicModal }, | |
31 | + components: { BasicModal, BasicForm }, | |
11 | 32 | setup() { |
12 | - const [register, { receiveModalDataRef }] = useModalInner(); | |
13 | - return { register, receiveModalDataRef }; | |
33 | + const modelRef = ref({}); | |
34 | + const [ | |
35 | + registerForm, | |
36 | + { | |
37 | + // setFieldsValue, | |
38 | + // setProps | |
39 | + }, | |
40 | + ] = useForm({ | |
41 | + labelWidth: 120, | |
42 | + schemas, | |
43 | + showActionButtonGroup: false, | |
44 | + actionColOptions: { | |
45 | + span: 24, | |
46 | + }, | |
47 | + }); | |
48 | + | |
49 | + const [register, { receiveModalDataRef }] = useModalInner((data) => { | |
50 | + nextTick(() => { | |
51 | + // 方式1 | |
52 | + // setFieldsValue({ | |
53 | + // field2: data.data, | |
54 | + // field1: data.info, | |
55 | + // }); | |
56 | + | |
57 | + // 方式2 | |
58 | + modelRef.value = { field2: data.data, field1: data.info }; | |
59 | + | |
60 | + // setProps({ | |
61 | + // model:{ field2: data.data, field1: data.info } | |
62 | + // }) | |
63 | + }); | |
64 | + }); | |
65 | + return { register, receiveModalDataRef, schemas, registerForm, model: modelRef }; | |
14 | 66 | }, |
15 | 67 | }); |
16 | 68 | </script> | ... | ... |
src/views/demo/comp/modal/index.vue