src
目录创建components
文件夹,并创建dynamic-form.vue
文件dynamic-form.vue
文件封装动态表单控件,根据传入的参数类型显示指定的表单控件,如文本框、下拉框、多选框等等 <div>
<a-form :form="form" ref="form" :label-col="labelCol" :wrapper-col="wrapperCol">
<div v-for="field in fieldItemOptions" :key="field.key">
<a-form-item :label="field.label" :required="field.required">
<template v-if="field.type === 'text'">template>
<template v-else-if="field.type === 'textarea'">template>
<template v-else-if="field.type === 'select'">template>
<template v-else-if="field.type === 'checkbox'">template>
<template v-else-if="field.type === 'radio'">template>
a-form-item>
div>
a-form>
div>
<template>
<div>
<a-form :form="form" ref="form" :label-col="labelCol" :wrapper-col="wrapperCol">
<div v-for="field in fieldItemOptions" :key="field.key">
<a-form-item :label="field.label" :required="field.required">
<template v-if="field.type === 'text'">
<a-input
v-decorator="[
field.key,
{
rules: [{ required: field.required, message: `${field.label}不能为空` }],
initialValue: field.value
},
]"
:placeholder="`请输入${field.label}`"
>a-input>
template>
<template v-else-if="field.type === 'textarea'">
<a-textarea
v-decorator="[
field.key,
{
rules: [{ required: field.required, message: `${field.label}不能为空` }],
initialValue: field.value
},
]"
:placeholder="`请输入${field.label}`"
:auto-size="{ minRows: field.minRows || 5, maxRows: field.maxRows || 8 }"
:maxLength="field.max || 256"
/>
template>
<template v-else-if="field.type === 'select'">
<a-select
:placeholder="`请选择${field.label}`"
v-decorator="[
field.key,
{
rules: [{ required: field.required, message: `请选择${field.label}` }],
initialValue: field.value
},
]"
>
<a-select-option
v-for="option in field.options"
:key="option.value"
:value="option.value"
>{{option.label}}a-select-option>
a-select>
template>
<template v-else-if="field.type === 'checkbox'">
<a-checkbox-group
v-decorator="[
field.key,
{
rules: [{ required: field.required, message: `请选择${field.label}` }],
initialValue: field.value
},
]"
>
<a-checkbox
v-for="option in field.options"
:key="option.value"
:value="option.value"
:style="{width: field.width}"
>{{option.label}}a-checkbox>
a-checkbox-group>
template>
<template v-else-if="field.type === 'radio'">
<a-radio-group
v-decorator="[
field.key,
{
rules: [{ required: field.required, message: `请选择${field.label}` }],
initialValue: field.value
},
]"
>
<a-radio
v-for="option in field.options"
:key="option.value"
:value="option.value"
>{{option.label}}a-radio>
a-radio-group>
template>
a-form-item>
div>
a-form>
<a-button type="primary" @click="handleSubmit()">提交a-button>
div>
template>
<script>
import { deepClone } from "@/common/utils";
export default {
props: {
// 表单域配置
fieldOptions: {
type: Array,
default: () => []
},
// 编辑时表单回显的默认数据
model: {
type: Object,
default: () => ({})
},
// 标签宽度
labelCol: {
type: Object,
default: () => {
return {
xs: { span: 24 },
sm: { span: 6 }
};
}
},
// 控件宽度
wrapperCol: {
type: Object,
default: () => {
return {
xs: { span: 24 },
sm: { span: 16 }
};
}
}
},
watch: {
fieldOptions: {
handler(fieldOptions) {
this.fieldItemOptions = deepClone(fieldOptions);
this.fieldItemOptions.forEach(c => {
for (const key in this.model) {
if (c.key === key) {
c.value = this.model[key];
}
}
});
},
deep: true,
immediate: true
}
},
data() {
return {
fieldItemOptions: [],
// 表单
form: this.$form.createForm(this)
};
},
methods: {
// 提交表单
handleSubmit() {
this.form.validateFields((err, formData) => {
if (err) {
return;
}
// 提交表单逻辑
});
}
},
created() {}
};
script>
model
为空对象,一般用于新增操作<template>
<div>
<dynamic-form
ref="dynamicForm"
:fieldOptions="fieldOptions"
:model="model"
:labelCol="labelCol"
:wrapperCol="wrapperCol"
>dynamic-form>
div>
template>
<script>
import DynamicForm from "./dynamic-form.vue";
export default {
components: {
DynamicForm
},
data() {
return {
fieldOptions: [
{
label: "姓名",
key: "name",
value: "",
type: "text",
required: true
},
{
label: "性别",
key: "sex",
value: 1,
type: "radio",
required: true,
options: [
{
value: 1,
label: "男"
},
{
value: 2,
label: "女"
}
]
},
{
label: "兴趣爱好",
key: "hobby",
value: [],
type: "checkbox",
required: true,
options: [
{
value: 1,
label: "足球"
},
{
value: 2,
label: "篮球"
},
{
value: 3,
label: "排球"
}
]
},
{
label: "国家",
key: "country",
value: undefined,
type: "select",
required: true,
options: [
{
value: 1,
label: '中国'
},
{
value: 2,
label: '美国'
},
{
value: 3,
label: '俄罗斯'
}
]
},
{
label: "个人简介",
key: "desc",
value: "",
type: "textarea",
required: false
}
],
// 表单默认内容
model: {
// name: '动态表单',
// sex: 2,
// hobby: [1, 2],
// country: 1,
// desc: '这是一个简单的例子'
},
labelCol: {
xs: { span: 24 },
sm: { span: 6 }
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 }
}
};
}
};
script>
5. 若表单有默认内容,需要回显,通常用于编辑操作,只需要给model
对应的键添加值即可
const getRegExp = (re) => {
var flags = "";
if (re.global) flags += "g";
if (re.ignoreCase) flags += "i";
if (re.multiline) flags += "m";
return flags;
};
const isType = (obj, type) => {
if (typeof obj !== "object") return false;
const typeString = Object.prototype.toString.call(obj);
let flag;
switch (type) {
case "Array":
flag = typeString === "[object Array]";
break;
case "Date":
flag = typeString === "[object Date]";
break;
case "RegExp":
flag = typeString === "[object RegExp]";
break;
default:
flag = false;
}
return flag;
};
export const deepClone = (parent) => {
// 维护两个储存循环引用的数组
const parents = [];
const children = [];
const _clone = (parent) => {
if (parent === null) return null;
if (typeof parent !== "object") return parent;
let child, proto;
if (isType(parent, "Array")) {
// 对数组做特殊处理
child = [];
} else if (isType(parent, "RegExp")) {
// 对正则对象做特殊处理
child = new RegExp(parent.source, getRegExp(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (isType(parent, "Date")) {
// 对Date对象做特殊处理
child = new Date(parent.getTime());
} else {
// 处理对象原型
proto = Object.getPrototypeOf(parent);
// 利用Object.create切断原型链
child = Object.create(proto);
}
// 处理循环引用
const index = parents.indexOf(parent);
if (index != -1) {
// 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
return children[index];
}
parents.push(parent);
children.push(child);
for (let i in parent) {
// 递归
child[i] = _clone(parent[i]);
}
return child;
};
return _clone(parent);
};
拓展
vue2 + antd 封装动态表单组件(二)