You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

239 lines
5.9 KiB

import memoizeOne from 'memoize-one';
import isEqual from 'lodash/isEqual';
import { formatMessage } from 'umi/locale';
import Authorized from '@/utils/Authorized';
import { menu } from '../defaultSettings';
import { getAdminMenus, getAdminUrls } from '../services/admin';
const { check } = Authorized;
// Conversion router to menu.
function formatter(data, parentAuthority, parentName) {
return data
.map(item => {
if (!item.name || !item.path) {
return null;
}
let locale = 'menu';
if (parentName) {
locale = `${parentName}.${item.name}`;
} else {
locale = `menu.${item.name}`;
}
// if enableMenuLocale use item.name,
// close menu international
const name = menu.disableLocal
? item.name
: formatMessage({ id: locale, defaultMessage: item.name });
const result = {
...item,
name,
locale,
authority: item.authority || parentAuthority,
};
if (item.routes) {
const children = formatter(item.routes, item.authority, locale);
// Reduce memory usage
result.children = children;
}
delete result.routes;
return result;
})
.filter(item => item);
}
const memoizeOneFormatter = memoizeOne(formatter, isEqual);
/**
* get SubMenu or Item
*/
const getSubMenu = item => {
// doc: add hideChildrenInMenu
if (item.children && !item.hideChildrenInMenu && item.children.some(child => child.name)) {
return {
...item,
children: filterMenuData(item.children), // eslint-disable-line
};
}
return item;
};
/**
* filter menuData
*/
const filterMenuData = menuData => {
if (!menuData) {
return [];
}
return menuData
.filter(item => item.name && !item.hideInMenu)
.map(item => check(item.authority, getSubMenu(item)))
.filter(item => item);
};
// 用于生成uuid
function S4() {
return ((1 + Math.random()) * 0x10000 || 0).toString(16).substring(1);
}
function guid() {
return S4() + S4() + S4() + S4() + S4() + S4() + S4() + S4();
}
const findRootMenu = (antDataMenus, rootAntDataMenu, requestDataMenu) => {
let res;
for (let i = 0; i < antDataMenus.length; i += 1) {
const antDataMenu = antDataMenus[i];
if (antDataMenu.path === requestDataMenu.handler) {
res = rootAntDataMenu;
break;
}
if (antDataMenu.children) {
res = findRootMenu(antDataMenu.children, antDataMenu, requestDataMenu);
break;
}
}
return res;
};
const buildTreeMenu = (antMenuData, moveChildrenMenusData, requestDataMenus) => {
return requestDataMenus.map(item => {
if (!item.handler) {
// root 节点
const uuid = `sms${guid()}`;
const res = {
icon: 'user',
name: item.displayName,
path: uuid,
};
// 子节点
if (item.children) {
// 通过子节点找到对于的父节点,设置 path没有则是 uuid
const rootMenu = findRootMenu(antMenuData, {}, item.children[0]);
if (rootMenu) {
res.path = rootMenu.path;
}
// 开始递归构建数据结构
const childrenMenus = buildTreeMenu(antMenuData, moveChildrenMenusData, item.children);
res.children = childrenMenus;
}
return res;
}
// moveChildrenMenusData 是一个 map对比 url 地址是否存在,不存在就给一个 404 的页面
const handleMapperData = moveChildrenMenusData[item.handler];
if (handleMapperData) {
return {
...handleMapperData,
icon: 'user',
name: item.displayName,
path: item.handler,
};
}
// 没有就返回404页面
return moveChildrenMenusData['/exception/404'];
});
};
const moveChildrenMenus = antDataMenus => {
let res = {};
for (let i = 0; i < antDataMenus.length; i += 1) {
const antDataMenu = antDataMenus[i];
res[antDataMenu.path] = {
...res,
...antDataMenu,
};
if (antDataMenu.children) {
const childrenMenus = moveChildrenMenus(antDataMenu.children);
res = {
...res,
...childrenMenus,
};
}
}
return res;
};
/**
* 获取面包屑映射
* @param {Object} menuData 菜单配置
*/
const getBreadcrumbNameMap = menuData => {
const routerMap = {};
const flattenMenuData = data => {
data.forEach(menuItem => {
if (menuItem.children) {
flattenMenuData(menuItem.children);
}
// Reduce memory usage
routerMap[menuItem.path] = menuItem;
});
};
flattenMenuData(menuData);
return routerMap;
};
const memoizeOneGetBreadcrumbNameMap = memoizeOne(getBreadcrumbNameMap, isEqual);
export default {
namespace: 'menu',
state: {
menuData: [],
urlsData: {},
breadcrumbNameMap: {},
},
effects: {
*getMenuData({ payload }, { put, call }) {
const { data } = yield call(getAdminMenus);
const { routes, authority } = payload;
// authority 已经不适用
const antMenuData = filterMenuData(memoizeOneFormatter(routes, authority));
let menuData = antMenuData;
// const resultMenuData = data;
if (data !== 'all') {
const moveChildrenMenusData = moveChildrenMenus(antMenuData);
const buildTreeMenuData = buildTreeMenu(antMenuData, moveChildrenMenusData, data);
menuData = buildTreeMenuData;
}
// 生成 menu 和 router mapping
const breadcrumbNameMap = memoizeOneGetBreadcrumbNameMap(menuData);
yield put({
type: 'save',
payload: { menuData, breadcrumbNameMap },
});
},
*getUrlsData(state, { put, call }) {
const { data } = yield call(getAdminUrls);
// 构建 {'/user': true} 这种 map 结构方便取数据、
const urlsData = {};
data.forEach(item => {
urlsData[item] = true;
});
yield put({
type: 'save',
payload: { urlsData },
});
},
},
reducers: {
save(state, { payload }) {
return {
...state,
...payload,
};
},
},
};