yeqrblog

描述:如图

项目路径如下图所示:

代码实现:

首先在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

 

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

 

补充:

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

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

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

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

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

 

分类:

技术点:

相关文章: