123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301 |
- <template>
- <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>
- </template>
- </FormItem>
- </template>
- <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 { CSSProperties, Ref } from 'vue';
- import { defineComponent, reactive, ref, computed, unref, onMounted, watch, nextTick } from 'vue';
- import { Form, Row } from 'ant-design-vue';
- import FormItem from './components/FormItem.vue';
- import FormAction from './components/FormAction.vue';
- import { dateItemType } from './helper';
- 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 { 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 },
- props: basicProps,
- emits: ['advanced-change', 'reset', 'submit', 'register'],
- setup(props, { emit }) {
- const formModel = reactive<Recordable>({});
- const modalFn = useModalContext();
- const advanceState = reactive<AdvanceState>({
- isAdvanced: true,
- hideAdvanceBtn: false,
- isLoad: false,
- actionSpan: 6,
- });
- const defaultValueRef = ref<Recordable>({});
- const isInitedDefaultRef = ref(false);
- const propsRef = ref<Partial<FormProps>>({});
- const schemaRef = ref<Nullable<FormSchema[]>>(null);
- const formElRef = ref<Nullable<FormActionType>>(null);
- const { prefixCls } = useDesign('basic-form');
- // Get the basic configuration of the form
- const getProps = computed(
- (): FormProps => {
- return { ...props, ...unref(propsRef) } as FormProps;
- }
- );
- 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 getSchema = computed((): FormSchema[] => {
- const schemas: FormSchema[] = unref(schemaRef) || (unref(getProps).schemas as any);
- for (const schema of schemas) {
- const { defaultValue, component } = schema;
- // 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 { handleToggleAdvanced } = useAdvanced({
- advanceState,
- emit,
- getProps,
- getSchema,
- formModel,
- defaultValueRef,
- });
- const { handleFormValues, initDefault } = useFormValues({
- getProps,
- defaultValueRef,
- getSchema,
- formModel,
- });
- useAutoFocus({
- getSchema,
- getProps,
- isInitedDefault: isInitedDefaultRef,
- formElRef: formElRef as Ref<FormActionType>,
- });
- const {
- handleSubmit,
- setFieldsValue,
- clearValidate,
- validate,
- validateFields,
- getFieldsValue,
- updateSchema,
- resetSchema,
- appendSchemaByField,
- removeSchemaByFiled,
- resetFields,
- scrollToField,
- } = useFormEvents({
- emit,
- getProps,
- formModel,
- getSchema,
- defaultValueRef,
- formElRef: formElRef as Ref<FormActionType>,
- schemaRef: schemaRef as Ref<FormSchema[]>,
- handleFormValues,
- });
- createFormContext({
- resetAction: resetFields,
- submitAction: handleSubmit,
- });
- watch(
- () => unref(getProps).model,
- () => {
- const { model } = unref(getProps);
- if (!model) return;
- setFieldsValue(model);
- },
- {
- immediate: true,
- }
- );
- 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);
- }
- function setFormModel(key: string, value: any) {
- formModel[key] = value;
- }
- const formActionType: Partial<FormActionType> = {
- getFieldsValue,
- setFieldsValue,
- resetFields,
- updateSchema,
- resetSchema,
- setProps,
- removeSchemaByFiled,
- appendSchemaByField,
- clearValidate,
- validateFields,
- validate,
- submit: handleSubmit,
- scrollToField: scrollToField,
- };
- onMounted(() => {
- initDefault();
- emit('register', formActionType);
- });
- return {
- handleToggleAdvanced,
- formModel,
- defaultValueRef,
- advanceState,
- getRowWrapStyle,
- getProps,
- formElRef,
- getSchema,
- 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;
- }
- .ant-form-item-control {
- margin-top: 4px;
- }
- .suffix {
- display: inline-flex;
- padding-left: 6px;
- margin-top: 1px;
- line-height: 1;
- align-items: center;
- }
- }
- }
- .ant-form-explain {
- font-size: 14px;
- }
- &--compact {
- .ant-form-item {
- margin-bottom: 8px !important;
- }
- }
- }
- </style>
|