标签:etl bar nsf mamicode imp res 操作 效果 category
在开发一个 PC 端的项目时,需要开发一个树状结构,直接上效果图如下:点击 "+" 号的时候则展开下一级,点击 "-" 号的时候则收起;
之所以写这篇博客,因为在实现过程中用到了组件递归,觉得之后再遇到此种功能时能借鉴一下,treeViewItem.vue 中通过 name: "treeViewItem" 实现组件内自己调用自己,实现组件递归,从而实现对树状结构数据的渲染;
数据结构如下:
代码如下:
menusModule.js (store/module/menusModule.js)
let menus = []; let levelNum = 1; let currentComCode = ‘‘; let startExpand = []; // 保存刷新后当前要展开的菜单项 if(localStorage.getItem(‘comInfoParam‘)){ currentComCode = JSON.parse(localStorage.getItem(‘comInfoParam‘)).key; } function setExpand(source, url) { let sourceItem = ‘‘; for (let i = 0; i < source.length; i++) { sourceItem = JSON.stringify(source[i]); // 把菜单项转为字符串 if (sourceItem.indexOf(url) > -1) { // 查找当前 URL 所对应的子菜单属于哪一个祖先菜单 if (source[i].type === ‘button‘) { // 导航菜单为按钮 source[i].isSelected = true; // 设置选中高亮 source[i].isExpanded = true; // 设置为展开 startExpand.push(source[i]); // 递归下一级菜单,以此类推 setExpand(source[i].subMenu, url); } break; } } } const state = { menus, levelNum, currentComCode }; const mutations = { findParents(state, payload) { if (payload.menu.ifExits === ‘true‘) { payload.menu.isExpanded = !payload.menu.isExpanded; } else if (payload.menu.ifExits === ‘false‘) { if (startExpand.length > 0) { for (let i = 0; i < startExpand.length; i++) { startExpand[i].isSelected = false; } } startExpand = []; // 清空展开菜单记录项 setExpand(state.menus, payload.menu.url); }; }, firstInit(state, payload) { setExpand(state.menus, payload.url); }, getList(state, payload) { state.menus = payload; }, getCurrentComCode(state,payload) { state.currentComCode = payload; } } export default { state, mutations };
index.js (store/index.js)
import Vue from ‘vue‘ import Vuex from ‘vuex‘ import menusModule from ‘./module/menusModule‘ Vue.use(Vuex); const store = new Vuex.Store({ modules: { menusModule } }) export default store
categoryManage.vue
<template> <main class=‘categoryManage‘> <h2>类目管理</h2> <div class="categoryContent"> <div class="operateCategory"> <button @click="addNewCategory">添加类目</button> </div> <div class="categoryTab"> <div class="categoryTabHeader"> <div class="categoryCode">类目编码</div> <div class="col-1 categoryName">类目名称</div> <div class="col-2 isDisplay">是否展示</div> <div class="col-3 categoryGrade">类目等级</div> <div class="col-4 categoryOperate">操作</div> </div> <div class="categoryTabBody"> <tree-view></tree-view> </div> <!-- 查询数据为空时 --> <div class="emptyTab" v-if=‘categoryList.length === 0‘> <span>啊噢~~ 查询不到数据,您可以点击右上角按钮手动添加类目.</span> </div> </div> </div> <hd-diolag :is-show="isShowDiolag" @getdata="isShowDiolag=false"> <p>{{contentText}}</p> </hd-diolag> </main> </template> <script> import treeView from ‘./treeView.vue‘; import { getCategoryList } from ‘../../../api/commodityDefine/categoryManage.js‘; export default { data() { return { categoryList:[], isShowDiolag:false, contentText:‘‘ }; }, mounted() { this.getCategoryListFn(); }, methods: { getCategoryListFn() { let _this = this; getCategoryList().then(res => { if(res.code === 200){ _this.categoryList = res.data; this.$store.commit("getList", _this.categoryList); }else{ this.isShowDiolag = true; this.contentText = res.msg; } }) }, addNewCategory() { let _this = this; _this.$router.push({ name: ‘newCategory‘, params: {editInfo:null,higherCategory:‘‘,isAddNewFlag:true,categoryList:_this.categoryList} }); }, }, components: { treeView } }; </script> <style scoped> /* 操作区域 */ .categoryManage > h2 { height: 60px; line-height: 60px; } .operateCategory { display: flex; justify-content: flex-end; } .operateCategory > button { background: rgb(45, 107, 145); border: 1px solid #ddd; width: 110px; border-radius: 5px; color: #fff; font-size: 16px; display: flex; align-items: center; justify-content: center; cursor: pointer; } .operateCategory > button:hover { background: #39f; } .operateCategory > button:before { content: ‘+‘; font-size: 30px; } /* 内容展示区域 */ .categoryTab { margin-top: 20px; } .categoryTab, .categoryTab .tabRow { font-size: 0; } .categoryTab .tabRow > div, .categoryTab .categoryTabHeader > div { display: inline-block; } .categoryTab .categoryTabHeader > div, .categoryTab .tabRow > div { display: inline-block; border-right: 1px solid #ccc; border-top: 1px solid #ccc; text-align: center; box-sizing: border-box; } .categoryTab > div.categoryTabHeader > div { font-size: 16px; } .categoryTab .tabRow > div { font-size: 14px; } .categoryTab .categoryCode{ width:17.13%; } .categoryTab .col-1 { width: 31.47%; border-left: 1px solid #ccc; position: relative; } .categoryTab > div .col-1 > span > span { cursor: pointer; position: absolute; left: 44%; font-size: 20px; top: -2px; font-weight: bold; } .categoryTab > div .col-1 > span > span:hover { color: #39f; } .categoryTab > div .col-2, .categoryTab > div .col-3, .categoryTab > div .col-4 { width: 17.13%; } .categoryTab .tabRow:last-child { border-bottom: 1px solid #ccc; } .categoryTab .categoryTabHeader { height: 32px; line-height: 32px; color: #fff; border-top-left-radius: 5px; } .categoryTab .categoryTabHeader > div { color: #fff; background: #444; } .categoryTab .tabRow > div { height: 30px; line-height: 30px; } .categoryOperate > span { cursor: pointer; } .categoryOperate > span:hover { color: #39f; } .categoryTab > div .col-1 > span > span.unfoldBtn { z-index: 10; background: #fff; text-align: center; width: 1rem; top: 0px; } /* 无数据时css start */ .emptyTab { font-size: 16px; height: 500px; display: flex; align-items: center; color: #999; justify-content: center; border: 1px solid #ccc; border-top:none; } </style>
treeView.vue
<template> <div class="tree-view-menu"> <tree-view-item :menus=‘menus‘></tree-view-item> </div> </template> <script> import treeViewItem from "./treeViewItem"; const menusData = []; export default { components: { treeViewItem }, name: "treeViewMenu", data() { return { menus: [] }; }, mounted(){ let _this = this; setTimeout(()=>{ _this.menus = _this.$store.state.menusModule.menus; for(var i=0;i<_this.menus.length;i++){ _this.$set(_this.menus[i],‘isExpanded‘,false); _this.$set(_this.menus[i],‘isSelected‘,false); let childs = _this.menus[i].childs; if(childs.length){ for(var j=0;j<childs.length;j++){ _this.$set(childs[j],‘isExpanded‘,false); _this.$set(childs[j],‘isSelected‘,false); } } } },100) } }; </script> <style scoped> .tree-view-menu { height: 100%; overflow-y: auto; overflow-x: hidden; } .tree-view-menu::-webkit-scrollbar { height: 6px; width: 6px; } .tree-view-menu::-webkit-scrollbar-trac { -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); } .tree-view-menu::-webkit-scrollbar-thumb { background-color: #6e6e6e; outline: 1px solid #333; } .tree-view-menu::-webkit-scrollbar { height: 4px; width: 4px; } .tree-view-menu::-webkit-scrollbar-track { -webkit-box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3); } .tree-view-menu::-webkit-scrollbar-thumb { background-color: #6e6e6e; outline: 1px solid #708090; } </style>
treeViewItem.vue
<template> <div class="tree-view-item"> <div class="level" :class="‘level-‘+ menu.gradeNo" v-for="(menu,index) in menus" :key="index"> <div> <div class=‘itemCode‘> {{menu.id}} </div> <div class="button heading name col-1" :class="{selected: menu.isSelected,expand:menu.isExpanded}" @click="toggle(menu)"> <!-- 折叠展开样式的控制 --> <span v-if=‘menu.childs.length && !menu.isExpanded‘ :class="{‘unfold‘:menu.childs.length && !menu.isExpanded}">+</span> <span v-if=‘!menu.childs.length || menu.isExpanded‘ :class="{‘fold‘:!menu.childs.length || menu.isExpanded}">-</span> <span>{{menu.name}}</span> </div> <div class="statusShow col-2">{{statusShowList[menu.ifShow]}}</div> <div class="gradeShow col-3">{{categoryList[menu.gradeNo]}}</div> <div class="operate col-4"> <span @click=‘editCategory(menu)‘>编辑</span> <!-- 此版本暂不做删除功能 --> <!-- <span>|</span> <span @click=‘deleteCategory(menu.gradeNo,index)‘>删除</span> --> </div> <transition name="fade"> <div class="heading-children" v-show="menu.isExpanded" v-if="menu.childs"> <tree-view-item :menus=‘menu.childs‘ :name=‘menu.name‘></tree-view-item> </div> </transition> </div> </div> </div> </template> <script> export default { name: "treeViewItem", props: ["menus","name"], data() { return { categoryList:[‘‘,‘一级类目‘,‘二级类目‘,‘三级类目‘,‘四级类目‘], statusShowList:[‘否‘,‘是‘] } }, created() { this.$store.commit("firstInit", { url: this.$route.path }); }, methods: { toggle(menu) { this.$store.commit("findParents", { menu }); }, editCategory(editItem){ var _this = this; this.$router.push({ name:‘newCategory‘, params:{editInfo:editItem,higherCategory:_this.name} }) }, deleteCategory(level,index){ this.menus.splice(index,1); } } }; </script> <style scoped> a { text-decoration: none; color: #333; } .button { position: relative; } .level-3:hover, .link:hover, .button:hover { color: #1976d2; background-color: #eee; cursor: pointer; } .icon { position: absolute; right: 0; display: inline-block; height: 24px; width: 24px; fill: currentColor; transition: -webkit-transform 0.15s; transition: transform 0.15s; transition: transform 0.15s, -webkit-transform 0.15s; transition-timing-function: ease-in-out; } .heading-children { padding-left: 14px; overflow: hidden; } .expand { display: block; } .collapsed { display: none; } .expand .icon { -webkit-transform: rotate(90deg); transform: rotate(90deg); } .selected { color: #1976d2; } .fade-enter-active { transition: all 0.5s ease 0s; } .fade-enter { opacity: 0; } .fade-enter-to { opacity: 1; } .fade-leave-to { height: 0; } /* 表格css start */ .tree-view-item .name>span:nth-child(1){ display: inline-block; font-size:20px; margin-top:-4px; } .tree-view-item .name>span{ vertical-align: middle; } .tree-view-item .name>span.unfold:hover, .tree-view-item .name>span.fold:hover{ color:#39f; } .tree-view-item .level{ width:100%; line-height: 24px; } .tree-view-item .level>div{ font-size:0; } .tree-view-item .heading-children{ padding-left:0; } .tree-view-item .level>div .statusShow, .tree-view-item .level>div .gradeShow, .tree-view-item .level>div .operate, .tree-view-item .level>div .name{ display: inline-block; font-size:14px; text-align: center; border-width:0 1px 1px 0; border-color:#ccc; border-style:solid; height:32px; line-height: 32px; box-sizing:border-box; } .tree-view-item .level>div .itemCode{ font-size: 14px; display: inline-block; text-align: center; color: #000; width:17.13%; box-sizing: border-box; height: 32px; line-height: 32px; border-left: 1px solid #ccc; border-bottom: 1px solid #ccc; } .tree-view-item .level>div .name{ width:31.47%; border-left:1px solid #ccc; text-align: left; } .tree-view-item .level>div .statusShow, .tree-view-item .level>div .gradeShow, .tree-view-item .level>div .operate{ width:17.13%; } .tree-view-item .level-1 .name{ padding-left:14.5%; } .tree-view-item .level-2 .name{ padding-left:15.5%; } .tree-view-item .level-3 .name{ padding-left:16.5%; } .tree-view-item .level-4 .name{ padding-left:17.5%; } .tree-view-item .operate >span{ cursor: pointer; } .tree-view-item .operate >span:hover{ color:#39f; } </style>
标签:etl bar nsf mamicode imp res 操作 效果 category
原文地址:https://www.cnblogs.com/rxqlx/p/10413864.html