在一次项目开发过程中,需要用到类似于级联选择器(Cascader)的组件,但是级联选择器是横向排列,ui给的样式是竖向排列的(如下图),于是动手用element的Tree和Select组件进行了二次封装,实现了如下功能支持单选和多选的树形选择
实现代码:
<div class="my-select">
<el-select :value="value" :multiple="multiple" placeholder="请选择" @remove-tag="removeTag"
:clearable="clearable" @clear="clearHandle">
<el-option :value="value">
<el-tree
id="tree-option"
ref="selectTree"
:accordion="accordion"
:data="treeData"
:props="defaultProps"
show-checkbox
:expand-on-click-node="false"
:node-key="defaultProps.value"
:check-strictly="true"
@check="handleNodeClick"
@check-change="handleCheckChange"
></el-tree>
</el-option>
</el-select>
</div>
//样式修改
.my-select {
//display: flex;
//flex-direction: column;
//align-items: center;
}
.el-scrollbar .el-scrollbar__view .el-select-dropdown__item {
height: auto;
padding: 0;
overflow: hidden;
overflow-y: auto;
}
.el-select-dropdown__item.selected {
font-weight: normal;
}
ul li >>> .el-tree .el-tree-node__content {
height: auto;
padding: 0 20px;
}
.el-tree-node__label {
font-weight: normal;
}
.el-tree >>> .is-current .el-tree-node__label {
color: #409eff;
font-weight: 700;
}
.el-tree >>> .is-current .el-tree-node__children .el-tree-node__label {
color: #606266;
font-weight: normal;
}
/deep/ .el-select {
width: 100% !important;
}
实现具体的方法:定义props,接受从父组件传递过来的参数
props: {
/*配置选项*/
defaultProps: {
type: Object,
default: () => {
return {
value: "id", // ID字段名
label: "label", // 显示名称
children: 'children' // 子级字段名
}
}
},
/*展示数据*/
treeData: {
type: Array,
default: () => []
},
/* 可清空选项 */
clearable: {
type: Boolean,
default: () => true
},
/* 自动收起 */
accordion: {
type: Boolean,
default: () => true
},
/*单选初始值*/
radioValue: {
type: String,
default: () => ''
},
/*多选初始值*/
multipleValues: {
type: Array,
default: () => []
},
/*是否多选*/
multiple: {
type: Boolean,
default: () => true
}
}
初始化加载组件时,判断是多选还是单选
mounted() {
// 父组件传递的multiple值为true,则是单选组件,反之为多选,调用对应的方法,初始化默认数据
this.multiple ? this.radioInit() : this.multipleInit();
}
接下来实现对应的方法:初始化默认值
// 初始化多选默认值,如果有传递默认值,则回显默认值
multipleInit() {
// 如果有传默认值过来,则渲染到组件中
if (this.multipleValues.length > 0) {
this.$refs.selectTree.setCheckedKeys(this.multipleValues);
this.selectedNodes = this.$refs.selectTree.getCheckedNodes();
this.value = this.selectedNodes.map(item => item[this.defaultProps.label]);
}
},
// 初始化单选默认值
radioInit() {
if (this.radioValue) {
this.$refs.selectTree.setCheckedKeys([this.radioValue]);
this.selectedNodes = this.$refs.selectTree.getCheckedNodes();
this.value = this.selectedNodes[0][this.defaultProps.label];
}
}
切换选项的方法
handleNodeClick(node, el) {
this.selectedNodes = el.checkedNodes;
if (this.multiple) {
// 多选
this.value = el.checkedNodes.map(item => item[this.defaultProps.label]);
} else {
// 单选
this.value = el.checkedNodes[0][this.defaultProps.label];
}
}
移除选项
// 移除选项(单项移除)
removeTag(tag) {
let arr = this.selectedNodes.filter(item => item[this.defaultProps.label] === tag);
this.$refs.selectTree.setChecked(arr[0].id, false);
this.value.splice(this.value.indexOf(tag), 1);
},
// 清除选中项(清除所有)
clearHandle() {
if (this.multiple) {
this.value = [];
} else {
this.value = '';
}
this.$refs.selectTree.setCheckedKeys([]);
}
// 获取选中id值,通过$emit将此事件弹射出去给父组件,获取选中的对应id值
getCheckedKeys() {
return this.$refs.selectTree.getCheckedKeys()
},
// 节点状态发生变化时的回调,针对单选做特殊处理
handleCheckChange(data, checked) {
if (!this.multiple && checked === true) {
// 单选
this.$refs.selectTree.setCheckedNodes([data]);
}
}