python权限管理系统_vue权限管理系统

vue权限系统

后台管理系统一般都会有权限模块,用来控制用户能访问哪些页面和哪些数据接口。大多数管理系统的页面都长这样。

下面预览图是我写的一个后台管理系统,源码请点击链接 vuets-iview-admin 技术栈 - vue+typescript+iview

左边为菜单,分为两级,右边为图表显示区域,有增删改查的按钮。

表的结构

SET NAMES utf8mb4;

SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------

-- Table structure for t_auth_rule

-- ----------------------------

DROP TABLE IF EXISTS `t_auth_rule`;

CREATE TABLE `t_auth_rule` (

`id_pk` bigint(20) NOT NULL AUTO_INCREMENT,

`auth_id` varchar(128) NOT NULL COMMENT '权限Id',

`pauth_id` varchar(128) DEFAULT NULL COMMENT '父级Id',

`auth_name` varchar(255) NOT NULL COMMENT '权限名称',

`auth_icon` varchar(255) NOT NULL COMMENT '权限图标',

`auth_type` smallint(6) NOT NULL COMMENT '权限类型,BIT表示其属性\r\n 0x00表示可显示的菜单权限节点;\r\n 0x01表示普通节点',

`auth_condition` text COMMENT '条件',

`remark` varchar(255) DEFAULT NULL COMMENT '备注',

`is_menu` smallint(255) DEFAULT '0' COMMENT '是否为菜单,0表示非,1表示是',

`weight` int(11) NOT NULL DEFAULT '0' COMMENT '权重',

`rule` varchar(256) DEFAULT NULL COMMENT '规则路径主要对应菜单或方法的路径名称',

`cr_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',

`up_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',

PRIMARY KEY (`id_pk`),

UNIQUE KEY `AK_auth_id` (`auth_id`)

) ENGINE=InnoDB AUTO_INCREMENT=264 DEFAULT CHARSET=utf8 COMMENT='权限规则表,记录权限相关的信息,权限以父子关系存在,菜单是权限的一种。';

SET FOREIGN_KEY_CHECKS = 1;

SET NAMES utf8mb4;

SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------

-- Table structure for t_role_auth

-- ----------------------------

DROP TABLE IF EXISTS `t_role_auth`;

CREATE TABLE `t_role_auth` (

`id_pk` bigint(20) NOT NULL AUTO_INCREMENT,

`role_id_fk` varchar(32) DEFAULT NULL COMMENT '角色id',

`auth_id_fk` varchar(128) DEFAULT NULL COMMENT '权限id',

`aa` varchar(255) DEFAULT NULL,

PRIMARY KEY (`id_pk`)

) ENGINE=InnoDB AUTO_INCREMENT=77 DEFAULT CHARSET=utf8 COMMENT='角色与权限的关系表';

SET FOREIGN_KEY_CHECKS = 1;

稍微解释一下表结构,t_auth_rule 表用来存储对应的权限菜单,一般来说,菜单分为一级和二级菜单,rule字段对应前端的路由规则;而按钮为第三级,rule对应的是接口url地址。

t_role_auth 表为角色权限关联表,一个角色拥有哪些权限是通过这张表查出来的。当然还有一个role表,还有一个账号表,账号表里有一个role的外键。

这样就是一个账号 --> 角色 --> 权限的关系。

对于菜单的权限,通过路由表匹配

// 本地写好路由列表(需要进行动态匹配的)

export const routerList: Array = [

// 首页

{

path: '/',

name: '_home',

redirect: '/home',

component: Layout,

meta: {

hideInMenu: true,

notCache: true

},

children: [

{

path: 'home',

name: 'home',

meta: {

hideInMenu: true,

title: '首页',

notCache: true,

icon: 'md-home'

},

component: () => import('@/views/overview-operations/data-center/DataCenter.vue')

}

]

}

]

// 负责将后台返回的 菜单列表 转成vue-router所需要的 router list

export function toRouterComponent(menuList: Array) {

if (!menuList.length) return []

let routerArr = [];

for (let j = 0; j < menuList.length; j++) {

let obj;

let firstIndex = routerList.findIndex(i => i.path === menuList[j].url); // 一级菜单

if (firstIndex !== -1) {

let children: Array = []

obj = {

path: routerList[firstIndex].path,

component: routerList[firstIndex].component,

redirect: routerList[firstIndex].redirect,

name: routerList[firstIndex].name,

meta: routerList[firstIndex].meta,

children

};

// 如果有子菜单

if (menuList[j].children && menuList[j].children.length) {

for (let k = 0; k < menuList[j].children.length; k++) {

const _children = routerList[firstIndex].children!

let secondIndex = _children.findIndex(i => {

let fullpath = '';

if (routerList[firstIndex].path === '/') {

fullpath = `${routerList[firstIndex].path}${i.path}`;

} else {

fullpath = `${routerList[firstIndex].path}/${i.path}`;

}

return fullpath === menuList[j].children[k].url;

});

if (secondIndex !== -1) {

obj.children.push(_children[secondIndex]);

}

}

}

}

if (obj) {

routerArr.push(obj);

}

}

return routerArr;

}

