基础工作
首先还是后台管理系统的基础工作,登录,侧边栏,导航栏什么的,因为给的时间实在太紧,我就直接用的网上已经有的基础框架 vue-admin-template 的,这些东西也没必要重复写,直接用现成的就好,主要还是总结一下权限相关。
菜单表设计
因为 vue-admin-template 框架中,侧边栏是根据路由生成的,所以我们只要用一个菜单表维护路由就行了,不需要单独再搞一个侧边栏管理,为了满足渲染路由所必须的参数,我们需要告诉后端我们都需要什么参数,一般情况下,具有一定开发经验的后端都知道要返回什么参数,但如果对方碰巧没啥经验,我们就要主动提出来了。
字段 | 含义 | 备注 | title | 标题 | 用于侧边栏标题展示 | icon | 图标 | 用于侧边栏图标展示 | type | 类型 | 区分目录/菜单/按钮 | parentId | 父级id | 记录父子关系 | name | 路由name | 路由必备 | path | 地址 | 地址栏的地址,用于跳转和展示 | url | 模块路径 | 模块位于文件夹的路径 | identification | 授权标识 | 用于权限判断,常见格式 crm:customer:list | hidden | 是否渲染在侧边栏 | 有一些路由我们需要可以访问,又不想让它出现在侧边栏 | parentId | 父级id | 记录父子关系 |
---|
*以上仅列出我们所必须的字段,像创建时间,创建人,排序等可以与后端协商按需求添加
角色分配
菜单表搞好之后,我们就可以开始开发角色列表,角色列表无非就是增删查改,这里仅记录自己碰到的几个小知识点。
给角色分配菜单时,保存的参数和回显
保存
大部分后台管理系统都是用的element-ui,而菜单展示一般会用element的el-tree组件,因为渲染路由的时候,需要有父子结构,我这里保存的时候会把选中的节点this.$refs.menuListTree.getCheckedKeys()
和半选中的节点this.$refs.menuListTree.getHalfCheckedKeys()
都保存下来
回显
因为保存的时候半选中的节点也给保存了下来,回显的时候如果给半选中的节点选中,它的子节点也会全部选中,如果要解决这个问题,我们只需要判断该节点是否是子节点就可以了
let menuId = res.data.menuId // 后端返回的id字符串 '1,3,4,5,8,9'
let _arr = menuId.split(",").map(item => {
return +item; // 用加号是因为字符串分割的数组每一项都是字符串,需要转成数字
});
_arr.map(item => {
//获取该id对应的tree节点
let node = this.$refs.menuListTree.getNode(item);
//判断该节点是否是子节点(即该节点是否是末级节点),是的话就设置选中状态
if (node.isLeaf) {
this.$refs.menuListTree.setChecked(node, true);
}
});
路由守卫判断
前端做权限,主要靠的就是操作路由,这一块想了好久,事实证明,好记性不如赖笔头,想半天想不明白,写下来一会儿就搞明白了。
获取用户权限列表及菜单信息
这里贴上我的代码,里面注释了一些遇到的小难点
router.beforeEach(async (to, from, next) => {
// vue-admin-template自带的进度条
NProgress.start();
// 设置浏览器标签标题
document.title = getPageTitle(to.meta.title);
// 获取token
const hasToken = getToken();
if (hasToken) {
if (to.path === "/login") {
// 如果已经登录,重定向至首页
next({ path: "/" });
NProgress.done();
} else {
const hasGetUserInfo = store.getters.name;
if (!hasGetUserInfo) {
try {
// 如果没有用户信息则获取用户信息
await store.dispatch("user/getInfo");
} catch (error) {
// 获取用户信息失败则清除token并跳转至首页
await store.dispatch("user/resetToken");
Message.error(error || "获取用户信息失败");
next(`/login?redirect=${to.path}`);
NProgress.done();
}
}
// 判断是否已经加载路由或者是否要访问白名单内的页面
if (
router.options.isAddDynamicMenuRoutes ||
whiteList.indexOf(to.path) !== -1
) {
next();
} else {
// 获取用户权限信息及菜单列表
menuApi
.getListById({ id: store.getters.userId })
.then(res => {
console.log(res);
let menuList = res.data.menuList;
let permissions = res.data.permission;
// 我这里是后端返回所有的菜单,然后前端根据权限筛选出有权限的菜单
// 筛选出有权限的路由或者是目录
menuList = menuList.filter(item => {
return (
permissions.indexOf(item.identifying) > -1 ||
item.parentId === 0
);
});
// 将数据转化成路由结构
menuList.map(item => {
if (item.parentId === 0) {
item.component = Layout;
} else {
item.component = _import(item.url);
}
item.meta = {
title: item.title,
icon: item.icon
};
});
// 将路由转换成父子结构
menuList = treeDataTranslate(menuList);
console.log(menuList);
menuList = menuList.filter(item => {
return item.children;
});
// 在添加完动态路由之后再添加404路由,以防止页面在匹配动态路由之前先匹配404
menuList.push({ path: "*", redirect: "/404", hidden: true });
router.options.isAddDynamicMenuRoutes = true;
router.addRoutes(menuList);
// this.$router不是响应式的,所以手动将路由元注入路由对象
router.options.routes.push(...menuList);
// 下面这个我也不知道为什么要加,但是我知道不加刷新就会404?
if (from.name == null) {
next(to);
} else {
next();
}
})
.catch(err => {
console.log(err);
next(`/login?redirect=${to.path}`);
});
}
}
} else {
/* has no token*/
if (whiteList.indexOf(to.path) !== -1) {
// in the free login whitelist, go directly
next();
} else {
// other pages that do not have permission to access are redirected to the login page.
next(`/login?redirect=${to.path}`);
NProgress.done();
}
}
});
才疏学浅,请各位大神多多指教,如果有哪里写的不好或者不详细的,请评论区留言
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!