目前Vue Element的 el-cascader 级联选择器,多选或者选择任意一级,需要点击左侧的checkbox才能选中。
目标:点击label选中,已选中状态再次点击label取消选中
有两种方式实现
- 通过添加点击事件
- 通过css样式控制
方法一:通过点击事件
<!-- 改造前 -->
<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>