zoukankan      html  css  js  c++  java
  • ElementUI 复杂顶部和左侧导航栏实现

    描述:如图

    项目路径如下图所示:

    代码实现:

    首先在store.js中添加两个状态:

    import Vue from 'vue'
    import Vuex from 'vuex'
    Vue.use(Vuex)
    const state = {
        topNavState: 'home',
        leftNavState: 'home'
    }
    export default new Vuex.Store({
        state
    })

    App.vue内容:

    <template>
      <div id="app">
        <router-view></router-view>
      </div>
    </template>
    <script>
      export default {
        name: 'app'
      }
    </script>

    main.js代码:

    import Vue from 'vue'
    import App from './App'
    import router from './router'
    import ElementUI from 'element-ui'
    import store from './store.js'
    import 'element-ui/lib/theme-chalk/index.css'
    import '@/assets/iconfont.css'
    import '@/assets/css/style.css'
    Vue.config.productionTip = false
    Vue.use(ElementUI)
    
    new Vue({
        router,
        store,
        el: '#app',
        render: h => h(App)
    })

    router/index.js文件声明路由:

    其中:行程计划、任务、通讯录属于首页大板块(topNavState=“home”);企业信息、车辆信息、部门信息都属于enterprise这一大板块(topNavState=“enterprise”)

    import Vue from 'vue'
    import Router from 'vue-router'
    import LeftNav from '@/components/nav/leftNav.vue'
    import Home from '@/views/home.vue'
    import Dashboard from '@/views/workbench/dashboard.vue'
    import Mission from '@/views/workbench/mission/mission.vue'
    import Plan from '@/views/workbench/plan.vue'
    import Maillist from '@/views/workbench/maillist.vue'
    import EnterpriseList from '@/views/enterprise/index.vue'
    import EnterpriseAdd from '@/views/enterprise/add.vue'
    import EnterpriseDetail from '@/views/enterprise/detail.vue'
    import EnterpriseValidate from '@/views/enterprise/validate.vue'
    import VehicleManage from '@/views/vehicle/index.vue'
    import DeptManager from '@/views/dept/index.vue'
    import NotFound from '@/components/404.vue'
    
    // 懒加载方式,当路由被访问的时候才加载对应组件
    const Login = resolve => require(['@/views/login'], resolve)
    
    Vue.use(Router)
    
    let router = new Router({
      routes: [
        {
          path: '/login',
          type: 'login',
          component: Login
        },
        {
          path: '*',
          component: NotFound
        },
        {
          path: '/',
          type: 'home', // 根据type区分不同模块(顶部导航)
          name: 'home', // 根据name区分不同子模块(左侧导航)
          redirect: '/dashboard',
          component: Home,
          menuShow: true,
          children: [
            {
              path: '/dashboard',
              component: LeftNav,
              name: 'dashboard', // 当前路由的name
              leaf: true, // 只有一个节点
              iconCls: 'iconfont icon-home', // 图标样式class
              menuShow: true,
              children: [
                { path: '/dashboard', component: Dashboard, name: '首页', menuShow: true }
              ]
            },
            {
              path: '/mySet',
              component: LeftNav,
              name: '我的设置',
              iconCls: 'el-icon-menu',
              menuShow: true,
              children: [
                { path: '/mySet/plan', component: Plan, name: '行程计划', menuShow: true },
                { path: '/mySet/mission', component: Mission, name: '我的任务', menuShow: true },
                { path: '/mySet/maillist', component: Maillist, name: '通讯录', menuShow: true }
              ]
            }
          ]
        },
        {
          path: '/enterpriseManager',
          type: 'enterprise',
          name: 'enterprise',
          component: Home,
          redirect: '/enterprise/list',
          menuShow: true,
          children: [
            {
              path: '/enterpriseList',
              component: LeftNav,
              name: 'enterpriseList',
              leaf: true, // 只有一个节点
              iconCls: 'iconfont icon-home', // 图标样式class
              menuShow: true,
              children: [
                { path: '/enterprise/list', component: EnterpriseList, name: '企业列表', menuShow: true },
                { path: '/enterprise/detail', component: EnterpriseDetail, name: '企业详情', menuShow: false }
              ]
            },
            {
              path: '/enterpriseAdd',
              component: LeftNav,
              name: 'enterpriseAdd',
              leaf: true, // 只有一个节点
              iconCls: 'el-icon-menu',
              menuShow: true,
              children: [
                { path: '/enterprise/add', component: EnterpriseAdd, name: '企业添加', menuShow: true }
              ]
            },
            {
              path: '/enterpriseValidate',
              component: LeftNav,
              name: 'enterpriseValidate',
              leaf: true, // 只有一个节点
              iconCls: 'el-icon-menu',
              menuShow: true,
              children: [
                { path: '/enterprise/validate', component: EnterpriseValidate, name: '企业认证', menuShow: true }
              ]
            }
          ]
        },
        {
          path: '/vehicleManager',
          type: 'enterprise',
          name: 'vehicle',
          component: Home,
          redirect: '/vehicle/list',
          menuShow: true,
          children: [
            {
              path: '/vehicleList',
              component: LeftNav,
              name: 'vehicleList',
              leaf: true, // 只有一个节点
              iconCls: 'iconfont icon-home', // 图标样式class
              menuShow: true,
              children: [
                { path: '/vehicle/list', component: VehicleManage, name: '车辆信息', menuShow: true }
              ]
            }
          ]
        },
        {
          path: '/deptManager',
          type: 'enterprise',
          name: 'dept',
          component: Home,
          redirect: '/dept/list',
          menuShow: true,
          children: [
            {
              path: '/deptList',
              component: LeftNav,
              name: 'deptList',
              leaf: true, // 只有一个节点
              iconCls: 'iconfont icon-home', // 图标样式class
              menuShow: true,
              children: [
                { path: '/dept/list', component: DeptManager, name: '部门信息', menuShow: true }
              ]
            }
          ]
        }
    
      ]
    });
    
    router.beforeEach((to, from, next) => {
      // console.log('to:' + to.path)
      if (to.path.startsWith('/login')) {
        window.localStorage.removeItem('access-user')
        next()
      } else if(to.path.startsWith('/register')){
        window.localStorage.removeItem('access-user')
        next()
      } else {
        let user = JSON.parse(window.localStorage.getItem('access-user'))
        if (!user) {
          next({path: '/login'})
        } else {
          next()
        }
      }
    });
    
    export default router

     特别说明:

    这里的路由对象router ,设置的是最多三级,一级路由主要对应的是顶部导航和其他无子页面的路由,二级和三级路由分别对应的是左侧导航的一级和二级菜单(比如三级路由对应的就是左侧导航的二级菜单),二级路由设置leaf属性,值为true表明该路由下没有子菜单(如果该路由下的某页面不显示在左侧导航,不算子菜单)。

    leftNav.vue文件中主要是左侧导航菜单加载代码:

    <template>
      <el-col :span="24" class="main">
        <!--左侧导航-->
        <aside :class="{showSidebar:!collapsed}">
          <!--展开折叠开关-->
          <div class="menu-toggle" @click.prevent="collapse">
            <i class="iconfont icon-outdent" v-show="!collapsed" title="收起"></i>
            <i class="iconfont icon-indent" v-show="collapsed" title="展开"></i>
          </div>
          <!--导航菜单-->
          <el-menu :default-active="$route.path" router :collapse="collapsed" ref="leftNavigation">
            <template v-for="(issue,index) in $router.options.routes">
              <template v-if="issue.name === $store.state.leftNavState"><!-- 注意:这里就是leftNavState状态作用之处,当该值与router的根路由的name相等时加载相应菜单组 -->
                <template v-for="(item,index) in issue.children">
                  <el-submenu v-if="!item.leaf" :index="index+''" v-show="item.menuShow">
                    <template slot="title"><i :class="item.iconCls"></i><span slot="title">{{item.name}}</span></template>
                    <el-menu-item v-for="term in item.children" :key="term.path" :index="term.path" v-if="term.menuShow"
                                  :class="$route.path==term.path?'is-active':''">
                      <i :class="term.iconCls"></i><span slot="title">{{term.name}}</span>
                    </el-menu-item>
                  </el-submenu>
                  <el-menu-item v-else-if="item.leaf&&item.children&&item.children.length" :index="item.children[0].path"
                                :class="$route.path==item.children[0].path?'is-active':''" v-show="item.menuShow">
                    <i :class="item.iconCls"></i><span slot="title">{{item.children[0].name}}</span>
                  </el-menu-item>
                </template>
              </template>
            </template>
          </el-menu>
        </aside>
        <!--右侧内容区-->
        <section class="content-container">
          <div class="grid-content bg-purple-light">
            <el-col :span="24" class="content-wrapper">
              <transition name="fade" mode="out-in">
                <router-view></router-view>
              </transition>
            </el-col>
          </div>
        </section>
      </el-col>
    </template>
    <script>
      export default {
        name: 'leftNav',
        data () {
          return {
            collapsed: false
          }
        },
        methods: {
          //折叠导航栏
          collapse: function () {
            this.collapsed = !this.collapsed;
          },
          // 左侧导航栏根据当前路径默认打开子菜单(如果当前路由是三级,则父级子菜单默认打开)
          defaultLeftNavOpened () {
            let cur_path = this.$route.path; //获取当前路由
            let routers = this.$router.options.routes; // 获取路由对象
            let subMenuIndex = 0, needOpenSubmenu = false;
            for (let i = 0; i < routers.length; i++) {
              let children = routers[i].children;
              if(children){
                for (let j = 0; j < children.length; j++) {
                  if (children[j].path === cur_path) {
                    break;
                  }
                  // 如果该菜单下有子菜单(个数>1且设置的leaf为false才有下拉子菜单)
                  if(children[j].children && !children[j].leaf) {
                    let grandChildren = children[j].children;
                    for(let z=0; z<grandChildren.length; z++) {
                      if(grandChildren[z].path === cur_path) {
                        subMenuIndex = j;
                        needOpenSubmenu = true;
                        break;
                      }
                    }
                  }
                }
              }
            }
            if(this.$refs['leftNavigation'] && needOpenSubmenu) {
              this.$refs['leftNavigation'].open(subMenuIndex); // 打开子菜单
            }
          },
        },
        mounted() {
          this.defaultLeftNavOpened();
        }
      }
    </script>

    home.vue是后台主页组件代码

    <template>
      <el-row class="container">
        <!--头部-->
        <el-col :span="24" class="topbar-wrap">
          <div class="topbar-logo topbar-btn">
            <a href="/"><img src="../assets/logo.png" style="padding-left:8px;"></a>
          </div>
          <div class="topbar-logos" v-show="!collapsed">
            <a href="/" style="color: #fff;">车车综合管理</a>
          </div>
          <div class="topbar-title">
            <el-row v-show="$store.state.topNavState==='home'"><!-- 注意:这里就是topNavState作用之处,根据当前路由所在根路由的type值判断显示不同顶部导航菜单 -->
              <el-col :span="24">
                <el-menu :default-active="defaultActiveIndex" class="el-menu-demo" mode="horizontal" @select="handleSelect" :router="true">
                  <el-menu-item index="/">工作台</el-menu-item>
                  <el-menu-item index="/enterpriseManager">企业管理</el-menu-item>
                  <el-menu-item index="/orderManager">订单管理</el-menu-item>
                  <el-menu-item index="/systemManager">系统管理</el-menu-item>
                </el-menu>
              </el-col>
            </el-row>
            <el-row v-show="$store.state.topNavState==='enterprise'">
              <el-col :span="24">
                <el-menu :default-active="defaultActiveIndex" class="el-menu-demo" mode="horizontal" @select="handleSelect" :router="true">
                  <el-menu-item index="/enterpriseManager">企业信息</el-menu-item>
                  <el-menu-item index="/vehicleManager">车辆信息</el-menu-item>
                  <el-menu-item index="/deptManager">组织架构</el-menu-item>
                </el-menu>
              </el-col>
            </el-row>
          </div>
          <div class="topbar-account topbar-btn">
            <el-dropdown trigger="click">
              <span class="el-dropdown-link userinfo-inner">
                <i class="iconfont icon-user"></i> {{nickname}}   <i class="el-icon-caret-bottom"></i></span>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item>
                  <div @click="jumpTo('/user/profile')"><span style="color: #555;font-size: 14px;">个人信息</span></div>
                </el-dropdown-item>
                <el-dropdown-item>
                  <div @click="jumpTo('/user/changepwd')"><span style="color: #555;font-size: 14px;">修改密码</span></div>
                </el-dropdown-item>
                <el-dropdown-item divided @click.native="logout">退出登录</el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
          </div>
        </el-col>
    
        <!--中间-->
        <transition name="fade" mode="out-in">
          <router-view></router-view>
        </transition>
      </el-row>
    </template>
    <script>
      export default {
        name: 'home',
        data () {
          return {
            defaultActiveIndex: "/",
            loading: false,
            nickname: '',
            collapsed: false
          }
        },
        created() {// 组件创建完后获取数据,
          // 此时 data 已经被 observed 了
          this.fetchNavData();
        },
        methods: {
          handleSelect(index){
            this.defaultActiveIndex = index;
          },
          //折叠导航栏
          collapse () {
            this.collapsed = !this.collapsed;
          },
          fetchNavData () { // 初始化菜单激活项
            var cur_path = this.$route.path; //获取当前路由
            var routers = this.$router.options.routes; // 获取路由对象
            var nav_type = "", nav_name = "";
            for (var i = 0; i < routers.length; i++) {
              var children = routers[i].children;
              if(children){
                for (var j = 0; j < children.length; j++) {
                  var grand_children = children[j].children;
                  if(grand_children){
                    for (var k = 0; k < grand_children.length; k++) {
                      if (grand_children[k].path === cur_path) {
                        nav_type = routers[i].type;
                        nav_name = routers[i].name;
                        break;
                      }
                      // 如果该菜单下还有子菜单
                      if(children[j].children) {
                        let grandChildren = children[j].children;
                        for(let z=0; z<grandChildren.length; z++) {
                          if(grandChildren[z].path === cur_path) {
                            nav_type = routers[i].type;
                            nav_name = routers[i].name;
                            break;
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
            this.$store.state.topNavState = nav_type;  // 改变topNavState状态的值
            this.$store.state.leftNavState = nav_name;  // 改变leftNavState状态的值
            if(nav_type == "home"){
              this.defaultActiveIndex = "/";
            } else {
              this.defaultActiveIndex = "/" + nav_name + "Manager";
            }
          },
          jumpTo(url){
            this.defaultActiveIndex = url;
            this.$router.push(url); //用go刷新
          },
          logout(){
            //logout
            let that = this;
            this.$confirm('确认退出吗?', '提示', {
              confirmButtonClass: 'el-button--warning'
            }).then(() => {
              //确认
              localStorage.removeItem('access-user');
              that.$router.go('/login'); //用go刷新
            }).catch(() => {});
          }
        },
        mounted() {
          let user = localStorage.getItem('access-user');
          if (user) {
            user = JSON.parse(user);
            this.nickname = user.nickname || '';
          }
        },
        watch: {
          '$route': 'fetchNavData'  //监听router值改变时,改变导航菜单激活项
        }
      }
    </script>

    注意fetchNavData()这个方法,主要是根据当前跳转的路由,去找到这个路由对应的type(对应顶部导航栏的分类)和name(对应左侧导航栏的分类),然后保存type和name到$store中,这样在顶部导航可以根据$store中的type显示相应的菜单,同样在左侧导航就可以取到这个name值并显示相应的左侧菜单栏了。

    之前写的左侧导航栏(leftNav.vue)的代码有个不足的地方——当前打开页面是三级路由(也就是左侧导航的二级菜单)时,刷新当前页,(在左侧导航中)当前路由所属的一级菜单没有默认打开。

    解决方案已修改,在leftNav.vue中添加defaultLeftNavOpened()方法,详情可以回看leftNav.vue的代码。

    对于这个项目的demo,也放到了github共享,请看github地址:https://github.com/yqrong/ccproject.git

    最近这段时间把项目修改了下(将顶部导航也提取出来作为命名视图),详情请看下篇:

    ElementUI+命名视图实现复杂顶部和左侧导航栏

    补充:

    今天有位细心的网友发现了一个小问题:

    左侧导航当前激活的是非第一个菜单时,切换其他顶部导航再切换回来,会有两个激活的菜单。

    解决:el-menu标签缺少default-active属性设置,加上即可。

    <el-menu :default-active="$route.path" router :collapse="collapsed" ref="leftNavigation">
    <!-- ... -->
    </el-menu>

    写代码还是不够细心,自我检讨一下。

  • 相关阅读:
    GIT版本控制工具使用
    Django一些常用settings设置
    排序算法
    pipenv简单使用
    scrapy以及redis简单应用
    GIT上传本地项目到Github
    Requests爬虫和scrapy框架多线程爬虫
    Django分页组件
    关于 KendoUI Grid的默认选中的一些事
    关于在笔记本使用eclipseIDE工具进行开发时怎么切换eclipse输入状态
  • 原文地址:https://www.cnblogs.com/yeqrblog/p/8991587.html
Copyright © 2011-2022 走看看