目前Vue Element的 el-cascader 级联选择器,多选或者选择任意一级,需要点击左侧的checkbox才能选中。
目标:点击label选中,已选中状态再次点击label取消选中
有两种方式实现
方法一:通过点击事件
<!-- 改造前 -->
<template>
<div>
<el-cascader
v-model="cascaderValue"
:options="options"
:props="{ multiple: true,checkStrictly:true }"
clearable>
</el-cascader>
{{cascaderValue}}
</div>
</template>
改造点
- 可以通过 scoped slot 对级联选择器的备选项的节点内容进行自定义
- 添加点击事件 @click="onClick(node)" 将选中的数据进行处理
- 区分单选/多选 单选:
- 将选中的数据赋值到v-model
多选
- 将之前选择的数据保存
- 处理:未选勾选选中/已选取消选中
- 将选中的数据赋值到v-model
<!-- 改造后 -->
<template>
<div>
<el-cascader
v-model="cascaderValue"
:options="options"
:props="{ multiple: true,checkStrictly:true }"
clearable>
<!-- 重点:第一步,增加slot-scope -->
<template slot-scope="{ node, data }">
<span style="display:block" @click="onClick(node)">{{ data.label }}</span>
</template>
</el-cascader>
{{cascaderValue}}
</div>
</template>
<script>
export default {
name: 'Test',
data () {
return {
cascaderValue: [],
options: [
{
value: 1,
label: '东南',
disabled: true,
children: [
{
value: 2,
label: '上海',
disabled: true,
children: [
{ value: 3, label: '普陀' },
{ value: 4, label: '黄埔' },
{ value: 5, label: '徐汇' }
]
}
]
},
{
value: 6,
label: '西北',
disabled: true,
children: [
{ value: 7, label: '陕西' },
{ value: 8, label: '新疆' }
]
},
{
value: 9,
label: '东北'
}
]
}
},
methods: {
// 单选的情况
// onClick (node) {
// if (!node.isDisabled) {
// this.cascaderValue = node.path
// }
// },
// 多选的情况
onClick (node) {
if (node.isDisabled) {
return
}
// 定义一个数组,用于处理数据
const data = []
// 将之前选择的数据保存
if (this.cascaderValue.length) {
this.cascaderValue.forEach(n => {
data.push(n)
console.log('之前的值', data)
})
}
// 处理:
// 未选状态:将当前点击的node.path(是一个数组[1,2,3]),添加到data中
// 已选状态:已选中的从data数组中查找node.path删除
if (!node.checked) {
data.push(node.path)
console.log('未选->选中', data)
} else {
if (data.length) {
// 将[ [ 1, 2, 3 ], [ 1, 2, 4 ], [ 1, 2, 5 ] ]数据转为["1,2,3", "1,2,4", "1,2,5"],因为数组不能比较
const map = data.map(n => n.join(','))
// 找出node.path删除
const idx = map.findIndex(q => q === node.path.join(','))
if (idx > -1) {
data.splice(idx, 1)
}
console.log('已选->取消选中', data)
}
}
// 最后双向绑定v-model的数据
this.cascaderValue = data
}
}
}
</script>
多选存在问题:当使用el-cascader属性 clearable 的时候,会出现删除错误的问题,因为级联选择器绑定的数据是按照指定的格式添加。
因此需要在 “数据赋值到v-model” 以前对数据进行处理
- 将之前选择的数据保存
- 处理:未选勾选选中/已选取消选中
将options树形数据转为数组数据
将满足条件的数据过滤出来
- 将选中的数据赋值到v-model
<template>
<div>
<!-- 多选 -->
<el-cascader
v-model="cascaderValue"
:options="options"
:props="{ multiple: true,checkStrictly:true }"
clearable>
<!-- 重点:增加slot-scope -->
<template slot-scope="{ node, data }">
<span style="display:block" @click="onClick(node)">{{ data.label }}</span>
</template>
</el-cascader>
{{cascaderValue}}
</div>
</template>
<script>
export default {
name: 'Test',
data () {
return {
cascaderValue: [],
options: [
{
value: 1,
label: '东南',
disabled: true,
children: [
{
value: 2,
label: '上海',
disabled: true,
children: [
{ value: 3, label: '普陀' },
{ value: 4, label: '黄埔' },
{ value: 5, label: '徐汇' }
]
}
]
},
{
value: 6,
label: '西北',
disabled: true,
children: [
{ value: 7, label: '陕西' },
{ value: 8, label: '新疆' }
]
},
{
value: 9,
label: '东北'
}
]
}
},
computed: {
// 1.第一步,将树形数据转数组数据
// ["1,2,3", "1,2,4", "1,2,5", "6,7", "6,8", "9"]
gradeLabelToList () {
return this.treeToList(this.options)
}
},
methods: {
// 单选的情况
// onClick (node) {
// if (!node.isDisabled) {
// this.cascaderValue = node.path
// }
// },
// 多选的情况
onClick (node) {
if (node.isDisabled) {
return
}
// 定义一个数组,用于处理数据
const data = []
// 将之前选择的数据保存
if (this.cascaderValue.length) {
this.cascaderValue.forEach(n => {
data.push(n)
console.log('之前的值', data)
})
}
// 处理:
// 未选状态:将当前点击的node.path(是一个数组[1,2,3]),添加到data中
// 已选状态:已选中的从data数组中查找node.path删除
if (!node.checked) {
data.push(node.path)
console.log('未选->选中', data)
} else {
if (data.length) {
// 将[ [ 1, 2, 3 ], [ 1, 2, 4 ], [ 1, 2, 5 ] ]数据转为["1,2,3", "1,2,4", "1,2,5"],因为数组不能比较
const map = data.map(n => n.join(','))
// 找出node.path删除
const idx = map.findIndex(q => q === node.path.join(','))
if (idx > -1) {
data.splice(idx, 1)
}
console.log('已选->取消选中', data)
}
}
// 如果不添加以下代码,当使用组件的属性 clearable 的时候,会出现删除错误的问题,因为数据是有顺序的
const joinData = data.map(n => n.join(','))
// 将满足条件的数据过滤出来
const tempData = this.gradeLabelToList.filter(n => joinData.find(m => m === n))
let retData = []
// 再将数据数据构造回[ [ 1, 2, 3 ], [ 1, 2, 4 ], [ 1, 2, 5 ] ]
retData = tempData.map(n => n.split(',').map(Number))
// 最后双向绑定v-model的数据
this.cascaderValue = retData
},
treeToList (tree, out, pid) {
if (out === undefined) {
out = []
}
tree.forEach(n => {
const path = pid ? `${pid},` : ''
const data = `${path}${n.value}`
if (n.children) {
this.treeToList(n.children, out, data)
} else {
out.push(data)
}
})
return out
}
}
}
</script>
方法二:通过CSS样式
还有另一种方法,仅根据css样式,扩大radio或者checkbox的区域达到选中效果
存在问题:无法展开下一级
// css样式
<style lang="less">
.el-cascader-panel .el-radio,
.el-checkbox {
width: 100%;
height: 100%;
z-index: 10;
position: absolute;
top: 10px;
right: 10px;
}
.el-cascader-panel .el-radio__input,
.el-checkbox__input {
visibility: hidden;
}
</style>