vue动态获取菜单,动态跳转

以前的旧项目的vue跳转是不单单要在数据库存储记录,而且前端还要写路由代码。这就造成了两边都要写。
这边接到一个需求是说,仅仅需要在数据库里进行配置,不再在前端里写代码。
花了一天时间试错研究做了出来。以下讲解

登录流程

首先是登录的流程,这边是点击登录按钮,然后执行以下一段js

methods: {
      // 表单重置按钮
      resetLoginForm() {
        this.$refs.loginFormRef.resetFields();
      },
      login() {
        // 表单预验证
        // valid:bool类型
        this.$refs.loginFormRef.validate(async valid => {
          // console.log(valid)
          if (!valid) return false;
          const res = await $login.login(this.loginForm);
          console.log(res);
          if (res.code !== 200) {
            this.loginMessage=res.msg;
            return this.$message.error(res.msg);
          }
          this.loginMessage=res.msg;
          this.$message.success(res.msg);
          // 1、将登陆成功之后的token, 保存到客户端的sessionStorage中; localStorage中是持久化的保存
          //   1.1 项目中出现了登录之外的其他API接口,必须在登陆之后才能访问
          //   1.2 token 只应在当前网站打开期间生效,所以将token保存在sessionStorage中
          setAuthToken(res.data);
          await setAuthResourceList();
          // 2、通过编程式导航跳转到后台主页, 路由地址为:/home
          this.$router.push('/index');
          // var wordArray = CryptoJS.enc.Utf8.parse(JSON.stringify(res.data));
          // var base64 = CryptoJS.enc.Base64.stringify(wordArray);
          // window.location.href = 'http://192.168.0.28:81/#/loginSSO?data='+base64
        });
      }
    }

设计思路和改造

我们研究的是倒数第二行命令await setAuthResourceList();
这里设计思路是怎样呢?

首先这个setAuthResourceList()方法是发后台接口,获取数据库里的菜单数据,然后封装成树,最后将其添加到vue路由上

那么知道后就开始贴代码

export async function setAuthResourceList() {

  // 获得加密的菜单
  let resourceList = await $loginUser.getCurrentResourceList();
  store.commit('setAuthResourceList', resourceList);



  const list = store.getters.authResourceList;

  const map = {};
  // 旧代码
  // forEachAuth(list, map);


  // 将数据转换成树
  let treeList = flatTreeData(list, 'id', 'id', 'name', 'name', 'parentId', 'children');


  // 旧代码
  /*const menu = menu_router.reduce((array, item, index) => {
    if(map[item.path]){
      array.push(item);
    }
    return array;
  }, []);*/

  // 旧代码
  // forEachGetSortNo(menu);
  // forEachSort(menu);
  // forEachRedirect(menu);
  // store.commit('router/setRoutes', menu);



  // 将树的数据格式整成路由的形式
  changeRescource2Router(treeList)

  // 此为直接读取menu_router.js里的路由,现在不读取js了,改成了从数据库里读
  // let menuList = Object.assign([], menu_router);


  let firstChild =  getFirstRedirectChild(treeList);


  // 用于重定向,index.js里获取
  // await $store.commit('setFirstRedirectUrl', firstChild.path);
  await localStorage.setItem("firstRedirectUrl", firstChild.path);

  // 此处需要加上这一延时,即使放置本地缓存使用了await标识但是路由读取还是会读不到
  // 猜测是命令已经执行完放置,但是此时还没有放置成功,仅仅是命令执行完毕
  setTimeout(()=>{},3000);

  // 放置路由
  store.commit('router/setRoutes', treeList);




  function changeRescource2Router(list, map){
    list.forEach(item => {
      let routerTitle = item.name;
      let routerName = item.moduleCode;
      let path = item.value;

      item.path = path;
      item.name = routerName;
      item.title = routerTitle;

      if(item.component == 'LayOut'){

        // 处理根目录
        item.component = LayOut;

      }else if(item.component){
        // 处理可跳转的组件
        let componentRead =  item.component;

        // 只能使用这种字符串拼接的方式,
        // 如果直接为字符串则识别不了非常奇怪
        item.component = (resolve) => require(['@/pages' + componentRead], resolve);
      }

      if (item.children && item.children.length > 0) {
        changeRescource2Router(item.children, map);
      }
    });
  }

  // 指定3级儿子,
  // 多于3级的可能会为3级的嵌入页面,
  // 而不是在导航栏中可以直接访问的那种
  function getFirstRedirectChild(list){
    for( let i = 0;  i<list.length; i++){
      if(list[i].children.length>0){
        for(let j = 0; j<list[i].children.length; j++){
          if(list[i].children[j].children.length>0){
            return list[i].children[j].children[0];
          }
        }
      }
    }
  }

  
  function forEachGetSortNo(list){
      list.forEach(item => {
        if(map[item.path]){
          item.sortNo = map[item.path].train;
        }else{
          item.sortNo = 0;
        }
        if (item.children && item.children.length > 0) {
          forEachGetSortNo(item.children);
        }
      });
  }
  function forEachSort(list){
    list.sort(compare);
    list.forEach(item => {
      if (item.children && item.children.length > 0) {
        forEachSort(item.children);
      }
    });
  }
  
  function compare (value1, value2) {
    if (value1.sortNo < value2.sortNo) {
      return -1;
    } else if (value1.sortNo > value2.sortNo) {
      return 1;
    } else {
      return 0;
    }
  }
}

