使用VUE开发用户后台时的动态路由问题、按钮权限问题以及其他页面处理问题
如今前后端分离是大势所趋,笔者虽然是做后台的,但也不得不学学前端的流行框架VUE -_-||| 。
为了学习VUE,笔者搭建了一个简单的用户后台,以此来了解VUE的开发思路(注:本项目不用于实际开发,只是为了学习,本文重点在于vue的动态路由添加,动态权限以及页面处理的一些小问题)。
一、项目组成
VUE 2.6.11 + axios +VueX + ElementUI 2.13.2
二、整体思路
1. 用户登录后,获取菜单数据,用以显示菜单。
2. 用户登录后,后台获取Vue路由数据,用以动态添加到VueRouter中。
3. 用户登录后,从后台获取用户的权限,用以控制用户是否对某一功能具有可操作权限。
三、具体实现
· 1. 登录。由于本人学习重点是使用VUE动态添加路由、菜单显示和权限控制,所以登录页面没有做具体功能,点击登录按钮就表示登录成功了。
由于登录页是用户的第一界面,不存在任何权限问题,所以笔者就直接将登录页的路由直接写在了VueRouter实例中。如下:
import Vue from 'vue'
import VueRouter from 'vue-router' Vue.use(VueRouter) const routes = [{
path: '/Login',
name: 'Login',
component: () => import('../views/Login.vue')
}] export function initRouter() {
return new VueRouter({
routes
})
} export default initRouter()
用户通过 http://localhost:8080/#/login 可访问到登陆页面:

点击登录按钮表示登录成功!
登录成功后的处理:
(1)向后台发请求拿到路由数据并存入VueX的state中,并通过addRoutes(routerObjArr)动态添加到路由实例中。注:后台返回的数据结构需跟route相一致,如图:
前端所需数据结构:

后台返回的数据结构:

细节处理:由于后台返回的component字段是个组件地址字符串,这就需要将后台返回的route数据 一 一 做处理,通过import() 方法动态的加载组件,然后将返回的compoent对象重新赋值到component字段上。如图:

代码:
const _import = require('@/router/_import_' + process.env.NODE_ENV) //获取组件的方法
/**将router的json字符串中的component转换为组件对象 */
export function filterAsyncRouter(asyncRouterMap) {
if (!asyncRouterMap) return [];
function _iter(before) {
const after = Object.assign({}, before)
if (after.component) {
after.component = _import(after.component);
}
if (after.children && after.children.length) {
after.children = filterAsyncRouter(after.children);
}
return after
}
return asyncRouterMap.map(_iter)
}
图中所用的import方法,根据生产环境不同,引用不同的文件,如图:

各自的代码如下:
_import_development.js:
module.exports = file => require('@/views/' + file + '.vue').default // vue-loader at least v13.0.0+
_import_production.js
module.exports = file => () => import('@/views/' + file + '.vue')
将后台的返回的路由数据处理完成后,最后就可以使用addRoutes方法动态加入VueRouter中了。此时,用户便可以按照路由访问页面了。代码如下:
//动态生成路由
axios
.get("https://localhost:5001/AdminApi/Home/GetMenuJson?userid=" + 1)
.then(res => {
//取出路由列表 (isRoute=1)
res.data.obj.router[0].children = res.data.obj.router[0].children.filter(
a => a.meta.isRoute == 1 //isRoute=1的
); //存入缓存
this.$store.commit("setRoutes", res.data.obj.router); //转换组件对象
var getRouter = filterAsyncRouter(res.data.obj.router); //打印当前路由列表
console.log("当前路由列表:", res.data.obj.router[0].children); //清空之前的路由信息
this.$router.matcher = initRouter().matcher; //重新添加路由信息
this.$router.addRoutes(getRouter); //跳转到 Layout 组件
this.$router.push("/");
});
(2)向后台发请求拿到权限数据,并存入VueX的state中。代码如下:
axios
.get(
"https://localhost:5001/AdminApi/Access/ActionPermission?userid=" + 1
)
.then(res => {
//存入权限
console.log("权限列表:", res.data.obj);
this.$store.commit("setAccess", res.data.obj);
});
(3)向后台请求数据并存入VueX中的state之前,需要清空上一次存入的数据(包括路由数据和权限数据),否则会造成数据混乱,如图:

