效果图
![在这里插入图片描述](https://img-blog.csdnimg.cn/fdf76a25a6dc44daac8b4580ad7a19bb.gif#pic_center)
使用代码
<template>
<div id="app">
<tree-select :list="list" :multiple="true" :clearable="true" :checkStrictly="true" width="120px" v-model="array"></tree-select>
</div>
</template>
<script>
import TreeSelect from '@/components/TreeSelect';
export default {
components: {
TreeSelect
},
data() {
return {
array: [1,3,4],
list:[
{
id:'1',
name: '上海市',
children: [
{
id:'2',
name: '嘉定区',
children: [
{
id:'3',
name: '江桥镇',
content:'嘉定',
pid:'2',
},
{
id:'4',
name: '安亭镇',
content:'安亭',
pid:'2',
}
],
content:'嘉定',
pid:'1',
},
],
content:'上海魔都',
pid:'0',
},
{
id:'11',
name: '北京市',
children: [
{
id:'12',
name: '朝阳区',
children: [
{
id:'13',
name: '三里屯',
content:'三里屯',
pid:'12',
},
{
id:'14',
name: '左家庄',
content:'左家庄',
pid:'12',
}
],
content:'朝阳',
pid:'11',
},
],
content:'北京',
pid:'0',
}
],
};
},
methods: {
},
};
</script>
组件代码
<template>
<div>
<div class="mask" v-show="isShowSelect"></div>
<el-popover placement="bottom-start" :width="popoverWidth" :close-on-click-modal = "false" trigger="manual" v-model="isShowSelect" @hide="popoverHide">
<el-tree class="common-tree" :width="width" ref="tree" :data="treeData" :props="obj"
:show-checkbox="multiple"
:node-key="obj.id"
:check-strictly="checkStrictly"
:default-expanded-keys="defaultKeys"
:expand-on-click-node="multiple&&expandClickNode"
:check-on-click-node="checkClickNode"
:highlight-current="true"
@check-change="nodeClick"
@node-click="nodeClick"
></el-tree>
<el-select slot="reference" ref="select" :size="size" :width="width"
v-model="returnDataKeys"
:multiple="multiple"
:clearable="clearable"
:collapse-tags="collapseTags"
@click.native="selectClick"
@remove-tag="removeTag"
@clear="clean"
class="tree-select">
<el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
<!-- <el-row>
<el-button v-if="multiple" class="ok" @click="isShowSelect=false" size="mini" type="text">确定</el-button>
</el-row> -->
</el-popover>
</div>
</template>
<script>
export default {
name: 'tree-select',
props: {
value: {
tyep: Object
},
list: {
type: Array,
default () {
return [];
}
},
obj: {
type: Object,
required: false,
default:()=>{
return {
id:'id',
label: 'name',
children: 'children',
path:'path',
content:'content',
pid:'pid',
}
}
},
multiple: {
type: Boolean,
default () {
return false;
}
},
clearable: {
type: Boolean,
default () {
return false;
}
},
collapseTags: {
type: Boolean,
default () {
return false;
}
},
checkStrictly: {
type: Boolean,
default () {
return false;
}
},
checkClickNode:{
type: Boolean,
default () {
return false;
}
},
expandClickNode:{
type: Boolean,
default () {
return false;
}
},
size: {
type: String,
default () {
return 'small';
}
},
width: {
type: String,
default () {
return '100%';
}
},
height: {
type: String,
default () {
return '300px';
}
}
},
data () {
return {
defaultKeys: [],
defaultKey: '',
first: false,
popoverWidth:"0px",
isShowSelect: false,
options: [],
returnDatas:[],
returnDataKeys:[],
};
},
computed: {
treeData() {
return JSON.stringify(this.list).indexOf(this.obj.children) !== -1 ? this.list : this.switchTree();
},
},
methods: {
init(){
if (this.multiple) {
if(this.defaultKeys!=undefined && this.defaultKeys.length>0){
if( Object.prototype.toString.call(this.defaultKeys).indexOf("Array")!=-1){
if(Object.prototype.toString.call(this.defaultKeys[0]).indexOf("Object")!=-1){
this.setDatas(this.defaultKeys);
}else if(Object.prototype.toString.call(this.defaultKeys[0]).indexOf("Number")!=-1
|| Object.prototype.toString.call(this.defaultKeys[0]).indexOf("String")!=-1){
this.setKeys(this.defaultKeys);
}else{
console.log("多选:传入参数类型不匹配");
return;
}
}else {
console.log("多选:传入参数类型不匹配");
return;
}
}
} else {
if( Object.prototype.toString.call(this.defaultKey).indexOf("Number")!=-1
|| Object.prototype.toString.call(this.defaultKey).indexOf("String")!=-1
|| Object.prototype.toString.call(this.defaultKey).indexOf("Object")!=-1 ){
this.setKey(this.defaultKey);
}else {
console.log("单选:传入参数类型不匹配");
return;
}
}
},
selectClick(){
this.$nextTick(function () {
this.popoverWidth= this.$refs.select.$el.clientWidth-26;
})
return this.isShowSelect = !this.isShowSelect
},
nodeClick (data, node) {
if (!this.multiple) {
this.isShowSelect=false;
this.setKey(node.key);
}else{
var checkedKeys = this.$refs.tree.getCheckedKeys();
var t=[];
this.options = checkedKeys.map((item) => {
var node = this.$refs.tree.getNode(item);
t.push(node.data);
return {label:node.label,value:node.key};
});
this.returnDataKeys = this.options.map((item) => {
return item.value;
});
this.returnDatas=t;
}
},
clean () {
this.$refs.tree.setCurrentKey(null);
this.returnDatas=null;this.returnDataKeys='';
this.popoverHide ();
},
setKey (thisKey) {
this.$refs.tree.setCurrentKey(thisKey);
var node = this.$refs.tree.getNode(thisKey);
this.setData(node.data);
},
setData(data){
this.options = [];
this.options.push({label:data[this.obj.label],value:data[this.obj.id]});
this.returnDatas = data;
this.returnDataKeys=data[this.obj.id]
},
setKeys (thisKeys) {
this.$refs.tree.setCheckedKeys(thisKeys);
this.returnDataKeys = thisKeys;
var t = [];
thisKeys.map((item) => {
var node = this.$refs.tree.getNode(item);
t.push(node.data);
return {label:node.label,value:node.key};
});
this.returnDatas=t;
this.popoverHide()
},
setDatas(data){
this.$refs.tree.setCheckedNodes(data);
this.returnDatas=data;
var t=[];
data.map((item) => {
t.push(item[this.obj.id]);
});
this.returnDataKeys=t;
this.popoverHide()
},
removeTag (val) {
this.$refs.tree.setChecked(val, false);
var node = this.$refs.tree.getNode(val);
if (!this.checkStrictly && node.childNodes.length > 0) {
this.treeToList(node).map(item => {
if (item.childNodes.length <= 0) {
this.$refs.tree.setChecked(item, false);
}
});
}
this.nodeClick();
this.popoverHide ();
},
popoverHide () {
this.$emit('getValue', this.returnDataKeys, this.returnDatas);
},
clearSelectedNodes () {
var checkedKeys = this.$refs.tree.getCheckedKeys();
for (let i = 0; i < checkedKeys.length; i++) {
this.$refs.tree.setChecked(checkedKeys[i], false);
}
},
treeToList (tree) {
var queen = [];
var out = [];
queen = queen.concat(tree);
while (queen.length) {
var first = queen.shift();
if (first.childNodes) {
queen = queen.concat(first.childNodes);
}
out.push(first);
}
return out;
},
switchTree() {
return this.buildTree(this.list, this.defaultValue);
},
buildTree(data, id) {
const fa = (id) => {
const temp = [];
for (let i = 0; i < data.length; i++) {
const n = data[i];
if (n[this.obj.pid] === id) {
n[this.obj.children] = fa(n[this.obj.id]);
temp.push(n);
}
}
return temp;
};
return fa(id);
},
},
watch: {
isShowSelect (val) {
this.$refs.select.blur();
},
treeData(){
this.$nextTick(()=> {
this.init();
})
},
value: {
handler (val) {
this.$nextTick(()=> {
if(this.multiple){
this.defaultKeys = val
}else{
this.defaultKey = val
}
this.init();
})
},
immediate: true
},
returnDataKeys: {
handler (val) {
if (this.first || val) {
this.first = true;
this.$emit('input', val);
} else {
this.first = true;
}
}
},
}
};
</script>
<style scoped>
.mask{
height: 100%;
position: fixed;
top: 0;
left: 0;
opacity: 0;
z-index: 11;
}
.common-tree{
overflow: auto;
}
.tree-select{
z-index: 111;
}
.ok{
float: right;
}
.el-row{
padding-top: 0px !important;
}
</style>