el-select + el-cascader-panel; 完成双向绑定的地区懒加载通用组件

1,420 阅读1分钟

一. 先上效果

20230111_124527.gif

二. 需求背景

省市区县镇乡多级联动, 根据父级懒加载显示子级, 写一个通用组件

  1. 这个通用组件需要满足双向数据绑定, 直接用v-model, 能表单校验
  2. 交互形式保持统一的下拉选择选择效果
  3. 不仅新增可以, 修改也能正常回显

三. 解决思路

vue3 + element-plus

  1. 定义子组件接收值, 和渲染值;使用属性监听, 然后发布事件, 完成双向绑定
  2. 使用el-select组件, 然后用empty插槽, 包裹住 el-cascader-panel
  3. 监听值改变时使用主键查询接口, 根据值获取对应的地区对象

四. 代码逻辑

<template>
  <el-select
    v-model="provinceId"
    placeholder="请选择地区"
    class="selectLazy"
    clearable
    @change="changeSelect"
    :disabled="disabled"
    ref="selectLazyRef"
  >
    <template #prefix>
      <div class="areaName" v-if="areaName">{{areaName}}</div>
      <div class="empty" v-else>请选择地区</div>
    </template>
    <template #empty>
      <el-cascader-panel
        :props="propsCascader"
        v-model="cascaderValue"
        @change="changeCascader"
        ref="cascaderLazyRef"
      />
    </template>
  </el-select>
</template>

<script setup>
import { provinceQuery, provinceById } from "@/api/system/dict/data";

const { proxy } = getCurrentInstance();
const props = defineProps({
  modelValue: [String, Object, Array],
  disabled: {
    type: Boolean,
    default: false
  }
});

const cascaderValue = ref([]);
const selectLazyRef = ref(null);

const areaName = ref("");
const provinceId = ref("");

const propsCascader = {
  label: "name",
  value: "id",
  checkStrictly: true,
  lazy: true,
  lazyLoad: cascaderLazyLoad,
};
watch(
  () => props.modelValue,
  (val) => {
    provinceId.value = val;
    getProvinceById();
  },
  { deep: true, immediate: true }
);

const emit = defineEmits();

// 懒加载接口
function cascaderLazyLoad(node, resolve) {
  if (!node) {
    return false;
  }
  const { level } = node;
  const { id } = node.data;
  //level代表当前点击选择哪一项,,比如0代表第一次进去加载数据,1是选择省后的操作
  if (level == 0) {
    provinceQuery({ pid: 0 })
      .then((res) => {
        res.data.forEach((item) => {
          item.leaf = false;
        });
        resolve(res.data);
      })
      .catch((err) => {
        resolve([]);
      });
  } else {
    if (id) {
      provinceQuery({ pid: id })
        .then((res) => {
          res.data.forEach((item) => {
            item.leaf = level >= 3;
          });
          resolve(res.data);
        })
        .catch((err) => {
          resolve([]);
        });
    } else {
      resolve([]);
    }
  }
}

// 数据 主键 查询
function getProvinceById() {
  if (provinceId.value) {
    provinceById({ id: provinceId.value }).then((res) => {
      areaName.value = res.data.fullPath;
    });
  }
}

// 选择 数据改变
function changeSelect() {;
  cascaderValue.value = [];
  areaName.value = "";
  emit("update:modelValue", null);
  selectLazyRef.value.visible = false;
}

// 级联 数据改变
function changeCascader() {;
  let emitProvinceId =
    cascaderValue.value && Array.isArray(cascaderValue.value)
      ? cascaderValue.value[cascaderValue.value.length - 1]
      : cascaderValue.value; // 所属区域
  emit("update:modelValue", emitProvinceId);
  selectLazyRef.value.visible = false;
}

</script>


最后

当然, 这种不是最优解, 确能快速有效的解决实际问题;