(4)addRoutes之前,不仅要做component字段的字符串转对象的处理,还要清掉上一个用户登录后存入router中的路由数据,否则会造成数据混乱或者vue警告重复的路由名称。

Login.vue组件中的全部代码如下:
<template>
<div class="about">
<button @click="login">登录</button>
</div>
</template> <script>
import { filterAsyncRouter } from "../common/promission";
import axios from "axios";
import { initRouter } from "@/router";
export default {
created() {
this.$store.commit("logout");
}, methods: {
login() {
//动态生成路由
axios
.get("https://localhost:5001/AdminApi/Home/GetMenuJson?userid=" + 1)
.then(res => {
//取出路由列表 (isRoute=1)
res.data.obj.router[0].children = res.data.obj.router[0].children.filter(
a => a.meta.isRoute == 1 //isRoute=1的
); //存入缓存
this.$store.commit("setRoutes", res.data.obj.router); //转换组件对象
var getRouter = filterAsyncRouter(res.data.obj.router); //打印当前路由列表
console.log("当前路由列表:", res.data.obj.router[0].children); //清空之前的路由信息
this.$router.matcher = initRouter().matcher; //重新添加路由信息
this.$router.addRoutes(getRouter); //跳转到 Layout 组件
this.$router.push("/");
}); axios
.get(
"https://localhost:5001/AdminApi/Access/ActionPermission?userid=" + 1
)
.then(res => {
//存入权限
console.log("权限列表:", res.data.obj);
this.$store.commit("setAccess", res.data.obj);
});
}
}
};
</script>
promiss.js代码如下:
const _import = require('@/router/_import_' + process.env.NODE_ENV) //获取组件的方法
/**将router的json字符串中的component转换为组件对象 */
export function filterAsyncRouter(asyncRouterMap) {
if (!asyncRouterMap) return [];
function _iter(before) {
const after = Object.assign({}, before)
if (after.component) {
after.component = _import(after.component);
}
if (after.children && after.children.length) {
after.children = filterAsyncRouter(after.children);
}
return after
}
return asyncRouterMap.map(_iter)
}
store.js代码如下:
import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersistence from 'vuex-persist' Vue.use(Vuex) export default new Vuex.Store({
state: {
routes: [],
},
mutations: {
setRoutes: (state, routes) => {
state.routes = routes
},
setAccess: (state, access) => {
state.access = access;
},
logout: (state) => {
state.routes = [];
state.access = []
}
},
actions: {},
modules: {},
plugins: [new VuexPersistence().plugin]
})
2. 菜单。将Layout组件用作菜显示组件,将ele中的菜单组件复制到该组件中,并通过向后台请求数据,拿到菜单和菜单对应的分组数据 。拿到菜单和菜单分组数据后,循环遍历,将菜单按照对应的分组全部显示(后台判断当前用户可显示的菜单,没有权限的菜单直接不返给前台)。vue代码以及后台数据如下:
<template>
<el-container>
<el-header>
<el-dropdown>
<i class="el-icon-setting"></i>
<el-dropdown-menu slot="dropdown">
<el-dropdown-item>修改密码</el-dropdown-item>
<el-dropdown-item>退出</el-dropdown-item>
</el-dropdown-menu>
</el-dropdown>
<span>王小虎</span>
</el-header>
<el-container>
<el-aside width="250px">
<el-menu @select="handleSelect">
<el-submenu :index="group.name" v-for="group in groupList" :key="group.id">
<template slot="title">
<i class="el-icon-message"></i>
{{group.title}}
</template>
<template v-for="router in routerList">
<el-menu-item
:index="router.path"
:key="router.meta.id"
v-if="router.meta.groupId == group.id"
>{{router.meta.title}}</el-menu-item>
</template>
</el-submenu>
</el-menu>
</el-aside>
<el-main>
<router-view />
</el-main>
</el-container>
</el-container>
</template> <script>
import axios from "axios"; export default {
data() {
return {
activeIndex: "/home/Index",
groupList: [],
routerList: []
};
},
mounted() {
this.getGroupList();
this.getRouterList();
},
methods: {
//菜单点击事件
handleSelect(key) {
this.$router.push(key);
},
//获取菜单分组数据
getGroupList() {
var that = this;
axios
.get("https://localhost:5001/AdminApi/Home/GetGroupJson")
.then(res => {
that.groupList = res.data.obj;
});
},
//获取菜单数据
getRouterList() {
var that = this;
axios
.get("https://localhost:5001/AdminApi/Home/GetMenuJson")
.then(res => {
that.routerList = res.data.obj.router[0].children.filter(
a => a.meta.display == 1 //取display=1的
);
console.log("当前菜单列表");
console.log(that.routerList);
});
}
}
};
</script> <style>
@import "../styles/layout.css"; /*引入公共样式*/
</style>
后台分组数据:
{
"id": 14,
"name": "Customer",
"title": "客户中心",
"target": "mainRigh",
"url": "#",
"icoCss": "layui-icon-username",
"delFlag": 0,
"sortNo": 0,
"createMan": 1,
"createTime": "2019-05-05T11:30:06"
},
{
"id": 9,
"name": "System",
"title": "系统设置",
"target": "123",
"url": "#",
"icoCss": "fa-gears",
"delFlag": 0,
"sortNo": 1,
"createMan": 1,
"createTime": "2019-05-05T11:29:56"
}
后台菜单数据:

