前言
在做vue2后台管理项目时,有个是使用el-cascader动态选择省市及回显的需求,并且一个页面中有多个el-cascader,在网上搜了很多,大都讲的是一个el-cascader怎么实现动态加载,现在记录一下一个页面中有多个el-cascader应该注意的点。
一、效果图
如图所示,第一行选择了东城、西城和市中心,第二行就不能再选择这三个。
二、使用ref来区分多个el-cascader
:ref=“cascader_${index}
”,其中 index 是 table 的行,要使用模板字符串``,optionsList 是数据源,
v-if=“showCascader” 很重要,没有它编辑的时候就回显不了,limitContent.limits是table的数据源。
代码如下:
<template slot="citiesCode" slot-scope="text, record, index">
<div class="casBox">
<el-cascader
:options="optionsList"
:props="props"
:ref="`cascader_${index}`"
v-if="showCascader"
:append-to-body="false"
v-model="limitContent.limits[index].citiesCode"
clearable
:show-all-levels="false"
@change="changeHandler($event, index)"
></el-cascader>
</div>
</template>
三、动态加载
获取省信息我放到了mounted里面执行了,这里的optionsList 里面默认添加上了全国,并且全国是叶子节点,然后当点击某一个省时,会触发 lazyLoads 方法。
代码如下:
// 获取省信息
getProviceList () {
'接口名称'({id:'00'}).then((res) => {
const result = JSON.parse(res.result)
this.optionsList = [...this.optionsList, ...result.orgInfoList]
})
},
// 动态加载方法
async lazyLoads (node, resolve) {
if (node.level == 1) {
if (!node.data.children && node.data.orgId != '00') {
const res = await this.getCityList(node.data.orgId)
resolve(res)
} else {
if (node.data.orgId != '00') {
if (node.children.length == 0) {
// 此处对于有多个级联选择器特别重要,如果不加判断那么会出现如下情况:当第二个级联选择器点击了天津,
//然后再点击第一个级联选择器,点击天津就不会出现二级面板,因为此时的node.children是空数组(我在这个坑
//里面挣扎了好久o(╥﹏╥)o)。resolve([])执行的数组追加方法。
resolve(node.data.children)
} else {
resolve([])
}
}
}
} else if (node.level == 2) {
resolve([])
}
},
// 通过省份id获取市
getCityList (id) {
return '接口名称'({ id: id })
.then((res) => {
if (res.code == 0) {
const result = JSON.parse(res.result)
const cityList = result.orgInfoList
cityList.forEach((item) => {
item.leaf = true // 因为默认只有两级,市级的就直接设置为叶子结点了
})
this.optionsList.forEach((option, index) => {
if (option.orgId == suprrOrgId) {
option.children = cityList
}
})
return cityList
}
})
.catch((error) => {
// 此处调用resolve 防止接口数据异常时级联选择器一直处于加载状态
return []
})
},
四、选择后置灰
1、置灰是产品提的,为了不重复选择省市。我虽然实现了,但是体验不好,因为选择了就会置灰,没有反悔的可能(-_-||),只能把这一行删除。getCheckedNodes 是获取选中的节点。
代码如下:
changeHandler (val, index) {
const nodesInfo = this.$refs['cascader_' + index].getCheckedNodes()
nodesInfo.forEach((item) => {
this.$set(item.data, 'disabled', true)
})
},
2、上面的方式不好用,好用的来啦~
修改之后的效果如下:
第一行选择后,第二行就不能再选,但当前行选择后可以修改。之前用的是change事件,只要选择某一项就会触发change事件,在change方法里面会把选择的选项置灰;现在使用的是visible-change事件,即下拉框出现/隐藏时触发。
代码如下:
<template slot="citiesCode" slot-scope="text, record, index">
<div class="casBox">
<el-cascader
:options="optionsList"
:props="props"
:ref="`cascader_${index}`"
v-if="showCascader"
:append-to-body="false"
v-model="limitContent.limits[index].citiesCode"
clearable
:show-all-levels="false"
@visible-change="visibleHandler($event, index)"
></el-cascader>
</div>
</template>
visibleHandler (val, index) { // val是 true 或 false
const nodesInfo = this.$refs['cascader_' + index].getCheckedNodes() // 获取当前级联选择器已选择的选项
if (val) {
nodesInfo.forEach((item) => {
this.$set(item.data, 'disabled', false)
if (item.parent) {
this.$set(item.parent.data, 'disabled', false)
}
})
} else {
nodesInfo.forEach((item) => {
this.$set(item.data, 'disabled', true)
})
}
}
五、回显
编辑的时候先请求详情接口,然后调 formatting() 方法来处理省市的回显。
limitHandler (record) {
this.getProviceList()
'接口名称'({ id: record.id }).then(async (res) => {
if (res && res.code === 0) {
const result = res.result
this.limitContent.limits = result
if (result.length > 0) {
result.forEach((item) => {
if (item) {
item.citiesCode = JSON.parse(item.citiesCode)
}
})
await this.formatting() // 调处理回显的方法
this.showCascader = true // 等回显的数据处理完后再显示级联选择器
this.$nextTick(() => { // 此处是为了把回显的数据置灰
this.limitContent.limits.forEach((item, index) => {
this.changeHandler('', index) // 手动调changeHandler方法
})
})
} else {
this.showCascader = true
}
}
})
},
async formatting () {
const limitData = JSON.parse(JSON.stringify(this.limitContent.limits)) // 简单的深复制一下
for (let i = 0; i < limitData.length; i++) {
const citiesList = limitData[i].citiesCode // 如:[[11,11600],[11,2333]]
let list = []
for (let m = 0; m < citiesList.length; m++) {
list.push(citiesList[m][0])
}
list = [...new Set(list)] // 去重,稍微提一下性能。。。
for (let m = 0; m < list.length; m++) {
// 此处循环调接口非常不可取(给产品说了,他能接受 (*^▽^*)),优化啥的以后再说吧-_-||
await this.getCityList(list[m])
}
}
},
findItem (res, arr, id) {
for (let i = 0; i < res.length; i++) {
if (!res[i].children && res[i].orgId == id) {
res[i].children = arr
return
}
if (res[i].children) {
this.findItem(res[i].children, arr, id)
}
}
},
六、删除
删除一行后,要把置灰的市,变为可选的。
// 删除
delHandler (index) {
const _this = this
_this.limitContent.limits.splice(index, 1)
const nodesInfo = _this.$refs['cascader_' + index].getCheckedNodes()
nodesInfo.forEach((item) => {
_this.$set(item.data, 'disabled', false)
if (item.parent) { // 如果是市,那么对应的省也要变为可选
_this.$set(item.parent.data, 'disabled', false)
}
})
}