此处我们通过发接口$loginUser.getCurrentResourceList()获得数据库里的数据列表,
然后用changeRescource2Router()方法将其做成树的形式
然后由于数据的格式是不一样的:获得的数据格式为:

{ 
	path:'路由组件存放的位置,(如:/userAudit/auditList.vue)'
	name:'存放的标题(如:系统管理)';
 }

需要将其改成路由能识别的数据格式,同时我加上了title属性用于显示菜单的中文名字

{ 
	path:'路由组件存放的位于项目的位置,需要拼接上@/page(如:@/pages/userAudit/auditList.vue)'
	name:'给路由组件的名字(如SystemManage)',
	title:'回显到网页的菜单中文名(如:系统管理)'
 }

这里需要拼接上’@/pages’才能够定位,如果不拼接的话,直接在数据库里存上’@/pages/userAudit/auditList.vue’然后回显,也是无法定位的非常奇怪
数据处理已经完成,接下来解决重定向的问题

放置路由

数据已经处理完毕了,该如何放置路由呢?
我们看到store.commit(‘router/setRoutes’, treeList);方法
这个是store,而不是$store,这个是我们自己定义的输出的一段js,以下为代码
store.js

import Vue from 'vue';
import Vuex from 'vuex';
import state from './states';
import getters from './getters';
import mutations from './mutations';
import actions from './actions';
// 引入了router.js
import router from './modules/router';
import config from './modules/config';

Vue.use(Vuex);

export const store = new Vuex.Store({
  modules: {
    router, config
  },
  state,
  getters,
  mutations,
  actions
});

export default store;

此处放置的为vue的缓存,
同时需要在geter.js和mutation.js里面进行缓存的获取与放置
getter.js

import CryptoJS from 'crypto-js';

const getters = {
authResourceList: state => {
    if(state.authResourceList){
      try{
        let resourceListStr = CryptoJS.enc.Base64.parse(state.authResourceList);
        let resourceList = JSON.parse(resourceListStr.toString(CryptoJS.enc.Utf8));
        return resourceList;
      }
      catch (e){
        console.error(e);
        return null;
      }
    }
    return null;
  },
}
export default getters;

mutation.js

import {AuthData, AuthTokenKey, AuthTokenType, AuthRoleCode, AuthResourceCode, AuthLoginUser, AuthResourceList} from "@/utils/auth";

const mutations = {
setAuthResourceList(state, authResourceList) {
    state.authResourceList = authResourceList;
    sessionStorage.setItem(AuthResourceList, authResourceList);
  },
}
export default mutations;

然后我们看看代码里的注释,里面引入了router.js

我们将其定义为router.js

import {asyncRoutes, defaultRoutes} from '@/router';
import router from '@/router';

