app.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  1. import { Footer, AvatarDropdown, AvatarName } from '@/components';
  2. import type { Settings as LayoutSettings } from '@ant-design/pro-components';
  3. import { PageLoading, SettingDrawer } from '@ant-design/pro-components';
  4. import type { RunTimeLayoutConfig } from '@umijs/max';
  5. import { history } from '@umijs/max';
  6. import defaultSettings from '../config/defaultSettings';
  7. import { errorConfig } from './requestErrorConfig';
  8. import { getMenu, currentUser as queryCurrentUser } from '@/services/user/api';
  9. import React from 'react';
  10. import { DesktopOutlined, MessageOutlined, SendOutlined, TeamOutlined, QrcodeOutlined, DatabaseOutlined, ReadOutlined, MobileOutlined, FundViewOutlined, RadarChartOutlined, BarChartOutlined, WechatOutlined, BookOutlined, FileImageOutlined, EyeOutlined, UserOutlined } from '@ant-design/icons';
  11. import { ReactComponent as LaunchSvg } from '@/assets/icons/launch.svg'
  12. import { ReactComponent as AdLaunchSvg } from '@/assets/icons/adLaunch.svg'
  13. import { ReactComponent as MaterialSvg } from '@/assets/icons/material.svg'
  14. import { ReactComponent as NumberSvg } from '@/assets/icons/number.svg'
  15. import { ReactComponent as GameSvg } from '@/assets/icons/game.svg'
  16. import { ReactComponent as GameServerSvg } from '@/assets/icons/gameServer.svg'
  17. import { ReactComponent as MediaSvg } from '@/assets/icons/media.svg'
  18. import { ReactComponent as PlayerSvg } from '@/assets/icons/player.svg'
  19. import { ReactComponent as RoleManageSvg } from '@/assets/icons/roleManage.svg'
  20. import { ReactComponent as MonitorSvg } from '@/assets/icons/monitor.svg'
  21. import { ReactComponent as BarChartSvg } from '@/assets/icons/barChart.svg'
  22. import { ReactComponent as PitcherDataSvg } from '@/assets/icons/pitcherData.svg'
  23. import { ReactComponent as WeChatDataSvg } from '@/assets/icons/weChatData.svg'
  24. import { ReactComponent as BookDataSvg } from '@/assets/icons/bookData.svg'
  25. import { ReactComponent as ImageDataSvg } from '@/assets/icons/imageData.svg'
  26. import { ReactComponent as RechargeDataSvg } from '@/assets/icons/rechargeData.svg'
  27. import { ReactComponent as NovelDataSystemSvg } from '@/assets/icons/novelDataSystem.svg'
  28. import { ReactComponent as CorpChatSvg } from '@/assets/icons/corpChat.svg'
  29. import { ReactComponent as WeChatSvg } from '@/assets/icons/weChat.svg'
  30. import { ReactComponent as AdLaunchsSvg } from '@/assets/icons/adLaunchs.svg'
  31. import { ReactComponent as IaaDataSvg } from '@/assets/icons/iaaData.svg'
  32. import { ConfigProvider, Watermark } from 'antd';
  33. const isDev = process.env.NODE_ENV === 'development';
  34. const loginPath = '/user/login';
  35. /**
  36. * @see https://umijs.org/zh-CN/plugins/plugin-initial-state
  37. * */
  38. export async function getInitialState(): Promise<{
  39. settings?: Partial<LayoutSettings>;
  40. currentUser?: API.CurrentUser;
  41. loading?: boolean;
  42. menu?: any,
  43. collapsed?: string,
  44. onCollapse?: (onCollapse: boolean) => void
  45. fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
  46. }> {
  47. const fetchUserInfo = async () => {
  48. try {
  49. const msg = await queryCurrentUser();
  50. return msg.data;
  51. } catch (error) {
  52. history.push(loginPath);
  53. }
  54. return undefined;
  55. };
  56. if (localStorage.getItem('Admin-Token')) {
  57. try {
  58. sessionStorage.removeItem('IS_MES')
  59. const currentUser = await fetchUserInfo();
  60. console.log('currentUser', currentUser)
  61. if (currentUser) {
  62. const { companyRelationInfo, userInfo: { powerLevel, nickname, userId, phone, sex }, onlineCompanyId } = currentUser
  63. const companyInfo = companyRelationInfo?.filter((item: { companyId: number }) => item.companyId !== 4 && item.companyId !== 3)
  64. localStorage.setItem('sex', sex)
  65. localStorage.setItem('userId', userId)
  66. localStorage.setItem('name', nickname)
  67. const navTheme: any = localStorage.getItem('NAVTHEME')
  68. const isNoDark = localStorage.getItem('ISNODARK')
  69. return {
  70. fetchUserInfo,
  71. currentUser: { access: 'admin', powerLevel, name: nickname || '', userId, phone, companyList: companyInfo, onlineCompanyId: onlineCompanyId },
  72. settings: { ...defaultSettings as Partial<LayoutSettings>, navTheme: navTheme || defaultSettings.navTheme, isNoDark: JSON.parse(isNoDark || 'false') } as any,
  73. loading: false,
  74. collapsed: '0',
  75. onCollapse: (collapsed: boolean) => {
  76. const v = collapsed ? '1' : '0'
  77. localStorage.setItem('collapsed', v)
  78. }
  79. };
  80. }
  81. } catch (error) {
  82. history.push(loginPath);
  83. }
  84. }
  85. return {
  86. fetchUserInfo,
  87. settings: defaultSettings as Partial<LayoutSettings>,
  88. };
  89. }
  90. const IconMap = {
  91. desktop: <DesktopOutlined />,
  92. message: <MessageOutlined />,
  93. send: <SendOutlined />,
  94. team: <TeamOutlined />,
  95. database: <DatabaseOutlined />,
  96. qrcode: <QrcodeOutlined />,
  97. read: <ReadOutlined />,
  98. mobile: <MobileOutlined />,
  99. fundView: <FundViewOutlined />,
  100. radarChart: <RadarChartOutlined />,
  101. wechat: <WechatOutlined />,
  102. book: <BookOutlined />,
  103. peoples: <UserOutlined />,
  104. barChart: <BarChartOutlined />,
  105. 'file-image': <FileImageOutlined />,
  106. launch: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><LaunchSvg /></span>,
  107. adLaunch: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><AdLaunchSvg /></span>,
  108. material: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><MaterialSvg /></span>,
  109. number: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><NumberSvg /></span>,
  110. game: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><GameSvg /></span>,
  111. gameServer: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><GameServerSvg /></span>,
  112. media: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><MediaSvg /></span>,
  113. player: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><PlayerSvg /></span>,
  114. roleManage: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><RoleManageSvg /></span>,
  115. monitor: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><MonitorSvg /></span>,
  116. pitcherData: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><PitcherDataSvg /></span>,
  117. barChartNoval: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><BarChartSvg /></span>,
  118. weChatData: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><WeChatDataSvg /></span>,
  119. bookData: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><BookDataSvg /></span>,
  120. imageData: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><ImageDataSvg /></span>,
  121. rechargeData: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><RechargeDataSvg /></span>,
  122. novelDataSystem: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><NovelDataSystemSvg /></span>,
  123. eye: <EyeOutlined />
  124. };
  125. /** 处理远程路由 */
  126. const handleMenuData = (menuData: any[]) => {
  127. return menuData.map(item => {
  128. const data: { [x: string]: any } = {
  129. name: item.title,
  130. path: item.path,
  131. icon: item.icon && IconMap[item.icon as keyof typeof IconMap]
  132. }
  133. if (item?.component) {
  134. data.component = item.component
  135. }
  136. if (item?.children?.length) {
  137. data.routes = handleMenuData(item.children)
  138. }
  139. return data
  140. })
  141. }
  142. // ProLayout 支持的api https://procomponents.ant.design/components/layout
  143. export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
  144. let isLoading = false;
  145. return {
  146. splitMenus: true,//切割菜单
  147. className: initialState?.settings?.navTheme === 'realDark' ? 'css-realDark' : (initialState?.settings as any)?.isNoDark ? 'css-light' : 'css-dark',
  148. menu: {
  149. params: {
  150. token: initialState?.currentUser?.userId
  151. },
  152. request: async (_, defaultMenuData) => {
  153. if (initialState?.currentUser?.userId) {
  154. isLoading = true;
  155. const menuData = await getMenu();
  156. isLoading = false;
  157. if (menuData?.data) {
  158. const protoMenu = Object.values(menuData.data).map((item: unknown) => {
  159. if (Array.isArray(item)) {
  160. return item[0];
  161. }
  162. return null; // 或者根据业务逻辑返回默认值
  163. }).filter(Boolean) as any[]
  164. const { location } = history;
  165. if (location.pathname === '/') {
  166. // 如果当前路径是根路径,则重定向到第一个菜单的第一个子菜单
  167. const firstPath = protoMenu?.[0]?.children?.[0]?.path || protoMenu?.[0]?.children?.[0]?.children?.[0]?.path;
  168. if (firstPath) {
  169. history.push(firstPath);
  170. }
  171. }
  172. const menuSync = handleMenuData(protoMenu)
  173. const menu = [
  174. {
  175. path: '/user',
  176. layout: false,
  177. routes: [
  178. {
  179. name: 'login',
  180. path: '/user/login',
  181. component: './User/Login',
  182. },
  183. ],
  184. },
  185. {
  186. path: '/',
  187. redirect: '/dataStatisticsNovel' //firStPath,
  188. },
  189. ...menuSync.map(({ routes, ...item }) => {
  190. return { ...item, routes: [{ path: item.path, redirect: routes[0].path }, ...routes] }
  191. }),
  192. {
  193. path: '/mp',
  194. name: '运营系统',
  195. icon: <WeChatSvg />
  196. },
  197. {
  198. path: '/corpChat',
  199. name: '企微系统',
  200. icon: <CorpChatSvg />
  201. },
  202. {
  203. path: '/adLaunch',
  204. name: '投放系统',
  205. icon: <AdLaunchsSvg />
  206. },
  207. {
  208. path: '/iaaData',
  209. name: 'IAA数据系统',
  210. icon: <IaaDataSvg />
  211. },
  212. ]
  213. setInitialState(s => ({ ...s, menu: menuData?.data }))
  214. return menu;
  215. }
  216. }
  217. return defaultMenuData
  218. },
  219. loading: isLoading
  220. },
  221. avatarProps: {
  222. src: initialState?.currentUser?.avatar || 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
  223. title: <AvatarName />,
  224. render: (_, avatarChildren) => {
  225. return <AvatarDropdown>{avatarChildren}</AvatarDropdown>;
  226. },
  227. },
  228. footerRender: () => <Footer />,
  229. onPageChange: (location) => {
  230. if (location?.pathname === '/mp') {
  231. window.open(`https://mp.zanxiangnet.com/#/user/login?token=${localStorage.getItem('Admin-Token')}`, '_blank')
  232. history.back() // 返回上一页
  233. return
  234. } else if (location?.pathname === '/corpChat') {
  235. window.open(`https://corp.zanxiangnet.com/#/login?token=${localStorage.getItem('Admin-Token')}`, '_blank')
  236. history.back() // 返回上一页
  237. return
  238. } else if (location?.pathname === '/adLaunch') {
  239. window.open(`https://adq.zanxiangnet.com/#/user/login?token=${localStorage.getItem('Admin-Token')}`, '_blank')
  240. history.back() // 返回上一页
  241. return
  242. } else if (location?.pathname === '/iaaData') {
  243. window.open(`https://iaadata.84game.cn/#/user/login?token=${localStorage.getItem('Admin-Token')}`, '_blank')
  244. history.back() // 返回上一页
  245. return
  246. }
  247. // 如果没有登录,重定向到 login
  248. if (location?.pathname !== loginPath && !localStorage.getItem('Admin-Token')) {
  249. history.push(loginPath);
  250. }
  251. if (initialState?.currentUser && location?.pathname !== loginPath) {
  252. let menu = []
  253. if (initialState?.menu) {
  254. menu = Object.values(initialState?.menu).map((item: unknown) => {
  255. if (Array.isArray(item)) {
  256. return item[0];
  257. }
  258. return null; // 或者根据业务逻辑返回默认值
  259. }).filter(Boolean) as any[]
  260. }
  261. if (menu?.length) {
  262. switch (location?.pathname) {
  263. case '/':
  264. history.push(menu?.[0]?.children?.[0]?.path)
  265. break;
  266. case '/dataStatisticsNovel':
  267. history.push(menu?.find(item => item.path === '/dataStatisticsNovel')?.children?.[0]?.path)
  268. break
  269. }
  270. }
  271. }
  272. },
  273. bgLayoutImgList: [
  274. {
  275. src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/D2LWSqNny4sAAAAAAAAAAAAAFl94AQBr',
  276. left: 85,
  277. bottom: 100,
  278. height: '303px',
  279. },
  280. {
  281. src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/C2TWRpJpiC0AAAAAAAAAAAAAFl94AQBr',
  282. bottom: -68,
  283. right: -45,
  284. height: '303px',
  285. },
  286. {
  287. src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/F6vSTbj8KpYAAAAAAAAAAAAAFl94AQBr',
  288. bottom: 0,
  289. left: 0,
  290. width: '331px',
  291. }
  292. ],
  293. menuHeaderRender: undefined,
  294. // 自定义 403 页面
  295. // unAccessible: <div>unAccessible</div>,
  296. // 增加一个 loading 的状态
  297. childrenRender: (children) => {
  298. if (initialState?.loading) return <PageLoading />;
  299. return (<ConfigProvider
  300. componentSize='middle'
  301. theme={initialState?.settings?.navTheme === 'realDark' ? undefined : {
  302. components: {
  303. Table: {
  304. colorBorderSecondary: '#e0e0e0',
  305. headerBg: '#F5F5F5'
  306. },
  307. Menu: {
  308. fontSizeSM: 20,
  309. fontSizeXL: 20,
  310. fontSize: 20,
  311. fontSizeLG: 20
  312. }
  313. }
  314. }}
  315. >
  316. <Watermark
  317. content={initialState?.currentUser?.name}
  318. font={{
  319. color: 'rgba(255, 0, 0, 0.06)',
  320. fontSize: 20,
  321. fontWeight: 600,
  322. }}
  323. gap={[50, 100]}
  324. >
  325. {children}
  326. {/* {isDev && (
  327. <SettingDrawer
  328. disableUrlParams
  329. enableDarkTheme
  330. settings={initialState?.settings}
  331. onSettingChange={(settings) => {
  332. setInitialState((preInitialState) => ({
  333. ...preInitialState,
  334. settings,
  335. }));
  336. }}
  337. />
  338. )} */}
  339. </Watermark>
  340. </ConfigProvider>);
  341. },
  342. ...initialState?.settings
  343. };
  344. };
  345. /**
  346. * @name request 配置,可以配置错误处理
  347. * 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
  348. * @doc https://umijs.org/docs/max/request#配置
  349. */
  350. export const request = {
  351. ...errorConfig,
  352. timeout: 300000,
  353. headers: {
  354. ['Authorization']: 'Bearer ' + localStorage.getItem('Admin-Token'),
  355. ['debug']: sessionStorage.getItem('debug') || ''
  356. }
  357. };