update api

This commit is contained in:
kae 2026-05-22 18:59:15 +09:00
parent 8096f4a848
commit 4626bf2771
8 changed files with 34 additions and 18 deletions

View File

@ -10,7 +10,7 @@ export default defineConfig({
title: '木羽清风仓储系统',
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',
],
proxy: {

View File

@ -5,11 +5,13 @@ import type { CurrentUser } from '@/types/api';
import { antdTheme } from '@/types/theme';
const LOGIN_PATH = '/login';
const USER_CACHE_KEY = 'muyu_user_state';
let redirectingToLogin = false;
function clearSessionAndRedirect(tip?: string) {
localStorage.removeItem('token');
localStorage.removeItem('tenantId');
localStorage.removeItem(USER_CACHE_KEY);
if (tip && !redirectingToLogin) {
message.warning(tip);
}
@ -34,16 +36,30 @@ export async function getInitialState(): Promise<InitialState> {
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 {
const res = await fetch('/api/v1/auth/info', {
headers: { Authorization: `Bearer ${token}` },
});
const data = await res.json();
if (data.code === 0 && data.data) {
return {
const state: InitialState = {
currentUser: data.data,
menuPaths: data.data.menuPaths || [],
};
localStorage.setItem(USER_CACHE_KEY, JSON.stringify(state));
return state;
}
} catch {
// network failure — fall through to redirect
@ -85,6 +101,7 @@ export const layout: RunTimeLayoutConfig = (initialState) => ({
logout: () => {
localStorage.removeItem('token');
localStorage.removeItem('tenantId');
localStorage.removeItem(USER_CACHE_KEY);
history.push(LOGIN_PATH);
},
menuDataRender: (menuData: any[]) => {

View File

@ -22,7 +22,7 @@ interface StatCardProps {
const StatCard: React.FC<StatCardProps> = ({ icon, title, value, suffix, prefix, gradient }) => (
<Card
bordered={false}
variant="borderless"
style={{
borderRadius: 16,
border: gradient ? '2px solid transparent' : '1px solid #E2E8F0',
@ -206,7 +206,7 @@ const DashboardPage: React.FC = () => {
].map((item) => (
<Col xs={24} sm={12} lg={6} key={item.path}>
<Card
bordered={false}
variant="borderless"
hoverable
style={{
borderRadius: 14,

View File

@ -10,7 +10,7 @@ const ImportPage: React.FC = () => {
return (
<PageContainer>
<Card title="Excel 批量导入" bordered={false}>
<Card title="Excel 批量导入" variant="borderless">
<Space direction="vertical" size={20} style={{ display: 'flex' }}>
<Alert
message="导入说明"

View File

@ -59,7 +59,7 @@ const StatsPage: React.FC = () => {
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
{stats.map((s) => (
<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 }}>
<span style={{
display: 'flex', justifyContent: 'center', alignItems: 'center',
@ -77,7 +77,7 @@ const StatsPage: React.FC = () => {
</Row>
<Row gutter={16}>
<Col xs={24} lg={12}>
<Card title="按颜色分类统计" bordered={false}>
<Card title="按颜色分类统计" variant="borderless">
<Table<StockGroup>
dataSource={colorData}
rowKey="name"
@ -91,7 +91,7 @@ const StatsPage: React.FC = () => {
</Card>
</Col>
<Col xs={24} lg={12}>
<Card title="按位置分布统计" bordered={false}>
<Card title="按位置分布统计" variant="borderless">
<Table<StockGroup>
dataSource={locationData}
rowKey="name"

View File

@ -5,6 +5,8 @@ import { LockOutlined, UserOutlined, BankOutlined } from '@ant-design/icons';
import { getUserInfo, login } from '@/services/api';
import type { LoginParams } from '@/types/api';
const USER_CACHE_KEY = 'muyu_user_state';
const LoginPage: React.FC = () => {
const { setInitialState } = useModel('@@initialState');
const [loading, setLoading] = useState(false);
@ -46,17 +48,14 @@ const LoginPage: React.FC = () => {
res.data.userInfo?.tenantId || values.tenantId || 't_default_001',
);
const infoRes = await getUserInfo();
let state: { currentUser: any; menuPaths: string[] };
if (infoRes?.code === 0 && infoRes.data) {
await setInitialState({
currentUser: infoRes.data,
menuPaths: infoRes.data.menuPaths || [],
});
state = { currentUser: infoRes.data, menuPaths: infoRes.data.menuPaths || [] };
} else {
await setInitialState({
currentUser: res.data.userInfo,
menuPaths: [],
});
state = { currentUser: res.data.userInfo, menuPaths: [] };
}
await setInitialState(state);
localStorage.setItem(USER_CACHE_KEY, JSON.stringify(state));
message.success('登录成功');
history.push('/dashboard');
} else {

View File

@ -60,7 +60,7 @@ const ConfigsPage: React.FC = () => {
return (
<PageContainer>
<Card bordered={false}>
<Card variant="borderless">
<Table<SystemConfig>
columns={columns}
dataSource={data}

View File

@ -177,7 +177,7 @@ const MenusPage: React.FC = () => {
return (
<PageContainer>
<Card bordered={false}>
<Card variant="borderless">
<Button type="primary" icon={<PlusOutlined />} style={{ marginBottom: 16 }}>
</Button>