| | |
| | | <template> |
| | | <Form v-bind="$attrs" ref="formElRef" :model="formModel"> |
| | | <Row :class="getProps.compact ? 'compact-form-row' : ''"> |
| | | <slot name="formHeader" /> |
| | | <Form v-bind="{ ...$attrs, ...$props }" :class="getFormClass" ref="formElRef" :model="formModel"> |
| | | <Row :style="getRowWrapStyle"> |
| | | <slot name="formHeader"></slot> |
| | | <template v-for="schema in getSchema" :key="schema.field"> |
| | | <FormItem |
| | | :tableAction="tableAction" |
| | | :formActionType="formActionType" |
| | | :schema="schema" |
| | | :formProps="getProps" |
| | | :allDefaultValues="defaultValueRef" |
| | | :formModel="formModel" |
| | | :setFormModel="setFormModel" |
| | | > |
| | | <template #[item]="data" v-for="item in Object.keys($slots)"> |
| | | <slot :name="item" v-bind="data" /> |
| | | <slot :name="item" v-bind="data"></slot> |
| | | </template> |
| | | </FormItem> |
| | | </template> |
| | | <FormAction |
| | | v-bind="{ ...getActionPropsRef, ...advanceState }" |
| | | @toggle-advanced="handleToggleAdvanced" |
| | | /> |
| | | <slot name="formFooter" /> |
| | | |
| | | <FormAction v-bind="{ ...getProps, ...advanceState }" @toggle-advanced="handleToggleAdvanced"> |
| | | <template |
| | | #[item]="data" |
| | | v-for="item in ['resetBefore', 'submitBefore', 'advanceBefore', 'advanceAfter']" |
| | | > |
| | | <slot :name="item" v-bind="data"></slot> |
| | | </template> |
| | | </FormAction> |
| | | <slot name="formFooter"></slot> |
| | | </Row> |
| | | </Form> |
| | | </template> |
| | | <script lang="ts"> |
| | | import type { FormActionType, FormProps, FormSchema } from './types/form'; |
| | | import type { AdvanceState } from './types/hooks'; |
| | | import type { Ref } from 'vue'; |
| | | import type { ValidateFields } from 'ant-design-vue/lib/form/interface'; |
| | | import type { CSSProperties, Ref } from 'vue'; |
| | | |
| | | import { defineComponent, reactive, ref, computed, unref, toRef, onMounted, watch } from 'vue'; |
| | | import { |
| | | defineComponent, |
| | | reactive, |
| | | ref, |
| | | computed, |
| | | unref, |
| | | onMounted, |
| | | watch, |
| | | toRefs, |
| | | nextTick, |
| | | } from 'vue'; |
| | | import { Form, Row } from 'ant-design-vue'; |
| | | import FormItem from './FormItem'; |
| | | import { basicProps } from './props'; |
| | | import FormAction from './FormAction'; |
| | | import FormItem from './components/FormItem'; |
| | | import FormAction from './components/FormAction.vue'; |
| | | |
| | | import { dateItemType } from './helper'; |
| | | import moment from 'moment'; |
| | | import { cloneDeep } from 'lodash-es'; |
| | | import { dateUtil } from '/@/utils/dateUtil'; |
| | | |
| | | // import { cloneDeep } from 'lodash-es'; |
| | | import { deepMerge } from '/@/utils'; |
| | | |
| | | import { useFormValues } from './hooks/useFormValues'; |
| | | import useAdvanced from './hooks/useAdvanced'; |
| | | import { useFormAction } from './hooks/useFormAction'; |
| | | import { useFormEvents } from './hooks/useFormEvents'; |
| | | import { createFormContext } from './hooks/useFormContext'; |
| | | import { useAutoFocus } from './hooks/useAutoFocus'; |
| | | import { useModalContext } from '/@/components/Modal'; |
| | | |
| | | import { basicProps } from './props'; |
| | | import { useDesign } from '/@/hooks/web/useDesign'; |
| | | |
| | | export default defineComponent({ |
| | | name: 'BasicForm', |
| | | components: { FormItem, Form, Row, FormAction }, |
| | | inheritAttrs: false, |
| | | props: basicProps, |
| | | emits: ['advanced-change', 'reset', 'submit', 'register'], |
| | | setup(props, { emit }) { |
| | | const formModel = reactive({}); |
| | | |
| | | const actionState = reactive({ |
| | | resetAction: {}, |
| | | submitAction: {}, |
| | | }); |
| | | const formModel = reactive<Recordable>({}); |
| | | const modalFn = useModalContext(); |
| | | |
| | | const advanceState = reactive<AdvanceState>({ |
| | | isAdvanced: true, |
| | |
| | | actionSpan: 6, |
| | | }); |
| | | |
| | | const defaultValueRef = ref<any>({}); |
| | | const defaultValueRef = ref<Recordable>({}); |
| | | const isInitedDefaultRef = ref(false); |
| | | const propsRef = ref<Partial<FormProps>>({}); |
| | | const schemaRef = ref<FormSchema[] | null>(null); |
| | | const schemaRef = ref<Nullable<FormSchema[]>>(null); |
| | | const formElRef = ref<Nullable<FormActionType>>(null); |
| | | |
| | | const getMergePropsRef = computed( |
| | | const { prefixCls } = useDesign('basic-form'); |
| | | |
| | | // Get the basic configuration of the form |
| | | const getProps = computed( |
| | | (): FormProps => { |
| | | return deepMerge(cloneDeep(props), unref(propsRef)); |
| | | return { ...props, ...unref(propsRef) } as FormProps; |
| | | } |
| | | ); |
| | | |
| | | // 获取表单基本配置 |
| | | const getProps = computed( |
| | | (): FormProps => { |
| | | return { |
| | | ...unref(getMergePropsRef), |
| | | resetButtonOptions: deepMerge( |
| | | actionState.resetAction, |
| | | unref(getMergePropsRef).resetButtonOptions || {} |
| | | ), |
| | | submitButtonOptions: deepMerge( |
| | | actionState.submitAction, |
| | | unref(getMergePropsRef).submitButtonOptions || {} |
| | | ), |
| | | }; |
| | | const getFormClass = computed(() => { |
| | | return [ |
| | | prefixCls, |
| | | { |
| | | [`${prefixCls}--compact`]: unref(getProps).compact, |
| | | }, |
| | | ]; |
| | | }); |
| | | |
| | | // Get uniform row style |
| | | const getRowWrapStyle = computed( |
| | | (): CSSProperties => { |
| | | const { baseRowStyle = {} } = unref(getProps); |
| | | return baseRowStyle; |
| | | } |
| | | ); |
| | | |
| | |
| | | const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any); |
| | | for (const schema of schemas) { |
| | | const { defaultValue, component } = schema; |
| | | if (defaultValue && dateItemType.includes(component!)) { |
| | | schema.defaultValue = moment(defaultValue); |
| | | // handle date type |
| | | if (defaultValue && dateItemType.includes(component)) { |
| | | if (!Array.isArray(defaultValue)) { |
| | | schema.defaultValue = dateUtil(defaultValue); |
| | | } else { |
| | | const def: moment.Moment[] = []; |
| | | defaultValue.forEach((item) => { |
| | | def.push(dateUtil(item)); |
| | | }); |
| | | schema.defaultValue = def; |
| | | } |
| | | } |
| | | } |
| | | return schemas as FormSchema[]; |
| | | }); |
| | | |
| | | const { getActionPropsRef, handleToggleAdvanced } = useAdvanced({ |
| | | const { handleToggleAdvanced } = useAdvanced({ |
| | | advanceState, |
| | | emit, |
| | | getMergePropsRef, |
| | | getProps, |
| | | getSchema, |
| | | formModel, |
| | | defaultValueRef, |
| | | }); |
| | | |
| | | const { transformDateFunc, fieldMapToTime, autoFocusFirstItem } = toRefs(props); |
| | | |
| | | const { handleFormValues, initDefault } = useFormValues({ |
| | | transformDateFuncRef: toRef(props, 'transformDateFunc') as Ref<Fn<any>>, |
| | | fieldMapToTimeRef: toRef(props, 'fieldMapToTime'), |
| | | transformDateFuncRef: transformDateFunc, |
| | | fieldMapToTimeRef: fieldMapToTime, |
| | | defaultValueRef, |
| | | getSchema, |
| | | formModel, |
| | | }); |
| | | |
| | | useAutoFocus({ |
| | | getSchema, |
| | | autoFocusFirstItem, |
| | | isInitedDefault: isInitedDefaultRef, |
| | | formElRef: formElRef as Ref<FormActionType>, |
| | | }); |
| | | |
| | | const { |
| | | // handleSubmit, |
| | | handleSubmit, |
| | | setFieldsValue, |
| | | clearValidate, |
| | | validate, |
| | |
| | | appendSchemaByField, |
| | | removeSchemaByFiled, |
| | | resetFields, |
| | | } = useFormAction({ |
| | | scrollToField, |
| | | } = useFormEvents({ |
| | | emit, |
| | | getProps, |
| | | formModel, |
| | | getSchema, |
| | | defaultValueRef, |
| | | formElRef: formElRef as any, |
| | | schemaRef: schemaRef as any, |
| | | formElRef: formElRef as Ref<FormActionType>, |
| | | schemaRef: schemaRef as Ref<FormSchema[]>, |
| | | handleFormValues, |
| | | actionState, |
| | | }); |
| | | |
| | | createFormContext({ |
| | | resetAction: resetFields, |
| | | submitAction: handleSubmit, |
| | | }); |
| | | |
| | | watch( |
| | | () => unref(getMergePropsRef).model, |
| | | () => unref(getProps).model, |
| | | () => { |
| | | if (!unref(getMergePropsRef).model) return; |
| | | setFieldsValue(unref(getMergePropsRef).model); |
| | | const { model } = unref(getProps); |
| | | if (!model) return; |
| | | setFieldsValue(model); |
| | | }, |
| | | { |
| | | immediate: true, |
| | | } |
| | | ); |
| | | |
| | | /** |
| | | * @description:设置表单 |
| | | */ |
| | | function setProps(formProps: Partial<FormProps>): void { |
| | | const mergeProps = deepMerge(unref(propsRef) || {}, formProps); |
| | | propsRef.value = mergeProps; |
| | | watch( |
| | | () => getSchema.value, |
| | | (schema) => { |
| | | nextTick(() => { |
| | | // Solve the problem of modal adaptive height calculation when the form is placed in the modal |
| | | modalFn?.redoModalHeight?.(); |
| | | }); |
| | | if (unref(isInitedDefaultRef)) { |
| | | return; |
| | | } |
| | | if (schema?.length) { |
| | | initDefault(); |
| | | isInitedDefaultRef.value = true; |
| | | } |
| | | } |
| | | ); |
| | | |
| | | async function setProps(formProps: Partial<FormProps>): Promise<void> { |
| | | propsRef.value = deepMerge(unref(propsRef) || {}, formProps); |
| | | } |
| | | |
| | | const methods: Partial<FormActionType> = { |
| | | function setFormModel(key: string, value: any) { |
| | | formModel[key] = value; |
| | | } |
| | | |
| | | const formActionType: Partial<FormActionType> = { |
| | | getFieldsValue, |
| | | setFieldsValue, |
| | | resetFields, |
| | |
| | | removeSchemaByFiled, |
| | | appendSchemaByField, |
| | | clearValidate, |
| | | validateFields: validateFields as ValidateFields, |
| | | validate: validate as ValidateFields, |
| | | validateFields, |
| | | validate, |
| | | submit: handleSubmit, |
| | | scrollToField: scrollToField, |
| | | }; |
| | | |
| | | onMounted(() => { |
| | | initDefault(); |
| | | emit('register', methods); |
| | | emit('register', formActionType); |
| | | }); |
| | | |
| | | return { |
| | | handleToggleAdvanced, |
| | | formModel, |
| | | getActionPropsRef, |
| | | defaultValueRef, |
| | | advanceState, |
| | | getRowWrapStyle, |
| | | getProps, |
| | | formElRef, |
| | | getSchema, |
| | | ...methods, |
| | | formActionType, |
| | | setFormModel, |
| | | prefixCls, |
| | | getFormClass, |
| | | ...formActionType, |
| | | }; |
| | | }, |
| | | }); |
| | | </script> |
| | | <style lang="less"> |
| | | @prefix-cls: ~'@{namespace}-basic-form'; |
| | | |
| | | .@{prefix-cls} { |
| | | .ant-form-item { |
| | | &-label label::after { |
| | | margin: 0 6px 0 2px; |
| | | } |
| | | |
| | | &-with-help { |
| | | margin-bottom: 0; |
| | | } |
| | | |
| | | &:not(.ant-form-item-with-help) { |
| | | margin-bottom: 20px; |
| | | } |
| | | |
| | | &.suffix-item { |
| | | .ant-form-item-children { |
| | | display: flex; |
| | | } |
| | | |
| | | .suffix { |
| | | display: inline-block; |
| | | padding-left: 6px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .ant-form-explain { |
| | | font-size: 14px; |
| | | } |
| | | |
| | | &--compact { |
| | | .ant-form-item { |
| | | margin-bottom: 8px !important; |
| | | } |
| | | } |
| | | } |
| | | </style> |