|
@@ -0,0 +1,371 @@
|
|
|
+import { Footer, AvatarDropdown, AvatarName } from '@/components';
|
|
|
+import type { Settings as LayoutSettings } from '@ant-design/pro-components';
|
|
|
+import { PageLoading } from '@ant-design/pro-components';
|
|
|
+import type { RequestConfig, RunTimeLayoutConfig } from '@umijs/max';
|
|
|
+import { history } from '@umijs/max';
|
|
|
+import defaultSettings from '../config/defaultSettings';
|
|
|
+import { errorConfig } from './requestErrorConfig';
|
|
|
+import { getMenu, currentUser as queryCurrentUser } from '@/services/user/api';
|
|
|
+import { DesktopOutlined, MessageOutlined, SendOutlined, TeamOutlined, QrcodeOutlined, DatabaseOutlined, ReadOutlined, MobileOutlined, FundViewOutlined, RadarChartOutlined, BarChartOutlined, WechatOutlined, BookOutlined, FileImageOutlined, EyeOutlined, UserOutlined } from '@ant-design/icons';
|
|
|
+import { ReactComponent as LaunchSvg } from '@/assets/icons/launch.svg'
|
|
|
+import { ReactComponent as AdLaunchSvg } from '@/assets/icons/adLaunch.svg'
|
|
|
+import { ReactComponent as MaterialSvg } from '@/assets/icons/material.svg'
|
|
|
+import { ReactComponent as NumberSvg } from '@/assets/icons/number.svg'
|
|
|
+import { ReactComponent as GameSvg } from '@/assets/icons/game.svg'
|
|
|
+import { ReactComponent as GameServerSvg } from '@/assets/icons/gameServer.svg'
|
|
|
+import { ReactComponent as MediaSvg } from '@/assets/icons/media.svg'
|
|
|
+import { ReactComponent as PlayerSvg } from '@/assets/icons/player.svg'
|
|
|
+import { ReactComponent as RoleManageSvg } from '@/assets/icons/roleManage.svg'
|
|
|
+import { ReactComponent as MonitorSvg } from '@/assets/icons/monitor.svg'
|
|
|
+import { ReactComponent as BarChartSvg } from '@/assets/icons/barChart.svg'
|
|
|
+import { ReactComponent as PitcherDataSvg } from '@/assets/icons/pitcherData.svg'
|
|
|
+import { ReactComponent as WeChatDataSvg } from '@/assets/icons/weChatData.svg'
|
|
|
+import { ReactComponent as BookDataSvg } from '@/assets/icons/bookData.svg'
|
|
|
+import { ReactComponent as ImageDataSvg } from '@/assets/icons/imageData.svg'
|
|
|
+import { ReactComponent as RechargeDataSvg } from '@/assets/icons/rechargeData.svg'
|
|
|
+import { ReactComponent as NovelDataSystemSvg } from '@/assets/icons/novelDataSystem.svg'
|
|
|
+import { ReactComponent as CorpChatSvg } from '@/assets/icons/corpChat.svg'
|
|
|
+import { ReactComponent as WeChatSvg } from '@/assets/icons/weChat.svg'
|
|
|
+import { ReactComponent as AdLaunchsSvg } from '@/assets/icons/adLaunchs.svg'
|
|
|
+import { ReactComponent as IaaDataSvg } from '@/assets/icons/iaaData.svg'
|
|
|
+import { ReactComponent as ExternalMaterialSvg } from '@/assets/icons/externalMaterial.svg'
|
|
|
+import { ConfigProvider, Watermark } from 'antd';
|
|
|
+const isDev = process.env.NODE_ENV === 'development';
|
|
|
+const loginPath = '/user/login';
|
|
|
+
|
|
|
+/**
|
|
|
+ * @see https://umijs.org/zh-CN/plugins/plugin-initial-state
|
|
|
+ * */
|
|
|
+export async function getInitialState(): Promise<{
|
|
|
+ settings?: Partial<LayoutSettings>;
|
|
|
+ currentUser?: API.CurrentUser;
|
|
|
+ loading?: boolean;
|
|
|
+ menu?: any,
|
|
|
+ collapsed?: string,
|
|
|
+ onCollapse?: (onCollapse: boolean) => void
|
|
|
+ fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
|
|
|
+}> {
|
|
|
+ const fetchUserInfo = async () => {
|
|
|
+ try {
|
|
|
+ const msg = await queryCurrentUser();
|
|
|
+ return msg.data;
|
|
|
+ } catch (error) {
|
|
|
+ history.push(loginPath);
|
|
|
+ }
|
|
|
+ return undefined;
|
|
|
+ };
|
|
|
+ if (localStorage.getItem('Admin-Token')) {
|
|
|
+ try {
|
|
|
+ sessionStorage.removeItem('IS_MES')
|
|
|
+ const currentUser = await fetchUserInfo();
|
|
|
+ console.log('currentUser', currentUser)
|
|
|
+ if (currentUser) {
|
|
|
+ const { companyRelationInfo, userInfo: { powerLevel, nickname, userId, phone, sex }, onlineCompanyId } = currentUser
|
|
|
+ const companyInfo = companyRelationInfo?.filter((item: { companyId: number }) => item.companyId !== 4 && item.companyId !== 3)
|
|
|
+ localStorage.setItem('sex', sex)
|
|
|
+ localStorage.setItem('userId', userId)
|
|
|
+ localStorage.setItem('name', nickname)
|
|
|
+ const navTheme: any = localStorage.getItem('NAVTHEME')
|
|
|
+ const isNoDark = localStorage.getItem('ISNODARK')
|
|
|
+ return {
|
|
|
+ fetchUserInfo,
|
|
|
+ currentUser: { access: 'admin', powerLevel, name: nickname || '', userId, phone, companyList: companyInfo, onlineCompanyId: onlineCompanyId },
|
|
|
+ settings: { ...defaultSettings as Partial<LayoutSettings>, navTheme: navTheme || defaultSettings.navTheme, isNoDark: JSON.parse(isNoDark || 'false') } as any,
|
|
|
+ loading: false,
|
|
|
+ collapsed: '0',
|
|
|
+ onCollapse: (collapsed: boolean) => {
|
|
|
+ const v = collapsed ? '1' : '0'
|
|
|
+ localStorage.setItem('collapsed', v)
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ history.push(loginPath);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return {
|
|
|
+ fetchUserInfo,
|
|
|
+ settings: defaultSettings as Partial<LayoutSettings>,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+const IconMap = {
|
|
|
+ desktop: <DesktopOutlined />,
|
|
|
+ message: <MessageOutlined />,
|
|
|
+ send: <SendOutlined />,
|
|
|
+ team: <TeamOutlined />,
|
|
|
+ database: <DatabaseOutlined />,
|
|
|
+ qrcode: <QrcodeOutlined />,
|
|
|
+ read: <ReadOutlined />,
|
|
|
+ mobile: <MobileOutlined />,
|
|
|
+ fundView: <FundViewOutlined />,
|
|
|
+ radarChart: <RadarChartOutlined />,
|
|
|
+ wechat: <WechatOutlined />,
|
|
|
+ book: <BookOutlined />,
|
|
|
+ peoples: <UserOutlined />,
|
|
|
+ barChart: <BarChartOutlined />,
|
|
|
+ 'file-image': <FileImageOutlined />,
|
|
|
+ launch: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><LaunchSvg /></span>,
|
|
|
+ adLaunch: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><AdLaunchSvg /></span>,
|
|
|
+ material: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><MaterialSvg /></span>,
|
|
|
+ number: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><NumberSvg /></span>,
|
|
|
+ game: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><GameSvg /></span>,
|
|
|
+ gameServer: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><GameServerSvg /></span>,
|
|
|
+ media: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><MediaSvg /></span>,
|
|
|
+ player: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><PlayerSvg /></span>,
|
|
|
+ roleManage: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><RoleManageSvg /></span>,
|
|
|
+ monitor: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><MonitorSvg /></span>,
|
|
|
+ pitcherData: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><PitcherDataSvg /></span>,
|
|
|
+ barChartNoval: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><BarChartSvg /></span>,
|
|
|
+ weChatData: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><WeChatDataSvg /></span>,
|
|
|
+ bookData: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><BookDataSvg /></span>,
|
|
|
+ imageData: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><ImageDataSvg /></span>,
|
|
|
+ rechargeData: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><RechargeDataSvg /></span>,
|
|
|
+ novelDataSystem: <span role="img" aria-label="fund-view" className="anticon anticon-fund-view"><NovelDataSystemSvg /></span>,
|
|
|
+ eye: <EyeOutlined />
|
|
|
+};
|
|
|
+
|
|
|
+/** 处理远程路由 */
|
|
|
+const handleMenuData = (menuData: any[]) => {
|
|
|
+ return menuData.map(item => {
|
|
|
+ const data: { [x: string]: any } = {
|
|
|
+ name: item.title,
|
|
|
+ path: item.path,
|
|
|
+ icon: item.icon && IconMap[item.icon as keyof typeof IconMap]
|
|
|
+ }
|
|
|
+ if (item?.component) {
|
|
|
+ data.component = item.component
|
|
|
+ }
|
|
|
+ if (item?.children?.length) {
|
|
|
+ data.routes = handleMenuData(item.children)
|
|
|
+ }
|
|
|
+ return data
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// ProLayout 支持的api https://procomponents.ant.design/components/layout
|
|
|
+export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
|
|
|
+ let isLoading = false;
|
|
|
+ return {
|
|
|
+ splitMenus: true,//切割菜单
|
|
|
+ className: initialState?.settings?.navTheme === 'realDark' ? 'css-realDark' : (initialState?.settings as any)?.isNoDark ? 'css-light' : 'css-dark',
|
|
|
+ menu: {
|
|
|
+ params: {
|
|
|
+ token: initialState?.currentUser?.userId
|
|
|
+ },
|
|
|
+ request: async (_, defaultMenuData) => {
|
|
|
+ if (initialState?.currentUser?.userId) {
|
|
|
+ isLoading = true;
|
|
|
+ const menuData = await getMenu();
|
|
|
+ isLoading = false;
|
|
|
+ if (menuData?.data) {
|
|
|
+ const protoMenu = Object.values(menuData.data).map((item: unknown) => {
|
|
|
+ if (Array.isArray(item)) {
|
|
|
+ return item[0];
|
|
|
+ }
|
|
|
+ return null; // 或者根据业务逻辑返回默认值
|
|
|
+ }).filter(Boolean) as any[]
|
|
|
+ const { location } = history;
|
|
|
+ if (location.pathname === '/') {
|
|
|
+ // 如果当前路径是根路径,则重定向到第一个菜单的第一个子菜单
|
|
|
+ const firstPath = protoMenu?.[0]?.children?.[0]?.path || protoMenu?.[0]?.children?.[0]?.children?.[0]?.path;
|
|
|
+ if (firstPath) {
|
|
|
+ history.push(firstPath);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const menuSync = handleMenuData(protoMenu)
|
|
|
+ const menu = [
|
|
|
+ {
|
|
|
+ path: '/user',
|
|
|
+ layout: false,
|
|
|
+ routes: [
|
|
|
+ {
|
|
|
+ name: 'login',
|
|
|
+ path: '/user/login',
|
|
|
+ component: './User/Login',
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ {
|
|
|
+ path: '/',
|
|
|
+ redirect: '/dataStatisticsNovel' //firStPath,
|
|
|
+ },
|
|
|
+ ...menuSync.map(({ routes, ...item }) => {
|
|
|
+ return { ...item, routes: [{ path: item.path, redirect: routes[0].path }, ...routes] }
|
|
|
+ }),
|
|
|
+ {
|
|
|
+ path: '/mp',
|
|
|
+ name: '运营系统',
|
|
|
+ icon: <WeChatSvg />
|
|
|
+ },
|
|
|
+ {
|
|
|
+ path: '/corpChat',
|
|
|
+ name: '企微系统',
|
|
|
+ icon: <CorpChatSvg />
|
|
|
+ },
|
|
|
+ {
|
|
|
+ path: '/adLaunch',
|
|
|
+ name: '投放系统',
|
|
|
+ icon: <AdLaunchsSvg />
|
|
|
+ },
|
|
|
+ {
|
|
|
+ path: '/iaaData',
|
|
|
+ name: 'IAA数据系统',
|
|
|
+ icon: <IaaDataSvg />
|
|
|
+ },
|
|
|
+ {
|
|
|
+ path: '/exteriorMedia',
|
|
|
+ name: '外部素材',
|
|
|
+ icon: <ExternalMaterialSvg />
|
|
|
+ },
|
|
|
+ ]
|
|
|
+ setInitialState(s => ({ ...s, menu: menuData?.data }))
|
|
|
+ return menu;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return defaultMenuData
|
|
|
+ },
|
|
|
+ loading: isLoading
|
|
|
+ },
|
|
|
+ avatarProps: {
|
|
|
+ src: initialState?.currentUser?.avatar || 'https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png',
|
|
|
+ title: <AvatarName />,
|
|
|
+ render: (_, avatarChildren) => {
|
|
|
+ return <AvatarDropdown>{avatarChildren}</AvatarDropdown>;
|
|
|
+ },
|
|
|
+ },
|
|
|
+ footerRender: () => <Footer />,
|
|
|
+ onPageChange: (location) => {
|
|
|
+ if (location?.pathname === '/mp') {
|
|
|
+ window.open(`https://mp.zanxiangnet.com/#/user/login?token=${localStorage.getItem('Admin-Token')}`, '_blank')
|
|
|
+ history.back() // 返回上一页
|
|
|
+ return
|
|
|
+ } else if (location?.pathname === '/corpChat') {
|
|
|
+ window.open(`https://corp.zanxiangnet.com/#/login?token=${localStorage.getItem('Admin-Token')}`, '_blank')
|
|
|
+ history.back() // 返回上一页
|
|
|
+ return
|
|
|
+ } else if (location?.pathname === '/adLaunch') {
|
|
|
+ window.open(`https://adq.zanxiangnet.com/#/user/login?token=${localStorage.getItem('Admin-Token')}`, '_blank')
|
|
|
+ history.back() // 返回上一页
|
|
|
+ return
|
|
|
+ } else if (location?.pathname === '/iaaData') {
|
|
|
+ window.open(`https://iaadata.84game.cn/#/user/login?token=${localStorage.getItem('Admin-Token')}`, '_blank')
|
|
|
+ history.back() // 返回上一页
|
|
|
+ return
|
|
|
+ } else if (location?.pathname === '/exteriorMedia') { // 外部素材
|
|
|
+ window.open(`https://mp.zanxiangnet.com/#/user/login?token=${localStorage.getItem('Admin-Token')}&redirect=${encodeURIComponent('/#/exteriorMedia/reyun')}`, '_blank')
|
|
|
+ history.back() // 返回上一页
|
|
|
+ return
|
|
|
+ }
|
|
|
+ // 如果没有登录,重定向到 login
|
|
|
+ if (location?.pathname !== loginPath && !localStorage.getItem('Admin-Token')) {
|
|
|
+ history.push(loginPath);
|
|
|
+ }
|
|
|
+ if (initialState?.currentUser && location?.pathname !== loginPath) {
|
|
|
+ let menu = []
|
|
|
+ if (initialState?.menu) {
|
|
|
+ menu = Object.values(initialState?.menu).map((item: unknown) => {
|
|
|
+ if (Array.isArray(item)) {
|
|
|
+ return item[0];
|
|
|
+ }
|
|
|
+ return null; // 或者根据业务逻辑返回默认值
|
|
|
+ }).filter(Boolean) as any[]
|
|
|
+ }
|
|
|
+ if (menu?.length) {
|
|
|
+ switch (location?.pathname) {
|
|
|
+ case '/':
|
|
|
+ history.push(menu?.[0]?.children?.[0]?.path)
|
|
|
+ break;
|
|
|
+ case '/dataStatisticsNovel':
|
|
|
+ history.push(menu?.find(item => item.path === '/dataStatisticsNovel')?.children?.[0]?.path)
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ },
|
|
|
+ bgLayoutImgList: [
|
|
|
+ {
|
|
|
+ src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/D2LWSqNny4sAAAAAAAAAAAAAFl94AQBr',
|
|
|
+ left: 85,
|
|
|
+ bottom: 100,
|
|
|
+ height: '303px',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/C2TWRpJpiC0AAAAAAAAAAAAAFl94AQBr',
|
|
|
+ bottom: -68,
|
|
|
+ right: -45,
|
|
|
+ height: '303px',
|
|
|
+ },
|
|
|
+ {
|
|
|
+ src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/F6vSTbj8KpYAAAAAAAAAAAAAFl94AQBr',
|
|
|
+ bottom: 0,
|
|
|
+ left: 0,
|
|
|
+ width: '331px',
|
|
|
+ }
|
|
|
+ ],
|
|
|
+ menuHeaderRender: undefined,
|
|
|
+ // 自定义 403 页面
|
|
|
+ // unAccessible: <div>unAccessible</div>,
|
|
|
+ // 增加一个 loading 的状态
|
|
|
+ childrenRender: (children) => {
|
|
|
+ if (initialState?.loading) return <PageLoading />;
|
|
|
+ return (<ConfigProvider
|
|
|
+ componentSize='middle'
|
|
|
+ theme={initialState?.settings?.navTheme === 'realDark' ? undefined : {
|
|
|
+ components: {
|
|
|
+ Table: {
|
|
|
+ colorBorderSecondary: '#e0e0e0',
|
|
|
+ headerBg: '#F5F5F5'
|
|
|
+ },
|
|
|
+ Menu: {
|
|
|
+ fontSizeSM: 20,
|
|
|
+ fontSizeXL: 20,
|
|
|
+ fontSize: 20,
|
|
|
+ fontSizeLG: 20
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }}
|
|
|
+ >
|
|
|
+ <Watermark
|
|
|
+ content={initialState?.currentUser?.name}
|
|
|
+ font={{
|
|
|
+ color: 'rgba(255, 0, 0, 0.06)',
|
|
|
+ fontSize: 20,
|
|
|
+ fontWeight: 600,
|
|
|
+ }}
|
|
|
+ gap={[50, 100]}
|
|
|
+ >
|
|
|
+ {children}
|
|
|
+ {/* {isDev && (
|
|
|
+ <SettingDrawer
|
|
|
+ disableUrlParams
|
|
|
+ enableDarkTheme
|
|
|
+ settings={initialState?.settings}
|
|
|
+ onSettingChange={(settings) => {
|
|
|
+ setInitialState((preInitialState) => ({
|
|
|
+ ...preInitialState,
|
|
|
+ settings,
|
|
|
+ }));
|
|
|
+ }}
|
|
|
+ />
|
|
|
+ )} */}
|
|
|
+ </Watermark>
|
|
|
+ </ConfigProvider>);
|
|
|
+ },
|
|
|
+ ...initialState?.settings
|
|
|
+ };
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * @name request 配置,可以配置错误处理
|
|
|
+ * 它基于 axios 和 ahooks 的 useRequest 提供了一套统一的网络请求和错误处理方案。
|
|
|
+ * @doc https://umijs.org/docs/max/request#配置
|
|
|
+ */
|
|
|
+export const request: RequestConfig = {
|
|
|
+ ...errorConfig,
|
|
|
+ timeout: 300000,
|
|
|
+ headers: {
|
|
|
+ ['Authorization']: 'Bearer ' + localStorage.getItem('Admin-Token'),
|
|
|
+ ['debug']: sessionStorage.getItem('debug') || ''
|
|
|
+ }
|
|
|
+};
|