目的: 上一节我们介绍了如何在element-ui表单中书写循环验证的逻辑,也简单提到如何使用validateField 对部分表单字段进行验证,且被验证的字段为数组对象中的一个字段,并给了一段代码,如下:
js
let lists = this.formData.targetList;
targetList.forEach((item, index) => {
this.$refs.formDialogRef.validateField(
`targetList.${index}.targetChildField`
);
});
其实有经验的同学或许已经知道了这段代码的意思,下面我来一个具体实例介绍一下。
需求场景:


产品需求文档提到,这里的会员等级不能重复选择,所以需要我们开发人员在选择会员等级时,对其选择的值进行验证。
由于我们的验证风格都是在表单字段下方报错提示,而不是在提交的代码中弹框提示,所以就用到了validateField
对部分表单字段进行校验。
使用validate
对整个表单进行校验,虽然也能达到效果,但会触发所有字段的校验,个人感觉体验感不好。
关键:
html
<template>
<KiFormDialog :show.sync="form.show" :title="'设置会员价格'" v-model="form.data" :confirm="submitFormData" ref="formRef"
:loading="form.loading">
<template>
<el-form-item label="商品售价">
{{ form.data.currentPrice | $cent2yuan({ symbol: "¥" }) }}
</el-form-item>
<el-form-item label="设置会员价格" prop="memberProductPriceDetailList" verify>
<el-button @click="addItem" icon="el-icon-circle-plus-outline" type="primary" size="small">
新增
</el-button>
</el-form-item>
<div class="mt20"></div>
<div class="flex mb20" v-for="(item, index) in form.data.memberProductPriceDetailList" :key="index">
<div class="one flex">
<el-form-item label="会员等级" :prop="`memberProductPriceDetailList.${index}.memberGradeId`"
:verify="validateMemberGradeId">
<MemberGradeSelect v-model="item.memberGradeId" placeholder="会员等级" @change="handleGradeSelectChange">
</MemberGradeSelect>
</el-form-item>
<el-form-item label="会员价格" :prop="`memberProductPriceDetailList.${index}.price`"
:verify="{ gte: 0, maxDecimalLength: 2, lte: 999999999 }">
<PriceInput v-model="item.price" placeholder="请输入" />
</el-form-item>
</div>
<div class="pl20 opts">
<div class="flex row-center">
<el-button @click="removeItem(index)" icon="el-icon-circle-close" type="danger" size="small">
删除
</el-button>
</div>
</div>
</div>
</template>
<div slot="footer" class="dialog-footer">
<el-button @click="form.show = false"> 取 消</el-button>
<el-button type="primary" :loading="form.loading" @click="submitFormData">
确 定
</el-button>
</div>
</KiFormDialog>
</template>
<script>
export default {
methods: {
validateMemberGradeId(rule, val, callback) {
let temp = this.form.data?.memberProductPriceDetailList;
if (temp?.length) {
let filterList = this.$lo.filter(
temp,
(item) => item.memberGradeId == val
);
if (filterList.length > 1) {
callback(Error("请勿重复选择会员等级"));
} else {
callback();
}
}
},
handleGradeSelectChange() {
let tm = setTimeout(() => {
clearTimeout(tm);
let lists = this.form.data.memberProductPriceDetailList;
lists.forEach((item, index) => {
this?.$refs.formRef?.$refs.elFormRef.validateField(
`memberProductPriceDetailList.${index}.memberGradeId`
);
});
}, 120);
},
}
}
</script>
这里啰嗦一下,大家不要过度关注 this?.$refs.formRef?.$refs.elFormRef
,被它迷糊了,这个是因为我们用到的框架KiFormDialog导致,其实就是取的弹框中表单里的el-form的ref对象名称。
this.$lo 为绑定到vue实例上的lodashjs方法。
至于代码中的verify,这是我们公司的几位小伙伴基于element-ui开发的一款表单验证插件,在npm中搜索 element-verify 可查看其文档,lement-verify
装了很多我们在后台开发中需要用到的校验规则,简单实用,有兴趣的可以下载试试。
全部代码:
html
<template>
<div class="app-container">
<el-form ref="listFilterRef" :model="list.filter" inline class="list-filter">
<el-form-item prop="productName">
<el-input v-model.trim="list.filter.productName" placeholder="商品名称" clearable />
</el-form-item>
<el-form-item prop="subMchId">
<SubMchSelect v-model="list.filter.subMchId" placeholder="店铺名称" clearable />
</el-form-item>
<el-form-item prop="merchantId">
<MerchantSelect v-model="list.filter.merchantId" placeholder="商户名称" clearable />
</el-form-item>
<el-form-item prop="status">
<StatusFilter v-model="list.filter.status" />
</el-form-item>
<el-form-item>
<el-button @click="reset">重置</el-button>
</el-form-item>
</el-form>
<div class="table-operation">
<span> </span>
<el-pagination v-bind="elPaginationProps" :total="list.total" :currentPage.sync="list.filter.pageNo"
:pageSize.sync="list.filter.pageSize" />
</div>
<el-table v-loading="list.loading" :data="list.data" v-bind="tableProp">
<el-table-column type="index" width="50" />
<el-table-column label="商品ID" prop="productId" />
<el-table-column label="商品名称" prop="productName" />
<el-table-column label="商户名称" prop="merchantName" />
<el-table-column label="店铺名称" prop="subMchName" />
<el-table-column label="商品基础售价(元)" prop="currentPrice">
<template slot-scope="{ row }">
{{ row.currentPrice | $cent2yuan({ symbol: "¥" }) }}
</template>
</el-table-column>
<el-table-column label="会员价格(元)">
<template slot-scope="{ row }">
<el-tooltip placement="top">
<div v-if="
row.memberProductPriceDetailList &&
row.memberProductPriceDetailList.length > 0 &&
memberGradeList &&
memberGradeList.length > 0
" slot="content">
<div v-for="(item, index) in row.memberProductPriceDetailList" :key="index">
{{
$value2label(item.memberGradeId, memberGradeList, "id", "name")
}}
- {{ item.price | $cent2yuan({ symbol: "¥" }) }}
</div>
</div>
<div class="ellipsis-3" v-if="
row.memberProductPriceDetailList &&
row.memberProductPriceDetailList.length > 0 &&
memberGradeList &&
memberGradeList.length > 0
">
<div v-for="(item, index) in row.memberProductPriceDetailList" :key="index">
{{
$value2label(item.memberGradeId, memberGradeList, "id", "name")
}}
- {{ item.price | $cent2yuan({ symbol: "¥" }) }}
</div>
</div>
</el-tooltip>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" />
<el-table-column label="状态" width="80" align="center">
<template slot-scope="{ row }">
<el-tag type="danger" v-if="row.status == 0" size="mini">停用</el-tag>
<el-tag type="success" v-if="row.status == 1" size="mini">
启用
</el-tag>
</template>
</el-table-column>
<el-table-column label="是否开启会员价格" width="160" align="center">
<template slot-scope="{ row: { id, showStatus } }">
<KiPopSwitch v-bind="popSwitchProps(showStatus)" @change="updateStatus({ id, status: showStatus ^ 1 })" />
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template slot-scope="{ row }">
<auth-button @click="r({ id: row.id })" name="设置会员价格" v-if="row.showStatus" />
</template>
</el-table-column>
</el-table>
<KiFormDialog :show.sync="form.show" :title="'设置会员价格'" v-model="form.data" :confirm="submitFormData" ref="formRef"
:loading="form.loading">
<template>
<el-form-item label="商品ID">
{{ form.data.productId }}
</el-form-item>
<el-form-item label="商品名称">
{{ form.data.productName }}
</el-form-item>
<el-form-item label="是否显示原价">
<el-tag type="danger" v-if="form.data.openOriginalCost == 0" size="mini">否</el-tag>
<el-tag type="success" v-if="form.data.openOriginalCost == 1" size="mini">
是
</el-tag>
</el-form-item>
<el-form-item label="商品原价">
{{ form.data.originalCost | $cent2yuan({ symbol: "¥" }) }}
</el-form-item>
<el-form-item label="商品售价">
{{ form.data.currentPrice | $cent2yuan({ symbol: "¥" }) }}
</el-form-item>
<el-form-item label="设置会员价格" prop="memberProductPriceDetailList" verify>
<el-button @click="addItem" icon="el-icon-circle-plus-outline" type="primary" size="small">
新增
</el-button>
</el-form-item>
<div class="mt20"></div>
<div class="flex mb20" v-for="(item, index) in form.data.memberProductPriceDetailList" :key="index">
<div class="one flex">
<el-form-item label="会员等级" :prop="`memberProductPriceDetailList.${index}.memberGradeId`"
:verify="validateMemberGradeId">
<MemberGradeSelect v-model="item.memberGradeId" placeholder="会员等级" @change="handleGradeSelectChange">
</MemberGradeSelect>
</el-form-item>
<el-form-item label="会员价格" :prop="`memberProductPriceDetailList.${index}.price`"
:verify="{ gte: 0, maxDecimalLength: 2, lte: 999999999 }">
<PriceInput v-model="item.price" placeholder="请输入" />
</el-form-item>
</div>
<div class="pl20 opts">
<div class="flex row-center">
<el-button @click="removeItem(index)" icon="el-icon-circle-close" type="danger" size="small">
删除
</el-button>
</div>
</div>
</div>
</template>
<div slot="footer" class="dialog-footer">
<el-button @click="form.show = false"> 取 消</el-button>
<el-button type="primary" :loading="form.loading" @click="submitFormData">
确 定
</el-button>
</div>
</KiFormDialog>
</div>
</template>
<script>
import pageMixin from "@/utils/pageMixin";
import useAdmateAdapter from "@/utils/useAdmateAdapter";
import SubMchSelect from "@/components/SubMchSelect/index";
import MemberGradeSelect from "@/components/MemberGradeSelect/index";
export default {
mixins: [pageMixin],
components: {
SubMchSelect,
MemberGradeSelect,
},
setup: () =>
useAdmateAdapter({
urlPrefix: "sot-admin-api/memberProductPrice/ticket",
axiosConfig: {
updateStatus: {
url: "changeShowStatus",
},
},
}),
data() {
return {
memberGradeList: [],
};
},
created() {
this.getMemberGrade();
},
methods: {
getMemberGrade() {
this.$POST("/sot-admin-api/member/mb-level/queryForList", {})
.then(({ data }) => {
this.memberGradeList = data || [];
})
.finally((_) => { });
},
validateMemberGradeId(rule, val, callback) {
let temp = this.form.data?.memberProductPriceDetailList;
if (temp?.length) {
let filterList = this.$lo.filter(
temp,
(item) => item.memberGradeId == val
);
if (filterList.length > 1) {
callback(Error("请勿重复选择会员等级"));
} else {
callback();
}
}
},
handleGradeSelectChange() {
let tm = setTimeout(() => {
clearTimeout(tm);
let lists = this.form.data.memberProductPriceDetailList;
lists.forEach((item, index) => {
this?.$refs.formRef?.$refs.elFormRef.validateField(
`memberProductPriceDetailList.${index}.memberGradeId`
);
});
}, 120);
},
submitFormData() {
this?.$refs.formRef?.$refs.elFormRef.validate().then(() => {
this.$set(this.form, "loading", true);
this.$POST(
"sot-admin-api/memberProductPrice/ticket/setMemberPrice",
this.form.data.memberProductPriceDetailList
)
.then((res) => {
this.$swal.success("设置成功");
this.$set(this.form, "show", false);
})
.finally(() => {
this.$set(this.form, "loading", false);
});
});
},
// 添加一项
addItem() {
if (!this.form?.data?.memberProductPriceDetailList) {
this.$set(this.form.data, "memberProductPriceDetailList", []);
}
this.form.data.memberProductPriceDetailList.push({
memberProductId: this.form.data.id,
productId: this.form.data.productId,
});
},
// 移除一项
removeItem(index) {
this.form.data.memberProductPriceDetailList.splice(index, 1);
},
},
};
</script>
<style lang="scss" scoped></style>