动态加载用户菜单

  • 2020 年 2 月 18 日
  • 筆記

流程是用户登录后进入模块页面,点击不同的模块,进入菜单页面(模块不同,菜单内容也不同)

遇到的问题

1、菜单数据存储到store中页面刷新后页面空白

解决方法:在全局导航守卫中每次都初始化菜单

2、如何动态生成路由 (动态生成路由会叠加,如果已经存在再生成会警告)

采用方法:router . addRoutes ( data );

3、不同模块切换进入菜单页面,高亮显示有问题

解决方法:

: default-active = " routePath "

  created() {      this.routePath = this.$route.path;    },

3、退出登录后如何清空store中数据(vuex中并没有清空的方法)

暂时采用: location . reload ();

下面全部采用json模拟数据

详细代码如下

userPermission.json

{  "data":{"name":"小李"},  "token":"111",  "responseCode":"0000"  }

Login.vue

<template>    <div>      <el-form        :rules="rules"        ref="loginForm"        v-loading="loading"        element-loading-text="正在登录..."        element-loading-spinner="el-icon-loading"        element-loading-background="rgba(0, 0, 0, 0.8)"        :model="loginForm"        class="loginContainer"      >        <h3 class="loginTitle">系统登录</h3>        <el-form-item prop="username">          <el-input            size="normal"            type="text"            v-model="loginForm.username"            auto-complete="off"            placeholder="请输入用户名"          ></el-input>        </el-form-item>        <el-form-item prop="password">          <el-input            size="normal"            type="password"            v-model="loginForm.password"            auto-complete="off"            placeholder="请输入密码"            @keydown.enter.native="submitLogin"          ></el-input>        </el-form-item>        <el-checkbox size="normal" class="loginRemember" v-model="checked"></el-checkbox>        <el-button size="normal" type="primary" style="width: 100%;" @click="submitLogin">登录</el-button>      </el-form>      <div class="login-bottom">       bottom      </div>    </div>  </template>  <script>  export default {    name: "Login",    data() {      return {        loading: false,        loginForm: {          username: "admin",          password: "1"        },        checked: true,        rules: {          username: [            { required: true, message: "请输入用户名", trigger: "blur" }          ],          password: [{ required: true, message: "请输入密码", trigger: "blur" }]        }      };    },    methods: {      submitLogin() {        this.$refs.loginForm.validate(valid => {          if (valid) {            this.loading = true;            this.$axios              .get("mock/userPermission.json")              .then(res => {                console.log(res.data);                let data = res.data                if(data.responseCode=="0000"){                   this.$store.commit("updatePermissionInfo", res.data);                   sessionStorage.setItem("user", data.data.name);                   sessionStorage.setItem("token", data.token);                   this.$router.push("/dashboard");                }              })              .catch(error => {                console.log(error.response)                this.$alert(error, "提示", {                  confirmButtonText: "确定",                  type: "warning"                });              });          } else {            this.$message.error("请输入所有字段");            return false;          }        });      }    }  };  </script>    <style scoped>  .loginContainer {    width: 450px;    height: 320px;    border-radius: 15px;    background: #fff;    position: absolute;    left: 0;    right: 0;    top: -60px;    bottom: 0;    /* background:linear-gradient(to bottom,#0675bd, #0363a1); */    box-shadow: 0 0 25px #cac6c6;    padding: 15px 35px 15px 35px;    margin: auto;  }  .loginTitle {    margin: 15px auto 20px auto;    text-align: center;    color: #505458;  }    .loginRemember {    text-align: left;    margin: 0px 0px 15px 0px;  }  .loginTip {    color: #d31245;    font-style: italic;    margin-bottom: 25px;  }  .login-bottom {    position: fixed;    bottom: 15px;    width: 100%;    text-align: center;  }  .login-bottom img {    vertical-align: middle;    width: 65px;    margin-right: 10px;  }  </style>

模块页面

dishboardInfo.json

{      "data": [{          "id":1,          "path": "/industry",          "name": "模块1",          "component": "Home"        }, {          "id":2,          "path": "/commercial",          "name": "模块2",          "component": "Home"      }]  }

dashboard.vue

<template>    <div class>      <ul class="dashboard">        <!-- <router-link tag="li" v-for="(item,index) in info" :key="index" :to="item.path">{{item.menuName}}</router-link> -->        <li v-for="(item,index) in info" :key="index" @click="handleClick(index)">{{item.name}}</li>      </ul>    </div>  </template>  <script>  export default {    data() {      return {        info: []      };    },    created() {      let userName = sessionStorage.getItem("user");      console.log(userName);      this.$axios.get("mock/dishboardInfo.json").then(res => {        console.log(res.data);        this.info = res.data.data;      });    },    computed: {},    methods: {      handleClick(index) {        this.$router.push({          path: this.info[index].path,          query: { menuId: this.info[index].id }        });      }    }  };  </script>

路由

import Vue from 'vue'  import VueRouter from 'vue-router'  import Login from '../views/Login.vue'  import Dashboard from '../views/dashboard.vue'  import Home from '../views/Home.vue'    Vue.use(VueRouter)    // const originalPush = VueRouter.prototype.push  // VueRouter.prototype.push = function push(location) {  //   return originalPush.call(this, location).catch(err => err)  // }    const routes = [{      path: '/',      redirect: "/login",    },    {      path: "/login",      name: 'login',      component: Login    },    {      path: "/dashboard",      name: 'dashboard',      component: Dashboard    },    {      path: '/:id',      name: "home",      //component: () => import('../views/Home.vue')      component: Home,    }  ]    const router = new VueRouter({    routes  })    export default router

Store

import Vue from 'vue'  import Vuex from 'vuex'    Vue.use(Vuex)    export default new Vuex.Store({    state: {      currentModule: sessionStorage.getItem("currentModule") || "",      currentMenuId: sessionStorage.getItem("currentMenuId") || "",      //routes:[],      routes: {        industry: [],        commercial: [],        person: []      },      permissionInfo: JSON.parse(sessionStorage.getItem("permissionInfo")) || {},    },    mutations: {      updatePermissionInfo(state, permissionInfo) {        state.permissionInfo = permissionInfo      },      initRoutes(state, data) {        console.log(data)        if (data) {          state.routes[data.key] = data.value        } else {          state.routes = {            industry: [],            commercial: []          }        }        // if (JSON.stringify(data) == "{}"){        //   console.log("空对象")        //   state.routes = {        //     industry: [],        //     commercial: []        //   }        //   return        // }          // console.log("有对象")        // state.routes[data.key]= data.value      },      setCurrentModule(state, currentModule) {        state.currentModule = currentModule        sessionStorage.setItem("currentModule", currentModule)      },      setCurrentMenuId(state, currentMenuId) {        state.currentMenuId = currentMenuId        sessionStorage.setItem("currentMenuId", currentMenuId)      }    },    actions: {},    modules: {}  })

menu.js

import axios from 'axios'  export const initMenu = (router, store, to) => {      if (to.path == "/dashboard" || to.path == "/login") {          return      }      let currentMenuId = to.query.menuId || sessionStorage.getItem("currentMenuId")      let currentModule = to.params.id || sessionStorage.getItem("currentModule")      console.log(currentModule)      if (currentModule && store.state.routes[currentModule].length > 0) {          //sessionStorage.setItem("currentModule", currentModule)          store.commit("setCurrentModule", currentModule)          store.commit("setCurrentMenuId", currentMenuId)          console.log("return")          return;      } else {          //console.log("nei-1")          axios.get("mock/menudata-"+currentModule+".json").then(res => {              let data = res.data.data              let fmRoutes = formatRoutes(data);              //router.options.routes = fmRoutes              router.addRoutes(fmRoutes);              let dataObj = {}              dataObj.key = currentModule              dataObj.value = data              console.log(dataObj)              //dataObj.value = fmRoutes              store.commit("initRoutes", dataObj)              store.commit("setCurrentModule", currentModule)              store.commit("setCurrentMenuId", currentMenuId)          });      }  }  export const formatRoutes = (menuData) => {      //console.log(data);      let fmRoutes = [];      menuData.forEach(item => {          let {              path,              menuName,              component,              childMenu          } = item;          //console.log(children)          if (childMenu && childMenu instanceof Array) {              childMenu = formatRoutes(childMenu);          }          let fmRouter = {              path: path,              name: menuName,              children: childMenu,              component(resolve) {                  if (component.startsWith("Home")) {                      require(["../views/" + component + ".vue"], resolve);                  } else if (component.startsWith("Baobiao")) {                      require(["../views/baobiao/" + component + ".vue"], resolve);                  } else if (component.startsWith("DataAccount")) {                      require(["../views/dataAccount/" + component + ".vue"], resolve);                  }              }              //component: () => import("../views/" + component + ".vue")          };          fmRoutes.push(fmRouter);      });      //console.log(fmRoutes);      return fmRoutes;  }

main.js

import Vue from 'vue'  import App from './App.vue'  import router from './router'  import store from './store'  // 引入element-ui 组件  import ElementUI from 'element-ui'  // 引入element-ui 样式文件  import 'element-ui/lib/theme-chalk/index.css'  import './assets/js/jQuery-2.1.4.min.js'  import axios from "axios"  import '@/assets/css/global.css'  import 'font-awesome/css/font-awesome.min.css'  import {hasPermission} from "./utils/hasPermission"  import {initMenu} from "./utils/menu"  Vue.use(ElementUI)  //axios.defaults.baseURL = 'http://xxxxx'    // var instance = axios.create({  //    baseURL: 'http://localhost:8080/'  // });  // Vue.prototype.$instance = instance  //添加一个请求拦截器  // axios.interceptors.request.use(function (config) {  //   config.data=JSON.stringify(config.data);  //   return config;  // }, function (error) {  //   // Do something with request error  //   console.info("error: ");  //   console.info(error);  //   return Promise.reject(error);  // });      import qs from "qs";  Vue.prototype.$qs = qs;  Vue.prototype.$axios = axios  Vue.prototype.hasPerm = hasPermission  Vue.config.productionTip = false    //正常运行的代码  router.beforeEach((to, from, next) => {    let token = window.sessionStorage.getItem('token');    if (to.path != '/login' && !token) {      next({        path: '/login'      })    } else {      if (to.path == '/login' && token) {        next('/dashboard')      } else {        initMenu(router,store,to)        next()      }    }  })    var VUE = new Vue({    router,    store,    render: h => h(App)  }).$mount('#app')

Home.vue

<template>    <div>      <el-container :style="{height: containerHeight, border: '1px solid #eee'}" id="con">        <el-header style="background:#3c8dbc;">          <div class="left pull-left">            <img src="../assets/imgs/weeglogo.png" alt />            <span>当前模块:</span>            <span>{{currentModuleToChinese}}</span>          </div>          <div class="right pull-right">            <router-link to="/dashboard">              <i class="fa fa-home margin_r20" style="font-size:20px;" aria-hidden="true"></i>            </router-link>            <span class="margin_r20">{{user}}</span>            <span style="color:#f9c05e;" @click="logout">              <i class="fa fa-power-off" aria-hidden="true"></i>              注销            </span>          </div>        </el-header>          <el-container>          <el-aside width="230px">            <el-menu              router              :default-active="routePath"              unique-opened              background-color="#1f3146"              text-color="#32acca"              active-text-color="#ffd04b"            >              <NavMenu :navMenus="menuData"></NavMenu>            </el-menu>          </el-aside>          <el-container>            <el-main>              <el-tabs                :value="activeTabItem"                @tab-remove="closeTab"                class="content-body"                @tab-click="tabClick"              >                <el-tab-pane label="首页" name="adminIndex">                  <admin-index></admin-index>                </el-tab-pane>                <el-tab-pane                  v-for="item in tabs"                  :label="item.label"                  :key="item.index"                  :name="item.index+''"                  :closable="item.closable"                >                  </el-tab-pane>              </el-tabs>              <bread-crumb></bread-crumb>              <!-- <div>{{breab}}</div> -->              <router-view></router-view>            </el-main>            <el-footer>             footer            </el-footer>          </el-container>        </el-container>      </el-container>    </div>  </template>    <script>  import NavMenu from "@/components/NavMenu.vue";  import BreadCrumb from "@/components/Breadcrumb.vue";  import AdminIndex from "@/components/AdminIndex.vue";  export default {    data() {      return {        containerHeight: "",        //menuData: [],        routePath: "",        currentModuleChinese: "",        user: window.sessionStorage.getItem("user")      };    },    created() {      this.routePath = this.$route.path;    },    computed: {      menuData() {        let id = this.$store.state.currentModule;        console.log(id);        console.log(this.$store.state.routes[id]);        return this.$store.state.routes[id];      },      currentModuleToChinese() {        let currentModule = this.$store.state.currentModule;        switch (currentModule) {          case "industry":            return "模块1";            break;          case "commercial":            return "模块2";            break;          case "person":            return "模块3";            break;        }      },      tabs(){        return this.$store.state.tabs      },      activeTabItem(){        return this.$store.state.activeTabItem      }    },    components: { NavMenu, BreadCrumb },    methods: {      logout() {        this.$confirm("此操作将注销登录,是否继续?", "提示", {          confirmButtonText: "确定",          cancelButtonText: "取消",          type: "warning"        })          .then(() => {            //this.$axios.get("/logout");            window.sessionStorage.removeItem("token");            window.sessionStorage.removeItem("currentMenuId");            //this.$store.commit("initRoutes",{})            //this.$store.commit("initRoutes",null)            console.log(this.$store.state.routes);            location.reload();            //this.$router.replace("/")          })          .catch(() => {            this.$message({              type: "info",              message: "已取消操作"            });          });      }    },    // watch:{    //  $route(){    //    console.log(this.$route.path)    //    this.routePath = this.$route.path    //  }    // },    mounted() {      console.log("mounted");      this.containerHeight = window.innerHeight + "px";      console.log($);      $(window).resize(function() {        console.log("hi");        $("#con").height($(window).height() - 2);      });        //this.$router.push("/industrySub2")    }  };  </script>    <style>  .el-header {    background-color: #377fa9;    color: #fff;    height: 50px !important;    line-height: 50px !important;  }    .el-header .left img {    width: 120px;    vertical-align: middle;  }  .el-header .left span {    font-size: 20px;    color: #edf8ff;    margin-left: 15px;  }  .el-header .right {    float: right;  }  .el-header .right a {    color: #fff;  }  .el-aside {    /* color: #32acca !important; */    background: #1f3146 !important;  }  .el-menu {    border-right: none !important;    /* background: #1f3146 !important; */  }  .el-main{padding-top:0 !important;}  .el-footer {    background: gray;    height: 40px !important;    line-height: 40px !important;  }  .el-footer {    border-top: 1px solid #ccc;    background: #f8fafd;    padding: 10px;    margin-left: 0;  }  .el-footer img {    vertical-align: middle;    width: 65px;    margin-right: 10px;  }  </style>

关于无限极菜单,上一篇博客中有详细的介绍