# el-form动态表单校验
动态表单校验示例如下
# 示例
动态表单校验
<template>
<div class="config-form">
<el-form ref="configForm" :model="configForm" :rules="configFormRules">
<el-form-item label-width="128px" required label="资质类型">
<el-radio-group :disabled="configForm.category_condition_has_use == '1'" v-model="configForm.type">
<el-radio :label="1">单选配置</el-radio>
<el-radio :label="2">多选配置</el-radio>
<el-radio :label="3">单项配置</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label-width="128px" label="资质要求名称" prop="condition_name">
<el-input v-model="configForm.condition_name" clearable placeholder="请输入资质要求名称"></el-input>
</el-form-item>
<el-form-item label-width="128px" label="判断类型" prop="judge">
<div class="judge-box">
<div class="row header">
<div class="name filed" v-if="configForm.type !== 3">判断项</div>
<div class="config filed">附件配置</div>
<div class="operation filed">
<span>操作</span><span class="add-project" @click="addProjectItem" v-show="configForm.type !== 3">新增选项</span>
</div>
</div>
<div class="row" v-if="configForm.value_dto_list.length === 0" style="text-align: center;display: block;">暂无配置项,请添加配置项</div>
<template v-for="(item, index) in configForm.value_dto_list">
<div class="row" v-if="!(configForm.type === 3 && index > 0)" :key="index">
<div class="name reset-padding filed" v-if="configForm.type !== 3">
<el-form-item :prop="'value_dto_list.' + index + '.name'" :rules="[{required: true, message: `请输入选项${index + 1}名称`, trigger: 'blur'}, { validator: checkSelectName, trigger: 'blur' }]" :label="`选项${index + 1}名称`">
<el-input :disabled="item.value_has_use == '1'" v-model="item.name" :placeholder="`请输入选项${index + 1}名称`" clearable></el-input>
</el-form-item>
</div>
<div class="config no-padding filed">
<div :class="['row-box radio-box flex-c-s', item.is_need_file == 1 ? 'border' : '' ]">
<el-form-item required class="flex-grow" label="是否上传相关资质">
<el-radio-group :disabled="item.value_has_use == '1'" v-model="item.is_need_file">
<el-radio label="1">是</el-radio>
<el-radio label="0">否</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :required="item.is_need_file == 1" class="right-radio flex-grow" v-show="item.is_need_file == '1'" label="是否必填至少一项">
<el-radio-group :disabled="item.value_has_use == '1'" v-model="item.at_least_one_value">
<el-radio label="1">是</el-radio>
<el-radio label="0">否</el-radio>
</el-radio-group>
</el-form-item>
</div>
<template v-if="item.is_need_file == '1'">
<div class="row-box" v-for="(materialItem, childIndex) in item.value_material_dto_list" :key="childIndex">
<el-form-item :prop="`value_dto_list[${index}].value_material_dto_list[${childIndex}].material_name`" :rules="[{required: true, message: `请输入上传资质附件名称`, trigger: 'change'},{ validator: checkMaterialName, trigger: 'change' }]" class="" label="上传资质附件名称">
<el-input v-model="materialItem.material_name" :disabled="materialItem.value_material_has_use == '1'" clearable placeholder="请输入上传资质附件名称"></el-input>
</el-form-item>
<el-form-item :prop="`value_dto_list[${index}].value_material_dto_list[${childIndex}].is_need_file`" :rules="[{required: true, validator: checkMaterialNeedFile, trigger: 'change' }]" class="" label="是否必填">
<el-radio-group :disabled="materialItem.value_material_has_use == '1'" v-model="materialItem.is_need_file">
<el-radio label="1">是</el-radio>
<el-radio label="2">否</el-radio>
</el-radio-group>
</el-form-item>
<div :class="['delete', materialItem.value_material_has_use == '1' ? 'disabled' : '']" @click="deleteMaterialItem(materialItem.value_material_has_use, index, childIndex)">删除</div>
</div>
</template>
<div class="row-box flex-c-s" v-show="item.is_need_file == '1'">
<div class="add-name-btn" @click="addMaterialItem(index)">新增资质附件名称</div>
</div>
</div>
<div class="operation filed">
<span :class="['delete', item.value_has_use == '1' ? 'disabled' : '']" @click="deleteProject(item.value_has_use, index)">删除</span>
</div>
</div>
</template>
</div>
</el-form-item>
<el-form-item label-width="128px" label="">
<div class="btn-box">
<el-button @click="dialogShow = false">取消</el-button>
<el-button type="primary" @click="saveConfig">保存配置</el-button>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
name: "el-form-nesting",
data() {
//校验选项明重复
const checkSelectName = (rule, value, callback) => {
const arr = this.configForm.value_dto_list.filter(item => item.name === value)
if (arr && arr.length > 1) {
callback("当前选项名已存在,请重新输入!")
} else {
callback()
}
}
//校验资质名称
const checkMaterialName = (rule, value, callback) => {
const targetIndex = rule.field.split(".")[0].replace(/[^0-9]/g, "")
const arr = this.configForm.value_dto_list[targetIndex].value_material_dto_list.filter(item => item.material_name === value)
if (arr && arr.length > 1) {
callback("当前附件资质名称已存在,请重新选择或输入!")
} else {
callback()
}
}
//校验必须有一个必填的时候 至少得一项设置必填
const checkMaterialNeedFile = (rule, value, callback) => {
const targetIndex = rule.field.split(".")[0].replace(/[^0-9]/g, "")
//如果 选了 必填至少一项 则不能全部为 非必填
if (this.configForm.value_dto_list[targetIndex].at_least_one_value == '1') {
const requireList = this.configForm.value_dto_list[targetIndex].value_material_dto_list.filter(item => item.is_need_file == "1")
if (requireList && requireList.length === 0) {
callback("资质附件列表至少一项为必填!")
} else {
callback()
}
} else {
callback()
}
}
//校验单项时 列表数据太多
const checkJudge = (rule, value, callback) => {
callback()
}
return {
checkSelectName, //校验选项名
checkMaterialName, //校验资质名称重复
checkMaterialNeedFile, //校验必须有一个必填的时候 至少得一项设置必填
configForm: {
category_condition_id: null, //类目条件id
category_id: "", //类目id
category_name: "",
is_shop_condition: "1", //是否为店铺资质配置条件:1:是,0:否
condition_type: "1", //条件类型,1:供应商店铺行业资质配置,2:经销商店铺行业资质配置,3:合伙人店铺行业资质配置,101:供应商商品资质配置,201:经销商商品资质配置
type: 1, //条件选项类型:1:单选 2:复选 3:单项选择
condition_name: "", //条件名
value_dto_list: [
{
name: "", //条件值名称
is_need_file: "0", //是否上传相关资质,0:无附件,1:有附件
at_least_one_value: "0", //是否必填至少一项,0:否,1:是
value_material_dto_list: [
{
material_name: "", //材料名称
is_need_file: "1", //是否需要附件,0:无附件,1:附件必填,2:选填
}
], //条件材料列表
del_value_material_id_list: [] //删除的条件材料_id列表
}
],
del_value_id_list: [] //删除的选项id_list
},
configFormRules: {
condition_name: [{ required: true, message: '请输入资质要求名称', trigger: 'change' }],
judge: [{ validator: checkJudge, trigger: 'change' }]
},
}
},
methods: {
/**
* @description: 添加配置项
* @param {*}
* @return {*}
* @author: syx
*/
addProjectItem() {
this.$set(this.configForm.value_dto_list, this.configForm.value_dto_list.length, {
name: "", //条件值名称
is_need_file: "0", //是否上传相关资质,0:无附件,1:有附件
at_least_one_value: "0", //是否必填至少一项,0:否,1:是
category_condition_id: this.configForm.category_condition_id || null,
value_material_dto_list: [
{
material_name: "", //材料名称
is_need_file: "1", //是否需要附件,0:无附件,1:附件必填,2:选填
}
], //条件材料列表
del_value_material_id_list: [] //删除的条件材料_id列表
})
},
/**
* @description: 删除配置项
* @param {*}
* @return {*}
* @author: syx
*/
deleteProject(useStatus, index) {
if (useStatus == '1') {
return
}
if (this.configForm.value_dto_list.length === 1) {
return this.$message.warning("至少得保留一项判断选项")
}
const id = this.configForm.value_dto_list[index].id || ""
if (!id) {
this.configForm.value_dto_list.splice(index, 1)
return
}
},
/**
* @description: 添加配置项
* @param {*}
* @return {*}
* @author: syx
*/
addMaterialItem(index) {
this.$set(this.configForm.value_dto_list[index].value_material_dto_list, this.configForm.value_dto_list[index].value_material_dto_list.length, {
value_id: this.configForm.value_dto_list[index].id || null,
material_name: "", //材料名称
is_need_file: "1", //是否需要附件,0:无附件,1:附件必填,2:选填
})
},
/**
* @description: 删除附件列表项
* @param {*} index
* @param {*} childIndex
* @return {*}
* @author: syx
*/
deleteMaterialItem(useStatus, index, childIndex) {
if (useStatus == '1') {
return
}
if (this.configForm.value_dto_list[index].value_material_dto_list.length === 1) {
return this.$message.warning("至少得保留一项资质附件名称")
}
const id = this.configForm.value_dto_list[index].value_material_dto_list[childIndex].id || ""
if (!id) {
this.configForm.value_dto_list[index].value_material_dto_list.splice(childIndex, 1)
return
}
},
/**
* @description: 保存配置
* @param {*}
* @return {*}
* @author: syx
*/
saveConfig() {
this.$refs.configForm.validate((valid) => {
if (valid) {
let arr = []
for (let i = 0; i < this.configForm.value_dto_list.length; i++) {
let item = this.configForm.value_dto_list[i]
if (item.is_need_file === "0") {
//过滤掉 没有 id的
const list = item.value_material_dto_list.filter(item => {
return item.id
})
let delList = []
for (let i = 0; i < list.length; i++) {
const item = list[i]
if (item.material_name !== "无附件") {
delList.push(item.id)
}
}
// 编辑情况 存在有 id 且 不用删除 则 为无 附件的 情况
if (this.isEdit) {
if (delList.length === 0) {
item.value_material_dto_list = item.value_material_dto_list
} else {
item.del_value_material_id_list = delList
item.value_material_dto_list = []
}
} else {
item.value_material_dto_list = []
}
}
//如果是单项配置,设置选项名为 资质条件名 去除多余被隐藏的判断项
if (this.configForm.type === 3) {
item.name = this.configForm.condition_name
arr.push(JSON.parse(JSON.stringify(item)))
break
}
arr.push(JSON.parse(JSON.stringify(item)))
}
this.configForm.value_dto_list = arr
qualificationAllocationApi.saveCategoryCondition(this.configForm).then(data => {
const msg = this.isEdit ? "编辑资质配置项成功" : "新增资质配置项成功"
this.dialogShow = false
this.$message.success(msg)
if (!this.isEdit) {
//新增成功 刷新侧边列表数据
this.getMenuList()
}
this.getList()
this.getCatetoryMaterials()
})
}
})
},
}
}
</script>
<style lang="less" scoped>
.flex-c-s {
display: flex;
justify-content: flex-start;
align-items: center;
}
.flex-grow {
flex-grow: 1;
}
.config-form {
margin-top: 0;
.el-form {
.el-form-item {
.inline-input {
width: 400px;
}
}
}
.judge-box {
width: 100%;
border: 1px solid #ccc;
.row {
display: flex;
line-height: 40px;
border-bottom: 1px solid #ccc;
&.header {
background: #eaeaeb;
height: 40px;
}
&:last-child {
border: none;
}
.name {
width: 200px;
&.reset-padding {
padding: 10px 12px !important;
}
}
.config {
flex-grow: 1;
&.no-padding {
padding: 0 !important;
}
.row-box {
border-top: 1px solid #ccc;
padding: 0 12px;
padding-top: 10px;
position: relative;
&.radio-box {
&.border::after {
position: absolute;
content: "";
top: 0;
left: 49%;
width: 1px;
height: 100%;
background: #ccc;
}
}
.delete {
position: absolute;
bottom: 0;
right: 24px;
color: red;
cursor: pointer;
&.disabled {
cursor: not-allowed;
color: #909399;
}
}
.el-form-item {
.inline-input {
width: 400px;
}
}
&:first-child {
border: none;
}
.right-radio {
padding-left: 12px;
}
.add-name-btn {
width: 100%;
height: 40px;
border: 1px dashed #409eff;
color: #409eff;
text-align: center;
line-height: 40px;
margin-bottom: 12px;
cursor: pointer;
}
}
}
.operation {
width: 150px;
text-align: center;
position: relative;
.add-project {
margin-left: 12px;
margin-top: 4px;
padding: 0 12px;
display: inline-block;
height: 32px;
border: 1px dashed #409eff;
color: #409eff;
text-align: center;
line-height: 32px;
cursor: pointer;
}
.delete {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: red;
cursor: pointer;
&.disabled {
cursor: not-allowed;
color: #909399;
}
}
}
.filed {
border-left: 1px solid #ccc;
padding: 0 12px;
&:first-child {
border: none;
}
}
}
}
}
</style>
显示代码
# 主要关键代码
:prop="'value_dto_list.' + index + '.name'"
prop一定要注意
可以从rule中拿到 prop值,然后拆字段拿到想要的数据
const targetIndex = rule.field.split(".")[0].replace(/[^0-9]/g, "")
WARNING
变动性太大,仅供参考