需求描述【省市區三級聯動】
- 后端需要所選中的地區的名字,如:
['北京市', '北京市', '東城區']
- 獲取后端省市區具體列表的接口返回數據:
// 省 - 參數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.js
getNodeByValue(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 } = node
let nodes = []
const findLevel = level + 1
if (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,
// 值的id
value: `${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>