效果图:

3. 功能页面的处理。
(1)组件的动态加载规则。 由于该vue项目中的组件是动态加载,那么后台返回的路由数据中的component字段中的路径自然也要按照某一种规则来返给前端。否则会造成import()组件的时候,由于地址不对解析加载不到组件而报错。
例如笔者是按照这种规则:
后台数据


斜杠”/“前边表示文件夹名称,后边表示组件名称,这样就可以按照这种规则动态加载到组件了。


(2).页面刷新变成空白页?(路由丢失)
遇到这个问题的话,在main.js中加入一段代码,每次刷新页面都把存入VueX state中的数据拿出来,判断一下路由里边还存不存在当前刷新页面的路由,如果没有,则对VueRouters重新赋值
main.js 代码如下:
import Vue from 'vue'
import App from './App.vue'
import router from './router'
import ElementUI from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
import store from './common/store'
import {
filterAsyncRouter
} from "./common/promission"; Vue.config.productionTip = false
Vue.use(ElementUI); new Vue({
router,
store,
render: h => h(App),
mounted() {
// 缓存的路由信息
const routes = this.$store.state.routes
// 判断当前路由是否被清除
const hasRoute = this.$router.options.routes.some(r => r.name == 'index')
// 如果 缓存中有路由信息 并且 当前路由被清除
if (routes.length && !hasRoute) {
//获取路由Json字符串
var getRouter = filterAsyncRouter(routes);
// 再次添加路由信息
this.$router.addRoutes(getRouter);
// 然后强制更新当前组件
this.$forceUpdate()
}
},
}).$mount('#app')
(3) 页面按钮的控制
将前面存入vuex state中的权限数据,在每个组件中都拿出来一下存入一个变量中,在按钮上使用v-if、array.indexOf('权限名称')来控制显示/隐藏。
原理是如果用户存在该权限,则v-if=”true“,按钮则显示,否则按钮隐藏。
代码如下:
<el-button
@click="edit(scope.row)"
type="text"
size="small"
v-if="accessList.indexOf('SysRole/AddEdit')>-1"
>编辑</el-button>

效果图:

