效果图
首先是tree组件
<ul class = "menu-tree">
<li v-for = "(item,index) in menus" :key = "index">
<!-- 遍历menus-->
<div :class = "{'itemTree':true,'active':actId == item.nodeId}" @click = "selectItem(item)">
<div :style = "transform">
<!-- 如果item没有孩子 -->
<i class = "el-icon-caret-right" v-if = "item.childList && item.childList.length === 0" style="opacity:0"></i>
<!-- 如果item有孩子且item.show为false,那么图标为折叠图标 -->
<i class = "el-icon-caret-right" v-if = "item.childList && item.childList.length > 0 && !item.show" ></i>
<!-- 如果item有孩子且item.show为true,那么图标为展开图标 -->
<i class = "el-icon-caret-bottom" v-if = "item.childList && item.childList.length > 0 && item.show"></i>
<!-- 如果item的selected为true,也就是里面有值的话就是勾选状态,否则就是不勾选状态 -->
<i class = "selectBox" :class = "{'checkName':item.selected,'indeterminateName':item.indeterminate}" v-if="showCheckbox" @click.stop = "checkItem(item)">
<i></i>
</i>
{{item.categoryName}}
</div>
</div>
<el-collapse-transition>
<!-- 递归组件就是自己调用自己,这里是在自己的组件内再次调用自己,但是务必要和调用使用的一模一样才可以,否则树不会生效 -->
<Tree v-if = "item.childList && item.childList.length > 0 && item.show" :menus = "item.childList" :depth = "depth+2"
:actId = "actId" :showCheckbox="showCheckbox" @selectItem = "selectItem"
@checkItem = "checkItem" ></Tree>
</el-collapse-transition>
</li>
</ul>
</template>
<script>
export default {
name: 'Tree',
props: {
menus: {type: Array, default () { return [] }},
depth: {type: [Number, String], default: 0},
actId: {type: [Number, String], default: ''},
showCheckbox: {type: Boolean, default: false}
},
data () {
return {
}
},
methods: {
// 将selectItem方法暴露出去
selectItem (item) {
console.log("selectItem");
this.$emit('selectItem', item)
},
// 将checkItem方法暴露出去
checkItem (item) {
console.log("checkItem");
this.$emit('checkItem', item)
},
},
computed: {
// 通过传过来的depth计算下级目录的偏移量,这里我用的transform
transform () {
return 'transform:translateX(' + this.depth * 10 + 'px)'
}
}
}
</script>
<style lang = "scss" scoped>
.menu-tree{
li{
.itemTree{
height: 40px;
line-height: 40px;
width: 100%;
padding-left:10px;
position: relative;
font-size: 14px;
&:hover{
background:#6eb2eb;
color:#fff;
}
.selectBox{
display: inline-block;
width: 16px;
height:16px;
border:1px solid #ccc;
border-radius: 3px;
position: relative;
background: #fff;
top:2px;
}
.checkName{
background: #6FD202;
border-color: #6FD202;
i{
position: absolute;
left: 0;
top: -1px;
display: inline-block;
width: 14px;
height:10px;
border:2px solid #ffffff;
border-right: none;
border-top: none;
transform: rotate(-50deg);
}
}
.indeterminateName{
background: #6FD202;
border-color: #6FD202;
}
}
.active{
background:#4E8EFF;
color:#fff;
}
}
}
</style>
使用
<template>
<!-- 弹窗组件 -->
<!-- top="20vh" 距离浏览器上面距离 -->
<elian-dialog ref="addDialog" :visible.sync="visible" top="2vh" title="医院选择" width="600px"
:before-close="beforeClosed">
<div class="app-container">
<div style="width: 500px; height: 450px;overflow:auto;">
<Tree :menus="menus" :depth="depth" :actId="actId" :showCheckbox="showCheckbox" @selectItem="selectItem"
@checkItem="checkItem"></Tree>
</div>
<div class="fixed-bottom-btn-box2">
<el-button @click="beforeClosed">
取消
</el-button>
<el-button type="primary" @click="getAllSelect">
确定
</el-button>
</div>
</div>
</elian-dialog>
</template>
</template>
<script>
import Tree from './tree/menuTree.vue'
//import data from './tree/medata.json'
import {
showLoading,
closeLoading
} from '@/utils/loading'
import {
queryAllRegion
} from '@/api/report';
export default {
components: {
Tree
},
data() {
return {
selectData: [],
selectCodes: '',
selectNames: '',
visible: false,
depth: 0,
menus: [],
actId: '',
showCheckbox: true // 是否显示多选的框,默认不显示
}
},
created() {
//this.getData()
},
methods: {
// 获取数据
getData() {
console.log("获取所有医院列表")
//tps-report/report/web/report/template/cateList
queryAllRegion().then(res => {
if (res && res.data) {
this.menus = res.data;
// 在每一项中添加show
this.addShow(this.menus)
}
});
},
// 递归函数在每一项中添加show
addShow(arr) {
for (var i = 0; i < arr.length; i++) {
this.$set(arr[i], 'show', false)
if (arr[i].childList && arr[i].childList.length > 0) {
this.addShow(arr[i].childList)
}
}
},
// 点击箭头使树展开收缩
selectItem(data) {
if (data.childList && data.childList.length > 0) {
// 如果此项下有childrenMenus且length大于0,则切换展开与折叠状态
data.show = !data.show
}
// 则将选中的menuId赋值给actId
this.actId = data.nodeId
},
// 进行多选勾选
checkItem(data) {
if (data.selected) {
// 如果这一项的selected有值,说明是被勾选状态,要把selected置为false,清空勾选
data.selected = false
data.indeterminate = false;
// 如果此选项清空勾选后,如果下面有childrenMenus的话,那么也同时要清空
if (data.childList && data.childList.length > 0) {
this.clearChild(data.childList)
}
this.selectFather(this.menus, data);
} else {
// 如果这一项的selected为false,说明是未被勾选状态,把selected置为true,添加勾选
data.selected = true
//data.indeterminate=false;
// 如果此选项勾选后,如果下面有childrenMenus的话,那么也同时勾选下面所有的孩子
if (data.childList && data.childList.length > 0) {
this.addChild(data.childList)
}
// 如果此选项勾选后,要判断所有的上级元素是不是应该全部打勾
this.selectFather(this.menus, data)
}
//data.indeterminate=false;
},
// 定义函数清空所有孩子的勾选
clearChild(arr) {
for (var i = 0; i < arr.length; i++) {
arr[i].selected = false
if (arr[i].childList && arr[i].childList.length > 0) {
this.clearChild(arr[i].childList)
}
}
},
// 定义函数添加所有孩子的勾选
addChild(arr) {
for (var i = 0; i < arr.length; i++) {
arr[i].selected = true
arr[i].indeterminate = false
if (arr[i].childList && arr[i].childList.length > 0) {
this.addChild(arr[i].childList)
}
}
},
// 定义函数一层一层的往上寻找父元素的childrenMenus
clearFather(father, data) {
for (var i = 0; i < father.length; i++) {
if (father[i].nodeId === data.nodeId) {
// 找到data所在的childrenMenus为father,然后再通过这个childrenMenus找到拥有这个childrenMenus的父元素
this.clearRealFather(this.menus, father)
} else if (father[i].childList && father[i].childList.length > 0) {
this.clearFather(father[i].childList, data)
}
}
},
// 定义函数根据找到的上层父元素的childrenMenus来寻找父元素,并将他们清除勾选
clearRealFather(menus, arr) {
for (var i = 0; i < menus.length; i++) {
if (menus[i].childList === arr) {
// 找到这个拥有childrenMenus的父元素后,将此selected置为false
menus[i].selected = false
// 找到这个拥有childrenMenus的父元素后,再调用clearFather,再进行向上寻找父元素,知道没有父元素为止
this.clearFather(this.menus, menus[i])
} else if (menus[i].childList && menus[i].childList.length > 0) {
this.clearRealFather(menus[i].childList, arr)
}
}
},
// 定义函数一层一层的往上寻找父元素的childrenMenus
selectFather(father, data) {
for (var i = 0; i < father.length; i++) {
if (father[i].nodeId === data.nodeId) {
// 执行selectRealFather,将上层父元素也勾选
this.selectRealFather(this.menus, father[i])
} else if (father[i].childList && father[i].childList.length > 0) {
this.selectFather(father[i].childList, data)
}
}
},
// 定义函数根据找到的上层父元素的childrenMenus来寻找父元素,并将他们勾选
selectRealFather(menus, obj) {
console.log("查找父级")
for (var i = 0; i < menus.length; i++) {
if (menus[i].nodeId === obj.parentId) {
// 找到这个拥有childrenMenus的父元素后,给selected赋值,使其勾选
// menus[i].selected = true
// menus[i].selected = false
this.selectOrClearFather(menus[i]);
//indeterminate
// menus[i].indeterminate = true
// 找到这个拥有childrenMenus的父元素后,再调用selectRealFather,再进行向上寻找父元素,知道没有父元素为止
this.selectRealFather(this.menus, menus[i])
} else if (menus[i].childList && menus[i].childList.length > 0) {
this.selectRealFather(menus[i].childList, obj)
}
}
},
//父类的勾选问题,如果子类全部勾选,父类勾选,如果子类没有全部勾选,父类不确定,如果子类没有勾选,不勾选
selectOrClearFather(menusParent) {
if (menusParent.childList && menusParent.childList.length > 0) {
let childNum = menusParent.childList.length;
console.log(menusParent.categoryName + "父元素中子元素个数:" + childNum)
let newparentMenu = menusParent.childList.filter((item) => {
return item.selected;
})
let seltNum = newparentMenu.length;
console.log(menusParent.categoryName + "父元素中子元素个数被选中的个数:" + seltNum)
let indeterparentMenu = menusParent.childList.filter((item) => {
return item.indeterminate;
})
let indeterNum = indeterparentMenu.length;
console.log(menusParent.categoryName + "父元素中子元素个数不确定的个数:" + indeterNum)
if (seltNum == 0 && indeterNum == 0) {
menusParent.selected = false;
menusParent.indeterminate = false
} else if (childNum == seltNum) {
menusParent.selected = true
menusParent.indeterminate = false;
} else {
menusParent.selected = false;
menusParent.indeterminate = true
}
}
},
//获取所有被选中的数据
getAllSelect() {
this.selectData=[];
this.selectNames = "";
this.selectCodes = "";
let menus = this.menus;
//console.log("获取所有选择数据")
//console.log("data="+JSON.stringify(menus))
for (var i = 0; i < menus.length; i++) {
if (menus[i].childList.length > 0) {
this.getAllSelect2(menus[i].childList);
} else {
if (menus[i].selected) {
this.selectValus += menus[i].categoryName + ",";
this.selectCodes += menus[i].nodeId + ",";
//let item={"entpName":menus[i].categoryName,"entpCode":menus[i].nodeId};
//this.selectData=Object.assign({}, []);
// console.log("item"+JSON.stringify(item));
this.selectData.push({
"orgName": menus[i].categoryName,
"entpCode": menus[i].nodeId
})
}
}
}
/* for (var i = 0; i < menus.length; i++) {
if (menus[i].selected) {
this.selectValus += menus[i].menuName + ","; //当前被勾选的,直接取父值
} else {
if (menus[i].childrenMenus.length > 0) {
this.getAllSelect2(menus[i].childrenMenus);
}
}
} */
this.selectNames = this.selectNames.substr(0, this.selectNames.length - 1);
this.selectCodes = this.selectCodes.substr(0, this.selectCodes.length - 1);
console.log("所有被选择的值1" + this.selectNames);
console.log("所有被选择的值" + JSON.stringify(this.selectData));
//this.changeValue(this.selectValus);
this.$emit('changeValue', this.selectData, this.selectCodes);
this.beforeClosed();
},
getAllSelect2(menus) {
console.log("递归调用" + menus.length)
/* for (var i = 0; i < menus.length; i++) {
if (menus[i].selected) {
// console.log("选择的值"+menus[i].menuName)
this.selectValus += menus[i].menuName + ",";
} else {
if (menus[i].childrenMenus.length > 0) {
//console.log("子菜单个数:"+menus[i].childrenMenus.length)
this.getAllSelect2(menus[i].childrenMenus);
}
}
} */
for (var i = 0; i < menus.length; i++) {
if (menus[i].childList && menus[i].childList.length > 0) {
//console.log("子菜单个数:"+menus[i].childrenMenus.length)
this.getAllSelect2(menus[i].childList);
} else {
//console.log("子菜单"+menus[i].selected)
if (menus[i].selected) {
// console.log("选择的值"+menus[i].menuName)
this.selectNames += menus[i].categoryName + ",";
this.selectCodes += menus[i].nodeId + ",";
//let item=;
//this.selectData=Object.assign({}, []);
// console.log("item"+JSON.stringify(item));
this.selectData.push({
"orgName": menus[i].categoryName,
"entpCode": menus[i].nodeId
})
}
}
}
},
handleClick() {},
open(value, params) {
console.log("打开的值"+value)
showLoading();
if (params || this.menus.length < 1) {
//如果有查询条件,或者数据为空
//showLoading();
queryAllRegion().then(res => {
closeLoading();
if (res && res.data) {
this.menus = res.data;
// 在每一项中添加show
this.addShow(this.menus)
//let valueArr = value.split(",");
this.beforeOpenFormate(this.menus, value);
this.visible = true
}
});
} else {
//let valueArr = value.split(",");
this.beforeOpenFormate(this.menus, value);
closeLoading();
this.visible = true
}
},
/* open2(value) {
if (this.menus.length < 1) {
console.log("还没有参数"+this.menus.length)
this.showLoading;
} else {
console.log("打开值" + value);
if (value.length > 0) {
let valueArr = value.split(",");
this.beforeOpenFormate(this.menus, valueArr);
}
this.visible = true
}
}, */
beforeClosed() {
closeLoading();
this.visible = false
},
//打开前的数据整理(勾选问题)
beforeOpenFormate(menus, valueArr) {
console.log("打开menus" + menus.length)
for (var i = 0; i < menus.length; i++) {
console.log("打开初始化")
if (valueArr.indexOf(menus[i].nodeId) != -1) {
menus[i].selected = true;
//如果有勾选,将子类全部勾选
if (menus[i].childList && menus[i].childList.length > 0) {
this.addChild(menus[i].childList)
}
} else {
menus[i].selected = false;
//没有勾选,需要判断子类是否有勾选,如果子类有勾选,父类需要改变indeterminate的值
if (menus[i].childList && menus[i].childList.length > 0) {
this.beforeOpenFormate(menus[i].childList, valueArr);
//this.selectOrClearFather(menus[i])
}
this.selectOrClearFather(menus[i])
}
}
}
}
}
</script>
<style lang="less" scoped>
.fixed-bottom-btn-box2 {
//position: fixed;
right: 16px;
bottom: 0;
left: 16px;
height: 50px;
margin: 5px;
padding-right: 100px;
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-pack: end;
-ms-flex-pack: end;
justify-content: flex-end;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
border-top: 1px solid #cecece;
background-color: #fff;
//z-index: 3;
}
</style>
数据
{
"nodeId": "520000",
"categoryName": "贵州省",
"parentId": "CN0001",
"childId": null,
"indeterminate": false,
"selected": false "childList": [{
"nodeId": "520100",
"categoryName": "贵阳市",
"parentId": "520000",
"childId": null,
"childList": [{
"nodeId": "520102",
"categoryName": "南明区",
"parentId": "520100",
"childId": null,
"childList": [{
"nodeId": "176b2701-2434-11e9-ae40-0894ef3ba64e",
"categoryName": "南明区铁建国际城卫生服务中心",
"parentId": "520102",
"childId": null,
"childList": null,
"indeterminate": false,
"selected": false
},
{
"nodeId": "a7d54e4b-d60b-4854-bb24-d1ca88e91427",
"categoryName": "test",
"parentId": "520102",
"childId": null,
"childList": null,
"indeterminate": false,
"selected": false
},
{
"nodeId": "b76efd1c-ddd6-472c-8c60-dc3bc21b72c7",
"categoryName": "test",
"parentId": "520102",
"childId": null,
"childList": null,
"indeterminate": false,
"selected": false
},
{
"nodeId": "H0860",
"categoryName": "南明区沙冲社区卫生服务中心(南明区人民医院第一门诊部)",
"parentId": "520102",
"childId": null,
"childList": null,
"indeterminate": false,
"selected": false
},
{
"nodeId": "H0861",
"categoryName": "南明区遵义社区卫生服务中心",
"parentId": "520102",
"childId": null,
"childList": null,
"indeterminate": false,
"selected": false
}
],
"indeterminate": false,
"selected": false
},
{
"nodeId": "520103",
"categoryName": "云岩区",
"parentId": "520100",
"childId": null,
"childList": [{
"nodeId": "H1741",
"categoryName": "贵阳中医学院第二附属医院、贵州省中西医结合医院",
"parentId": "520103",
"childId": null,
"childList": null,
"indeterminate": false,
"selected": false
},
{
"nodeId": "H1742",
"categoryName": "贵州医科大学附属医院",
"parentId": "520103",
"childId": null,
"childList": null,
"indeterminate": false,
"selected": false
}
],
"indeterminate": false,
"selected": false
]
}]
}
|