react + spring boot 選單許可權控制-動態載入二級選單
阿新 • • 發佈:2018-12-21
首先是給路徑建表,存在資料庫裡
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='許可權';
後臺用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:[]}
}
}
到這裡,前端部分結束,完成動態載入選單