登录与品类
This commit is contained in:
@ -1,15 +1,15 @@
|
||||
// src/layouts/BasicLayout.tsx
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React, { useState, useEffect } from 'react';
|
||||
import ProLayout, { PageContainer } from '@ant-design/pro-layout';
|
||||
import { Link, useLocation, useIntl, useHistory } from 'umi';
|
||||
import { Link, useLocation, useIntl } from 'umi';
|
||||
import { connect } from 'dva';
|
||||
import defaultSettings from '../../config/defaultSettings';
|
||||
import routes from '../../config/router.config'; // 引入你的自定义路由结构
|
||||
import routes from '../../config/router.config';
|
||||
import { ConfigProvider, Breadcrumb } from 'antd';
|
||||
import HeaderComponent from './Header';
|
||||
import IconFont from '@/components/IconFont/IconFont';
|
||||
import type { BreadcrumbState } from '@/models/breadcrumb';
|
||||
import { SupplierDetailModalProvider } from '@/components/SupplierDetailModalContext/SupplierDetailModalContext';
|
||||
import type { TabModelState } from '@/models/tab';
|
||||
|
||||
const MenuRender = (item: any, isSubMenu: boolean) => {
|
||||
const intl = useIntl();
|
||||
@ -19,14 +19,14 @@ const MenuRender = (item: any, isSubMenu: boolean) => {
|
||||
<span className="ant-pro-menu-item">
|
||||
<IconFont type={item.icon as string} />
|
||||
<span className="ant-pro-menu-item-title">
|
||||
{intl.formatMessage({ id: `menu.${item.name}` || '' })}
|
||||
{intl.formatMessage({ id: `${item.name}` || '' })}
|
||||
</span>
|
||||
</span>
|
||||
) : (
|
||||
<Link className="ant-pro-menu-item" key={item.path} to={item.path || '/'} innerRef={null}>
|
||||
<IconFont type={item.icon as string} />
|
||||
<span className="ant-pro-menu-item-title">
|
||||
{intl.formatMessage({ id: `menu.${item.name}` || '' })}
|
||||
{intl.formatMessage({ id: `${item.name}` || '' })}
|
||||
</span>
|
||||
</Link>
|
||||
)}
|
||||
@ -34,28 +34,6 @@ const MenuRender = (item: any, isSubMenu: boolean) => {
|
||||
);
|
||||
};
|
||||
|
||||
// 递归交集过滤函数
|
||||
function filterMenuByRouter(routes: any, menuRoutes: any) {
|
||||
return routes.reduce((result: any, route: any) => {
|
||||
// 只看有 name 的节点
|
||||
const menu = menuRoutes.find((m) => m.name === route.name);
|
||||
if (menu) {
|
||||
// 匹配到后,递归处理 children
|
||||
let children = [];
|
||||
if (route.children && menu.children) {
|
||||
children = filterMenuByRouter(route.children, menu.children);
|
||||
}
|
||||
// 构建新节点,保留原 router.config.ts 路径等信息(比如 path, icon, ...其它字段)
|
||||
result.push({
|
||||
...route,
|
||||
// ...menu, // 可按需决定是否 menu 字段覆盖 route 字段(比如 path)
|
||||
// children: children.length > 0 ? children : undefined
|
||||
});
|
||||
}
|
||||
return result;
|
||||
}, []);
|
||||
}
|
||||
|
||||
const BreadcrumbRender = (
|
||||
routeBreadcrumb: any,
|
||||
intl: any,
|
||||
@ -89,64 +67,123 @@ const BreadcrumbRender = (
|
||||
interface BasicLayoutProps {
|
||||
children: React.ReactNode;
|
||||
breadcrumb: BreadcrumbState;
|
||||
tab: TabModelState;
|
||||
dispatch: any;
|
||||
}
|
||||
|
||||
function convertMenuData(menus: any[]): any[] {
|
||||
return menus.map(item => {
|
||||
// 保留 icon 字段,如果没有就给默认
|
||||
const icon = item.icon || undefined;
|
||||
// 递归 children->routes
|
||||
let routes;
|
||||
if (item.children && item.children.length > 0) {
|
||||
routes = convertMenuData(item.children);
|
||||
}
|
||||
// 国际化优先找 menu.name,否则直接显示 name
|
||||
return {
|
||||
...item,
|
||||
icon,
|
||||
routes, // prolayout 只认 routes
|
||||
name: item.name, // 不要转 menu.xxx,MenuRender 时才转
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
const BasicLayout: React.FC<BasicLayoutProps> = (props) => {
|
||||
const { children, breadcrumb } = props;
|
||||
const { children, tab, dispatch } = props;
|
||||
const location = useLocation();
|
||||
const intl = useIntl();
|
||||
const history = useHistory();
|
||||
|
||||
const handleTabChange = (key: string) => {
|
||||
dispatch({
|
||||
type: 'tab/switchTab',
|
||||
payload: { key },
|
||||
});
|
||||
};
|
||||
|
||||
const handleTabEdit = (targetKey: any, action: string) => {
|
||||
if (action === 'remove') {
|
||||
dispatch({
|
||||
type: 'tab/closeTab',
|
||||
payload: { key: targetKey },
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
const [menuRoutes, setMenuRoutes] = useState<any[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const menuStr = sessionStorage.getItem('menuList');
|
||||
if (menuStr) {
|
||||
const menusFromApi = JSON.parse(menuStr);
|
||||
// routes是本地静态路由,menusFromApi是接口菜单
|
||||
const finalMenus = filterMenuByRouter(routes, menusFromApi);
|
||||
console.log(finalMenus);
|
||||
|
||||
setMenuRoutes(finalMenus);
|
||||
const menus = JSON.parse(menuStr);
|
||||
setMenuRoutes(convertMenuData(menus));
|
||||
}
|
||||
}, []);
|
||||
|
||||
|
||||
if (menuRoutes.length === 0) return null;
|
||||
|
||||
|
||||
return (
|
||||
<ConfigProvider>
|
||||
<SupplierDetailModalProvider>
|
||||
<ProLayout
|
||||
{...defaultSettings}
|
||||
route={{ routes }}
|
||||
// route={{ routes: menuRoutes }}
|
||||
subMenuItemRender={(menuItemProps, defaultDom) => {
|
||||
return MenuRender(menuItemProps, true);
|
||||
<ProLayout
|
||||
{...defaultSettings}
|
||||
route={{ path: '/', routes: menuRoutes }}
|
||||
breadcrumbRender={() => undefined}
|
||||
subMenuItemRender={(menuItemProps, defaultDom) => {
|
||||
return MenuRender(menuItemProps, true);
|
||||
}}
|
||||
menuItemRender={(item, dom) => {
|
||||
return MenuRender(item, false);
|
||||
}}
|
||||
location={location}
|
||||
fixSiderbar
|
||||
layout="mix"
|
||||
headerRender={() => {
|
||||
return <HeaderComponent />;
|
||||
}}
|
||||
>
|
||||
<PageContainer
|
||||
ghost={true}
|
||||
header={{
|
||||
title: false,
|
||||
breadcrumbRender: () => null,
|
||||
// breadcrumbRender: ({ breadcrumb: routeBreadcrumb }) =>
|
||||
// BreadcrumbRender(routeBreadcrumb, intl, history, breadcrumb.breadcrumbName),
|
||||
}}
|
||||
menuItemRender={(item, dom) => {
|
||||
return MenuRender(item, false);
|
||||
}}
|
||||
location={location}
|
||||
fixSiderbar
|
||||
layout="mix"
|
||||
headerRender={() => {
|
||||
return <HeaderComponent />;
|
||||
// 将tab.tabList转换为需要的格式,添加国际化处理
|
||||
tabList={tab.tabList.map((item) => ({
|
||||
...item,
|
||||
tab:
|
||||
typeof item.tab === 'string'
|
||||
? intl.formatMessage({ id: `menu.${item.tab}` })
|
||||
: item.tab,
|
||||
}))}
|
||||
tabProps={{
|
||||
type: 'editable-card',
|
||||
hideAdd: true,
|
||||
activeKey: tab.activeKey,
|
||||
onChange: handleTabChange,
|
||||
onEdit: handleTabEdit,
|
||||
size: 'small',
|
||||
tabBarGutter: 6,
|
||||
renderTabBar: (propsTab, DefaultTabBar) => (
|
||||
<DefaultTabBar {...propsTab} className="custom-tab-bar" />
|
||||
),
|
||||
}}
|
||||
>
|
||||
<PageContainer
|
||||
ghost={true}
|
||||
header={{
|
||||
title: false,
|
||||
breadcrumbRender: ({ breadcrumb: routeBreadcrumb }) =>
|
||||
BreadcrumbRender(routeBreadcrumb, intl, history, breadcrumb.breadcrumbName),
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</PageContainer>
|
||||
</ProLayout>
|
||||
</SupplierDetailModalProvider>
|
||||
{children}
|
||||
</PageContainer>
|
||||
</ProLayout>
|
||||
</ConfigProvider>
|
||||
);
|
||||
};
|
||||
|
||||
export default connect(({ breadcrumb }: { breadcrumb: BreadcrumbState }) => ({
|
||||
breadcrumb,
|
||||
}))(BasicLayout);
|
||||
export default connect(
|
||||
({ breadcrumb, tab }: { breadcrumb: BreadcrumbState; tab: TabModelState }) => ({
|
||||
breadcrumb,
|
||||
tab,
|
||||
}),
|
||||
)(BasicLayout);
|
||||
|
@ -1,44 +1,17 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import React from 'react';
|
||||
//导入logo图片
|
||||
import LogoImg from '@/assets/img/logo.png';
|
||||
//导入菜单组件
|
||||
import Language from './Language';
|
||||
import User from './User';
|
||||
import { history } from 'umi';
|
||||
import './layout.less';
|
||||
import { Logout } from '@/servers/api/login';
|
||||
|
||||
const HeaderComponent: React.FC = () => {
|
||||
// 用 state 保存 userId
|
||||
const [userId, setUserId] = useState(() => sessionStorage.getItem('userId'));
|
||||
|
||||
const handLogout = () => {
|
||||
Logout()
|
||||
sessionStorage.clear();
|
||||
history.replace('/login');
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
// 定义一个方法用于手动刷新 userId
|
||||
const refreshUserId = () => setUserId(sessionStorage.getItem('userId'));
|
||||
// 登录后你可以手动调用 refreshUserId
|
||||
// 或者监听页面 storage 事件(多窗口/多tab同步)
|
||||
window.addEventListener('storage', refreshUserId);
|
||||
// 页面内操作,比如登录成功后,也可以在登录回调里调用 setUserId
|
||||
return () => window.removeEventListener('storage', refreshUserId);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className="headerComponent">
|
||||
<img className="logo" src={LogoImg} alt="logo" />
|
||||
<div className="flex">
|
||||
<Language />
|
||||
{!userId ? (
|
||||
<User />
|
||||
) : (
|
||||
<div style={{cursor: 'pointer'}} onClick={handLogout}>退出</div>
|
||||
)}
|
||||
|
||||
<User />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -1,13 +1,94 @@
|
||||
import React from 'react';
|
||||
import { Link, useIntl } from 'umi';
|
||||
const User: React.FC = (props) => {
|
||||
const intl = useIntl();
|
||||
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { Link, useIntl, connect, history } from 'umi';
|
||||
import { Dropdown, Button, Modal } from 'antd';
|
||||
import { DownOutlined, ExclamationCircleOutlined } from '@ant-design/icons';
|
||||
import type { ConnectProps, Dispatch } from 'umi';
|
||||
import type { UserModelState } from '@/models/user';
|
||||
import { message } from 'antd';
|
||||
|
||||
interface PageProps extends ConnectProps {
|
||||
user: UserModelState; // dva model状态
|
||||
dispatch: Dispatch; // dva dispatch方法
|
||||
}
|
||||
const User: React.FC<PageProps> = ({ user, dispatch }) => {
|
||||
|
||||
|
||||
const intl = useIntl();
|
||||
useEffect(() => {
|
||||
if (!user.userInfo) {
|
||||
const userinfoStr = sessionStorage.getItem('Userinfo');
|
||||
if (userinfoStr) {
|
||||
try {
|
||||
const userInfo = JSON.parse(userinfoStr);
|
||||
dispatch({
|
||||
type: 'user/saveUserInfo',
|
||||
payload: userInfo,
|
||||
});
|
||||
} catch (e) {
|
||||
// 解析失败时可以报错提示
|
||||
console.error('Userinfo 解析失败', e);
|
||||
}
|
||||
}
|
||||
} else if (user.token) {
|
||||
dispatch({
|
||||
type: 'user/fetchUserInfo',
|
||||
});
|
||||
}
|
||||
}, [user.token]);
|
||||
const handleMenuClick = (e: any) => {
|
||||
if (e.key === 'logout') {
|
||||
Modal.confirm({
|
||||
title: '确定退出登录吗?',
|
||||
icon: <ExclamationCircleOutlined />,
|
||||
content: '退出登录后,您将需要重新登录',
|
||||
onOk() {
|
||||
dispatch({
|
||||
type: 'user/logout',
|
||||
}).then(() => {
|
||||
sessionStorage.clear();
|
||||
message.success('退出登录成功');
|
||||
history.push('/login');
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
return;
|
||||
},
|
||||
});
|
||||
} else if(e.key === 'profile') {
|
||||
console.log(111);
|
||||
|
||||
// history.push('/profile');
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="user">
|
||||
<Link to={'/login'}>{intl.formatMessage({ id: '登录/注册' })}</Link>
|
||||
{user.userInfo?.fullName ? (
|
||||
<Dropdown
|
||||
trigger={['hover']}
|
||||
menu={{
|
||||
items: [
|
||||
{
|
||||
key: 'profile',
|
||||
label: '个人中心',
|
||||
},
|
||||
{
|
||||
key: 'logout',
|
||||
label: '退出登录',
|
||||
},
|
||||
],
|
||||
onClick: handleMenuClick,
|
||||
}}
|
||||
>
|
||||
<Button type="link">
|
||||
{`${user.userInfo?.fullName}`}
|
||||
<DownOutlined />
|
||||
</Button>
|
||||
</Dropdown>
|
||||
) : (
|
||||
<Link to={'/login'}>{intl.formatMessage({ id: '登录' })}</Link>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
export default User;
|
||||
|
||||
export default connect(({ user }: { user: UserModelState }) => ({ user }))(User);
|
||||
|
@ -20,8 +20,32 @@
|
||||
align-items: center;
|
||||
}
|
||||
.layout-content {
|
||||
background: rgb(245,246,250);
|
||||
background: rgb(245, 246, 250);
|
||||
padding: 0 15px;
|
||||
height: calc(100vh - 64px);
|
||||
overflow: auto;
|
||||
}
|
||||
.ant-page-header.has-footer{
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
.custom-tab-bar {
|
||||
margin-bottom: 10px !important;
|
||||
.ant-tabs-tab {
|
||||
border-radius: 5px !important;
|
||||
font-size: 13px !important;
|
||||
padding: 6px 10px !important;
|
||||
.ant-tabs-tab-remove{
|
||||
margin-left: 0 !important;
|
||||
font-size: 10px !important;
|
||||
}
|
||||
&.ant-tabs-tab-active{
|
||||
background-color: @main-color !important;
|
||||
.ant-tabs-tab-btn{
|
||||
color: #fff !important;
|
||||
}
|
||||
.ant-tabs-tab-remove{
|
||||
color: #fff !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user