menu.ts 3.57 KB
Newer Older
1
import { MenuDataItem, Route } from '@/components/SiderMenu';
2
import Authorized from '@/utils/Authorized';
3 4 5 6
import { Effect } from 'dva';
import isEqual from 'lodash/isEqual';
import memoizeOne from 'memoize-one';
import { Reducer } from 'redux';
7
import { formatMessage } from 'umi-plugin-react/locale';
ไฝ•ไน's avatar
ไฝ•ไน committed
8
import { IRoute } from 'umi-types';
9
import defaultSettings from '../../config/defaultSettings';
10 11

// Conversion router to menu.
ไฝ•ไน's avatar
ไฝ•ไน committed
12
function formatter(
13
  data: Route[],
ไฝ•ไน's avatar
ไฝ•ไน committed
14 15 16
  parentAuthority?: string[] | string,
  parentName?: string,
): MenuDataItem[] {
17
  return data
ไฝ•ไน's avatar
ไฝ•ไน committed
18
    .filter(item => item.name && item.path)
19
    .map(item => {
ไฝ•ไน's avatar
ไฝ•ไน committed
20
      const locale = `${parentName || 'menu'}.${item.name!}`;
Yu's avatar
Yu committed
21 22
      // if enableMenuLocale use item.name,
      // close menu international
ไฝ•ไน's avatar
ไฝ•ไน committed
23 24 25 26
      const name = defaultSettings.menu.disableLocal
        ? item.name!
        : formatMessage({ id: locale, defaultMessage: item.name! });
      const result: MenuDataItem = {
27
        ...item,
Yu's avatar
Yu committed
28
        name,
29
        locale,
30
        routes: void 0,
31 32 33 34 35 36 37 38
        authority: item.authority || parentAuthority,
      };
      if (item.routes) {
        const children = formatter(item.routes, item.authority, locale);
        // Reduce memory usage
        result.children = children;
      }
      return result;
ไฝ•ไน's avatar
ไฝ•ไน committed
39
    });
40 41 42 43 44 45 46
}

const memoizeOneFormatter = memoizeOne(formatter, isEqual);

/**
 * get SubMenu or Item
 */
ไฝ•ไน's avatar
ไฝ•ไน committed
47 48 49 50 51 52 53 54
const getSubMenu: (item: MenuDataItem) => MenuDataItem = item => {
  if (
    Array.isArray(item.children) &&
    !item.hideChildrenInMenu &&
    item.children.some(child => (child.name ? true : false))
  ) {
    const children = filterMenuData(item.children);
    if (children.length) return { ...item, children };
55
  }
ไฝ•ไน's avatar
ไฝ•ไน committed
56
  return { ...item, children: void 0 };
57 58 59 60 61
};

/**
 * filter menuData
 */
ไฝ•ไน's avatar
ไฝ•ไน committed
62
const filterMenuData = (menuData: MenuDataItem[] = []): MenuDataItem[] => {
63 64
  return menuData
    .filter(item => item.name && !item.hideInMenu)
ไฝ•ไน's avatar
ไฝ•ไน committed
65
    .map(item => Authorized.check<any, any>(item.authority!, getSubMenu(item), null))
66 67
    .filter(item => item);
};
ไฝ•ไน's avatar
ไฝ•ไน committed
68

69 70
/**
 * ่Žทๅ–้ขๅŒ…ๅฑ‘ๆ˜ ๅฐ„
ไฝ•ไน's avatar
ไฝ•ไน committed
71
 * @param MenuDataItem[] menuData ่œๅ•้…็ฝฎ
72
 */
ไฝ•ไน's avatar
ไฝ•ไน committed
73 74 75
const getBreadcrumbNameMap = (menuData: MenuDataItem[]) => {
  const routerMap: { [key: string]: MenuDataItem } = {};
  const flattenMenuData: (data: MenuDataItem[]) => void = data => {
76 77 78 79 80 81 82 83 84 85 86 87 88
    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);
89

90
export interface MenuModelState {
ไฝ•ไน's avatar
ไฝ•ไน committed
91 92
  menuData: MenuDataItem[];
  routerData: IRoute[];
93 94 95 96 97 98 99 100 101 102
  breadcrumbNameMap: object;
}

export interface MenuModelType {
  namespace: 'menu';
  state: MenuModelState;
  effects: {
    getMenuData: Effect;
  };
  reducers: {
ไฝ•ไน's avatar
ไฝ•ไน committed
103
    save: Reducer<MenuModelState>;
104 105
  };
}
ไฝ•ไน's avatar
ไฝ•ไน committed
106

107
const MenuModel: MenuModelType = {
108 109 110 111
  namespace: 'menu',

  state: {
    menuData: [],
Yu's avatar
Yu committed
112
    routerData: [],
113
    breadcrumbNameMap: {},
114 115 116 117 118
  },

  effects: {
    *getMenuData({ payload }, { put }) {
      const { routes, authority } = payload;
Yu's avatar
Yu committed
119 120 121
      const originalMenuData = memoizeOneFormatter(routes, authority);
      const menuData = filterMenuData(originalMenuData);
      const breadcrumbNameMap = memoizeOneGetBreadcrumbNameMap(originalMenuData);
122 123
      yield put({
        type: 'save',
Yu's avatar
Yu committed
124
        payload: { menuData, breadcrumbNameMap, routerData: routes },
125 126 127 128 129 130 131 132
      });
    },
  },

  reducers: {
    save(state, action) {
      return {
        ...state,
133
        ...action.payload,
134 135 136 137
      };
    },
  },
};
138 139

export default MenuModel;