最近项目要重构权限这一块,后台使用JWT token存在redis中JWT就不细说了(也可以使用shiro可自行选择),详情可百度,主要说一下实现
1.创建表resourcr(资源表,前台菜单显示的信息),user(用户表,用户的个人信息),role(角色表),user_role(用户与角色对应表),role_resource(角色资源对应表存的都是表主键id)
主要思想:创建用户,给用户分配角色,角色加载对应的权限(菜单的url)再用户登录的时候,根据角色查找资源,根据资源找到权限,最后返回tree树型结构,前台加载菜单显示
2.后台生成tree
根据需求自行创建实体类
package cn.rocketai.saas.domain.vo;
import java.util.List;
/** Author: dengWenBo Date: 19-2-20 */
public class MenuNode {
private String id;
private String label;
private String pid;
private String title;
private String level;
private String icon;
private String url;
private boolean expand = true; // 节点是否展开 默认true
private boolean checked = false; // 选中显示勾选框
private boolean selected = false; // 选中显示勾选背景
private List<MenuNode> children = null;
public MenuNode() {
super();
}
public MenuNode(
String id,
String label,
String pid,
String title,
String level,
String icon,
String url,
boolean expand,
boolean checked,
boolean selected) {
super();
this.id = id;
this.label = label;
this.pid = pid;
this.title = title;
this.level = level;
this.icon = icon;
this.url = url;
this.expand = expand;
this.checked = checked;
this.selected = selected;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public String getPid() {
return pid;
}
public void setPid(String pid) {
this.pid = pid;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<MenuNode> getChildren() {
return children;
}
public void setChildren(List<MenuNode> children) {
this.children = children;
}
public boolean isExpand() {
return expand;
}
public void setExpand(boolean expand) {
this.expand = expand;
}
public boolean isChecked() {
return checked;
}
public void setChecked(boolean checked) {
this.checked = checked;
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean selected) {
this.selected = selected;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
}
主要实现类结果返回tree结构
@Override
public Map<String, Object> treeNode( List<Resources> resources) {
Map<String, Object> map = new HashMap<>();
List<MenuNode> menuNodeList = Lists.newArrayList();
for (Resources res :resources ) {
MenuNode node = new MenuNode();
node.setId(String.valueOf(res.getId()));
node.setPid(res.getPid());
node.setLabel(res.getResourceName());
node.setTitle(res.getResourceName());
node.setLevel(String.valueOf(res.getLevel()));
node.setIcon(res.getIcon());
node.setUrl(res.getUrl());
menuNodeList.add(node);
}
List<MenuNode> menuNodeTree = Lists.newArrayList();
for (MenuNode node : menuNodeList) {
if (StringUtils.equals(node.getPid(), "0")) {
menuNodeTree.add(node);
}
// 找子节点
for (MenuNode modeTree : menuNodeList) {
if (StringUtils.equals(modeTree.getPid(), node.getId())) {
if (node.getChildren() == null) {
node.setChildren(new ArrayList<>());
}
node.getChildren().add(modeTree);
}
}
}
map.put("data", menuNodeTree);
return map;
}
3.前台接收数据,vue生成无限菜单
创建sideMenuItem.vue 子主键
<template>
<Submenu :name="parentItem.title">
<template slot="title">
<Icon :type="parentItem.icon" :size="15"/>
<span style="font-size: 15px">{{ parentItem.title }}</span>
</template>
<template v-for="item in parentItem.children">
<side-menu-item
v-if="item.children&&item.children.length!==0"
:parent-item="item"
:key="'menu-'+item.id"
>
</side-menu-item>
<menu-item v-else :name="item.url" :key="'menu-'+item.id">
<!-- <Icon :type="item.icon" :size="15" />-->
<span>{{ item.title }}</span>
</menu-item>
</template>
</Submenu>
</template>
<script>
export default {
name: 'sideMenuItem',
props: {
parentItem: {
type: Object,
default: () => {}
}
}
}
</script>
<style lang="less" scoped>
</style>
父主键 sideMenuItem.vue
<template>
<div class="sideMenu">
<Menu width="auto"
:active-name="activeName"
:open-names="openNames"
@on-select="handleSelect"
>
<template v-for="item in itemList">
<side-menu-item
v-if="item.children&&item.children.length!==0"
:parent-item="item"
:key="'menu-'+item.id"
>
</side-menu-item>
<menu-item v-else
:name="item.url"
:key="'menu-'+item.id"
>
<Icon :type="item.icon" :size="15"/>
<span>{{ item.title }}</span>
</menu-item>
</template>
</Menu>
</div>
</template>
<script>
import sideMenuItem from '../components/sideMenuItem'
import Bus from '../components/bus.js'
export default {
name: 'pageFooter',
data() {
return {
roleIds: window.localStorage.getItem('roleIds'),
activeName: '',//选中那个菜单
openNames: [''], //打开选中菜子项
itemList: [],
changeUrl: ''
}
},
created() {
this.getResource();
this.getChangeUrl();
},
methods: {
getResource(){
this.$ajax({
method: 'post',
url: 你自己获取treeList接口地址,
params: data
}).then(response => {
var res = response.data;
if (res && res.code === '0') {
this.itemList = res.data.data;
} else {
this.$Message.error(res.msg);
}
}, error => {
this.loading = false;
if (error.response) {
const _result = error.response.data;
if (_result && _result.msg) {
this.$Message.error(_result.msg);
} else {
this.$Message.error('网络异常,请稍后再试!');
}
}
})
}
handleSelect(url) {
this.$emit('on-select', url);
this.$router.push({
path: url
})
},
getChangeUrl() {
let self = this;
Bus.$on('change', (e) => {
self.activeName = e;
})
},
},
components: {
sideMenuItem,
Bus
},
}
</script>
<style lang="less" scoped>
.sideMenu {
padding: 30px 0 0 0;
width: 160px;
background-color: #fff;
box-shadow: 0px 0px 3px rgba(0, 0, 0, 0.1);
}
.ivu-menu-item {
height: 60px;
}
</style>
最总显示结果