前置条件:
vue
版本 v3.3.11
ant-design-vue
版本 v4.1.1
创建动态组件配置文件config.js
import { Input, Textarea, InputNumber, Select, RadioGroup, CheckboxGroup, DatePicker } from 'ant-design-vue';// 表单域组件类型
export const componentsMap = {Text: Input,Textarea,Number: InputNumber,Select,Radio: RadioGroup,Checkbox: CheckboxGroup,DatePicker,
}
创建dynamic-form.vue
组件
<template><div><a-form ref="formRef" :model="formModel"><a-form-item:name="item.field":label="item.label"v-for="item in formSchema":key="item.field"v-bind="item.formItemProps"><component:is="componentsMap[item.component]"v-bind="item.componentProps"v-model:value="formModel[item.field]"/></a-form-item></a-form></div>
</template><script setup>
import { ref, watch, onMounted } from "vue";
import { componentsMap } from "./config.js";const props = defineProps({// 表单项配置schema: {type: Array,default: () => [],},// 表单model配置,一般用于默认值、回显数据model: {type: Object,default: () => ({}),},
});const formRef = ref(null);const formSchema = ref([]);
const formModel = ref({});// 表单初始化
const initForm = () => {formSchema.value = props.schema.map((x) => {return {...x,};});// model初始数据formModel.value = props.schema.reduce((pre, cur) => {if (!pre[cur.field]) {// 表单初始数据(默认值)pre[cur.field] = cur.value;return pre;}}, {});
};onMounted(() => {initForm();// 构建表单项后才回显model值,model会覆盖schema配置的value值watch(() => props.model,(newVal) => {formModel.value = { ...formModel.value, ...newVal };},{immediate: true,deep: true,});
});// 表单验证
const validateFields = () => {return new Promise((resolve, reject) => {formRef.value.validateFields().then((formData) => {resolve(formData);}).catch((err) => reject(err));});
};// 表单重置
const resetFields = (isInit = true) => {// 是否清空默认值if (isInit) {formModel.value = {};}formRef.value.resetFields();
};// 暴露方法
defineExpose({validateFields,resetFields,
});
</script>
使用dynamic-form.vue
组件
<template><div style="padding: 200px"><DynamicForm ref="formRef" :schema="schema" :model="model" /><div style="display: flex; justify-content: center"><a-button @click="handleReset(true)">重置(全部清空)</a-button><a-button style="margin-left: 50px" @click="handleReset(false)">重置</a-button><a-button type="primary" style="margin-left: 50px" @click="handleSubmit">提交</a-button></div></div>
</template><script setup>
import DynamicForm from "@/components/form/dynamic-form.vue";
import { ref } from "vue";
import dayjs from "dayjs";
const formRef = ref(null);const schema = ref([{label: "姓名",field: "name",component: "Text",componentProps: {allowClear: true,showCount: true,maxlength: 20,style: {width: "500px",},},formItemProps: {rules: [{required: true,message: "请输入姓名",trigger: "blur",},],},},{label: "性别",field: "sex",component: "Radio",componentProps: {options: [{ value: 1, label: "男" },{ value: 2, label: "女" },{ value: 3, label: "保密" },],},formItemProps: {rules: [{required: true,message: "请选择性别",trigger: "blur",},],},value: 1,},{label: "生日",field: "birthday",component: "DatePicker",formItemProps: {rules: [{required: true,message: "生日日期不能为空",trigger: "blur",},],},},{label: "兴趣",field: "hobby",component: "Checkbox",componentProps: {options: [{ value: 1, label: "足球" },{ value: 2, label: "篮球" },{ value: 3, label: "排球" },],},},{label: "国家",field: "country",component: "Select",componentProps: {allowClear: true,options: [{ value: 1, label: "中国" },{ value: 2, label: "美国" },{ value: 3, label: "俄罗斯" },],},},{label: "简介",field: "desc",component: "Textarea",componentProps: {allowClear: true,autoSize: {minRows: 4,maxRows: 4,},maxlength: 200,showCount: true,},},
]);
const model = ref({ name: "百里守约" });
// 提交
const handleSubmit = async () => {const formData = await formRef.value.validateFields();if (formData.birthday) {formData.birthday = dayjs(formData.birthday).format("YYYY-MM-DD");}console.log("提交信息:", formData);
};// 重置
const handleReset = (isInit) => {formRef.value.resetFields(isInit);
};
</script>
效果图