react + spring boot 菜单权限控制-动态加载二级菜单

12 篇文章 0 订阅
6 篇文章 0 订阅

首先是给路径建表,存在数据库里

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:[]}
    }
}

到这里,前端部分结束,完成动态加载菜单

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值