好了,笔者就介绍到这里。当然,如果要做一个完整的后台,肯定还有很多要做的东西,比如用户角色啊、角色授权啊等等;但笔者这次学习的重点是VUE的动态路由、动态权限,以及页面处理中一些比较常见的坑,所以别的就不多介绍了。
如有需要,朋友们可以联系我,大家多多交流。
使用VUE开发用户后台时的动态路由问题、按钮权限问题以及其他页面处理问题的更多相关文章
- vue+elementui搭建后台管理界面(6登录和菜单权限控制)
不同的权限对应不同的路由(菜单),同时侧边栏也根据权限异步生成,实现登录和鉴权思路如下: 登录:点击登录,服务器验证通过后返回一个 token ,然后存到 cookie,再根据 token 拉取用户权 ...
- 基于hi-nginx的web开发(python篇)——动态路由和请求方法
hi.py的提供的路由装饰器接受两个参数,第一个参数指定动态路由的正则模式,第二个参数指定同意的http请求方法列表. 比如: @app.route(r"^/client/?$", ...
- vue 开发系列(八) 动态表单开发
概要 动态表单指的是我们的表单不是通过vue 组件一个个编写的,我们的表单是根据后端生成的vue模板,在前端通过vue构建出来的.主要的思路是,在后端生成vue的模板,前端通过ajax的方式加载后端的 ...
- node vue 开发环境部署时,外部访问页面出现: Invalid Host header 服务器域名访问出现的问题
这是新版本 webpack-dev-server 出于安全考虑, 默认检查 hostname,如果hostname不是配置内的,将中断访问.顾仅存在于开发环境: npm run dev,打包之后不会 ...
- vue开发 - 根据vue-router的meta动态设置html里标签的内容
路由文件 :router/index.js import Vue from 'vue'import Router from 'vue-router'import index '@/view/index ...
- vue开发 - 根据vue-router的meta动态设置html里title标签内容
1.路由文件 :router/index.js 添加 meta属性配置: import Vue from 'vue' import Router from 'vue-router' import in ...
- vue+elementui搭建后台管理界面(6登录和菜单权限控制[二])
根据权限计算路由的代码 /** * 通过meta.role判断是否与当前用户权限匹配 * @param roles * @param route */ function hasRoles (roles ...
- vue开发关于微信授权登录以及路由mode模式(Hash|History)和手机平台(andriod|IOS)不得不说的故事
引用链接: https://segmentfault.com/a/1190000010753247?utm_source=tuicool&utm_medium=referral
- .net简单页面后台绑定下拉框,按钮,分页 前台aspx页面
一.aspx页面 <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Updat ...
随机推荐
- springboot使用druid连接池连接Oracle数据库的基本配置
#阿里连接池配置 #spring.datasource.druid.driver-class-name=oracle.jdbc.driver.OracleDriver #可配可不配,阿里的数据库连接池 ...
- Java常见的集合的数据结构
数据结构 数据结构__栈:先进后出 栈:stack,又称堆栈,它是运算受限的线性表,其限制是仅允许在标的一端进行插入和删除操作,不允许在其他任何位置进行添加.查找.删除等操作. 简单的说:采用该结构的 ...
- 201771010113 李婷华 《面向对象程序设计(Java)》第八周总结
一.理论知识部分 1.Java为了克服单继承的缺点,Java使用了接口,一个类可以实现一个或多个接口. 2.在Java程序设计语言中,接口不是类,而是对类的一组需求描述,由常量和一组抽象方法组成.接口 ...
- opencv-12-高斯滤波-双边滤波(附C++代码实现)
开始之前 这几天由于自己的原因没有写, 一个是因为自己懒了, 一个是感觉这里遇到点问题不想往下写了, 我们先努力结束这个章节吧, 之前介绍了比较常用而且比较好理解的均值和中值滤波, 但是呢,在例程Sm ...
- 【Flink】使用之前,先简单了解一下Flink吧!
目录 Flink简单介绍 概述 无边界数据流和有边界数据流 技术栈核心组成 架构体系 重要角色 Flink与Spark架构概念转换 Flink简单介绍 概述 在使用Flink之前,我们需要大概知 ...
- 构建自己的专用OpenCV----记录一次由applyColorMap()引发的探索
在编写实际项目的过程中,我需要实现绿色主题的"伪彩色"变换.在目前提供的模板中,只有summer最为接近,但是它的颜色太浅了,看上去不是很清晰.所以我结合ocean和summer两 ...
- 数据库连接池Druid的介绍,配置分析对比总结
Druid的简介 Druid首先是一个数据库连接池.Druid是目前最好的数据库连接池,在功能.性能.扩展性方面,都超过其他数据库连接池,包括DBCP.C3P0.BoneCP.Proxool.JBos ...
- Web_php_include-攻防世界
0x00 简介 记录这个题纯粹是为了记录以下有关strstr()函数的相关知识. 0x01 题目 <?php show_source(__FILE__); echo $_GET['hello'] ...
- 搜索引擎优化(SEO)
一.SEM SEM(Search Engine Marketing)即搜索引擎营销.SEM是一种新的网络营销模式.SEM所做的就是全面有效地利用所搜引擎来进行网络行销推广.SEM追求最高的性价比,以最 ...
- .Net Core3.0 WebApi 项目框架搭建 二:API 文档神器 Swagger
.Net Core3.0 WebApi 项目框架搭建:目录 为什么使用Swagger 随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了:前端渲染.后端分离的形态,而且前端技术和后端技 ...