253 lines
7.2 KiB
TypeScript
Raw Normal View History

import React, { useEffect, useState } from 'react';
import { PageContainer } from '@ant-design/pro-components';
import { Card, Col, Row, Spin } from 'antd';
import {
ShoppingOutlined,
InboxOutlined,
DatabaseOutlined,
DollarOutlined,
RiseOutlined,
} from '@ant-design/icons';
import { getStockSummary } from '@/services/api';
import type { StockSummary } from '@/types/api';
interface StatCardProps {
icon: React.ReactNode;
title: string;
value: string | number;
suffix?: string;
prefix?: string;
gradient?: boolean;
}
const StatCard: React.FC<StatCardProps> = ({ icon, title, value, suffix, prefix, gradient }) => (
<Card
bordered={false}
style={{
borderRadius: 16,
border: gradient ? '2px solid transparent' : '1px solid #E2E8F0',
background: gradient
? 'linear-gradient(135deg, #0052FF, #4D7CFF)'
: '#FFFFFF',
boxShadow: gradient
? '0 8px 24px rgba(0,82,255,0.25)'
: '0 1px 3px rgba(0,0,0,0.06)',
transition: 'all 0.3s ease',
cursor: 'default',
height: '100%',
}}
hoverable
styles={{
body: { padding: '28px 24px' },
}}
>
<div style={{
display: 'flex',
alignItems: 'center',
gap: 16,
marginBottom: 16,
}}>
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: 44,
height: 44,
borderRadius: 12,
background: gradient
? 'rgba(255,255,255,0.2)'
: 'rgba(0,82,255,0.06)',
fontSize: 20,
color: gradient ? '#FFFFFF' : '#0052FF',
}}>
{icon}
</div>
<span style={{
fontSize: 13,
color: gradient ? 'rgba(255,255,255,0.8)' : '#64748B',
fontWeight: 500,
}}>
{title}
</span>
</div>
<div style={{
fontSize: 32,
fontWeight: 700,
fontFamily: '"Inter", system-ui, sans-serif',
color: gradient ? '#FFFFFF' : '#0F172A',
letterSpacing: '-0.02em',
lineHeight: 1.1,
}}>
{prefix}{typeof value === 'number' ? value.toLocaleString() : value}
{suffix && (
<span style={{
fontSize: 14,
fontWeight: 500,
marginLeft: 4,
color: gradient ? 'rgba(255,255,255,0.7)' : '#94A3B8',
}}>
{suffix}
</span>
)}
</div>
</Card>
);
const DashboardPage: React.FC = () => {
const [summary, setSummary] = useState<StockSummary | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
getStockSummary()
.then((res) => {
if (res?.code === 0) setSummary(res.data);
})
.finally(() => setLoading(false));
}, []);
if (loading) {
return (
<PageContainer>
<div style={{ display: 'flex', justifyContent: 'center', padding: 120 }}>
<Spin size="large" />
</div>
</PageContainer>
);
}
return (
<PageContainer
header={{
title: (
<span style={{ fontFamily: '"Calistoga", Georgia, serif', fontSize: 24 }}>
</span>
),
}}
>
<Row gutter={[20, 20]}>
<Col xs={24} sm={12} lg={8}>
<StatCard
icon={<ShoppingOutlined />}
title="产品总数"
value={summary?.productCount ?? 0}
suffix="种"
gradient
/>
</Col>
<Col xs={24} sm={12} lg={8}>
<StatCard
icon={<InboxOutlined />}
title="总匹数"
value={summary?.totalPieces ?? 0}
suffix="匹"
/>
</Col>
<Col xs={24} sm={12} lg={8}>
<StatCard
icon={<DatabaseOutlined />}
title="总盘数"
value={summary?.totalRolls ?? 0}
suffix="盘"
/>
</Col>
<Col xs={24} sm={12} lg={12}>
<StatCard
icon={<DollarOutlined />}
title="库存总值(成本)"
value={summary?.totalCostValue ?? '0.00'}
prefix="¥"
/>
</Col>
<Col xs={24} sm={12} lg={12}>
<StatCard
icon={<RiseOutlined />}
title="库存总值(销售)"
value={summary?.totalSalesValue ?? '0.00'}
prefix="¥"
/>
</Col>
</Row>
{/* Quick navigation section */}
<div style={{ marginTop: 32 }}>
<div style={{
display: 'inline-flex',
alignItems: 'center',
gap: 10,
borderRadius: 24,
border: '1px solid rgba(0,82,255,0.2)',
background: 'rgba(0,82,255,0.04)',
padding: '8px 18px',
marginBottom: 20,
}}>
<span style={{
width: 7,
height: 7,
borderRadius: '50%',
background: '#0052FF',
}} />
<span style={{
fontFamily: '"JetBrains Mono", monospace',
fontSize: 11,
textTransform: 'uppercase' as const,
letterSpacing: '0.15em',
color: '#0052FF',
}}>
</span>
</div>
<Row gutter={[16, 16]}>
{[
{ title: '产品档案', desc: '管理产品信息', path: '/inventory/products', icon: <ShoppingOutlined /> },
{ title: '库存查询', desc: '实时库存数据', path: '/inventory/stocks', icon: <DatabaseOutlined /> },
{ title: '库存统计', desc: '分类统计分析', path: '/inventory/stats', icon: <RiseOutlined /> },
{ title: 'Excel导入', desc: '批量导入产品', path: '/inventory/import', icon: <InboxOutlined /> },
].map((item) => (
<Col xs={24} sm={12} lg={6} key={item.path}>
<Card
bordered={false}
hoverable
style={{
borderRadius: 14,
border: '1px solid #E2E8F0',
cursor: 'pointer',
transition: 'all 0.2s ease',
}}
styles={{ body: { padding: '20px 22px' } }}
onClick={() => window.location.assign(item.path)}
>
<div style={{
display: 'flex',
alignItems: 'center',
gap: 14,
}}>
<div style={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
width: 40,
height: 40,
borderRadius: 10,
background: 'linear-gradient(135deg, #0052FF, #4D7CFF)',
color: '#FFFFFF',
fontSize: 18,
}}>
{item.icon}
</div>
<div>
<div style={{ fontSize: 15, fontWeight: 600, color: '#0F172A' }}>{item.title}</div>
<div style={{ fontSize: 12, color: '#94A3B8', marginTop: 2 }}>{item.desc}</div>
</div>
</div>
</Card>
</Col>
))}
</Row>
</div>
</PageContainer>
);
};
export default DashboardPage;