|
|
|
@ -25,6 +25,8 @@ |
|
|
|
<script lang="ts"> |
|
|
|
import type { FormActionType, FormProps, FormSchema } from './types/form'; |
|
|
|
import type { Form as FormType, ValidateFields } from 'ant-design-vue/types/form/form'; |
|
|
|
import type { AdvanceState } from './types/hooks'; |
|
|
|
import type { Ref } from 'vue'; |
|
|
|
|
|
|
|
import { |
|
|
|
defineComponent, |
|
|
|
@ -32,27 +34,22 @@ |
|
|
|
ref, |
|
|
|
computed, |
|
|
|
unref, |
|
|
|
toRaw, |
|
|
|
watch, |
|
|
|
toRef, |
|
|
|
onMounted, |
|
|
|
watchEffect, |
|
|
|
} from 'vue'; |
|
|
|
import { Form, Row } from 'ant-design-vue'; |
|
|
|
import FormItem from './FormItem'; |
|
|
|
import { basicProps } from './props'; |
|
|
|
import { deepMerge, unique } from '/@/utils'; |
|
|
|
import { deepMerge } from '/@/utils'; |
|
|
|
import FormAction from './FormAction'; |
|
|
|
|
|
|
|
import { dateItemType } from './helper'; |
|
|
|
import moment from 'moment'; |
|
|
|
import { isArray, isBoolean, isFunction, isNumber, isObject, isString } from '/@/utils/is'; |
|
|
|
import { cloneDeep } from 'lodash-es'; |
|
|
|
import { useBreakpoint } from '/@/hooks/event/useBreakpoint'; |
|
|
|
// import { useThrottle } from '/@/hooks/core/useThrottle'; |
|
|
|
import { useFormValues } from './hooks/useFormValues'; |
|
|
|
import type { ColEx } from './types'; |
|
|
|
import { NamePath } from 'ant-design-vue/types/form/form-item'; |
|
|
|
const BASIC_COL_LEN = 24; |
|
|
|
import useAdvanced from './hooks/useAdvanced'; |
|
|
|
import { useFormAction } from './hooks/useFormAction'; |
|
|
|
|
|
|
|
export default defineComponent({ |
|
|
|
name: 'BasicForm', |
|
|
|
@ -61,13 +58,20 @@ |
|
|
|
props: basicProps, |
|
|
|
emits: ['advanced-change', 'reset', 'submit', 'register'], |
|
|
|
setup(props, { emit }) { |
|
|
|
let formModel = reactive({}); |
|
|
|
const advanceState = reactive({ |
|
|
|
const formModel = reactive({}); |
|
|
|
|
|
|
|
const actionState = reactive({ |
|
|
|
resetAction: {}, |
|
|
|
submitAction: {}, |
|
|
|
}); |
|
|
|
|
|
|
|
const advanceState = reactive<AdvanceState>({ |
|
|
|
isAdvanced: true, |
|
|
|
hideAdvanceBtn: false, |
|
|
|
isLoad: false, |
|
|
|
actionSpan: 6, |
|
|
|
}); |
|
|
|
|
|
|
|
const defaultValueRef = ref<any>({}); |
|
|
|
const propsRef = ref<Partial<FormProps>>({}); |
|
|
|
const schemaRef = ref<FormSchema[] | null>(null); |
|
|
|
@ -78,50 +82,24 @@ |
|
|
|
return deepMerge(cloneDeep(props), unref(propsRef)); |
|
|
|
} |
|
|
|
); |
|
|
|
|
|
|
|
// 获取表单基本配置 |
|
|
|
const getProps = computed( |
|
|
|
(): FormProps => { |
|
|
|
const resetAction = { |
|
|
|
onClick: resetFields, |
|
|
|
}; |
|
|
|
const submitAction = { |
|
|
|
onClick: handleSubmit, |
|
|
|
}; |
|
|
|
return { |
|
|
|
...unref(getMergePropsRef), |
|
|
|
resetButtonOptions: deepMerge( |
|
|
|
resetAction, |
|
|
|
actionState.resetAction, |
|
|
|
unref(getMergePropsRef).resetButtonOptions || {} |
|
|
|
) as any, |
|
|
|
), |
|
|
|
submitButtonOptions: deepMerge( |
|
|
|
submitAction, |
|
|
|
actionState.submitAction, |
|
|
|
unref(getMergePropsRef).submitButtonOptions || {} |
|
|
|
) as any, |
|
|
|
), |
|
|
|
}; |
|
|
|
} |
|
|
|
); |
|
|
|
|
|
|
|
const getActionPropsRef = computed(() => { |
|
|
|
const { |
|
|
|
resetButtonOptions, |
|
|
|
submitButtonOptions, |
|
|
|
showActionButtonGroup, |
|
|
|
showResetButton, |
|
|
|
showSubmitButton, |
|
|
|
showAdvancedButton, |
|
|
|
actionColOptions, |
|
|
|
} = unref(getProps); |
|
|
|
return { |
|
|
|
resetButtonOptions, |
|
|
|
submitButtonOptions, |
|
|
|
show: showActionButtonGroup, |
|
|
|
showResetButton, |
|
|
|
showSubmitButton, |
|
|
|
showAdvancedButton, |
|
|
|
actionColOptions, |
|
|
|
}; |
|
|
|
}); |
|
|
|
|
|
|
|
const getSchema = computed((): FormSchema[] => { |
|
|
|
const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any); |
|
|
|
for (const schema of schemas) { |
|
|
|
@ -133,305 +111,51 @@ |
|
|
|
return schemas as FormSchema[]; |
|
|
|
}); |
|
|
|
|
|
|
|
const getEmptySpanRef = computed((): number => { |
|
|
|
if (!advanceState.isAdvanced) { |
|
|
|
return 0; |
|
|
|
} |
|
|
|
const emptySpan = unref(getMergePropsRef).emptySpan || 0; |
|
|
|
|
|
|
|
if (isNumber(emptySpan)) { |
|
|
|
return emptySpan; |
|
|
|
} |
|
|
|
if (isObject(emptySpan)) { |
|
|
|
const { span = 0 } = emptySpan; |
|
|
|
const screen = unref(screenRef) as string; |
|
|
|
|
|
|
|
const screenSpan = (emptySpan as any)[screen.toLowerCase()]; |
|
|
|
return screenSpan || span || 0; |
|
|
|
} |
|
|
|
return 0; |
|
|
|
const { getActionPropsRef, handleToggleAdvanced } = useAdvanced({ |
|
|
|
advanceState, |
|
|
|
emit, |
|
|
|
getMergePropsRef, |
|
|
|
getProps, |
|
|
|
getSchema, |
|
|
|
formModel, |
|
|
|
defaultValueRef, |
|
|
|
}); |
|
|
|
|
|
|
|
const { realWidthRef, screenEnum, screenRef } = useBreakpoint(); |
|
|
|
// const [throttleUpdateAdvanced] = useThrottle(updateAdvanced, 30, { immediate: true }); |
|
|
|
watch( |
|
|
|
[() => unref(getSchema), () => advanceState.isAdvanced, () => unref(realWidthRef)], |
|
|
|
() => { |
|
|
|
const { showAdvancedButton } = unref(getProps); |
|
|
|
if (showAdvancedButton) { |
|
|
|
updateAdvanced(); |
|
|
|
} |
|
|
|
}, |
|
|
|
{ immediate: true } |
|
|
|
); |
|
|
|
|
|
|
|
function initDefault() { |
|
|
|
const schemas = unref(getSchema); |
|
|
|
const obj: any = {}; |
|
|
|
schemas.forEach((item) => { |
|
|
|
if (item.defaultValue) { |
|
|
|
obj[item.field] = item.defaultValue; |
|
|
|
(formModel as any)[item.field] = item.defaultValue; |
|
|
|
} |
|
|
|
}); |
|
|
|
defaultValueRef.value = obj; |
|
|
|
} |
|
|
|
|
|
|
|
function updateAdvanced() { |
|
|
|
let itemColSum = 0; |
|
|
|
let realItemColSum = 0; |
|
|
|
for (const schema of unref(getSchema)) { |
|
|
|
const { show, colProps } = schema; |
|
|
|
let isShow = true; |
|
|
|
|
|
|
|
if (isBoolean(show)) { |
|
|
|
isShow = show; |
|
|
|
} |
|
|
|
|
|
|
|
if (isFunction(show)) { |
|
|
|
isShow = show({ |
|
|
|
schema: schema, |
|
|
|
model: formModel, |
|
|
|
field: schema.field, |
|
|
|
values: { |
|
|
|
...unref(defaultValueRef), |
|
|
|
...formModel, |
|
|
|
}, |
|
|
|
}); |
|
|
|
} |
|
|
|
if (isShow && colProps) { |
|
|
|
const { itemColSum: sum, isAdvanced } = getAdvanced(colProps, itemColSum); |
|
|
|
|
|
|
|
itemColSum = sum || 0; |
|
|
|
if (isAdvanced) { |
|
|
|
realItemColSum = itemColSum; |
|
|
|
} |
|
|
|
schema.isAdvanced = isAdvanced; |
|
|
|
} |
|
|
|
} |
|
|
|
advanceState.actionSpan = (realItemColSum % BASIC_COL_LEN) + unref(getEmptySpanRef); |
|
|
|
getAdvanced( |
|
|
|
unref(getActionPropsRef).actionColOptions || { span: BASIC_COL_LEN }, |
|
|
|
itemColSum, |
|
|
|
true |
|
|
|
); |
|
|
|
emit('advanced-change'); |
|
|
|
} |
|
|
|
function getAdvanced(itemCol: Partial<ColEx>, itemColSum = 0, isLastAction = false) { |
|
|
|
const width = unref(realWidthRef); |
|
|
|
|
|
|
|
const mdWidth = |
|
|
|
parseInt(itemCol.md as string) || |
|
|
|
parseInt(itemCol.xs as string) || |
|
|
|
parseInt(itemCol.sm as string) || |
|
|
|
(itemCol.span as number) || |
|
|
|
BASIC_COL_LEN; |
|
|
|
const lgWidth = parseInt(itemCol.lg as string) || mdWidth; |
|
|
|
const xlWidth = parseInt(itemCol.xl as string) || lgWidth; |
|
|
|
const xxlWidth = parseInt(itemCol.xxl as string) || xlWidth; |
|
|
|
if (width <= screenEnum.LG) { |
|
|
|
itemColSum += mdWidth; |
|
|
|
} else if (width < screenEnum.XL) { |
|
|
|
itemColSum += lgWidth; |
|
|
|
} else if (width < screenEnum.XXL) { |
|
|
|
itemColSum += xlWidth; |
|
|
|
} else { |
|
|
|
itemColSum += xxlWidth; |
|
|
|
} |
|
|
|
if (isLastAction) { |
|
|
|
advanceState.hideAdvanceBtn = false; |
|
|
|
if (itemColSum <= BASIC_COL_LEN * 2) { |
|
|
|
// 小于等于2行时,不显示收起展开按钮 |
|
|
|
advanceState.hideAdvanceBtn = true; |
|
|
|
advanceState.isAdvanced = true; |
|
|
|
} else if ( |
|
|
|
itemColSum > BASIC_COL_LEN * 2 && |
|
|
|
itemColSum <= BASIC_COL_LEN * (props.autoAdvancedLine || 3) |
|
|
|
) { |
|
|
|
advanceState.hideAdvanceBtn = false; |
|
|
|
|
|
|
|
// 大于3行默认收起 |
|
|
|
} else if (!advanceState.isLoad) { |
|
|
|
advanceState.isLoad = true; |
|
|
|
advanceState.isAdvanced = !advanceState.isAdvanced; |
|
|
|
} |
|
|
|
return { isAdvanced: advanceState.isAdvanced, itemColSum }; |
|
|
|
} |
|
|
|
if (itemColSum > BASIC_COL_LEN) { |
|
|
|
return { isAdvanced: advanceState.isAdvanced, itemColSum }; |
|
|
|
} else { |
|
|
|
// 第一行始终显示 |
|
|
|
return { isAdvanced: true, itemColSum }; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
async function resetFields(): Promise<any> { |
|
|
|
const { resetFunc, submitOnReset } = unref(getProps); |
|
|
|
resetFunc && isFunction(resetFunc) && (await resetFunc()); |
|
|
|
const formEl = unref(formElRef); |
|
|
|
if (!formEl) return; |
|
|
|
Object.keys(formModel).forEach((key) => { |
|
|
|
(formModel as any)[key] = defaultValueRef.value[key]; |
|
|
|
}); |
|
|
|
// const values = formEl.resetFields(); |
|
|
|
emit('reset', toRaw(formModel)); |
|
|
|
// return values; |
|
|
|
submitOnReset && handleSubmit(); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @description: 设置表单值 |
|
|
|
*/ |
|
|
|
async function setFieldsValue(values: any): Promise<void> { |
|
|
|
const fields = unref(getSchema) |
|
|
|
.map((item) => item.field) |
|
|
|
.filter(Boolean); |
|
|
|
const formEl = unref(formElRef); |
|
|
|
Object.keys(values).forEach((key) => { |
|
|
|
const element = values[key]; |
|
|
|
if (fields.includes(key) && element !== undefined && element !== null) { |
|
|
|
// 时间 |
|
|
|
if (itemIsDateType(key)) { |
|
|
|
if (Array.isArray(element)) { |
|
|
|
const arr: any[] = []; |
|
|
|
for (const ele of element) { |
|
|
|
arr.push(moment(ele)); |
|
|
|
} |
|
|
|
(formModel as any)[key] = arr; |
|
|
|
} else { |
|
|
|
(formModel as any)[key] = moment(element); |
|
|
|
} |
|
|
|
} else { |
|
|
|
(formModel as any)[key] = element; |
|
|
|
} |
|
|
|
if (formEl) { |
|
|
|
formEl.validateFields([key]); |
|
|
|
} |
|
|
|
} |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @description: 表单提交 |
|
|
|
*/ |
|
|
|
async function handleSubmit(e?: Event): Promise<void> { |
|
|
|
e && e.preventDefault(); |
|
|
|
const { submitFunc } = unref(getProps); |
|
|
|
if (submitFunc && isFunction(submitFunc)) { |
|
|
|
await submitFunc(); |
|
|
|
return; |
|
|
|
} |
|
|
|
const formEl = unref(formElRef); |
|
|
|
if (!formEl) return; |
|
|
|
try { |
|
|
|
const values = await formEl.validate(); |
|
|
|
const res = handleFormValues(values); |
|
|
|
emit('submit', res); |
|
|
|
} catch (error) {} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @description: 根据字段名删除 |
|
|
|
*/ |
|
|
|
function removeSchemaByFiled(fields: string | string[]): void { |
|
|
|
const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); |
|
|
|
if (!fields) { |
|
|
|
return; |
|
|
|
} |
|
|
|
let fieldList: string[] = fields as string[]; |
|
|
|
if (isString(fields)) { |
|
|
|
fieldList = [fields]; |
|
|
|
} |
|
|
|
for (const field of fieldList) { |
|
|
|
_removeSchemaByFiled(field, schemaList); |
|
|
|
} |
|
|
|
schemaRef.value = schemaList as any; |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @description: 根据字段名删除 |
|
|
|
*/ |
|
|
|
function _removeSchemaByFiled(field: string, schemaList: FormSchema[]): void { |
|
|
|
if (isString(field)) { |
|
|
|
const index = schemaList.findIndex((schema) => schema.field === field); |
|
|
|
if (index !== -1) { |
|
|
|
schemaList.splice(index, 1); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
/** |
|
|
|
* @description: 往某个字段后面插入,如果没有插入最后一个 |
|
|
|
*/ |
|
|
|
function appendSchemaByField(schema: FormSchema, prefixField?: string) { |
|
|
|
const schemaList: FormSchema[] = cloneDeep(unref(getSchema)); |
|
|
|
|
|
|
|
const index = schemaList.findIndex((schema) => schema.field === prefixField); |
|
|
|
const hasInList = schemaList.find((item) => item.field === schema.field); |
|
|
|
|
|
|
|
if (hasInList) { |
|
|
|
return; |
|
|
|
} |
|
|
|
if (!prefixField || index === -1) { |
|
|
|
schemaList.push(schema); |
|
|
|
schemaRef.value = schemaList as any; |
|
|
|
return; |
|
|
|
} |
|
|
|
if (index !== -1) { |
|
|
|
schemaList.splice(index + 1, 0, schema); |
|
|
|
} |
|
|
|
schemaRef.value = schemaList as any; |
|
|
|
} |
|
|
|
|
|
|
|
function updateSchema(data: Partial<FormSchema> | Partial<FormSchema>[]) { |
|
|
|
let updateData: Partial<FormSchema>[] = []; |
|
|
|
if (isObject(data)) { |
|
|
|
updateData.push(data as FormSchema); |
|
|
|
} |
|
|
|
if (isArray(data)) { |
|
|
|
updateData = [...data]; |
|
|
|
} |
|
|
|
const hasField = updateData.every((item) => Reflect.has(item, 'field') && item.field); |
|
|
|
if (!hasField) { |
|
|
|
throw new Error('Must pass in the `field` field!'); |
|
|
|
} |
|
|
|
const schema: FormSchema[] = []; |
|
|
|
updateData.forEach((item) => { |
|
|
|
unref(getSchema).forEach((val) => { |
|
|
|
if (val.field === item.field) { |
|
|
|
const newScheam = deepMerge(val, item); |
|
|
|
schema.push(newScheam as FormSchema); |
|
|
|
} else { |
|
|
|
schema.push(val); |
|
|
|
} |
|
|
|
}); |
|
|
|
}); |
|
|
|
schemaRef.value = unique(schema, 'field') as any; |
|
|
|
} |
|
|
|
|
|
|
|
function handleToggleAdvanced() { |
|
|
|
advanceState.isAdvanced = !advanceState.isAdvanced; |
|
|
|
} |
|
|
|
|
|
|
|
const handleFormValues = useFormValues( |
|
|
|
toRef(props, 'transformDateFunc'), |
|
|
|
toRef(props, 'fieldMapToTime') |
|
|
|
); |
|
|
|
const { handleFormValues, initDefault } = useFormValues({ |
|
|
|
transformDateFuncRef: toRef(props, 'transformDateFunc') as Ref<Fn<any>>, |
|
|
|
fieldMapToTimeRef: toRef(props, 'fieldMapToTime'), |
|
|
|
defaultValueRef, |
|
|
|
getSchema, |
|
|
|
formModel, |
|
|
|
}); |
|
|
|
|
|
|
|
function getFieldsValue(): any { |
|
|
|
const formEl = unref(formElRef); |
|
|
|
if (!formEl) return; |
|
|
|
return handleFormValues(toRaw(unref(formModel))); |
|
|
|
} |
|
|
|
const { |
|
|
|
// handleSubmit, |
|
|
|
setFieldsValue, |
|
|
|
clearValidate, |
|
|
|
validate, |
|
|
|
validateFields, |
|
|
|
getFieldsValue, |
|
|
|
updateSchema, |
|
|
|
appendSchemaByField, |
|
|
|
removeSchemaByFiled, |
|
|
|
resetFields, |
|
|
|
} = useFormAction({ |
|
|
|
emit, |
|
|
|
getProps, |
|
|
|
formModel, |
|
|
|
getSchema, |
|
|
|
defaultValueRef, |
|
|
|
formElRef: formElRef as any, |
|
|
|
schemaRef: schemaRef as any, |
|
|
|
handleFormValues, |
|
|
|
actionState, |
|
|
|
}); |
|
|
|
|
|
|
|
/** |
|
|
|
* @description: 是否是时间 |
|
|
|
*/ |
|
|
|
function itemIsDateType(key: string) { |
|
|
|
return unref(getSchema).some((item) => { |
|
|
|
return item.field === key ? dateItemType.includes(item.component!) : false; |
|
|
|
}); |
|
|
|
} |
|
|
|
watchEffect(() => { |
|
|
|
if (!unref(getMergePropsRef).model) return; |
|
|
|
setFieldsValue(unref(getMergePropsRef).model); |
|
|
|
}); |
|
|
|
|
|
|
|
/** |
|
|
|
* @description:设置表单 |
|
|
|
@ -441,21 +165,6 @@ |
|
|
|
propsRef.value = mergeProps; |
|
|
|
} |
|
|
|
|
|
|
|
function validateFields(nameList?: NamePath[] | undefined) { |
|
|
|
if (!formElRef.value) return; |
|
|
|
return formElRef.value.validateFields(nameList); |
|
|
|
} |
|
|
|
|
|
|
|
function validate(nameList?: NamePath[] | undefined) { |
|
|
|
if (!formElRef.value) return; |
|
|
|
return formElRef.value.validate(nameList); |
|
|
|
} |
|
|
|
|
|
|
|
function clearValidate(name: string | string[]) { |
|
|
|
if (!formElRef.value) return; |
|
|
|
formElRef.value.clearValidate(name); |
|
|
|
} |
|
|
|
|
|
|
|
const methods: Partial<FormActionType> = { |
|
|
|
getFieldsValue, |
|
|
|
setFieldsValue, |
|
|
|
|