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 | ## Wip | 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 | ### 🎫 Chores | 15 | ### 🎫 Chores |
4 | 16 | ||
5 | - 添加部分注释 | 17 | - 添加部分注释 |
@@ -10,6 +22,7 @@ | @@ -10,6 +22,7 @@ | ||
10 | 22 | ||
11 | - 修复本地代理 post 接口到 https 地址超时错误 | 23 | - 修复本地代理 post 接口到 https 地址超时错误 |
12 | - 修复 modal 在不显示 footer 的时候全屏高度计算问题 | 24 | - 修复 modal 在不显示 footer 的时候全屏高度计算问题 |
25 | +- 修复表单重置未删除校验信息错误 | ||
13 | 26 | ||
14 | ## 2.0.0-rc.6 (2020-10-28) | 27 | ## 2.0.0-rc.6 (2020-10-28) |
15 | 28 |
src/components/Form/src/BasicForm.vue
@@ -25,6 +25,8 @@ | @@ -25,6 +25,8 @@ | ||
25 | <script lang="ts"> | 25 | <script lang="ts"> |
26 | import type { FormActionType, FormProps, FormSchema } from './types/form'; | 26 | import type { FormActionType, FormProps, FormSchema } from './types/form'; |
27 | import type { Form as FormType, ValidateFields } from 'ant-design-vue/types/form/form'; | 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 | import { | 31 | import { |
30 | defineComponent, | 32 | defineComponent, |
@@ -32,27 +34,22 @@ | @@ -32,27 +34,22 @@ | ||
32 | ref, | 34 | ref, |
33 | computed, | 35 | computed, |
34 | unref, | 36 | unref, |
35 | - toRaw, | ||
36 | - watch, | ||
37 | toRef, | 37 | toRef, |
38 | onMounted, | 38 | onMounted, |
39 | + watchEffect, | ||
39 | } from 'vue'; | 40 | } from 'vue'; |
40 | import { Form, Row } from 'ant-design-vue'; | 41 | import { Form, Row } from 'ant-design-vue'; |
41 | import FormItem from './FormItem'; | 42 | import FormItem from './FormItem'; |
42 | import { basicProps } from './props'; | 43 | import { basicProps } from './props'; |
43 | - import { deepMerge, unique } from '/@/utils'; | 44 | + import { deepMerge } from '/@/utils'; |
44 | import FormAction from './FormAction'; | 45 | import FormAction from './FormAction'; |
45 | 46 | ||
46 | import { dateItemType } from './helper'; | 47 | import { dateItemType } from './helper'; |
47 | import moment from 'moment'; | 48 | import moment from 'moment'; |
48 | - import { isArray, isBoolean, isFunction, isNumber, isObject, isString } from '/@/utils/is'; | ||
49 | import { cloneDeep } from 'lodash-es'; | 49 | import { cloneDeep } from 'lodash-es'; |
50 | - import { useBreakpoint } from '/@/hooks/event/useBreakpoint'; | ||
51 | - // import { useThrottle } from '/@/hooks/core/useThrottle'; | ||
52 | import { useFormValues } from './hooks/useFormValues'; | 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 | export default defineComponent({ | 54 | export default defineComponent({ |
58 | name: 'BasicForm', | 55 | name: 'BasicForm', |
@@ -61,13 +58,20 @@ | @@ -61,13 +58,20 @@ | ||
61 | props: basicProps, | 58 | props: basicProps, |
62 | emits: ['advanced-change', 'reset', 'submit', 'register'], | 59 | emits: ['advanced-change', 'reset', 'submit', 'register'], |
63 | setup(props, { emit }) { | 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 | isAdvanced: true, | 69 | isAdvanced: true, |
67 | hideAdvanceBtn: false, | 70 | hideAdvanceBtn: false, |
68 | isLoad: false, | 71 | isLoad: false, |
69 | actionSpan: 6, | 72 | actionSpan: 6, |
70 | }); | 73 | }); |
74 | + | ||
71 | const defaultValueRef = ref<any>({}); | 75 | const defaultValueRef = ref<any>({}); |
72 | const propsRef = ref<Partial<FormProps>>({}); | 76 | const propsRef = ref<Partial<FormProps>>({}); |
73 | const schemaRef = ref<FormSchema[] | null>(null); | 77 | const schemaRef = ref<FormSchema[] | null>(null); |
@@ -78,50 +82,24 @@ | @@ -78,50 +82,24 @@ | ||
78 | return deepMerge(cloneDeep(props), unref(propsRef)); | 82 | return deepMerge(cloneDeep(props), unref(propsRef)); |
79 | } | 83 | } |
80 | ); | 84 | ); |
85 | + | ||
81 | // 获取表单基本配置 | 86 | // 获取表单基本配置 |
82 | const getProps = computed( | 87 | const getProps = computed( |
83 | (): FormProps => { | 88 | (): FormProps => { |
84 | - const resetAction = { | ||
85 | - onClick: resetFields, | ||
86 | - }; | ||
87 | - const submitAction = { | ||
88 | - onClick: handleSubmit, | ||
89 | - }; | ||
90 | return { | 89 | return { |
91 | ...unref(getMergePropsRef), | 90 | ...unref(getMergePropsRef), |
92 | resetButtonOptions: deepMerge( | 91 | resetButtonOptions: deepMerge( |
93 | - resetAction, | 92 | + actionState.resetAction, |
94 | unref(getMergePropsRef).resetButtonOptions || {} | 93 | unref(getMergePropsRef).resetButtonOptions || {} |
95 | - ) as any, | 94 | + ), |
96 | submitButtonOptions: deepMerge( | 95 | submitButtonOptions: deepMerge( |
97 | - submitAction, | 96 | + actionState.submitAction, |
98 | unref(getMergePropsRef).submitButtonOptions || {} | 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 | const getSchema = computed((): FormSchema[] => { | 103 | const getSchema = computed((): FormSchema[] => { |
126 | const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any); | 104 | const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any); |
127 | for (const schema of schemas) { | 105 | for (const schema of schemas) { |
@@ -133,305 +111,51 @@ | @@ -133,305 +111,51 @@ | ||
133 | return schemas as FormSchema[]; | 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 | * @description:设置表单 | 161 | * @description:设置表单 |
@@ -441,21 +165,6 @@ | @@ -441,21 +165,6 @@ | ||
441 | propsRef.value = mergeProps; | 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 | const methods: Partial<FormActionType> = { | 168 | const methods: Partial<FormActionType> = { |
460 | getFieldsValue, | 169 | getFieldsValue, |
461 | setFieldsValue, | 170 | setFieldsValue, |
src/components/Form/src/FormAction.tsx
@@ -57,6 +57,7 @@ export default defineComponent({ | @@ -57,6 +57,7 @@ export default defineComponent({ | ||
57 | ...props.resetButtonOptions, | 57 | ...props.resetButtonOptions, |
58 | }; | 58 | }; |
59 | }); | 59 | }); |
60 | + | ||
60 | const getSubmitBtnOptionsRef = computed(() => { | 61 | const getSubmitBtnOptionsRef = computed(() => { |
61 | return { | 62 | return { |
62 | text: '查询', | 63 | text: '查询', |
@@ -80,10 +81,12 @@ export default defineComponent({ | @@ -80,10 +81,12 @@ export default defineComponent({ | ||
80 | function toggleAdvanced() { | 81 | function toggleAdvanced() { |
81 | emit('toggle-advanced'); | 82 | emit('toggle-advanced'); |
82 | } | 83 | } |
84 | + | ||
83 | return () => { | 85 | return () => { |
84 | if (!props.show) { | 86 | if (!props.show) { |
85 | return; | 87 | return; |
86 | } | 88 | } |
89 | + | ||
87 | const { | 90 | const { |
88 | showAdvancedButton, | 91 | showAdvancedButton, |
89 | hideAdvanceBtn, | 92 | hideAdvanceBtn, |
@@ -91,50 +94,49 @@ export default defineComponent({ | @@ -91,50 +94,49 @@ export default defineComponent({ | ||
91 | showResetButton, | 94 | showResetButton, |
92 | showSubmitButton, | 95 | showSubmitButton, |
93 | } = props; | 96 | } = props; |
97 | + | ||
94 | return ( | 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 | import { defineComponent, computed, unref, toRef } from 'vue'; | 6 | import { defineComponent, computed, unref, toRef } from 'vue'; |
2 | import { Form, Col } from 'ant-design-vue'; | 7 | import { Form, Col } from 'ant-design-vue'; |
3 | import { componentMap } from './componentMap'; | 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 | import { isBoolean, isFunction } from '/@/utils/is'; | 11 | import { isBoolean, isFunction } from '/@/utils/is'; |
9 | -import { useItemLabelWidth } from './hooks/useLabelWidth'; | ||
10 | import { getSlot } from '/@/utils/helper/tsxHelper'; | 12 | import { getSlot } from '/@/utils/helper/tsxHelper'; |
11 | -import { BasicHelp } from '/@/components/Basic'; | ||
12 | import { createPlaceholderMessage } from './helper'; | 13 | import { createPlaceholderMessage } from './helper'; |
13 | import { upperFirst, cloneDeep } from 'lodash-es'; | 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 | export default defineComponent({ | 18 | export default defineComponent({ |
16 | name: 'BasicFormItem', | 19 | name: 'BasicFormItem', |
17 | inheritAttrs: false, | 20 | inheritAttrs: false, |
@@ -50,6 +53,7 @@ export default defineComponent({ | @@ -50,6 +53,7 @@ export default defineComponent({ | ||
50 | schema: schema, | 53 | schema: schema, |
51 | }; | 54 | }; |
52 | }); | 55 | }); |
56 | + | ||
53 | const getShowRef = computed(() => { | 57 | const getShowRef = computed(() => { |
54 | const { show, ifShow, isAdvanced } = props.schema; | 58 | const { show, ifShow, isAdvanced } = props.schema; |
55 | const { showAdvancedButton } = props.formProps; | 59 | const { showAdvancedButton } = props.formProps; |
@@ -226,6 +230,7 @@ export default defineComponent({ | @@ -226,6 +230,7 @@ export default defineComponent({ | ||
226 | </span> | 230 | </span> |
227 | ); | 231 | ); |
228 | } | 232 | } |
233 | + | ||
229 | function renderItem() { | 234 | function renderItem() { |
230 | const { itemProps, slot, render, field } = props.schema; | 235 | const { itemProps, slot, render, field } = props.schema; |
231 | const { labelCol, wrapperCol } = unref(itemLabelWidthRef); | 236 | const { labelCol, wrapperCol } = unref(itemLabelWidthRef); |
@@ -255,11 +260,8 @@ export default defineComponent({ | @@ -255,11 +260,8 @@ export default defineComponent({ | ||
255 | const { colProps = {}, colSlot, renderColContent, component } = props.schema; | 260 | const { colProps = {}, colSlot, renderColContent, component } = props.schema; |
256 | if (!componentMap.has(component)) return null; | 261 | if (!componentMap.has(component)) return null; |
257 | const { baseColProps = {} } = props.formProps; | 262 | const { baseColProps = {} } = props.formProps; |
258 | - | ||
259 | const realColProps = { ...baseColProps, ...colProps }; | 263 | const realColProps = { ...baseColProps, ...colProps }; |
260 | - | ||
261 | const { isIfShow, isShow } = unref(getShowRef); | 264 | const { isIfShow, isShow } = unref(getShowRef); |
262 | - | ||
263 | const getContent = () => { | 265 | const getContent = () => { |
264 | return colSlot | 266 | return colSlot |
265 | ? getSlot(slots, colSlot) | 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 | * @description: 生成placeholder | 4 | * @description: 生成placeholder |
4 | */ | 5 | */ |
@@ -21,9 +22,11 @@ export function createPlaceholderMessage(component: ComponentType) { | @@ -21,9 +22,11 @@ export function createPlaceholderMessage(component: ComponentType) { | ||
21 | } | 22 | } |
22 | return ''; | 23 | return ''; |
23 | } | 24 | } |
25 | + | ||
24 | function genType() { | 26 | function genType() { |
25 | return ['DatePicker', 'MonthPicker', 'RangePicker', 'WeekPicker', 'TimePicker']; | 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 | import { tryOnUnmounted } from '/@/utils/helper/vueHelper'; | 2 | import { tryOnUnmounted } from '/@/utils/helper/vueHelper'; |
2 | import { add, del } from '../componentMap'; | 3 | import { add, del } from '../componentMap'; |
3 | - | ||
4 | -import { ComponentType } from '../types/index'; | ||
5 | export function useComponentRegister(compName: ComponentType, comp: any) { | 4 | export function useComponentRegister(compName: ComponentType, comp: any) { |
6 | add(compName, comp); | 5 | add(compName, comp); |
7 | tryOnUnmounted(() => { | 6 | tryOnUnmounted(() => { |
src/components/Form/src/hooks/useForm.ts
1 | import { ref, onUnmounted, unref } from 'vue'; | 1 | import { ref, onUnmounted, unref } from 'vue'; |
2 | 2 | ||
3 | import { isInSetup } from '/@/utils/helper/vueHelper'; | 3 | import { isInSetup } from '/@/utils/helper/vueHelper'; |
4 | +import { isProdMode } from '/@/utils/env'; | ||
4 | 5 | ||
5 | import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form'; | 6 | import type { FormProps, FormActionType, UseFormReturnType, FormSchema } from '../types/form'; |
6 | -import { isProdMode } from '/@/utils/env'; | ||
7 | import type { NamePath } from 'ant-design-vue/types/form/form-item'; | 7 | import type { NamePath } from 'ant-design-vue/types/form/form-item'; |
8 | import type { ValidateFields } from 'ant-design-vue/types/form/form'; | 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,6 +11,7 @@ export function useForm(props?: Partial<FormProps>): UseFormReturnType { | ||
11 | isInSetup(); | 11 | isInSetup(); |
12 | const formRef = ref<FormActionType | null>(null); | 12 | const formRef = ref<FormActionType | null>(null); |
13 | const loadedRef = ref<boolean | null>(false); | 13 | const loadedRef = ref<boolean | null>(false); |
14 | + | ||
14 | function getForm() { | 15 | function getForm() { |
15 | const form = unref(formRef); | 16 | const form = unref(formRef); |
16 | if (!form) { | 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 | import { isArray, isFunction, isObject, isString } from '/@/utils/is'; | 1 | import { isArray, isFunction, isObject, isString } from '/@/utils/is'; |
2 | import moment from 'moment'; | 2 | import moment from 'moment'; |
3 | import { unref } from 'vue'; | 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 | function handleFormValues(values: any) { | 22 | function handleFormValues(values: any) { |
13 | if (!isObject(values)) { | 23 | if (!isObject(values)) { |
@@ -35,6 +45,7 @@ export function useFormValues( | @@ -35,6 +45,7 @@ export function useFormValues( | ||
35 | } | 45 | } |
36 | return handleRangeTimeValue(resMap); | 46 | return handleRangeTimeValue(resMap); |
37 | } | 47 | } |
48 | + | ||
38 | /** | 49 | /** |
39 | * @description: 处理时间区间参数 | 50 | * @description: 处理时间区间参数 |
40 | */ | 51 | */ |
@@ -58,5 +69,18 @@ export function useFormValues( | @@ -58,5 +69,18 @@ export function useFormValues( | ||
58 | 69 | ||
59 | return values; | 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,6 +3,10 @@ import type { PropType } from 'vue'; | ||
3 | import type { ColEx } from './types'; | 3 | import type { ColEx } from './types'; |
4 | 4 | ||
5 | export const basicProps = { | 5 | export const basicProps = { |
6 | + model: { | ||
7 | + type: Object as PropType<any>, | ||
8 | + default: {}, | ||
9 | + }, | ||
6 | // 标签宽度 固定宽度 | 10 | // 标签宽度 固定宽度 |
7 | labelWidth: { | 11 | labelWidth: { |
8 | type: [Number, String] as PropType<number | string>, | 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,6 +35,8 @@ export type RegisterFn = (formInstance: FormActionType) => void; | ||
35 | export type UseFormReturnType = [RegisterFn, FormActionType]; | 35 | export type UseFormReturnType = [RegisterFn, FormActionType]; |
36 | 36 | ||
37 | export interface FormProps { | 37 | export interface FormProps { |
38 | + // 表单值 | ||
39 | + model?: any; | ||
38 | // 整个表单所有项宽度 | 40 | // 整个表单所有项宽度 |
39 | labelWidth?: number | string; | 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,13 +15,12 @@ import { | ||
15 | import { Spin } from 'ant-design-vue'; | 15 | import { Spin } from 'ant-design-vue'; |
16 | 16 | ||
17 | import { useWindowSizeFn } from '/@/hooks/event/useWindowSize'; | 17 | import { useWindowSizeFn } from '/@/hooks/event/useWindowSize'; |
18 | -// import { useTimeout } from '/@/hooks/core/useTimeout'; | 18 | +import { useTimeout } from '/@/hooks/core/useTimeout'; |
19 | 19 | ||
20 | import { getSlot } from '/@/utils/helper/tsxHelper'; | 20 | import { getSlot } from '/@/utils/helper/tsxHelper'; |
21 | import { useElResize } from '/@/hooks/event/useElResize'; | 21 | import { useElResize } from '/@/hooks/event/useElResize'; |
22 | export default defineComponent({ | 22 | export default defineComponent({ |
23 | name: 'ModalWrapper', | 23 | name: 'ModalWrapper', |
24 | - emits: ['heightChange', 'getExtHeight'], | ||
25 | props: { | 24 | props: { |
26 | loading: { | 25 | loading: { |
27 | type: Boolean as PropType<boolean>, | 26 | type: Boolean as PropType<boolean>, |
@@ -52,6 +51,7 @@ export default defineComponent({ | @@ -52,6 +51,7 @@ export default defineComponent({ | ||
52 | default: false, | 51 | default: false, |
53 | }, | 52 | }, |
54 | }, | 53 | }, |
54 | + emits: ['heightChange', 'getExtHeight'], | ||
55 | setup(props: ModalWrapperProps, { slots, emit }) { | 55 | setup(props: ModalWrapperProps, { slots, emit }) { |
56 | const wrapperRef = ref<HTMLElement | null>(null); | 56 | const wrapperRef = ref<HTMLElement | null>(null); |
57 | const spinRef = ref<any>(null); | 57 | const spinRef = ref<any>(null); |
@@ -66,7 +66,7 @@ export default defineComponent({ | @@ -66,7 +66,7 @@ export default defineComponent({ | ||
66 | }); | 66 | }); |
67 | 67 | ||
68 | // 重试次数 | 68 | // 重试次数 |
69 | - // let tryCount = 0; | 69 | + let tryCount = 0; |
70 | let stopElResizeFn: Fn = () => {}; | 70 | let stopElResizeFn: Fn = () => {}; |
71 | 71 | ||
72 | watchEffect(() => { | 72 | watchEffect(() => { |
@@ -123,17 +123,17 @@ export default defineComponent({ | @@ -123,17 +123,17 @@ export default defineComponent({ | ||
123 | } | 123 | } |
124 | await nextTick(); | 124 | await nextTick(); |
125 | const spinEl = unref(spinRef); | 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 | const spinContainerEl = spinEl.$el.querySelector('.ant-spin-container') as HTMLElement; | 138 | const spinContainerEl = spinEl.$el.querySelector('.ant-spin-container') as HTMLElement; |
139 | if (!spinContainerEl) return; | 139 | if (!spinContainerEl) return; |
@@ -142,7 +142,7 @@ export default defineComponent({ | @@ -142,7 +142,7 @@ export default defineComponent({ | ||
142 | 142 | ||
143 | if (props.fullScreen) { | 143 | if (props.fullScreen) { |
144 | realHeightRef.value = | 144 | realHeightRef.value = |
145 | - window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 26; | 145 | + window.innerHeight - props.modalFooterHeight - props.modalHeaderHeight - 6; |
146 | } else { | 146 | } else { |
147 | realHeightRef.value = realHeight > maxHeight ? maxHeight : realHeight + 16 + 30; | 147 | realHeightRef.value = realHeight > maxHeight ? maxHeight : realHeight + 16 + 30; |
148 | } | 148 | } |
src/components/Modal/src/useModal.ts
@@ -5,8 +5,9 @@ import type { | @@ -5,8 +5,9 @@ import type { | ||
5 | ReturnMethods, | 5 | ReturnMethods, |
6 | UseModalInnerReturnType, | 6 | UseModalInnerReturnType, |
7 | } from './types'; | 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 | import { isProdMode } from '/@/utils/env'; | 9 | import { isProdMode } from '/@/utils/env'; |
10 | +import { isFunction } from '/@/utils/is'; | ||
10 | const dataTransferRef = reactive<any>({}); | 11 | const dataTransferRef = reactive<any>({}); |
11 | 12 | ||
12 | /** | 13 | /** |
@@ -58,7 +59,7 @@ export function useModal(): UseModalReturnType { | @@ -58,7 +59,7 @@ export function useModal(): UseModalReturnType { | ||
58 | return [register, methods]; | 59 | return [register, methods]; |
59 | } | 60 | } |
60 | 61 | ||
61 | -export const useModalInner = (): UseModalInnerReturnType => { | 62 | +export const useModalInner = (callbackFn?: Fn): UseModalInnerReturnType => { |
62 | const modalInstanceRef = ref<ModalMethods | null>(null); | 63 | const modalInstanceRef = ref<ModalMethods | null>(null); |
63 | const currentInstall = getCurrentInstance(); | 64 | const currentInstall = getCurrentInstance(); |
64 | const uidRef = ref<string>(''); | 65 | const uidRef = ref<string>(''); |
@@ -81,6 +82,13 @@ export const useModalInner = (): UseModalInnerReturnType => { | @@ -81,6 +82,13 @@ export const useModalInner = (): UseModalInnerReturnType => { | ||
81 | currentInstall.emit('register', modalInstance); | 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 | return [ | 92 | return [ |
85 | register, | 93 | register, |
86 | { | 94 | { |
src/views/demo/comp/modal/Modal4.vue
1 | <template> | 1 | <template> |
2 | <BasicModal v-bind="$attrs" @register="register" title="Modal Title"> | 2 | <BasicModal v-bind="$attrs" @register="register" title="Modal Title"> |
3 | <p class="h-20">外部传递数据: {{ receiveModalDataRef }}</p> | 3 | <p class="h-20">外部传递数据: {{ receiveModalDataRef }}</p> |
4 | + <BasicForm @register="registerForm" :model="model" /> | ||
4 | </BasicModal> | 5 | </BasicModal> |
5 | </template> | 6 | </template> |
6 | <script lang="ts"> | 7 | <script lang="ts"> |
7 | - import { defineComponent } from 'vue'; | 8 | + import { defineComponent, nextTick, ref } from 'vue'; |
8 | import { BasicModal, useModalInner } from '/@/components/Modal'; | 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 | export default defineComponent({ | 30 | export default defineComponent({ |
10 | - components: { BasicModal }, | 31 | + components: { BasicModal, BasicForm }, |
11 | setup() { | 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 | </script> | 68 | </script> |
src/views/demo/comp/modal/index.vue
@@ -48,6 +48,12 @@ | @@ -48,6 +48,12 @@ | ||
48 | data: 'content', | 48 | data: 'content', |
49 | info: 'Info', | 49 | info: 'Info', |
50 | }); | 50 | }); |
51 | + // setTimeout(() => { | ||
52 | + // transferModalData({ | ||
53 | + // data: 'content1', | ||
54 | + // info: 'Info1', | ||
55 | + // }); | ||
56 | + // }, 3000); | ||
51 | openModal4(true); | 57 | openModal4(true); |
52 | } | 58 | } |
53 | function openModalLoading() { | 59 | function openModalLoading() { |