需求: 根据数组转成树,渲染成菜单

背景

原先系统用户具有点击菜单的权限,而对于那些没有页面url访问权限的用户来说,点击菜单是很影响用户体验的。我们需要pm或者研发可以一开始就可以去获取用户菜单的访问权限。即将在代码中写死的menu放在平台上可配置。
for me: node端接入后端的接口获取当前用户可访问的菜单权限,然后在前端渲染

难点

1、如何在node端自动请求rpc接口获取数据
2、获取到的数据如何解决
3、对于后端的平铺数组,如何将其转成树结构然后在前端渲染

踩坑点

  • 1、一开始是想在 node端单点登陆的时候同时请求后端 thrift接口,然后对数据做树转换处理,写到固定路径下的文件中,最后client端拿到权限数据,渲染到页面上
    看上去,这种处理是合理的,本地也能跑通,但是一旦发布到test环境机器上面,请求存在,但是文件并没有写入,权限也并没有生效,一直走的是默认配置,本地生效是因为本地client端代码一直在不停构建,但是线上代码是构建打包后在请求的,所以一直走的是默认权限配置

    • 2、在一开始定的时候,想着node自请求,但是容易出现文件写乱的情况
    • 3、后来换了另一个方案,node端写一个接口,client端在原先获取默认菜单配置的时候去调这个接口,然后node端处理返回的数据,再将处理过的数据返回到前端渲染,这样会存在一个问题,在network中会请求多次。
      *解决办法:利用react shouldComponentUpdate 合理请求渲染即可**

    部分代码如下

  • node 端 rpc请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
const { thriftOptions: options } = require('config');
const koaRouter = require('koa-router');
const koaBody = require('koa-bodyparser');
const RPC = require('../lib/rpc');
const getTreeData = require('../lib/menu');

module.exports = app => {
const router = koaRouter();

router
.post('/rpc/mpm', koaBody(), function* rpcHandler() {
const rpc = new RPC(options);
const { request: { body = {} } } = this;
const { service, method: methodName, params } = body;
const { data } = yield rpc.exec({ service, methodName, params });

const bdMenu = data.data.filter(item => {
return item.title.indexOf('xxxxx-') === -1;
});
const adminMenu = data.data.filter(item => {
return item.title.indexOf('xxxxxxxxxx-') > -1;
});
bdMenu.splice(0, 0, bdMenu.find(item => item.title === 'xxxxxxx'));
const bdMenudata = getTreeData(bdMenu || [], -1);
const adminMenudata = getTreeData(adminMenu || [], -1);
const response = {
success: true,
message: '',
data: [{
name: 'xxxxx',
href: '/xxxxx',
children: bdMenudata || []
}, {
name: 'xxxxx',
href: '/xxxx',
children: adminMenudata || []
}]
};
console.log('response', response);
this.body = response;
});
app.use(router.routes());
};
  • 对thrift平铺的数据转成树
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
const getTreeData = (data, parentId) => {
const result = [];
let temp = [];
for (let i = 0; i < data.length; i++) {
if (data[i].parentId === parentId) {
const name = data[i].title.indexOf('xxxxxxx-') > -1 ? data[i].title.split('-')[1] : data[i].title;
const obj = {name, href: data[i].url, parentId: data[i].parentId, menuId: data[i].menuId};
temp = getTreeData(data, data[i].menuId);
if (temp.length > 0) {
obj.children = temp;
}
result.push(obj);
}
}
return result;
};

module.exports = getTreeData;
  • 前端处理多请求

父组件 willMount的时候 调用接口,第一次渲染后,自组件的 shouldComponentUpdate 返回false即可

1
2
3
4
5
6
7
{currentMenuData.length > 0 ? (
<SlideMenu data={currentMenuData} selected={current} active={current} />
) : null}
// 在组件SlideMenu中
shouldComponentUpdate() {
return false;
}