vue项目 解决有多个el-select需要联动时,调用混乱

vue项目 解决有多个el-select需要联动时,调用混乱

痛点:写一堆 handleA/handleB/handleC 重复代码、层级越多越乱;切换、回显两套逻辑分开维护容易出错。

核心思路:用 watch 监听选中值,统一触发下级数据加载 ,所有联动逻辑收敛一处,不用在每个 @change 写业务。

整体设计

  1. 下拉选中值统一放在 form 对象;
  2. 每一级选项数组、loading统一命名规范;
  3. watch 监听上级选中值自动刷新下级,清空下级值;
  4. 抽通用异步加载方法,loading、异常处理;
  5. change 只做一件事:赋值(甚至可以不写 change,v-model 自动触发 watch);
  6. 编辑回显串行执行,复用同一套加载逻辑,无重复代码。
  • html
html 复制代码
  <!-- A一级 -->
  <el-select
    v-model="form.a"
    placeholder="请选择A"
    :loading="loading.a"
  >
    <el-option v-for="item in opt.a" :key="item.id" :label="item.name" :value="item.id"/>
  </el-select>

  <!-- B二级依赖A -->
  <el-select
    v-model="form.b"
    placeholder="请选择B"
    :loading="loading.b"
    :disabled="!form.a || opt.b.length === 0"
  >
    <el-option v-for="item in opt.b" :key="item.id" :label="item.name" :value="item.id"/>
  </el-select>

  <!-- C三级依赖B -->
  <el-select
    v-model="form.c"
    placeholder="请选择C"
    :loading="loading.c"
    :disabled="!form.b || opt.c.length === 0"
  >
    <el-option v-for="item in opt.c" :key="item.id" :label="item.name" :value="item.id"/>
  </el-select>
  • data
js 复制代码
data() {
  return {
    form: {
      a: "",
      b: "",
      c: ""
    },
    // 各级下拉选项
    opt: {
      a: [],
      b: [],
      c: []
    },
    // 加载状态,防止重复请求
    loading: {
      a: false,
      b: false,
      c: false
    },
    // 层级顺序,用于批量清空下级
    levelList: ["a", "b", "c"]
  }
},
created() {
  // 初始化加载一级A
  this.getList("a")
}
  • methods
js 复制代码
methods: {
  /**
   * 通用请求下拉数据
   * @param level 当前层级 a/b/c
   * @param parentVal 上级选中值
   */
  async getList(level, parentVal = null) {
    // 接口映射,新增层级只在这里加一行
    const apiMap = {
      a: this.$api.getAList,
      b: this.$api.getBList,
      c: this.$api.getCList
    }
    const api = apiMap[level]
    if (!api) return

    this.loading[level] = true
    try {
      let res
      if (level === "a") {
        // 一级无父参数
        res = await api()
      } else {
        // 二级/三级带上级ID请求
        res = await api({ parentId: parentVal })
      }
      this.opt[level] = res.data || []
    } catch (err) {
      console.error(`${level}下拉请求失败`, err)
      this.opt[level] = []
    } finally {
      this.loading[level] = false
    }
  },

  /**
   * 清空当前层级之后所有下级的值和选项
   * @param level 当前操作层级
   */
  clearChild(level) {
    const index = this.levelList.indexOf(level)
    if (index === -1) return
    // 遍历下级全部清空
    for (let i = index + 1; i < this.levelList.length; i++) {
      const key = this.levelList[i]
      this.form[key] = ""
      this.opt[key] = []
    }
    // 清除表单校验红提示
    this.$refs.form?.clearValidate()
  },

  /**
   * 编辑回显入口,串行加载下级数据
   */
  async setEditRow(row) {
    const { a, b, c } = row
    // 赋值A,watch自动清空并请求B
    this.form.a = a
    await this.$nextTick()
    await this.getList("b", a)

    // 赋值B,watch自动清空并请求C
    this.form.b = b
    await this.$nextTick()
    await this.getList("c", b)

    this.form.c = c
  }
}
  • watch
js 复制代码
watch: {
  "form.a"(val) {
    // 切换A,清空B、C
    this.clearChild("a")
    if (!val) return
    // 请求B列表
    this.getList("b", val)
  },
  "form.b"(val) {
    // 切换B,清空C
    this.clearChild("b")
    if (!val) return
    // 请求C列表
    this.getList("c", val)
  }
}