element-ui表单使用validateField校验多层循环中的字段

目的: 上一节我们介绍了如何在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>
相关推荐
风吹落叶花飘荡37 分钟前
2025 Next.js项目提前编译并在服务器
服务器·开发语言·javascript
加减法原则37 分钟前
Vue3 组合式函数:让你的代码复用如丝般顺滑
前端·vue.js
yanlele1 小时前
我用爬虫抓取了 25 年 6 月掘金热门面试文章
前端·javascript·面试
天若有情6731 小时前
React、Vue、Angular的性能优化与源码解析概述
vue.js·react.js·angular.js
烛阴2 小时前
WebSocket实时通信入门到实践
前端·javascript
草巾冒小子2 小时前
vue3实战:.ts文件中的interface定义与抛出、其他文件的调用方式
前端·javascript·vue.js
DoraBigHead2 小时前
你写前端按钮,他们扛服务器压力:搞懂后端那些“黑话”!
前端·javascript·架构
eggcode2 小时前
Vue+Openlayers加载OSM、加载天地图
vue.js·openlayers·webgis
前端世界3 小时前
鸿蒙UI开发全解:JS与Java双引擎实战指南
javascript·ui·harmonyos
@Dream_Chaser4 小时前
uniapp ruoyi-app 中使用checkbox 无法选中问题
前端·javascript·uni-app