form组件内容(具体需要的控件根据所需配置):
<!-- 组件内容 -->
<template>
<a-form
:model="formState"
:rules="formRules"
ref="formRef"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
@finish="handleSubmit"
>
<template v-for="item in formItems" :key="item.name">
<!-- 输入框 -->
<a-form-item v-if="item.type === 'input' || !item.type" :label="item.label" :name="item.name" :extra="item.extra">
<a-input
v-model:value="formState[item.name]"
:placeholder="item.placeholder || `请输入${item.label}`"
:disabled="item.disabled"
:allow-clear="item.allowClear !== false"
/>
</a-form-item>
<!-- 数字输入框 -->
<a-form-item v-else-if="item.type === 'number'" :label="item.label" :name="item.name" :extra="item.extra">
<a-input-number
v-model:value="formState[item.name]"
:placeholder="item.placeholder || `请输入${item.label}`"
:disabled="item.disabled"
:min="item.min"
:max="item.max"
:step="item.step || 1"
style="width: 100%"
/>
</a-form-item>
<!-- 密码框 -->
<a-form-item v-else-if="item.type === 'password'" :label="item.label" :name="item.name" :extra="item.extra">
<a-input-password
v-model:value="formState[item.name]"
:placeholder="item.placeholder || `请输入${item.label}`"
:disabled="item.disabled"
:allow-clear="item.allowClear !== false"
/>
</a-form-item>
<!-- 下拉选择 -->
<a-form-item v-else-if="item.type === 'select'" :label="item.label" :name="item.name" :extra="item.extra">
<a-select
v-model:value="formState[item.name]"
:placeholder="item.placeholder || `请选择${item.label}`"
:disabled="item.disabled"
:allow-clear="item.allowClear !== false"
:options="item.options"
:mode="item.mode"
:show-search="item.showSearch"
/>
</a-form-item>
<!-- 单选按钮 -->
<a-form-item v-else-if="item.type === 'radio'" :label="item.label" :name="item.name" :extra="item.extra">
<a-radio-group v-model:value="formState[item.name]" :disabled="item.disabled" :options="item.options" />
</a-form-item>
<!-- 多选框 -->
<a-form-item v-else-if="item.type === 'checkbox'" :label="item.label" :name="item.name" :extra="item.extra">
<a-checkbox-group v-model:value="formState[item.name]" :disabled="item.disabled" :options="item.options" />
</a-form-item>
<!-- 开关 -->
<a-form-item v-else-if="item.type === 'switch'" :label="item.label" :name="item.name" :extra="item.extra">
<a-switch
v-model:checked="formState[item.name]"
:disabled="item.disabled"
:checked-children="item.checkedChildren || '开启'"
:un-checked-children="item.unCheckedChildren || '关闭'"
/>
</a-form-item>
<!-- 日期选择器 -->
<a-form-item v-else-if="item.type === 'date'" :label="item.label" :name="item.name" :extra="item.extra">
<a-date-picker
v-model:value="formState[item.name]"
:placeholder="item.placeholder || `请选择${item.label}`"
:disabled="item.disabled"
:allow-clear="item.allowClear !== false"
style="width: 100%"
:show-time="item.showTime"
:format="item.format || 'YYYY-MM-DD'"
/>
</a-form-item>
<!-- 时间选择器 -->
<a-form-item v-else-if="item.type === 'time'" :label="item.label" :name="item.name" :extra="item.extra">
<a-time-picker
v-model:value="formState[item.name]"
:placeholder="item.placeholder || `请选择${item.label}`"
:disabled="item.disabled"
:allow-clear="item.allowClear !== false"
style="width: 100%"
:format="item.format || 'HH:mm:ss'"
/>
</a-form-item>
<!-- 日期范围选择器 -->
<a-form-item v-else-if="item.type === 'range'" :label="item.label" :name="item.name" :extra="item.extra">
<a-range-picker
v-model:value="formState[item.name]"
:placeholder="item.placeholder || ['开始日期', '结束日期']"
:disabled="item.disabled"
:allow-clear="item.allowClear !== false"
style="width: 100%"
:show-time="item.showTime"
:format="item.format || 'YYYY-MM-DD'"
/>
</a-form-item>
<!-- 上传组件 -->
<a-form-item v-else-if="item.type === 'upload'" :label="item.label" :name="item.name" :extra="item.extra">
<a-upload
v-model:file-list="formState[item.name]"
:disabled="item.disabled"
:multiple="item.multiple"
:accept="item.accept"
:list-type="item.listType || 'text'"
:before-upload="item.beforeUpload || beforeUpload"
>
<a-button v-if="item.listType !== 'picture-card'">
<upload-outlined></upload-outlined>
点击上传
</a-button>
<div v-else>
<plus-outlined></plus-outlined>
<div style="margin-top: 8px">上传</div>
</div>
</a-upload>
</a-form-item>
<!-- 文本域 -->
<a-form-item v-else-if="item.type === 'textarea'" :label="item.label" :name="item.name" :extra="item.extra">
<a-textarea
v-model:value="formState[item.name]"
:placeholder="item.placeholder || `请输入${item.label}`"
:disabled="item.disabled"
:allow-clear="item.allowClear !== false"
:rows="item.rows || 4"
:show-count="item.showCount"
:maxlength="item.maxlength"
/>
</a-form-item>
</template>
<a-form-item :wrapper-col="{ offset: 6, span: 16 }">
<a-space>
<a-button type="primary" html-type="submit">提交</a-button>
<a-button @click="handleReset">重置</a-button>
</a-space>
</a-form-item>
</a-form>
</template>
<script setup>
import { ref, reactive, watch, onMounted } from "vue";
import { UploadOutlined, PlusOutlined } from "@ant-design/icons-vue";
const props = defineProps({
// 表单配置项
formItems: {
type: Array,
required: true,
default: () => [],
validator: (value) => {
return value.every((item) => item.name && item.label);
},
},
// 初始值
initialValues: {
type: Object,
default: () => ({}),
},
});
const emit = defineEmits(["submit", "reset"]);
const formRef = ref();
const formState = reactive({});
const formRules = reactive({});
// 初始化表单数据和校验规则
const initForm = () => {
props.formItems.forEach((item) => {
// 确保所有字段都被初始化
if (!(item.name in formState)) {
formState[item.name] = props.initialValues[item.name] ?? getDefaultValueByType(item.type);
}
// 初始化校验规则
if (item.rules) {
formRules[item.name] = item.rules;
}
});
};
// 提交表单
const handleSubmit = () => {
formRef.value
.validate()
.then(() => {
emit("submit", formState);
})
.catch((error) => {
console.log("验证失败:", error);
});
};
// 重置表单
const handleReset = () => {
formRef.value.resetFields();
emit("reset");
};
// 上传前处理
const beforeUpload = (file) => {
const isJpgOrPng = file.type === "image/jpeg" || file.type === "image/png";
if (!isJpgOrPng) {
message.error("只能上传 JPG/PNG 文件!");
}
const isLt2M = file.size / 1024 / 1024 < 2;
if (!isLt2M) {
message.error("图片大小不能超过 2MB!");
}
return isJpgOrPng && isLt2M;
};
// 暴露方法给父组件
defineExpose({
validate: () => formRef.value.validate(),
resetFields: () => formRef.value.resetFields(),
getFormData: () => formState,
setFieldsValue: (values) => {
Object.keys(values).forEach((key) => {
if (formState[key] !== undefined) {
formState[key] = values[key];
}
});
},
});
// 根据类型获取默认值
const getDefaultValueByType = (type) => {
switch (type) {
case "checkbox":
return [];
case "switch":
return false;
case "upload":
return [];
case "number":
return null;
case "select":
return undefined;
case "radio":
return undefined;
default:
return undefined;
}
};
// 监听初始值变化
watch(
() => props.initialValues,
(newVal) => {
Object.keys(newVal).forEach((key) => {
if (formState[key] !== undefined) {
formState[key] = newVal[key];
}
});
},
{ deep: true }
);
// 组件挂载时初始化
onMounted(() => {
initForm();
});
</script>
组件的引用和数据处理:
<template>
<DynamicForm :form-items="formItems" :initial-values="initialValues" @submit="handleSubmit"> </DynamicForm>
</template>
const formItems = ref([
{
name: "username",
label: "用户名",
type: "input",
width: "200px",
rules: [
{ required: true, message: "请输入用户名" },
{ min: 3, max: 10, message: "用户名长度在3-10个字符" },
],
},
{
name: "password",
label: "密码",
width: "200px",
type: "password",
rules: [
{ required: true, message: "请输入密码" },
{ pattern: /^[a-zA-Z0-9]{6,12}$/, message: "密码必须是6-12位字母或数字" },
],
},
{
name: "age",
label: "年龄",
width: "200px",
type: "number",
min: 0,
max: 120,
rules: [{ required: false, message: "请输入年龄" }],
},
{
name: "gender",
label: "性别",
width: "200px",
type: "select",
options: [
{ label: "男", value: "male" },
{ label: "女", value: "female" },
],
rules: [{ required: true, message: "请选择性别" }],
},
{
name: "hobbies",
label: "爱好",
width: "200px",
type: "checkbox",
options: [
{ label: "游泳", value: "swimming" },
{ label: "跑步", value: "running" },
{ label: "阅读", value: "reading" },
],
},
{
name: "agree",
label: "同意协议",
width: "200px",
type: "switch",
rules: [
{
validator: (_, value) => (value ? Promise.resolve() : Promise.reject("请同意协议")),
},
],
},
{
name: "birthday",
label: "生日",
width: "200px",
type: "date",
rules: [{ required: true, message: "请选择生日" }],
},
{
name: "resume",
label: "个人简介",
width: "200px",
type: "textarea",
maxlength: 200,
showCount: true,
},
{
name: "avatar",
label: "头像",
width: "200px",
type: "upload",
listType: "picture-card",
accept: "image/*",
},
]);
const initialValues = ref({
username: "张三",
age: 25,
gender: "male",
hobbies: ["reading"],
agree: true,
customField: "", // 确保自定义字段有初始值
});
const handleSubmit = (values) => {
console.log("提交数据:", values);
message.success("表单提交成功");
};
const handleReset = () => {
console.log("表单已重置");
};
最后的效果: