vue 树形目录结构
原先使用的是Ztree,后来因为更多的定制化要求,还是手写一个算了
<template> <div> <menu-tree :nodes="nodes" :selected="currentCameraId"></menu-tree> </div> </template> <script> import EventBus from "@/utils/EventBus"; import MenuTree from "@/views/HomePage2D/components/MenuTree"; import dh_config from "static/config/dh-camera"; export default { name: "zoneDeviceDir", components: {MenuTree}, data() { return { nodes: [], allNodes: [], currentCameraId: '' } }, watch: {}, computed: {}, methods: { //选中 cameraClick(item) { this.currentCameraId = this.currentCameraId === item.id ? '' : item.id; if (this.currentCameraId !== '') { EventBus.$emit('showThisVideo',item) } }, //全部展开或折叠 allNoExpand(flag) { this.nodes.forEach(node => { this.noExpand(node, flag) }) }, noExpand(node, flag) { node.expand = flag; if (node.children && node.children.length > 0) { node.children.forEach(childNode => { this.noExpand(childNode, flag) }) } }, //反向定位 reverseCamera(id) { this.currentCameraId = id; this.allNoExpand(false); this.nodes = this.allNodes; this.cameraSelect(this.nodes, id); }, cameraSelect(nodes, queryData) { for (let i = 0; i < nodes.length; i++) { let node = nodes[i]; if (node.children === null && this.corCamera(node, queryData)) { node.expand = true; } else { let deepQuery = this.corCamera(node, queryData); if (deepQuery) { node.expand = true; if (node.children) { this.cameraSelect(node.children, queryData); } } } } }, corCamera(node, queryData) { if (node.children === null) { if (node.id === queryData) { return true; } else { return false; } } for (let i = 0; i < node.children.length; i++) { if (this.corCamera(node.children[i], queryData)) { return true; } } return false; }, //查找 search(queryData) { let array = this.treeSelect(this.allNodes, queryData); this.nodes = array; this.allNoExpand(true); }, treeSelect(nodes, queryData) { let newArray = []; for (let i = 0; i < nodes.length; i++) { let node = nodes[i]; if (node.children === null && this.treeDeepQuery(node, queryData)) { newArray.push(node); } else { let deepQuery = this.treeDeepQuery(node, queryData); if (deepQuery) { let newNode = JSON.parse(JSON.stringify(node)); if (node.children) { newNode.children = this.treeSelect(node.children, queryData); } newArray.push(newNode) } } } return newArray; }, treeDeepQuery(node, queryData) { if (node.children === null) { if (node.name.indexOf(queryData) !== -1) { return true; } else { return false; } } for (let i = 0; i < node.children.length; i++) { if (this.treeDeepQuery(node.children[i], queryData)) { return true; } } return false; } }, mounted() { EventBus.$on("item-click", this.cameraClick) EventBus.$on('ReversePositioning',this.reverseCamera) let T = this this.$axios.get(`/camera/menu/list?&time=${new Date().getTime()}`).then(res => { if (res.data.code !== 200) { this.$Message.error(res.data.msg) } else { T.nodes = res.data.data T.allNodes = res.data.data } }) }, beforeDestroy() { } } </script> <style lang="scss" scoped> </style>
树组件
<template> <div class="menu-tree" :class="{'menu-tree-top' : top}"> <div v-for="item in nodes" :key="item.id"> <!--菜单--> <div class="menu-item" @click="folderClick(item)" v-show="item.children !== null && item.num > 0"> <div style="position: absolute;top: 0;left: 20px;width: calc(100% - 20px);height: 100%"></div> <i class="iconfont iconjia" v-show="!item.expand" style="color: #0ABBE5"></i> <i class="iconfont iconjian" v-show="item.expand"></i> {{item.name}}<span v-show="item.num > 0">({{item.num}})</span> </div> <!--摄像头--> <div class="menu-item" @click="itemClick(item)" v-show="item.children === null" :class="{'menu-item-selected-parent':item.id === selected}"> <div style="position: absolute;top: 0;left: 20px;width: calc(100% - 20px);height: 100%" :class="{'menu-item-selected':item.id === selected}"></div> <i class="iconfont iconshexiangtou" :class="[{'online': item.statusName === '在线'},{'offline': item.statusName !== '在线'}]"></i> <span>({{item.resource}}) </span>{{item.name}} </div> <!--子菜单递归--> <div v-show="item.children && item.children.length > 0 && item.expand" class="child-menu"> <menu-tree :nodes="item.children" :selected="selected" :top="false"></menu-tree> </div> </div> </div> </template> <script> import EventBus from "@/utils/EventBus"; export default { props: { nodes: Array, selected: String, top: { required: false, default: true } }, name: "MenuTree", data() { return {} }, mounted() { }, methods: { itemClick(item) { EventBus.$emit('item-click', item); }, folderClick(item) { this.nodes.forEach(node => { if (node.id === item.id) { node.expand = !node.expand; } }) } } } </script> <style lang="scss"> .menu-tree { color: #333; font-size: 17px; position: relative; .menu-item { position: relative; padding-left: 25px; height: 40px; line-height: 40px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; .online { color: #1686D8; } .offline { color: #B7C3CE; } .menu-item-selected { border: 1px solid #1686D8; background: rgba(22, 134, 216, 0.1); } &:hover { cursor: pointer; > div { &:first-child { border: 1px solid #1686D8; background: rgba(22, 134, 216, 0.1); } } } &:after { content: ""; width: 11px; height: 20px; position: absolute; left: 12px; top: 19px; border-width: 1px; border-top: 1px dashed #52627C; } } .menu-item-selected-parent { font-weight: bold; } .menu-item-camera { width: 100%; } .child-menu { margin-left: 20px; position: relative; &:before { content: ""; height: calc(100% - 11px); width: 1px; position: absolute; left: 12px; top: -11px; border-width: 1px; border-left: 1px dashed #52627C; } } } .menu-tree-top > div > .menu-item { /*padding-left: 0;*/ &:before { border-left: none; } &:after { border-top: none; } } </style>