// 根据菜单权限,获取路由数组

// 本地只保存后台返回的菜单,在页面刷新的时候从本地拿到菜单重新调用toRouterComponent生成 路由数组

export function getRouterList() {

if (!storage.get('username')) {

storage.set('menuTree', ''); // 清空菜单权限数据

}

let menuList = storage.get('menuTree') ? storage.get('menuTree') : [];

const routerArr = toRouterComponent(menuList);

return routerArr;

}

menuList,菜单数组(或对象), 由后台返回; routerList为前端定义的路由表;遍历routerList,如果routerList的path在menuMap里能找到的话,就表示该路由存在。最后生成一个过滤后的路由表,用vue提供的addRoutes方法动态添加到路由中,并把过滤后的路由表存到本地。

在页面刷新的时候,从本地获取路由表,添加到路由表中,代码如下,constRouterArr为基础路由表,比如登录,404等

注意这一步有个问题,由于我写的storage库用了JSON.stringify,把路由表中的component(实际为一个函数)丢失了,所以在从本地获取路由的时候,还要重新生成一个新的路由表,重新把component加上去,即把上面的addrouters重新执行一遍

对于按钮的权限

if (res.data.auth_rule_map) {

let obj = {}

Object.keys(res.data.auth_rule_map).forEach(i => {

// 将所有的按钮放到一个obj里 key 为接口地址

if (res.data.auth_rule_map[i].is_menu === 0) { // 如果是按钮

obj[res.data.auth_rule_map[i].rule] = 1

}

})

storage.set("btnList", obj);

storage.set("menuTree", res.data.auth_rule_map);

}

auth_rule_map为接口返回权限map,把按钮的权限过滤出来存到本地。

将map添加到每个路由组件的data里,(这里有一个问题,怎么判断一个组件是否是路由组件),目前想到的是通过组件name来判断,把所有的路由组件放到一个数组里做判断。

在组件内部的按钮上加上v-if,如果this.uri__里的uri在uriMap里存在就显示。

也可以通过方法来判断,如下面的__isBtnShow,不仅可以控制按钮的显示隐藏,还可以控制其样式,比如颜色等,更加灵活,推荐使用方法来控制

uri = {

ADD_MEMBER: '/api/add_member'

}

export default function install (Vue) {

const uriMap = storage.get('btnList')

//uriMap['/admin/api/auth_rule/update_auth_rule.action'] = 1

Vue.mixin({

created() {

const arr = ['MemberManage', 'PayManage', '...']

if (arr.indexOf(this.$options.name) !== -1) {

this.dataUri__ = uriMap

this.uri__ = uri

}

},

data() {

return {

dataUri__: {}

}

},

methods: {

__isBtnShow(uri) {

return uriMap[uri] ? 'display: inline-block' : 'display: none'

},

}

})

}

添加会员

// 通过方法来控制,更加灵活

添加会员

登出的问题

**登出后要清空缓存,routerArr,btnList 等。

由于之前登录,调用addRouter把权限上个账号的路由表加进去了,所以登出后要location.reload()一次,重新实例化路由表,去掉动态添加的路由,只保留基础路由。

location.reload()体验不是太好,但是vue-router没有提供动态删除路由的api,比如 deleteRouter。**

同时登两个账号,导致刷新页面的时候,前者页面的本地缓存被覆盖,权限菜单等数据发生变化,路由表也发生变化

最简单的方法是存到localStorage里,只有登出才会清空缓存,只能登一个账号。

关于菜单和按钮的多语言放在前台还是后台

经过思考还是觉得放在前台好,有两点原因

放在前台方便前台操作,切换语言时不需要重新从后台获取菜单和按钮的名称

如果放在后台,新增语言的时候,后台还得加字段, 放在前台的话只需要更新一下语言包就行了

关于menutree 是否存到本地

之前一直是存在localstorage里,但是仔细一想还是有安全问题,用户可以通过api改变本地的menutree,刷新页面后还是会得到想要的权限,所以menutree 不能存到本地,只读到内存中,页面刷新后,再重新获取一次menutree,代价就是会牺牲一点用户体验。

关于左侧菜单栏重复push的问题

window.addEventListener('storage', () => {

// 当同时打开两个网页 其中一个网页退出登录的时候 通知另一个网页刷新 把之前的路由清掉

// 这样就不会重复push路由进去了

if (getLsCache('username') === '') {

window.location.reload();

setTimeout(() => {

this.$router.push('/login');

}, 200);

}

});

this.setLocal(this.$i18n.locale);

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值