首先是给路径建表,存在数据库里
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `route_config`
-- ----------------------------
DROP TABLE IF EXISTS `route_config`;
CREATE TABLE `route_config` (
`id` int(11) NOT NULL,
`key` varchar(255) DEFAULT NULL,
#图标
`icon` varchar(255) DEFAULT NULL,
#父路径id
`parent_id` int(11) DEFAULT NULL,
#路径
`url` varchar(255) DEFAULT NULL,
#路径对应的文本
`title` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
路径表和权限表的连接表
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `route_permission`
-- ----------------------------
DROP TABLE IF EXISTS `route_permission`;
CREATE TABLE `route_permission` (
`id` int(11) NOT NULL,
#路径id
`rid` int(11) DEFAULT NULL,
#权限id
`pid` int(11) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
权限表
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `permission`
-- ----------------------------
DROP TABLE IF EXISTS `permission`;
CREATE TABLE `permission` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID,主键',
`name` varchar(64) DEFAULT NULL COMMENT '权限名',
`parent_id` int(10) unsigned DEFAULT NULL COMMENT '上级权限ID',
`abbreviation` varchar(32) DEFAULT NULL COMMENT '简称',
`type` varchar(32) DEFAULT NULL COMMENT '权限类型',
`description` varchar(255) DEFAULT NULL COMMENT '描述',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=32 DEFAULT CHARSET=utf8 COMMENT='权限';
关于RouteConfig的mapper
后台用shiro框架来管理权限,在RouteServiceImpl中实现getRoutes(),流程可以概括为 先获取当前登录的用户,从用户对象中取到用户所对应的角色, 再遍历角色,得到对应的权限,根据权限查询到对应的路径信息,再调用getTree方法生成菜单树。
@Service
public class RouteServiceImpl implements RouteService{
@Autowired
private CurrentUser currentUser;
@Autowired
RouteConfigMapper routeConfigMapper;
@Override
public List getRoutes() {
List list = new ArrayList();
if(currentUser.getCurrentUser()!=null) {
List permission = new ArrayList();
List roles = currentUser.getCurrentUser().getRoles();
for(int i=0;i permissions = roles.get(i).getPermissions();
for(int j=0;j routes = routeConfigMapper.selectRouteConfig(permission);
List parentRoute = routeConfigMapper.selectByPid(0);
//给数组加入父路径
for(int i=0;i routeList = new ArrayList(routes);
//按id排序
Collections.sort(routeList);
list = RouteConfigTree.getTree(routeList);
}
return list;
}
}
RouteConfigTree类
public class RouteConfigTree {
/**
* 计数
*/
private static int length = 0;
/**
* 递归构造树
*/
public static void buildTree(Tree parentTree, List list) {
List childList = new ArrayList<>();
for(int i = 0; i < list.size(); i++) {
if(list.get(i).getParentRouteConfig()!= null &&
list.get(i).getParentRouteConfig().getId() == parentTree.getId()) {
Tree tree = new Tree();
tree.setId(list.get(i).getId());
tree.setKey(list.get(i).getKey());
tree.setTitle(list.get(i).getTitle());
tree.setParentKey(parentTree.getId());
tree.setIcon(list.get(i).getIcon());
tree.setUrl(list.get(i).getUrl());
childList.add(tree);
length--;
}
}
if(childList.size() > 0) {
parentTree.setRoute(childList);
}
if(length > 0) {
for(int i = 0; i < childList.size(); i++) {
buildTree(childList.get(i), list);
}
}
}
/**
* 构造树
*/
public static List getTree(List list) {
System.out.println("routes"+list);
List treeList = new ArrayList<>();
length = list.size();
//先找根
for(int i = 0; i < list.size(); i++) {
if(list.get(i).getParentRouteConfig()== null) {
Tree tree = new Tree();
tree.setId(list.get(i).getId());
tree.setKey(list.get(i).getKey());
tree.setTitle(list.get(i).getTitle());
tree.setIcon(list.get(i).getIcon());
tree.setParentKey(null);
tree.setUrl(list.get(i).getUrl());
treeList.add(tree);
length--;
}
}
//再找子
for(int i = 0; i < treeList.size(); i++) {
buildTree(treeList.get(i), list);
}
//把没有子节点的根节点删除
for(int i = 0; i < treeList.size(); i++) {
System.out.println(treeList.get(i));
if(treeList.get(i).getRoute()==null) {
treeList.remove(i);
}
}
return treeList;
}
}
Tree类
public class Tree implements Serializable {
private static final long serialVersionUID = 1L;
//key
private String key;
//当前节点id
private Integer Id;
//父id
private Integer parentKey;
//路径文本内容
private String title;
private List route;
private String icon;
private String url;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getIcon() {
return icon;
}
public void setIcon(String icon) {
this.icon = icon;
}
public Integer getId() {
return Id;
}
public void setId(Integer id) {
Id = id;
}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public static long getSerialversionuid() {
return serialVersionUID;
}
public Integer getParentKey() {
return parentKey;
}
public void setParentKey(Integer parentKey) {
this.parentKey = parentKey;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List getRoute() {
return route;
}
public void setRoute(List route) {
this.route = route;
}
@Override
public String toString() {
return "Tree [key=" + key + ", Id=" + Id + ", parentKey=" + parentKey + ", title=" + title + ", route=" + route
+ ", icon=" + icon + ", url=" + url + "]";
}
}
Controller中,把菜单树转成json形式返回前端,到这里,后端部分就结束了
@RequestMapping("/getSliderList")
@ResponseBody
public String getSliderList() {
List routes = routeService.getRoutes();
JSONObject resultJS = new JSONObject();
resultJS.put("list", routes);
return resultJS.toJSONString();
}
前端 react + redux
代码结构如图
首先是store下的index.js
import { createStore, compose, applyMiddleware } from 'redux';
import thunk from 'redux-thunk';
import reducer from './reducer';
const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
const store = createStore(reducer, composeEnhancers(
applyMiddleware(thunk)
));
export default store;
store下的reducer.js
import { combineReducers } from 'redux';
import CommonReducer from './CommonReducer/reducer'
const reducer = combineReducers({
common:CommonReducer
});
export default reducer;
app.js
import Leftsider from './pages/left'
class App extends Component {
render() {
return (
left.js 侧边栏部分,每次加载这个组件,都会从后台请求侧边栏数据,存在store中
class Leftsider extends React.Component{
componentDidMount(){
this.props.initSliderList();
}
render(){
return(
);
}
}
const mapStateToProps = (state)=>({
list:state.common.list
})
const mapDispatch = (dispath)=>{
return {
initSliderList(){
let list=[]
axios.get('getSliderList').then(res=>{
list = res.data.list
dispath({
type:'init_slider_list',
list:list
})
})
}
}
}
export default connect(mapStateToProps,mapDispatch)(withRouter(Leftsider));
commonReducer-reducer.js
const defaultState = {
list:[]
}
export default function CommonReducer(state = defaultState,action){
switch(action.type){
case 'init_slider_list':
return initSliderList(state,action);
case 'reset_slider_list':
return resetSliderList(state,action);
default:
return state
}
}
const initSliderList=(state,action)=>{
return {
...state,...{list:action.list}
}
}
const resetSliderList=(state,action)=>{
return {
...state,...{list:[]}
}
}
到这里,前端部分结束,完成动态加载菜单