IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> JavaScript知识库 -> vue树形菜单复选功能 -> 正文阅读

[JavaScript知识库]vue树形菜单复选功能

效果图
在这里插入图片描述

首先是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
				]
			}]
	}

  JavaScript知识库 最新文章
ES6的相关知识点
react 函数式组件 & react其他一些总结
Vue基础超详细
前端JS也可以连点成线(Vue中运用 AntVG6)
Vue事件处理的基本使用
Vue后台项目的记录 (一)
前后端分离vue跨域,devServer配置proxy代理
TypeScript
初识vuex
vue项目安装包指令收集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-18 10:03:42  更:2021-09-18 10:04:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 22:39:28-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码