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>

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

  • 相关阅读:
    arcgis api 3.x for js 入门开发系列八聚合效果(附源码下载)
    arcgis api 3.x for js 入门开发系列七图层控制(附源码下载)
    arcgis api 3.x for js 入门开发系列六地图分屏对比(附源码下载)
    arcgis api 3.x for js 入门开发系列五地图态势标绘(附源码下载)
    arcgis api 3.x for js 入门开发系列四地图查询(附源码下载)
    Java里面获取当前服务器的IP地址
    Flutter at Google I/O 2018
    Modbus RTU 协议使用汇总
    plsql 创建表空间、用户、赋予权限
    Oracle:ODP.NET Managed 小试牛刀
  • 原文地址:https://www.cnblogs.com/yeqrblog/p/8991587.html
Copyright © 2011-2022 走看看