Files
makemd/dashboard/src/pages/Product/ROIAnalysis/index.tsx

325 lines
8.1 KiB
TypeScript
Raw Normal View History

import React, { useState, useEffect } from 'react';
import { Card, Layout, Typography, Row, Col, DatePicker, Select, Button, Table, Statistic, Spin, message } from 'antd';
import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer, LineChart, Line } from 'recharts';
import { ArrowUpOutlined, ArrowDownOutlined, ReloadOutlined } from '@ant-design/icons';
import { Link } from 'umi';
const { Content } = Layout;
const { Title, Text } = Typography;
const { RangePicker } = DatePicker;
const { Option } = Select;
interface ROIItem {
id: string;
productId: string;
productName: string;
cost: number;
price: number;
profit: number;
roi: number;
salesVolume: number;
date: string;
}
const ROIAnalysis: React.FC = () => {
const [loading, setLoading] = useState<boolean>(false);
const [roiData, setRoiData] = useState<ROIItem[]>([]);
const [timeRange, setTimeRange] = useState<any>(null);
const [productId, setProductId] = useState<string>('');
const [summary, setSummary] = useState<{
totalSales: number;
totalProfit: number;
averageROI: number;
bestProduct: string;
}>({
totalSales: 0,
totalProfit: 0,
averageROI: 0,
bestProduct: '',
});
const mockROIData: ROIItem[] = [
{
id: '1',
productId: 'P001',
productName: '智能手表',
cost: 150,
price: 299,
profit: 149,
roi: 99.33,
salesVolume: 120,
date: '2026-03-01',
},
{
id: '2',
productId: 'P001',
productName: '智能手表',
cost: 150,
price: 299,
profit: 149,
roi: 99.33,
salesVolume: 110,
date: '2026-03-02',
},
{
id: '3',
productId: 'P001',
productName: '智能手表',
cost: 150,
price: 299,
profit: 149,
roi: 99.33,
salesVolume: 130,
date: '2026-03-03',
},
{
id: '4',
productId: 'P002',
productName: '无线耳机',
cost: 80,
price: 199,
profit: 119,
roi: 148.75,
salesVolume: 200,
date: '2026-03-01',
},
{
id: '5',
productId: 'P002',
productName: '无线耳机',
cost: 80,
price: 199,
profit: 119,
roi: 148.75,
salesVolume: 210,
date: '2026-03-02',
},
{
id: '6',
productId: 'P002',
productName: '无线耳机',
cost: 80,
price: 199,
profit: 119,
roi: 148.75,
salesVolume: 190,
date: '2026-03-03',
},
];
const fetchROIData = () => {
setLoading(true);
// 模拟API请求
setTimeout(() => {
setRoiData(mockROIData);
// 计算汇总数据
const totalSales = mockROIData.reduce((sum, item) => sum + item.salesVolume, 0);
const totalProfit = mockROIData.reduce((sum, item) => sum + item.profit * item.salesVolume, 0);
const averageROI = mockROIData.reduce((sum, item) => sum + item.roi, 0) / mockROIData.length;
const bestProduct = mockROIData.reduce((best, current) =>
current.roi > best.roi ? current : best
).productName;
setSummary({
totalSales,
totalProfit,
averageROI: parseFloat(averageROI.toFixed(2)),
bestProduct,
});
setLoading(false);
}, 1000);
};
useEffect(() => {
fetchROIData();
}, []);
const handleTimeRangeChange = (dates: any) => {
setTimeRange(dates);
};
const handleProductChange = (value: string) => {
setProductId(value);
};
const handleRefresh = () => {
fetchROIData();
};
const columns = [
{
title: '商品ID',
dataIndex: 'productId',
key: 'productId',
},
{
title: '商品名称',
dataIndex: 'productName',
key: 'productName',
},
{
title: '成本',
dataIndex: 'cost',
key: 'cost',
render: (text: number) => `¥${text}`,
},
{
title: '售价',
dataIndex: 'price',
key: 'price',
render: (text: number) => `¥${text}`,
},
{
title: '利润',
dataIndex: 'profit',
key: 'profit',
render: (text: number) => `¥${text}`,
},
{
title: 'ROI',
dataIndex: 'roi',
key: 'roi',
render: (text: number) => `${text}%`,
},
{
title: '销量',
dataIndex: 'salesVolume',
key: 'salesVolume',
},
{
title: '日期',
dataIndex: 'date',
key: 'date',
},
];
// 准备图表数据
const chartData = mockROIData.map(item => ({
date: item.date,
销量: item.salesVolume,
利润: item.profit * item.salesVolume,
}));
const roiTrendData = mockROIData.map(item => ({
date: item.date,
ROI: item.roi,
}));
return (
<Content style={{ padding: 24, margin: 0, minHeight: 280, background: '#fff' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 24 }}>
<Title level={4}>ROI分析</Title>
<div style={{ display: 'flex', gap: 12 }}>
<Select
placeholder="选择商品"
style={{ width: 200 }}
onChange={handleProductChange}
>
<Option value=""></Option>
<Option value="P001"></Option>
<Option value="P002">线</Option>
</Select>
<RangePicker onChange={handleTimeRangeChange} />
<Button
type="primary"
icon={<ReloadOutlined />}
onClick={handleRefresh}
loading={loading}
>
</Button>
</div>
</div>
{/* 汇总统计 */}
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
<Col span={6}>
<Card>
<Statistic
title="总销量"
value={summary.totalSales}
prefix={<ArrowUpOutlined />}
suffix="件"
valueStyle={{ color: '#3f8600' }}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="总利润"
value={summary.totalProfit}
prefix="¥"
valueStyle={{ color: '#3f8600' }}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="平均ROI"
value={summary.averageROI}
suffix="%"
valueStyle={{ color: '#3f8600' }}
/>
</Card>
</Col>
<Col span={6}>
<Card>
<Statistic
title="最佳商品"
value={summary.bestProduct}
valueStyle={{ color: '#1890ff' }}
/>
</Card>
</Col>
</Row>
{/* 图表区域 */}
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
<Col span={12}>
<Card title="销量与利润趋势">
<ResponsiveContainer width="100%" height={300}>
<BarChart data={chartData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Legend />
<Bar dataKey="销量" fill="#1890ff" />
<Bar dataKey="利润" fill="#52c41a" />
</BarChart>
</ResponsiveContainer>
</Card>
</Col>
<Col span={12}>
<Card title="ROI趋势">
<ResponsiveContainer width="100%" height={300}>
<LineChart data={roiTrendData}>
<CartesianGrid strokeDasharray="3 3" />
<XAxis dataKey="date" />
<YAxis />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="ROI" stroke="#ff4d4f" strokeWidth={2} />
</LineChart>
</ResponsiveContainer>
</Card>
</Col>
</Row>
{/* 详细数据表格 */}
<Card title="ROI详细数据">
<Table
columns={columns}
dataSource={roiData}
rowKey="id"
loading={loading}
pagination={{ pageSize: 10 }}
/>
</Card>
</Content>
);
};
export default ROIAnalysis;