update api
This commit is contained in:
parent
8096f4a848
commit
4626bf2771
@ -10,7 +10,7 @@ export default defineConfig({
|
|||||||
title: '木羽清风仓储系统',
|
title: '木羽清风仓储系统',
|
||||||
locale: false,
|
locale: false,
|
||||||
},
|
},
|
||||||
headScripts: [
|
styles: [
|
||||||
'https://fonts.googleapis.com/css2?family=Calistoga&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap',
|
'https://fonts.googleapis.com/css2?family=Calistoga&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap',
|
||||||
],
|
],
|
||||||
proxy: {
|
proxy: {
|
||||||
|
|||||||
19
src/app.tsx
19
src/app.tsx
@ -5,11 +5,13 @@ import type { CurrentUser } from '@/types/api';
|
|||||||
import { antdTheme } from '@/types/theme';
|
import { antdTheme } from '@/types/theme';
|
||||||
|
|
||||||
const LOGIN_PATH = '/login';
|
const LOGIN_PATH = '/login';
|
||||||
|
const USER_CACHE_KEY = 'muyu_user_state';
|
||||||
let redirectingToLogin = false;
|
let redirectingToLogin = false;
|
||||||
|
|
||||||
function clearSessionAndRedirect(tip?: string) {
|
function clearSessionAndRedirect(tip?: string) {
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token');
|
||||||
localStorage.removeItem('tenantId');
|
localStorage.removeItem('tenantId');
|
||||||
|
localStorage.removeItem(USER_CACHE_KEY);
|
||||||
if (tip && !redirectingToLogin) {
|
if (tip && !redirectingToLogin) {
|
||||||
message.warning(tip);
|
message.warning(tip);
|
||||||
}
|
}
|
||||||
@ -34,16 +36,30 @@ export async function getInitialState(): Promise<InitialState> {
|
|||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Return cached state immediately to avoid blocking first render.
|
||||||
|
// An expired token will be caught lazily by the response interceptor on the next real API call.
|
||||||
|
const raw = localStorage.getItem(USER_CACHE_KEY);
|
||||||
|
if (raw) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(raw) as InitialState;
|
||||||
|
} catch {
|
||||||
|
localStorage.removeItem(USER_CACHE_KEY);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cache miss (first load after login or cache cleared) — blocking fetch.
|
||||||
try {
|
try {
|
||||||
const res = await fetch('/api/v1/auth/info', {
|
const res = await fetch('/api/v1/auth/info', {
|
||||||
headers: { Authorization: `Bearer ${token}` },
|
headers: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.code === 0 && data.data) {
|
if (data.code === 0 && data.data) {
|
||||||
return {
|
const state: InitialState = {
|
||||||
currentUser: data.data,
|
currentUser: data.data,
|
||||||
menuPaths: data.data.menuPaths || [],
|
menuPaths: data.data.menuPaths || [],
|
||||||
};
|
};
|
||||||
|
localStorage.setItem(USER_CACHE_KEY, JSON.stringify(state));
|
||||||
|
return state;
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
// network failure — fall through to redirect
|
// network failure — fall through to redirect
|
||||||
@ -85,6 +101,7 @@ export const layout: RunTimeLayoutConfig = (initialState) => ({
|
|||||||
logout: () => {
|
logout: () => {
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem('token');
|
||||||
localStorage.removeItem('tenantId');
|
localStorage.removeItem('tenantId');
|
||||||
|
localStorage.removeItem(USER_CACHE_KEY);
|
||||||
history.push(LOGIN_PATH);
|
history.push(LOGIN_PATH);
|
||||||
},
|
},
|
||||||
menuDataRender: (menuData: any[]) => {
|
menuDataRender: (menuData: any[]) => {
|
||||||
|
|||||||
@ -22,7 +22,7 @@ interface StatCardProps {
|
|||||||
|
|
||||||
const StatCard: React.FC<StatCardProps> = ({ icon, title, value, suffix, prefix, gradient }) => (
|
const StatCard: React.FC<StatCardProps> = ({ icon, title, value, suffix, prefix, gradient }) => (
|
||||||
<Card
|
<Card
|
||||||
bordered={false}
|
variant="borderless"
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 16,
|
borderRadius: 16,
|
||||||
border: gradient ? '2px solid transparent' : '1px solid #E2E8F0',
|
border: gradient ? '2px solid transparent' : '1px solid #E2E8F0',
|
||||||
@ -206,7 +206,7 @@ const DashboardPage: React.FC = () => {
|
|||||||
].map((item) => (
|
].map((item) => (
|
||||||
<Col xs={24} sm={12} lg={6} key={item.path}>
|
<Col xs={24} sm={12} lg={6} key={item.path}>
|
||||||
<Card
|
<Card
|
||||||
bordered={false}
|
variant="borderless"
|
||||||
hoverable
|
hoverable
|
||||||
style={{
|
style={{
|
||||||
borderRadius: 14,
|
borderRadius: 14,
|
||||||
|
|||||||
@ -10,7 +10,7 @@ const ImportPage: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<Card title="Excel 批量导入" bordered={false}>
|
<Card title="Excel 批量导入" variant="borderless">
|
||||||
<Space direction="vertical" size={20} style={{ display: 'flex' }}>
|
<Space direction="vertical" size={20} style={{ display: 'flex' }}>
|
||||||
<Alert
|
<Alert
|
||||||
message="导入说明"
|
message="导入说明"
|
||||||
|
|||||||
@ -59,7 +59,7 @@ const StatsPage: React.FC = () => {
|
|||||||
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
||||||
{stats.map((s) => (
|
{stats.map((s) => (
|
||||||
<Col xs={24} sm={12} lg={4} xl={4} key={s.title} style={{ flex: 1 }}>
|
<Col xs={24} sm={12} lg={4} xl={4} key={s.title} style={{ flex: 1 }}>
|
||||||
<Card bordered={false} styles={{ body: { padding: '20px 18px' } }}>
|
<Card variant="borderless" styles={{ body: { padding: '20px 18px' } }}>
|
||||||
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
|
<div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
|
||||||
<span style={{
|
<span style={{
|
||||||
display: 'flex', justifyContent: 'center', alignItems: 'center',
|
display: 'flex', justifyContent: 'center', alignItems: 'center',
|
||||||
@ -77,7 +77,7 @@ const StatsPage: React.FC = () => {
|
|||||||
</Row>
|
</Row>
|
||||||
<Row gutter={16}>
|
<Row gutter={16}>
|
||||||
<Col xs={24} lg={12}>
|
<Col xs={24} lg={12}>
|
||||||
<Card title="按颜色分类统计" bordered={false}>
|
<Card title="按颜色分类统计" variant="borderless">
|
||||||
<Table<StockGroup>
|
<Table<StockGroup>
|
||||||
dataSource={colorData}
|
dataSource={colorData}
|
||||||
rowKey="name"
|
rowKey="name"
|
||||||
@ -91,7 +91,7 @@ const StatsPage: React.FC = () => {
|
|||||||
</Card>
|
</Card>
|
||||||
</Col>
|
</Col>
|
||||||
<Col xs={24} lg={12}>
|
<Col xs={24} lg={12}>
|
||||||
<Card title="按位置分布统计" bordered={false}>
|
<Card title="按位置分布统计" variant="borderless">
|
||||||
<Table<StockGroup>
|
<Table<StockGroup>
|
||||||
dataSource={locationData}
|
dataSource={locationData}
|
||||||
rowKey="name"
|
rowKey="name"
|
||||||
|
|||||||
@ -5,6 +5,8 @@ import { LockOutlined, UserOutlined, BankOutlined } from '@ant-design/icons';
|
|||||||
import { getUserInfo, login } from '@/services/api';
|
import { getUserInfo, login } from '@/services/api';
|
||||||
import type { LoginParams } from '@/types/api';
|
import type { LoginParams } from '@/types/api';
|
||||||
|
|
||||||
|
const USER_CACHE_KEY = 'muyu_user_state';
|
||||||
|
|
||||||
const LoginPage: React.FC = () => {
|
const LoginPage: React.FC = () => {
|
||||||
const { setInitialState } = useModel('@@initialState');
|
const { setInitialState } = useModel('@@initialState');
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
@ -46,17 +48,14 @@ const LoginPage: React.FC = () => {
|
|||||||
res.data.userInfo?.tenantId || values.tenantId || 't_default_001',
|
res.data.userInfo?.tenantId || values.tenantId || 't_default_001',
|
||||||
);
|
);
|
||||||
const infoRes = await getUserInfo();
|
const infoRes = await getUserInfo();
|
||||||
|
let state: { currentUser: any; menuPaths: string[] };
|
||||||
if (infoRes?.code === 0 && infoRes.data) {
|
if (infoRes?.code === 0 && infoRes.data) {
|
||||||
await setInitialState({
|
state = { currentUser: infoRes.data, menuPaths: infoRes.data.menuPaths || [] };
|
||||||
currentUser: infoRes.data,
|
|
||||||
menuPaths: infoRes.data.menuPaths || [],
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
await setInitialState({
|
state = { currentUser: res.data.userInfo, menuPaths: [] };
|
||||||
currentUser: res.data.userInfo,
|
|
||||||
menuPaths: [],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
await setInitialState(state);
|
||||||
|
localStorage.setItem(USER_CACHE_KEY, JSON.stringify(state));
|
||||||
message.success('登录成功');
|
message.success('登录成功');
|
||||||
history.push('/dashboard');
|
history.push('/dashboard');
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@ -60,7 +60,7 @@ const ConfigsPage: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<Card bordered={false}>
|
<Card variant="borderless">
|
||||||
<Table<SystemConfig>
|
<Table<SystemConfig>
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataSource={data}
|
dataSource={data}
|
||||||
|
|||||||
@ -177,7 +177,7 @@ const MenusPage: React.FC = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<Card bordered={false}>
|
<Card variant="borderless">
|
||||||
<Button type="primary" icon={<PlusOutlined />} style={{ marginBottom: 16 }}>
|
<Button type="primary" icon={<PlusOutlined />} style={{ marginBottom: 16 }}>
|
||||||
新增菜单
|
新增菜单
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user