首先是给路径建表,存在数据库里
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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.example.demo.dao.RouteConfigMapper">
<resultMap type="RouteConfig" id="routeConfigAllField_resultMap">
<id column="id" property="id" jdbcType="INTEGER"/>
<result column="key" property="key" jdbcType="VARCHAR"/>
<result column="icon" property="icon" jdbcType="VARCHAR"/>
<result column="parent_id" property="parentId" jdbcType="INTEGER"/>
<result column="url" jdbcType="VARCHAR" property="url" />
<result column="title" property="title" jdbcType="VARCHAR"/>
<association property="parentRouteConfig" column="parent_id" javaType="RouteConfig"
select="selectById">
</association>
</resultMap>
<select id="selectRouteConfig" resultMap="routeConfigAllField_resultMap" parameterType="java.util.List">
select * from route_config where id in(
select rid from route_permission where
<foreach collection="list" item="item" separator="or">pid
<choose>
<when test="item != null"> = </when>
<otherwise> is </otherwise>
</choose>#{item}
</foreach>
)
</select>
<select id="selectById" resultType="RouteConfig">
select * from route_config where id=#{parent_id}
</select>
<select id="selectByPid" resultMap="routeConfigAllField_resultMap">
select * from route_config where parent_id=#{parentId}
</select>
</mapper>
后台用shiro框架来管理权限,在RouteServiceImpl中实现getRoutes(),流程可以概括为 先获取当前登录的用户,从用户对象中取到用户所对应的角色, 再遍历角色,得到对应的权限,根据权限查询到对应的路径信息,再调用getTree方法生成菜单树。
@Service
public class RouteServiceImpl implements RouteService{
@Autowired
private CurrentUser currentUser;
@Autowired
RouteConfigMapper routeConfigMapper;
@Override
public List<Tree> getRoutes() {
List<Tree> list = new ArrayList<Tree>();
if(currentUser.getCurrentUser()!=null) {
List<Integer> permission = new ArrayList<Integer>();
List<Role> roles = currentUser.getCurrentUser().getRoles();
for(int i=0;i<roles.size();i++) {
List<Permission> permissions = roles.get(i).getPermissions();
for(int j=0;j<permissions.size();j++) {
permission.add(permissions.get(j).getId());
}
}
//符合权限的子路径
Set<RouteConfig> routes = routeConfigMapper.selectRouteConfig(permission);
List<RouteConfig> parentRoute = routeConfigMapper.selectByPid(0);
//给数组加入父路径
for(int i=0;i<parentRoute.size();i++) {
routes.add(parentRoute.get(i));
}
List<RouteConfig> routeList = new ArrayList<RouteConfig>(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<RouteConfig> list) {
List<Tree> 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<Tree> getTree(List<RouteConfig> list) {
System.out.println("routes"+list);
List<Tree> 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<Tree> 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<Tree> getRoute() {
return route;
}
public void setRoute(List<Tree> 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<Tree> 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 (
<Provider store={store}>
<Router history={history}>
<Leftsider/>
<div id="right">
<Right></Right>
</div>
</div>
</Router>
</Provider>
);
}
}
export default App;
left.js 侧边栏部分,每次加载这个组件,都会从后台请求侧边栏数据,存在store中
class Leftsider extends React.Component{
componentDidMount(){
this.props.initSliderList();
}
render(){
return(
<div id="left">
<Sider
trigger={null}
>
<div className="logo" />
<Menu
defaultSelectedKeys={['1']}
defaultOpenKeys={['sub1']}
mode="inline"
theme="dark"
>
{
this.props.list.map(function(item,index){
return (
<SubMenu key={index} title={<span>{item.title}</span>}>
{
item.route.map(function(item1,number){
return (
<Menu.Item key={item1.id} id={item1.id}><Link to={item1.url}>{item1.title}</Link></Menu.Item>
);
})
}
</SubMenu>
);
}
)
}
</Menu>
</Sider>
</div>
);
}
}
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:[]}
}
}
到这里,前端部分结束,完成动态加载菜单