一. 先上效果
二. 需求背景
省市区县镇乡多级联动, 根据父级懒加载显示子级, 写一个通用组件
- 这个通用组件需要满足双向数据绑定, 直接用v-model, 能表单校验
- 交互形式保持统一的下拉选择选择效果
- 不仅新增可以, 修改也能正常回显
三. 解决思路
vue3 + element-plus
- 定义子组件接收值, 和渲染值;使用属性监听, 然后发布事件, 完成双向绑定
- 使用el-select组件, 然后用empty插槽, 包裹住 el-cascader-panel
- 监听值改变时使用主键查询接口, 根据值获取对应的地区对象
四. 代码逻辑
<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>
最后
当然, 这种不是最优解, 确能快速有效的解决实际问题;