需求描述【省市区三级联动】
组件:Cascader 级联选择器
- 后端需要所选中的地区的名字,如:
['北京市', '北京市', '东城区']
- 获取后端省市区具体列表的接口返回数据:
// 省 - 参数1
[{value: 1,label: '北京市'},...
]
// 市 - 参数2
[{value: 1,label: '北京市'},...
]
// 区 - 参数3
[{value: 1,label: '东城区'},...
]
- 因 element-ui 级联选择器 Cascader 有可以直接获取指定字段的属性,所以直接使用
{value: this.label
}
- 一般情况下都没有问题,偶然发现当省市区有名字一样时出现没有值选中的情况,如:
['北京市', '北京市', '东城区']
- 发现接口也有点问题
组件的回调级别 level 在相同内容的情况下,返回的一直是最上层的节点,猜想可能是 level 的判断的问题,于是只能找源码了
经常不断打断点终于找到的原因所在
- 问题原因:查找节点的方法,不管当前找到了几个节点,都会只返回第一个节点,然后第一个节点肯定是在最前面的所以级别最高 level 是1,这个应该是一个问题,还得想下怎么才能解决自己的问题
// cascader-panel/src/store.jsgetNodeByValue(value) {if (value) {const nodes = this.getFlattedNodes(false, !this.config.lazy).filter(node => (valueEquals(node.path, value) || node.value === value));return nodes && nodes.length ? nodes[0] : null;}return null;}
- 既然组件是必须唯一找到才能正常,那我只能设置一个唯一的值了,把层级【级别】也加到里面去,这样就能显示唯一的,最后返回出来时再把层级【级别】去掉就好了
> 本来是想用 computed 做转换的,结果发现转换有问题,只能用 watch 了,感觉 watch 性能可能会不大好,自己一般情况下能用 computed 解决的绝不用 watch 的 - 组件实现代码:
> 显示的值还是 label
> 比对的值是 value, 确定唯一的值
> 返回到父组件的值要做对应的去格式化处理
<template><div class="the-city-cascader"><el-cascader class="city-cascader" :separator="separator" v-on="$listeners" v-bind="$attrs" v-model="innerValue" :options="cityOptions" :props="cascaderProps" @change="changeHandle"></el-cascader></div>
</template><script>
export default {name: 'the-city-cascader',props: {value: {type: [Array, String],default: () => []},expandTrigger: {type: String,default: 'click'},/*** value 传出的字段组*/valueKey: {type: String,default: 'value',},separator: {type: String,default: ' / ',},},data() {return {cityOptions: [],innerValue: [],isLazy: true,};},computed: {cascaderProps() {return {expandTrigger: this.expandTrigger,value: this.valueKey,lazy: true,lazyLoad: async (node, resolve) => {const { data, level } = nodelet nodes = []const findLevel = level + 1if (level === 0) {nodes = await this.initCityList(findLevel)} else {nodes = await this.initCityList(findLevel, data.code)}nodes.forEach(item => {if (findLevel < 3) {item.leaf = false} else {item.leaf = true}})// 通过调用resolve将子节点数据返回,通知组件数据加载完成resolve(nodes)}}},},created() {this.innerValue = this.value.map((item, itemI) => `${itemI + 1}-${item}`)},methods: {/*** 省1市2区3*/async initCityList(flag, areaId = 0) {const params = { flag, areaId }let result = []try {const { data, status } = await this.axios.get(this.$API.cascaderCity, { params })if (status === 200) {result = (data.result || []).map(item => ({// 显示的值label: item.label,// 值的idvalue: `${flag}-${item.label}`,// 接口参数的值code: item.value,}))}} catch (error) {console.error(error)}return result},changeHandle(val) {const list = val.map(item => {const splitList = item.split('-')if (splitList.length > 0) {return splitList[1]} else {return splitList[0]}})this.$emit('input', list)}}
}
</script><style lang="scss" scoped="">
.the-city-cascader {width: 100%;.city-cascader {width: 100%;}
}
</style>