2026-03-18 19:12:38 +08:00
|
|
|
import React, { useState, useEffect } from 'react';
|
|
|
|
|
import { Card, DatePicker, Select, Spin, Statistic, Row, Col, Tabs, Button } from 'antd';
|
|
|
|
|
import { Area, AreaChart, Bar, BarChart, Line, LineChart, Pie, PieChart, ResponsiveContainer, Tooltip, XAxis, YAxis, CartesianGrid, Legend } from 'recharts';
|
|
|
|
|
import { useParams } from 'react-router-dom';
|
2026-03-19 01:39:34 +08:00
|
|
|
import { independentSiteDataSource, SiteAnalytics } from '@/services/independentSiteDataSource';
|
2026-03-18 19:12:38 +08:00
|
|
|
|
|
|
|
|
const { RangePicker } = DatePicker;
|
|
|
|
|
const { Option } = Select;
|
|
|
|
|
const { TabPane } = Tabs;
|
|
|
|
|
|
|
|
|
|
interface SalesData {
|
|
|
|
|
date: string;
|
|
|
|
|
sales: number;
|
|
|
|
|
orders: number;
|
|
|
|
|
customers: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ProductData {
|
|
|
|
|
name: string;
|
|
|
|
|
sales: number;
|
|
|
|
|
quantity: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface TrafficData {
|
|
|
|
|
date: string;
|
|
|
|
|
visitors: number;
|
|
|
|
|
pageViews: number;
|
|
|
|
|
bounceRate: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface ConversionData {
|
|
|
|
|
name: string;
|
|
|
|
|
value: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const IndependentSiteAnalytics: React.FC = () => {
|
|
|
|
|
const { id } = useParams<{ id: string }>();
|
|
|
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
|
const [dateRange, setDateRange] = useState<any>(null);
|
|
|
|
|
const [timeRange, setTimeRange] = useState('30d');
|
|
|
|
|
const [salesData, setSalesData] = useState<SalesData[]>([]);
|
|
|
|
|
const [productData, setProductData] = useState<ProductData[]>([]);
|
|
|
|
|
const [trafficData, setTrafficData] = useState<TrafficData[]>([]);
|
|
|
|
|
const [conversionData, setConversionData] = useState<ConversionData[]>([]);
|
|
|
|
|
const [summary, setSummary] = useState({
|
|
|
|
|
totalSales: 0,
|
|
|
|
|
totalOrders: 0,
|
|
|
|
|
totalCustomers: 0,
|
|
|
|
|
conversionRate: 0,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
fetchAnalyticsData();
|
|
|
|
|
}, [id, timeRange, dateRange]);
|
|
|
|
|
|
|
|
|
|
const fetchAnalyticsData = async () => {
|
|
|
|
|
setLoading(true);
|
|
|
|
|
// 模拟API调用
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
// 模拟销售数据
|
|
|
|
|
const mockSalesData: SalesData[] = Array.from({ length: 30 }, (_, i) => ({
|
|
|
|
|
date: `2026-03-${String(i + 1).padStart(2, '0')}`,
|
|
|
|
|
sales: Math.random() * 1000 + 500,
|
|
|
|
|
orders: Math.random() * 50 + 10,
|
|
|
|
|
customers: Math.random() * 30 + 5,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// 模拟商品数据
|
|
|
|
|
const mockProductData: ProductData[] = [
|
|
|
|
|
{ name: 'Product 1', sales: 1500, quantity: 15 },
|
|
|
|
|
{ name: 'Product 2', sales: 2000, quantity: 20 },
|
|
|
|
|
{ name: 'Product 3', sales: 1000, quantity: 10 },
|
|
|
|
|
{ name: 'Product 4', sales: 500, quantity: 5 },
|
|
|
|
|
{ name: 'Product 5', sales: 800, quantity: 8 },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// 模拟流量数据
|
|
|
|
|
const mockTrafficData: TrafficData[] = Array.from({ length: 30 }, (_, i) => ({
|
|
|
|
|
date: `2026-03-${String(i + 1).padStart(2, '0')}`,
|
|
|
|
|
visitors: Math.random() * 500 + 100,
|
|
|
|
|
pageViews: Math.random() * 1000 + 500,
|
|
|
|
|
bounceRate: Math.random() * 50 + 20,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// 模拟转化数据
|
|
|
|
|
const mockConversionData: ConversionData[] = [
|
|
|
|
|
{ name: '已转化', value: 20 },
|
|
|
|
|
{ name: '未转化', value: 80 },
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// 模拟汇总数据
|
|
|
|
|
const mockSummary = {
|
|
|
|
|
totalSales: 5800,
|
|
|
|
|
totalOrders: 78,
|
|
|
|
|
totalCustomers: 55,
|
|
|
|
|
conversionRate: 20,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
setSalesData(mockSalesData);
|
|
|
|
|
setProductData(mockProductData);
|
|
|
|
|
setTrafficData(mockTrafficData);
|
|
|
|
|
setConversionData(mockConversionData);
|
|
|
|
|
setSummary(mockSummary);
|
|
|
|
|
setLoading(false);
|
|
|
|
|
}, 1000);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleTimeRangeChange = (value: string) => {
|
|
|
|
|
setTimeRange(value);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const handleDateRangeChange = (dates: any) => {
|
|
|
|
|
setDateRange(dates);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="independent-site-analytics">
|
|
|
|
|
<div className="page-header">
|
|
|
|
|
<h1>独立站数据分析</h1>
|
|
|
|
|
<div style={{ display: 'flex', gap: 16 }}>
|
|
|
|
|
<Select
|
|
|
|
|
value={timeRange}
|
|
|
|
|
onChange={handleTimeRangeChange}
|
|
|
|
|
style={{ width: 120 }}
|
|
|
|
|
>
|
|
|
|
|
<Option value="7d">7天</Option>
|
|
|
|
|
<Option value="30d">30天</Option>
|
|
|
|
|
<Option value="90d">90天</Option>
|
|
|
|
|
<Option value="1y">1年</Option>
|
|
|
|
|
</Select>
|
|
|
|
|
<RangePicker onChange={handleDateRangeChange} />
|
|
|
|
|
<Button type="primary" onClick={fetchAnalyticsData}>
|
|
|
|
|
刷新数据
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{loading ? (
|
|
|
|
|
<div style={{ textAlign: 'center', padding: '50px 0' }}>
|
|
|
|
|
<Spin size="large" />
|
|
|
|
|
</div>
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<Row gutter={16} style={{ marginBottom: 24 }}>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Card>
|
|
|
|
|
<Statistic
|
|
|
|
|
title="总销售额"
|
|
|
|
|
value={summary.totalSales}
|
|
|
|
|
precision={2}
|
|
|
|
|
prefix="$"
|
|
|
|
|
suffix="USD"
|
|
|
|
|
/>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Card>
|
|
|
|
|
<Statistic
|
|
|
|
|
title="总订单数"
|
|
|
|
|
value={summary.totalOrders}
|
|
|
|
|
/>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Card>
|
|
|
|
|
<Statistic
|
|
|
|
|
title="总客户数"
|
|
|
|
|
value={summary.totalCustomers}
|
|
|
|
|
/>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={6}>
|
|
|
|
|
<Card>
|
|
|
|
|
<Statistic
|
|
|
|
|
title="转化率"
|
|
|
|
|
value={summary.conversionRate}
|
|
|
|
|
precision={2}
|
|
|
|
|
suffix="%"
|
|
|
|
|
/>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
|
|
|
|
|
<Tabs defaultActiveKey="sales">
|
|
|
|
|
<TabPane tab="销售分析" key="sales">
|
|
|
|
|
<Row gutter={16}>
|
|
|
|
|
<Col span={24}>
|
|
|
|
|
<Card title="销售趋势">
|
|
|
|
|
<ResponsiveContainer width="100%" height={400}>
|
|
|
|
|
<AreaChart data={salesData}>
|
|
|
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
|
|
|
<XAxis dataKey="date" />
|
|
|
|
|
<YAxis />
|
|
|
|
|
<Tooltip />
|
|
|
|
|
<Legend />
|
|
|
|
|
<Area type="monotone" dataKey="sales" stackId="1" stroke="#8884d8" fill="#8884d8" />
|
|
|
|
|
<Area type="monotone" dataKey="orders" stackId="2" stroke="#82ca9d" fill="#82ca9d" />
|
|
|
|
|
</AreaChart>
|
|
|
|
|
</ResponsiveContainer>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Card title="商品销售排行">
|
|
|
|
|
<ResponsiveContainer width="100%" height={400}>
|
|
|
|
|
<BarChart data={productData}>
|
|
|
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
|
|
|
<XAxis dataKey="name" />
|
|
|
|
|
<YAxis />
|
|
|
|
|
<Tooltip />
|
|
|
|
|
<Legend />
|
|
|
|
|
<Bar dataKey="sales" fill="#8884d8" />
|
|
|
|
|
</BarChart>
|
|
|
|
|
</ResponsiveContainer>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Card title="转化率">
|
|
|
|
|
<ResponsiveContainer width="100%" height={400}>
|
|
|
|
|
<PieChart>
|
|
|
|
|
<Pie
|
|
|
|
|
data={conversionData}
|
|
|
|
|
cx="50%"
|
|
|
|
|
cy="50%"
|
|
|
|
|
labelLine={false}
|
|
|
|
|
label={({ name, percent }) => `${name}: ${(percent * 100).toFixed(0)}%`}
|
|
|
|
|
outerRadius={150}
|
|
|
|
|
fill="#8884d8"
|
|
|
|
|
dataKey="value"
|
|
|
|
|
/>
|
|
|
|
|
<Tooltip />
|
|
|
|
|
</PieChart>
|
|
|
|
|
</ResponsiveContainer>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
</TabPane>
|
|
|
|
|
|
|
|
|
|
<TabPane tab="流量分析" key="traffic">
|
|
|
|
|
<Row gutter={16}>
|
|
|
|
|
<Col span={24}>
|
|
|
|
|
<Card title="流量趋势">
|
|
|
|
|
<ResponsiveContainer width="100%" height={400}>
|
|
|
|
|
<LineChart data={trafficData}>
|
|
|
|
|
<CartesianGrid strokeDasharray="3 3" />
|
|
|
|
|
<XAxis dataKey="date" />
|
|
|
|
|
<YAxis />
|
|
|
|
|
<Tooltip />
|
|
|
|
|
<Legend />
|
|
|
|
|
<Line type="monotone" dataKey="visitors" stroke="#8884d8" />
|
|
|
|
|
<Line type="monotone" dataKey="pageViews" stroke="#82ca9d" />
|
|
|
|
|
<Line type="monotone" dataKey="bounceRate" stroke="#ff7300" />
|
|
|
|
|
</LineChart>
|
|
|
|
|
</ResponsiveContainer>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
</TabPane>
|
|
|
|
|
|
|
|
|
|
<TabPane tab="客户分析" key="customer">
|
|
|
|
|
<Row gutter={16}>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Card title="客户地域分布">
|
|
|
|
|
<div style={{ textAlign: 'center', padding: '50px 0' }}>
|
|
|
|
|
<p>地域分布图表</p>
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
<Col span={12}>
|
|
|
|
|
<Card title="客户购买行为">
|
|
|
|
|
<div style={{ textAlign: 'center', padding: '50px 0' }}>
|
|
|
|
|
<p>购买行为图表</p>
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
</Col>
|
|
|
|
|
</Row>
|
|
|
|
|
</TabPane>
|
|
|
|
|
</Tabs>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default IndependentSiteAnalytics;
|