需求: vue实现仿zTree折叠树,此文章仅作为记录文档。
实现:
<template> <div class="line-tree"> <div v-for="(item, index) in treeData" :key="index" class="single-content" :class="treeLine ? getSecond(item) : ''"> <span></span> <p class="parent-node"> <!-- treeLine 是否显示连接线 --> <span v-if="treeLine"> <span class="line-7" v-if="level !== 0 && (item.children && item.children.length)"></span> <span class="line-25" v-if="level !== 0 && (!item.children || !item.children.length)"></span> <span v-if="item.last" class="line-10-last"></span> </span> <span v-if="item.children && item.children.length && item.name" class="arrow-icon" @click="changeState(item)"> <img src="../../../../static/images/jurisdiction-add.png" v-if="!item.expand" alt="+" class="expand-icon"> <img src="../../../../static/images/jurisdiction-cut.png" v-else alt="-" class="expand-icon"> </span> <span v-else> <i style="display: inline-block; height: 14px; width: 16px; position: relative; top: 2px;"></i> </span> <span v-if="keyTree == 'role'"> <span class="city" :class="{'active-name' : treeActive === item.id}" @click="item.children.length === 0 ? skipCost(item) : parentClick(item)">{{item.name}}</span> </span> <span v-else> <span class="city" :class="{'active-name' : treeActive === item.id}" @click="(item.children.length === 0 || item.deep === 1) ? skipCost(item) : parentClick(item)">{{item.name}}</span> </span> </p> <div v-if="item.children && item.children.length && item.expand" class="sub-content" :class="treeLine ? cancelBor(item) : ''"> <permission-base-tree @skipCost="skipCost" :currentDep="currentDep" :level="level + 1" :treeData="item.children" :keyTree="keyTree"></permission-base-tree> </div> </div> </div> </template> <script> import { mapGetters, mapMutations } from "vuex"; export default { name: 'permission-base-tree', data() { return {} }, props: { treeData: { type: Array, default: () => [] }, level: { type: Number, default: () => 0 }, currentDep: { type: Number, default: () => 0 }, keyTree: { type: String, default: () => "" }, treeLine: { type: Boolean, default: () => false } }, computed: { ...mapGetters(["treeActive"]) }, watch: { treeData: { handler(newVal, oldVal) { if (newVal) { this.treeData = newVal; } }, deep:true }, }, created() { }, methods: { ...mapMutations({ setActive: 'SET_treeActive' }), skipCost(item) { this.setActive(item.id); this.$emit('skipCost', item); }, parentClick(item) { this.setActive(item.id); item.expand = !item.expand; }, /** * 改变生效状态 */ changeEffect(item, sub) { this.$emit('changeEffect', [item, sub]); }, /** * 添加 */ add(item) { console.log('item', item); this.$emit('add', item); }, /** * 编辑 */ edit(item, sub) { this.$emit('edit', [item, sub]); }, /** * 删除 */ deleteWarning(item, sub) { this.$emit('deleteWarning', [item, sub]); }, changeSelect(item, index){ item.select = !item.select; if (item.select) { // 子类全选 if (item.children && item.children.length) { // 子类变为选中状态 this.transferTrue(item.children, true); } console.log('select',[true, item.levelList]); this.$emit('changeParent', [true, item.levelList]); } else { // 取消全选 if (item.children && item.children.length) { this.transferTrue(item.children, false); } this.$emit('changeParent', [false, item.levelList]); } }, changeParent(val) { if (val[0]) { // 子类为true let flag = true; let arr = val[1].slice(0, val[1].length - 1); let idx = arr[arr.length - 1]; for (let i = 0; i < this.treeData[idx].children.length; i++) { if (!this.treeData[idx].children[i].select) { flag = false; break; } } if (flag) { this.treeData[idx].select = true; } else { this.treeData[idx].select = false; } this.$emit('changeParent', [true, this.treeData[idx].levelList]) } else { let arr = val[1].slice(0, val[1].length - 1); let idx = arr[arr.length - 1]; this.treeData[idx].select = false; this.$emit('changeParent', [false, this.treeData[idx].levelList],val[2]); } }, cancelBor(item) { if (!item.last) { return 'add-second'; } }, getSecond(item) { if (!item.last) { return 'add-second'; } }, getMargin(item) { let currentLevel = item.level; if (item.children && item.children.length) { return { marginRight: (this.currentDep - item.level - 1) * 18 + 'px' } } }, transferTrue(arr, flag) { arr.forEach((item, index) => { item.select = flag; if (item.children && item.children.length) { this.transferTrue(item.children, flag); } }) }, changeState(item) { item.expand = !item.expand; } } } </script> <style lang="less" scoped> .line-tree { font-size: #000; .single-content { position: relative; left: -11px; padding-left: 11px; } .single-troditional { display: inline-block; height: 24px; line-height: 24px; background: #B1D6FF; margin-right: 10px; padding: 0 5px; color: #000; border-radius: 3px; } .common-img { display: inline-block; margin-left: 5px; } .common-img:hover { cursor: pointer; } .add-continal { color: #0847A9; margin-right: 15px; } .add-continal:hover { cursor: pointer; } .add-second { border-left: 1px dashed #FFF; } .line-15 { position: absolute; left: -10px; top: 15px; display: inline-block; width: 1px; height: 13px; border-left: 1px dashed #FFF; } .line-10 { position: absolute; left: -10px; top: 0; display: inline-block; width: 1px; height: 15px; border-left: 1px dashed #FFF; } .line-10-last { position: absolute; left: -11px; top: 0; display: inline-block; width: 1px; height: 15px; border-left: 1px dashed #FFF; } .line-7 { position: absolute; left: -10px; top: 15px; display: inline-block; width: 7px; height: 1px; border-bottom: 1px dashed #FFF; } .line-25 { position: absolute; left: -10px; top: 15px; display: inline-block; width: 25px; height: 1px; border-bottom: 1px dashed #FFF; } .sub-content { margin-left: 7px; padding-left: 11px; } .add-border { border-left: 1px dashed #FFF; } .parent-node { height: 30px; vertical-align: middle; position: relative; white-space: nowrap; } img { vertical-align: middle; } .file-icon { display: inline-block; width: auto; height: 14px; } .arrow:hover { cursor: pointer; } .select-icon { display: inline-block; width: 15px; height: 15px; } .last-content { padding-left: 30px; } .select-box:hover { cursor: pointer; } .city { font-size: 14px; color: #000; display: inline-block; height: 30px; line-height: 30px; } .active-name { color: #0847A9; font-weight: bold; text-decoration: underline; } .city:hover { cursor: pointer; } .expand-icon { display: inline-block; height: 14px; width: auto; } .expand-icon:hover { cursor: pointer; } .init-line { position: absolute; left: 7px; top: -3px; display: inline-block; width: 0; height: 8px; box-sizing: border-box; border-left: 1px dashed #FFF; } .outer { position: absolute; left: 7px; top: 2px; display: inline-block; width: 14px; height: 20px; box-sizing: border-box; border-left: 1px dashed #FFF; .inner { display: inline-block; width: 10px; height: 0; box-sizing: border-box; border-bottom: 1px dashed #FFF; position: relative; top: 0px; } } .outer-sub { position: absolute; left: 7px; display: inline-block; width: 14px; height: 13px; top: 2px; box-sizing: border-box; border-left: 1px dashed #FFF; .inner-sub { display: inline-block; width: 10px; height: 0; box-sizing: border-box; border-bottom: 1px dashed #FFF; position: relative; top: 0px; } } } </style>