预览
单选
多选
代码
<template>
<div class="ts-tree-select">
<el-popover placement="bottom" ref="el-popper"
popper-class="ts-tree-popper"
trigger="click">
<el-tree ref="el-tree"
:show-checkbox="multiple"
:expand-on-click-node="multiple"
:default-checked-keys="defaultCheckedKeys"
:render-after-expand="false"
:filter-node-method="filterNode"
:node-key="valueKey"
@node-click="treeNodeClick"
@check-change="treeNodeCheck"
:data="treeOptions"
v-bind="treeAttribute"
v-on="treeListener"></el-tree>
<el-select slot="reference" :value="selectData" popper-class="ts-select-popper" @remove-tag="removeTag"
:value-key="valueKey"
@blur="blurFn"
filterable
:filter-method="filterMethod"
:multiple="multiple"
v-bind="selectAttribute" v-on="selectListener" ref="el-select">
<el-option v-if="multiple" v-for="item in selectData" :label="item[selectProps.label]"
:value="item"></el-option>
<el-option v-else :label="selectData[selectProps.label]" :value="selectData"></el-option>
</el-select>
</el-popover>
</div>
</template>
<script>
export default {
name: 'TsTreeSelect',
props: {
selectData: {
type: [Object, Array],
required: true,
},
valueKey: {
type: String,
},
treeOptions: {
required: true,
type: Array,
},
selectProps: {
type: Object,
default() {
return {
label: 'label',
value: 'value'
}
}
},
selectAttribute: {},
selectListener: {},
treeAttribute: {},
treeListener: {},
multiple: {
type: Boolean,
default() {
return false;
}
},
beforeRemoveTag: {
type: Function,
default(tag, resolve) {
resolve()
}
}
},
model: {
prop: 'selectData',
event: 'updateSelectData'
},
data() {
return {
defaultCheckedKeys: [],
minWidth: '',
filterText: ''
}
},
watch: {
filterText(val) {
this.$refs['el-tree'].filter(val);
}
},
mounted() {
if (this.multiple) {
if (this.valueKey) {
this.defaultCheckedKeys = this.selectData.map(item => {
return item[this.valueKey]
})
} else {
throw '多选必须传入valueKey作为唯一标识'
}
}
this.minWidth = this.$refs['el-select'].$el.getBoundingClientRect().width;
this.$refs['el-popper'].$refs['popper'].style.minWidth = this.minWidth + 'px';
},
methods: {
filterMethod(val) {
this.filterText = val
},
blurFn() {
this.filterText = ''
},
filterNode(value, data) {
if (!value) return true;
const label = this.$refs['el-tree'].props.label;
return data[label].indexOf(value) !== -1;
},
treeNodeClick(data, node, ref) {
if (!this.multiple) {
this.$emit('updateSelectData', data);
this.$nextTick(() => {
this.$refs['el-select'].$el.click();
this.$refs['el-select'].blur();
})
}
},
treeNodeCheck(data, isSelect) {
let newList = [];
if (isSelect) {
newList = this.selectData.concat(data);
} else {
newList = this.selectData.filter(item => {
return item[this.valueKey] !== data[this.valueKey]
})
}
this.$emit('updateSelectData', newList)
this.$nextTick(() => {
this.$refs['el-popper'].createPopper();
})
},
removeTag(tag) {
if (this.multiple) {
this.beforeRemoveTag(tag, () => {
this.$refs['el-tree'].setChecked(tag, false);
let newList = this.selectData.filter(item => {
return item[this.valueKey] !== tag[this.valueKey];
})
this.$emit('updateSelectData', newList)
})
}
}
}
}
</script>
<style>
.ts-select-popper {
display: none !important;
}
.ts-tree-popper {
max-height: 226px;
overflow: auto;
box-sizing: border-box;
}
</style>
<template>
<div id="app">
<ts-tree-select v-model="selectData" value-key="value" :multiple="true" :tree-options="treeOptions"
:before-remove-tag="beforeRemoveTag"></ts-tree-select>
</div>
</template>
<script>
import tsTreeSelect from './components/tsTreeSelect.vue'
export default {
name: 'App',
data() {
return {
selectData: [],
treeOptions: [
{
value: '1',
label: 'Level one 1',
children: [
{
value: '1-1',
label: 'Level two 1-1',
children: [
{
value: '1-1-1',
label: 'Level three 1-1-1',
},
],
},
],
},
{
value: '2',
label: 'Level one 2',
children: [
{
value: '2-1',
label: 'Level two 2-1',
children: [
{
value: '2-1-1',
label: 'Level three 2-1-1',
},
],
},
{
value: '2-2',
label: 'Level two 2-2',
children: [
{
value: '2-2-1',
label: 'Level three 2-2-1',
},
],
},
],
},
{
value: '3',
label: 'Level one 3',
children: [
{
value: '3-1',
label: 'Level two 3-1',
children: [
{
value: '3-1-1',
label: 'Level three 3-1-1',
},
],
},
{
value: '3-2',
label: 'Level two 3-2',
children: [
{
value: '3-2-1',
label: 'Level three 3-2-1',
},
],
},
],
},
]
}
},
components: {
tsTreeSelect
},
methods:{
// 移除tag提示
beforeRemoveTag(tag,resolve){
this.$confirm('此操作将永久删除该文件, 是否继续?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
center: true
}).then(() => {
resolve()
}).catch(() => {
this.$message({
type: 'info',
message: '已取消删除'
});
});
}
}
}
</script>
移除multiple
selectProps 用于自定义显示的label 例如:{label:‘objectName’},value暂不支持自定义。
selectAttribute 用于传递el-select原生属性如disabled size clearable等属性 例如:{disabled:true,loading:false}
selectListener 用于监听el-select原生事件 例如:{change:this.handleChange}
treeAttribute,treeListener类似
beforeRemoveTag:多选模式,移除tag标签前触发的方法