以前的旧项目的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