const state = {
  routes: defaultRoutes,
  addRoutes: []
};
const mutations = {
  setRoutes(state, routes) {

    state.routes = defaultRoutes.concat(routes);

    // defaultRoutes = defaultRoutes.concat(routes);
    state.addRoutes = routes;
    router.addRoutes(routes);

    console.log("router.getRoutes();--->",router.getRoutes());
  }
};

const actions = {
  setRoutes({commit}, routes) {
    commit('setRoutes', routes);
  }
};

export default {
  namespaced: true,
  state,
  mutations,
  actions
};

我们看到上面的import router,这个同样不是系统方法,而是我们默认定义的路由,代码如下,我们将其定义为index.js

import Vue from 'vue';
import VueRouter from 'vue-router';
import LayOut from '@/components/layout/index.vue';
import menu_router from './menu_router';

import store from '@/store/index';
import $loginUser from '@/api/auth/loginUser';
Vue.use(VueRouter);

let app = store.state.config.app;

/**
 * 菜单显示规则:
 * 1.meta无值的,不显示
 * 2.meta有值的:
 *  1.meta.title无值的,不显示
 *  2.meta.title有值的:
 *    1.meta.code无值的,不判断权限
 *    2.meto.code有值的,会判断权限,由用户的resourceCodeList中是否存在决定,如果不存在则不显示
 */
export let defaultRoutes = [
  {
    path: '/',
    redirect: '/login'
  },
  {
    name: 'index',
    path: '/index',
    // redirect: '/audit'

    // 放置localStorage的地点auth.js
	// 删除localStorage的地点login.vue
    // 此处重定向不能用一行的形式,否则会拿不到缓存,非常奇怪
	redirect: ()=>{
      return  localStorage.getItem('firstRedirectUrl')
    }
    


    // meta: {
    //   title: '首页',
    //   icon: 'home',
    // },
    // component: LayOut,
    // children: [
    //   {
    //     name: "index",
    //     path: '/index',
    //     component: resolve=>(require(['../pages/index.vue'],resolve)),
    //     meta: {
    //       showSider: false,
    //     },
    //   },
    // ],
  },
  {
    name: 'login',
    path: '/login',
    component: resolve=>(require(['../pages/login.vue'],resolve))
  },
  {
    name: 'loginSSO',
    path: '/loginSSO',
    component: resolve=>(require(['../pages/loginSSO.vue'],resolve))
  },
];
// defaultRoutes = defaultRoutes.concat(menu_router);

let routes = defaultRoutes.concat(menu_router);
// let routes = defaultRoutes.concat(this.$store.state.routes);

const router = new VueRouter({
  base: '/bms',
  routes: routes
});


const originalPush = VueRouter.prototype.push;
VueRouter.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err);
};

export default router;

Vue的重定向

为什么要做重定向呢?首先,不同角色显示的页面是不一样的,那么,如果不同
角色进入系统首页,那么他们显示的菜单是不同的,那么就意味着他们默认跳转的位置是不一样的
这里看到第一章节里的
let firstChild = getFirstRedirectChild(treeList);
来获取子路由信息
然后通过localStorage.setItem(“firstRedirectUrl”, firstChild.path);放置缓存
值得一提的是直接放置是读不到的,原因猜测是命令已经执行完放置,但是此时还没有放置成功,仅仅是命令执行完毕
所以需要用
setTimeout(()=>{},3000);
进行延时

最后在index.js里的默认路由里进行定义
更新:无需使用setTimeout()延时,而需要在默认路由里将重定向的形式由一行
redirect: localStorage.getItem(‘firstRedirectUrl’)
改成:
redirect: ()=>{
return localStorage.getItem(‘firstRedirectUrl’)
}
形式,不然会拿不到本地缓存,原因未知

export let defaultRoutes = [
  {
    path: '/',
    redirect: '/login'
  },
  {
    name: 'index',
    path: '/index',
    // redirect: '/audit'

    // 放置localStorage的地点auth.js
    redirect: ()=>{
      return  localStorage.getItem('firstRedirectUrl')
    }
   }
    ....
}

此处就可以获取本地缓存而进行登录首页后动态跳转了
相关文件如下,包含了js以及页面样式,页面是用的蚂蚁ui库
百度网盘
提取码:1qhn

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值