适用vue 3.4 版本以上
在日常的前端开发中,表单开发必不可少,尤其是在遇到一些大型的复杂的表单,这往往会变成一个痛点。于是就想着开发一个可以list来配置的表单组件。
先上组件代码
<!-- 该组件 VUE 版本在 3.4 以上可使用-->
<template><Formref="formRef":model="model"v-bind='form'><Row :gutter="[8, 8]"><template v-for="config in newConfigs" :key="config.key"><Col v-bind="config.wrapperCol" v-if="config.when(model)"><Form.Item v-bind="config.forImteAttrs":label-col="config.labelCol"><component:is="config.type"v-bind="config.componentAttrs":config="config":raw="config.raw":options="config.componentAttrs.options || options?.[config.key]"v-model:[config.model]="model[config.key]"v-on='{change: (e: any) => {change(e, config.key);config.componentAttrs.onChange?.({update: change,value: model})}}'>{{ config.componentAttrs.buttonText || null }}</component></Form.Item></Col></template><slot /></Row></Form>
</template><script setup lang="ts">
import {Col,Form,FormInstance,Row,
} from 'ant-design-vue';
import {computed, Ref,toRefs,useAttrs,
} from 'vue';
import { FormItemConfig } from './ConfigFormType';const formRef = defineModel<Ref<null | undefined> | FormInstance>('formRef');
const model = defineModel<any>();interface IConfigFormProps {layout?: 'inline' | 'vertical' | 'horizontal';FormGrid?: {};labelCol?: any;wrapperCol?: any;form?: any;configs: FormItemConfig[];options?: { [index: string]: any[] }readonly?: boolean;
}
const props = defineProps<IConfigFormProps>();
const {configs, FormGrid, labelCol, options, wrapperCol, readonly,
} = toRefs(props);const generateUUID = () => 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {// eslint-disable-next-line no-bitwiseconst r = Math.random() * 16 | 0;// eslint-disable-next-line no-bitwise, no-mixed-operatorsconst v = c === 'x' ? r : (r & 0x3) | 0x8;return v.toString(16);
});const gloablAttrs = useAttrs();
const newConfigs = computed(() => configs.value.map((config: any) => {const {grid, key, type, attrs, itemModel, when, children, ...rest} = config;if (key) {rest.name = key;}if (!config.wrapperCol) {rest.wrapperCol = wrapperCol.value;}if (!config.labelCol) {rest.labelCol = labelCol.value;}const forImteAttrs: Partial<any> = rest;const componentAttrs = attrs || {};return {layout: { ...FormGrid, ...grid },name: [key || rest.name],key: key || rest.name || generateUUID(),when: when || (() => true),type,children,model: itemModel!,forImteAttrs,wrapperCol: rest.wrapperCol,labelCol: rest.labelCol,componentAttrs,readonly: gloablAttrs.readonly || readonly.value,disabled: gloablAttrs.disabled,raw: config,};
}));interface IConfigFormEmits {(e: 'finish', value: any): void;(e: 'change', value: any): void;
}
const emit = defineEmits<IConfigFormEmits>();
const change = (e: any, key: string) => emit('change', { value: e, key });
</script>
具体使用
<ConfigForm:configs="configComputed"v-model:formRef="formRef"v-model="formState"@finish="fetchInfo"layout="inline" />
configs
用来生成表单的具体配置,类型如下
// FormItemConfig.ts
import { ColProps } from 'ant-design-vue';
import { Slot } from 'vue';export type FormItemConfig = {title?: string;type: any;layout?: 'horizontal' | 'vertical' | 'inline';key?: string;name?: string;label?: string;rules?: any[] | any;colon?: boolean;options?: any[];hasFeedback?: boolean;help?: string | Slot;labelAlign?: 'left' | 'right';labelCol?: ColProps;required?: boolean;tooltip?: string | Slot;validateFirst?: boolean;validateStatus?: 'success' | 'warning' | 'error' | 'validating';validateTrigger?: string | string[];wrapperCol?: ColProps;extra?: string | Slot | any;model?: any;// eslint-disable-next-line no-unused-varswhen?: (values: Partial<any>, currentValue?: Partial<any>) => boolean;itemModel?: string;attrs?: Partial<any>;children?: FormItemConfig[];
};
configs配置
1、attrs属性为绑定到具体表单组件的属性,例如Select,Input之类的
2、其余大部分属性绑定到<Form.Item>组件上,故可以使用label,rules之类的属性,
3、itemModel一般情况下为value,type为Switch组件时,需根据实际情况变化
4、type属性是直接注入< components >组件的is属性上,所以一些自定义的组件也可以使用,注入到type组件中
import {AutoComplete, Button, Spin,
} from 'ant-design-vue';
import {ref } from 'vue';const autoCompleteLoading = ref<boolean>(false)
const configs = [{title: 'XXX',key: 'name',type: autoCompleteLoading.value ? Spin : AutoComplete,label: 'item 1',rules: { required: true, message: '请输入XXX', trigger: 'blur' },itemModel: 'value',attrs: {options: [{ label: 'item1', value: 'value1' }],allowClear: true,placeholder: 'XXXXXXXX',style: 'width: 280px',filterOption: (inputValue: string, option: any) => option.value.toLowerCase().indexOf(inputValue.toLowerCase()) >= 0,},},{title: 'XXXXXXX',type: Button,attrs: {type: 'primary',htmlType: 'submit',buttonText: '查询',},},];
也可以配合计算属性和响应式函数动态响应表单
// config.ts
import { computed, Ref } from 'vue';
import {AutoComplete, Button, Spin,
} from 'ant-design-vue';export function useConfigComputed(autoCompleteLoading:Ref<boolean>,dataNameList: Ref<{ label: string, value: any }[]>
) {return computed(() => [{title: 'XXX',key: 'name',type: autoCompleteLoading.value ? Spin : AutoComplete,label: 'item 1',rules: { required: true, message: '请输入XXX', trigger: 'blur' },itemModel: 'value',attrs: {options: [{ label: 'item1', value: 'value1' }],allowClear: true,placeholder: 'XXXXXXXX',style: 'width: 280px',filterOption: (inputValue: string, option: any) => option.value.toLowerCase().indexOf(inputValue.toLowerCase()) >= 0,},},{title: 'XXXXXXX',type: Button,attrs: {type: 'primary',htmlType: 'submit',buttonText: '查询',},},]);
}// index.vue <script setup lang='ts'>
const autoCompleteLoading = ref<boolean>(false);
const dataNameList = ref<{ label: string, value: any }[]>([]);
const configs = useConfigComputed(autoCompleteLoading,dataNameList,
)