根据配置的item列表渲染form表单:
子组件
动态配置Form表单组件:
子组件接收的props.config
中元素配置讲解:
[
{
label: "姓名",
prop: "name",
type: "el-input",
hidden: false, // 元素是否需要隐藏
contentSlotName: 'myContent', // 自定义的插槽
slotName: 'mySlot', // 在el-form-item中的插槽
attrs: { // 表单元素自身的属性
disabled: false,
placeholder: "请输入姓名",
},
rules: { required: true, message: "请输入姓名", trigger: "blur" }, // 表单自己的校验规则
listeners: { // 表单元素自身的事件
input: (value) => {
this.formData.name = value;
},
},
elSlots: { // 表单元素如el-input中的prepend或者append,可以输入组件名称或者字符串、数字
// 1. 组件对象形式
prepend: {
autocompleteSlotName: 'myPrepend'
},
// 2.字符串形式的append
append: ".com",
},
}
]
dynamic-form.vue:
<template>
<el-form
:model="formData"
ref="dynamicForm"
v-if="formItemList.length > 0"
:label-width="labelWidth"
>
<div class="dynamic-form-box">
<template v-for="(item, idx) in formItemList" :key="idx">
<template v-if="item.contentSlotName">
<slot
:item="item"
:index="idx"
:key="item.prop + idx"
:name="item.contentSlotName"
></slot>
</template>
<template v-else>
<el-form-item
v-if="!item.hidden"
:label="item.label"
:prop="item.prop"
:key="item.prop + idx"
:class="item.className"
:style="getStyle(item.style, item.width)"
:rules="item.rules || { required: false }"
>
<template v-if="item.slotName">
<slot
:item="item"
:index="idx"
:name="item.slotName"
:key="item.prop + idx"
></slot>
</template>
<component
v-else
:is="item.type"
:key="item.prop + idx"
v-bind="item.attrs || {}"
v-on="item.listeners || {}"
v-model="formData[item.prop]"
>
<!-- 处理组件内部插槽 -->
<template v-for="(slot, slotName) in item.elSlots || {}"
v-slot:[slotName]
:key="slotName"
>
<div
v-if="typeof slot === 'string' || typeof slot === 'number'"
v-html="slot"
></div>
<template v-else-if="slot.autocompleteSlotName">
<slot :name="slot.autocompleteSlotName" :item="item"></slot>
</template>
</template>
<!-- select选项 -->
<template v-if="item.type === 'el-select'">
<el-option
v-for="option in item.attrs.options"
:key="option.value"
:label="option.label"
:value="option.value"
></el-option>
</template>
<!-- checkbox-group选项 -->
<template v-if="item.type === 'el-checkbox-group'">
<el-checkbox
v-for="option in item.attrs.options"
:key="option.value"
:value="option.value"
>{{ option.label }}</el-checkbox
>
</template>
<!-- radio-group选项 -->
<template v-if="item.type === 'el-radio-group'">
<el-radio
v-for="option in item.attrs.options"
:key="option.value"
:label="option.value"
:value="option.value"
>{{ option.label }}</el-radio
>
</template>
</component>
</el-form-item>
</template>
</template>
</div>
</el-form>
</template>
<script>
export default {
name: "FormTemplate",
components: {},
props: {
labelWidth: {
type: String,
default: "100px",
},
config: {
type: Array,
default: () => [],
},
modelValue: {
type: Object,
default: null,
},
},
data() {
return {};
},
computed: {
formData() {
return this.modelValue || {};
},
// 添加计算属性
formItemList() {
return (
this.config || []
);
},
},
watch: {
formData: {
deep: true,
handler(newVal) {
this.$emit("update:modelValue", newVal);
},
},
},
methods: {
getStyle(style = {}, width = "100%") {
return {
...style,
width: style.width || width,
};
},
validateForm() {
return new Promise((resolve, reject) => {
this.$refs.dynamicForm.validate((valid) => {
resolve(valid);
});
});
},
},
};
</script>
<style scoped lang="scss">
.dynamic-form-box {
display: flex;
align-items: center;
justify-content: flex-start;
flex-wrap: wrap;
}
</style>
在父组件中件使用
模板
<template>
<div class="dynamic-form-container">
<dynamic-form :config="config" v-model="formData" labelWidth="200" ref="dynamicFormRef">
<template #title1="{ item }">
<p class="title">{{ item.title }}</p>
</template>
<template #title2="{ item }">
<p class="title">{{ item.title }}</p>
</template>
<template #title3="{ item }">
<p class="title">{{ item.title }}</p>
</template>
<template #lineFeed>
<p class="line-feed"></p>
</template>
<template #pwdAppend="{ item }">
<span class="my-append">{{ item.appendText }}</span>
</template>
<template #subAccountAppend>
<span class="my-append">账号</span>
</template>
</dynamic-form>
<el-button @click="submitForm">提交</el-button>
</div>
</template>
JS
<script>
import dynamicForm from "./components/dynamic-form.vue";
export default {
name: "FormTemplate",
components: {
dynamicForm,
},
data() {
return {
formData: {
companyId: "bb123d50f96d45558270260a22612",
companyName: "test公司名称",
englishName: "en",
mainAccount: "admin@qcc.com",
password: "",
phone: "",
scene: "",
passwordPolicy: "0",
passwordResetDays: 90,
userInactiveTimeout: 15,
mfa: "1",
isInternal: "1",
isApiService: "0",
subAccountCount: "10",
startDate: "",
role: ["1"],
startDate: ""
},
};
},
computed: {
config() {
const list = [
{
contentSlotName: "title1",
title: "公司基本信息",
},
{
label: "公司ID",
prop: "companyId",
type: "el-input",
hidden: false,
attrs: {
disabled: true,
placeholder: "请输入公司ID",
},
},
{
label: "公司名称",
prop: "companyName",
type: "el-input",
hidden: false,
attrs: {
disabled: false,
placeholder: "请输入公司名称",
},
},
{
label: "英文名称",
prop: "englishName",
type: "el-input",
hidden: false,
attrs: {
disabled: false,
placeholder: "请输入英文名称",
},
},
{
label: "主账号登录名",
prop: "mainAccount",
type: "el-input",
hidden: false,
attrs: {
disabled: false,
placeholder: "请输入主账号登录名",
},
},
{
label: "密码",
prop: "password",
type: "el-input",
hidden: false,
appendText: "生成密码",
attrs: {
placeholder: "请输入密码",
showPassword: true,
},
elSlots: {
append: {
autocompleteSlotName: "pwdAppend",
},
},
},
{
label: "联系电话",
prop: "phone",
type: "el-input",
hidden: false,
attrs: {
disabled: false,
placeholder: "请输入联系电话",
},
},
{
label: "应用场景",
prop: "scene",
type: "el-input",
hidden: false,
attrs: {
rows: 4,
disabled: false,
type: "textarea",
placeholder: "请输入应用场景",
},
},
{
contentSlotName: "title2",
title: "账号设置",
},
{
label: "密码策略",
prop: "passwordPolicy",
type: "el-radio-group",
hidden: false,
attrs: {
disabled: false,
options: [
{ label: "增强密码", value: "1" },
{ label: "普通密码", value: "0" },
],
"popper-options": {
modifiers: [
{ name: "computeStyles", options: { adaptive: false } },
],
},
},
},
{
label: "账号密码强制重置天数",
prop: "passwordResetDays",
type: "el-input",
hidden: false,
width: "390px",
attrs: {
disabled: true,
placeholder: "请输入账号密码强制重置天数",
},
elSlots: {
append: "天",
},
},
{
contentSlotName: "lineFeed",
},
{
label: "用户未操作时超时时间",
prop: "userInactiveTimeout",
type: "el-input",
hidden: false,
width: "390px",
attrs: {
disabled: true,
placeholder: "请输入用户未操作时超时时间",
},
elSlots: {
append: "分钟",
},
},
{
label: "二次认证",
prop: "mfa",
type: "el-radio-group",
hidden: false,
attrs: {
disabled: false,
options: [
{ label: "Google MFA", value: "1" },
{ label: "无", value: "0" },
],
},
},
{
label: "是否是内部账号",
prop: "isInternal",
type: "el-radio-group",
hidden: false,
attrs: {
disabled: false,
options: [
{ label: "是", value: "1" },
{ label: "否", value: "0" },
],
},
},
{
label: "是否开启API服务",
prop: "isApiService",
type: "el-radio-group",
hidden: false,
attrs: {
disabled: true,
options: [
{ label: "是", value: "1" },
{ label: "否", value: "0" },
],
},
},
{
label: "子账号数量",
prop: "subAccountCount",
type: "el-input",
hidden: false,
attrs: {
placeholder: "请输入子账号数量",
},
rules: {
required: true,
trigger: ["blur", "change"],
validator: (rule, value, callback) => {
if (!value) {
callback(new Error("请输入子账号数量"));
} else if (isNaN(value)) {
callback(new Error("请输入数字"));
} else if (value < 1) {
callback(new Error("请输入大于0的数字"));
} else {
callback();
}
},
},
elSlots: {
append: {
autocompleteSlotName: "subAccountAppend",
},
},
},
{
label: "开始日期",
prop: "startDate",
type: "el-date-picker",
hidden: false,
attrs: {
type: "date",
placeholder: "请选择开始日期",
valueFormat: "yyyy MM dd",
popper: {
modifiers: [
{ name: "computeStyles", options: { adaptive: false } },
],
},
"popper-options": {
modifiers: [
{ name: "computeStyles", options: { adaptive: false } },
],
},
},
listeners: {
change: (value) => {
this.formData.startDate = value;
},
},
},
{
contentSlotName: "title3",
title: "权益与单价",
},
{
label: "角色",
prop: "role",
type: "el-select",
hidden: false,
attrs: {
placeholder: "请选择角色",
clearable: true,
multiple: true,
options: [
{ label: "内部服务", value: "1" },
{ label: "公共模块", value: "2" },
],
"popper-options": {
modifiers: [
{ name: "computeStyles", options: { adaptive: false } },
],
},
},
},
];
return list;
},
},
watch: {
formData: {
deep: true,
handler(newVal) {
console.log(1, newVal);
},
},
},
methods: {
submitForm() {
this.$refs.dynamicFormRef.validateForm().then((flag) => {
if (flag) {
console.log("表单验证通过");
} else {
console.log("表单验证不通过");
}
});
},
},
};
</script>