改组件包含了勾选和编辑,以及数据的限制
代码如下:
父组件:
<template>
<div :class="$style.root">
<Indextree
:options="dataList"
:defaultValue="[
{
label: '三级 1-1-1',
value: '1-1-1'
}
]"
/>
</div>
</template>
<script>
import Indextree from './indextree.vue';
export default {
components: { Indextree },
data() {
return {
defaultexpanded: [],
defaultValue: [],
dataList: [
{
label: '一级 1',
value: '1',
children: [
{
label: '二级 1-1',
value: '1-1',
children: [
{
label: '三级 1-1-1',
value: '1-1-1',
disabled: true
}
]
}
]
},
{
label: '一级 2',
value: '2',
children: [
{
label: '二级 2-1',
value: '2-1',
children: [
{
value: '2-1-1',
label: '三级 2-1-1',
disabled: false
},
{
value: '2-1-2',
label: '三级 2-1-2'
}
]
},
{
label: '二级 2-2',
value: '2-2',
children: [
{
value: '2-2-1',
label: '三级 2-2-1'
}
]
}
]
},
{
label: '一级 3',
value: '3',
children: [
{
label: '二级 3-1',
value: '3-1',
children: [
{
value: '3-1-1',
label: '三级 3-1-1'
}
]
},
{
label: '二级 3-2',
value: '3-2',
children: [
{
value: '3-2-1',
label: '三级 3-2-1'
}
]
}
]
}
]
};
}
};
</script>
<style lang="scss" module>
.root {
}
</style>
子组件:
<template>
<div>
<el-select
:collapse-tags="collapseTags"
v-model="selectValues"
:multiple="true"
:placeholder="placeholder"
:filterable="filterable"
:filter-method="dataFilter"
:popper-append-to-body="appendToBody"
@remove-tag="removeTag"
style="width: 100%"
:disabled="disabledSelect"
>
<el-option
:value="selectTree"
v-loading="treeLoading"
element-loading-background="rgba(255, 255, 255, 0.5)"
element-loading-text="loading"
class="option-style"
disabled
>
<el-tree
:data="options"
:props="defaultProps"
class="tree-style"
ref="treeNode"
show-checkbox
:node-key="defaultProps.value"
:filter-node-method="filterNode"
:default-checked-keys="defaultValue"
:check-strictly="checkStrictly"
:default-expanded-keys="defaultexpanded"
@check-change="handleNodeChange"
@check="handleNodeClick"
>
</el-tree>
</el-option>
</el-select>
</div>
</template>
<script>
export default {
name: 'TreeSelect',
props: {
limit: {
// 限制个数
type: Number,
default: 2
},
defaultexpanded: {
type: Array,
default: () => []
},
//编辑时回显的数组
defaultValue: {
type: Array,
default: () => []
},
//可用选项的数组
options: {
type: Array,
default: () => []
},
collapseTags: {
type: Boolean,
default: false
},
// 配置选项
defaultProps: {
type: Object,
default: () => ({
// 属性值为后端返回的对应的字段名
children: 'children',
label: 'label',
value: 'value'
})
},
// 是否将组件添加到body上面(组件在弹窗或者表格里面时可设为true)
appendToBody: {
type: Boolean,
default: false
},
// 是否可搜索
filterable: {
type: Boolean,
default: true // 不可以搜索
},
// 是否禁用下拉框
disabledSelect: {
type: Boolean,
default: false
},
// 父子不互相关联
checkStrictly: {
type: Boolean,
default: false // 关联
},
placeholder: {
type: String,
default: '请选择'
},
// 不可删除报错
errMessage: {
type: String,
default: '该选项不可被删除'
}
},
data() {
return {
checkChildlist: [],
selectTree: [], // 绑定el-option的值
selectValue: [], // 文本框中的标签
VALUE_NAME: this.defaultProps.value, // value转换后的字段
VALUE_TEXT: this.defaultProps.label, // label转换后的字段
treeLoading: false // 加载loading~
};
},
computed: {
// 是否勾选内容的超出最大限制
isToLimit() {
return this.limit < this.checkChildlist?.length;
},
selectValues: {
get() {
return this.selectTree.map(v => v[this.VALUE_TEXT]);
},
set(val) {
return this.selectTree.map(v => v[this.VALUE_TEXT]);
}
}
},
watch: {
// 监听回显的数据
defaultValue: {
handler(val) {
if (val.length) {
this.$nextTick(() => {
// 回显tag数据
this.selectValue = val.map(v => v[this.VALUE_TEXT]);
// 树选中
this.$refs.treeNode.setCheckedNodes(val);
// 数据绑定
this.checkChildlist = [...val];
this.selectTree = [...val];
});
}
},
immediate: true,
deep: true
}
},
methods: {
// 输入框关键字
dataFilter(val) {
this.$refs.treeNode.filter(val);
},
/**
* @description: tree搜索过滤
* @param {*} value 搜索的关键字
* @param {*} data 筛选到的节点
* @return {*}
*/
filterNode(value, data) {
if (!value) return true;
return data[this.defaultProps.label].toLowerCase().indexOf(value.toLowerCase()) !== -1;
},
/**
* @description: 勾选树形选项
* @param {*} data 该节点所对应的对象
* @param {*} self 节点本身是否被选中
* @param {*} child 节点的子树中是否有被选中的节点
* @return {*}
*/
handleNodeChange(data, self, child) {
let datalist = this.$refs.treeNode.getCheckedNodes();
// 过滤数据,拿到子节点数据
datalist = datalist.filter(v => !v[this.defaultProps.children]);
// 所有勾选的子数据集合
this.checkChildlist = [...datalist];
if (!this.isToLimit) {
// 勾选对象集合
this.selectTree = datalist;
// 设置tag显示字段
this.selectValue = datalist.map(v => v[this.VALUE_TEXT]);
}
},
// 移除单个标签
removeTag(tagName) {
let data = this.selectTree.find(v => v[this.VALUE_TEXT] === tagName);
if (data?.disabled) {
this.$message({
message: this.errMessage,
type: 'error'
});
return;
}
this.selectTree = this.selectTree.filter(item => item[this.VALUE_TEXT] != tagName);
this.$nextTick(() => {
this.$refs.treeNode.setCheckedNodes(this.selectTree);
});
},
// 当前选择的节点
handleNodeClick(e, i) {
if (this.isToLimit) {
this.$message({
message: `最多可选${this.limit}个`,
type: 'warning'
});
// 变量当前勾选父元素的所有,挨个取消勾选
this.flatChildren([e]).forEach(item => {
this.$refs.treeNode.setChecked(item, false);
});
} else {
this.$refs.treeNode.setCheckedNodes(i.checkedNodes);
}
},
// 拍平子节点
flatChildren(arr) {
return arr.reduce((acc, cur) => {
acc.push(cur);
if (cur?.children?.length) {
acc.push(...this.flatChildren(cur.children));
}
return acc;
}, []);
}
}
};
</script>
<style lang="scss" scoped>
.check-box {
padding: 0 20px;
}
::v-deep .el-scrollbar {
height: 280px;
.el-select-dropdown__wrap {
max-height: 280px;
overflow: hidden;
.el-select-dropdown__list {
padding: 0;
}
}
}
:deep .el-tree > .el-tree-node {
display: block;
}
.option-style {
height: 280px;
padding: 0 0 10px 0 !important;
margin: 0;
overflow-y: auto;
cursor: default !important;
}
</style>
有什么问题可以评论指出