Files
makemd/dashboard/src/pages/IndependentSite/IndependentSiteAnalytics.tsx

349 lines
12 KiB
TypeScript
Raw Normal View History

import React, { useState, useEffect } from 'react';
import { Card, DatePicker, Select, Spin, Statistic, Row, Col, Tabs, Button, Table, Tag, Space, Typography } from 'antd';
import { useParams } from 'react-router-dom';
import { Line, Column, Pie, Area, DualAxes } from '@ant-design/plots';
import { ReloadOutlined, ExportOutlined, DollarOutlined, ShoppingCartOutlined, TeamOutlined, RiseOutlined } from '@ant-design/icons';
const { RangePicker } = DatePicker;
const { Option } = Select;
const { Title, Text } = Typography;
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;
}
const SALES_TREND_DATA: SalesData[] = [
{ date: '2026-03-01', sales: 5200, orders: 42, customers: 35 },
{ date: '2026-03-02', sales: 6100, orders: 51, customers: 42 },
{ date: '2026-03-03', sales: 4800, orders: 38, customers: 31 },
{ date: '2026-03-04', sales: 7500, orders: 62, customers: 48 },
{ date: '2026-03-05', sales: 6800, orders: 55, customers: 45 },
{ date: '2026-03-06', sales: 8200, orders: 68, customers: 52 },
{ date: '2026-03-07', sales: 9100, orders: 75, customers: 58 },
{ date: '2026-03-08', sales: 7800, orders: 64, customers: 49 },
{ date: '2026-03-09', sales: 8500, orders: 70, customers: 55 },
{ date: '2026-03-10', sales: 9800, orders: 82, customers: 65 },
];
const PRODUCT_DATA: ProductData[] = [
{ name: '无线蓝牙耳机', sales: 15800, quantity: 158 },
{ name: '智能手表', sales: 12500, quantity: 50 },
{ name: '便携充电宝', sales: 8900, quantity: 178 },
{ name: '手机支架', sales: 6200, quantity: 310 },
{ name: '数据线套装', sales: 4500, quantity: 450 },
];
const TRAFFIC_DATA: TrafficData[] = [
{ date: '2026-03-01', visitors: 1250, pageViews: 3800, bounceRate: 35 },
{ date: '2026-03-02', visitors: 1480, pageViews: 4200, bounceRate: 32 },
{ date: '2026-03-03', visitors: 1100, pageViews: 3200, bounceRate: 38 },
{ date: '2026-03-04', visitors: 1800, pageViews: 5500, bounceRate: 28 },
{ date: '2026-03-05', visitors: 1650, pageViews: 4800, bounceRate: 30 },
{ date: '2026-03-06', visitors: 2100, pageViews: 6200, bounceRate: 25 },
{ date: '2026-03-07', visitors: 2350, pageViews: 7100, bounceRate: 22 },
{ date: '2026-03-08', visitors: 1900, pageViews: 5600, bounceRate: 27 },
{ date: '2026-03-09', visitors: 2200, pageViews: 6500, bounceRate: 24 },
{ date: '2026-03-10', visitors: 2500, pageViews: 7500, bounceRate: 20 },
];
const CONVERSION_DATA = [
{ type: '直接购买', value: 35 },
{ type: '加购后购买', value: 28 },
{ type: '收藏后购买', value: 15 },
{ type: '未转化', value: 22 },
];
const REGION_DATA = [
{ region: '美国', customers: 450, percentage: 35 },
{ region: '欧洲', customers: 320, percentage: 25 },
{ region: '东南亚', customers: 280, percentage: 22 },
{ region: '中东', customers: 150, percentage: 12 },
{ region: '其他', customers: 80, percentage: 6 },
];
const DEVICE_DATA = [
{ device: '移动端', value: 65, color: '#1890ff' },
{ device: '桌面端', value: 30, color: '#52c41a' },
{ device: '平板', value: 5, color: '#faad14' },
];
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 [activeTab, setActiveTab] = useState('sales');
useEffect(() => {
fetchAnalyticsData();
}, [id, timeRange, dateRange]);
const fetchAnalyticsData = async () => {
setLoading(true);
setTimeout(() => {
setLoading(false);
}, 500);
};
const salesTrendConfig = {
data: SALES_TREND_DATA.flatMap(item => [
{ date: item.date, value: item.sales, type: '销售额' },
{ date: item.date, value: item.orders * 100, type: '订单数' },
]),
xField: 'date',
yField: 'value',
seriesField: 'type',
smooth: true,
legend: { position: 'top' as const },
yAxis: { label: { formatter: (v: number) => `$${v / 1000}k` } },
};
const productSalesConfig = {
data: PRODUCT_DATA,
xField: 'name',
yField: 'sales',
color: '#1890ff',
label: { text: 'sales', position: 'top' as const },
};
const trafficConfig = {
data: [TRAFFIC_DATA, TRAFFIC_DATA],
xField: 'date',
yField: ['visitors', 'pageViews'],
geometryOptions: [
{ geometry: 'line', color: '#1890ff', smooth: true },
{ geometry: 'line', color: '#52c41a', smooth: true },
],
legend: { position: 'top' as const },
};
const conversionConfig = {
data: CONVERSION_DATA,
angleField: 'value',
colorField: 'type',
radius: 0.8,
innerRadius: 0.6,
label: { text: 'type', position: 'outside' },
legend: { position: 'bottom' as const },
};
const regionConfig = {
data: REGION_DATA,
xField: 'region',
yField: 'customers',
color: '#722ed1',
label: { text: 'customers', position: 'top' as const },
};
const deviceConfig = {
data: DEVICE_DATA,
angleField: 'value',
colorField: 'device',
radius: 0.8,
innerRadius: 0.6,
label: { text: 'device', position: 'outside' },
legend: { position: 'bottom' as const },
};
const productColumns = [
{ title: '商品名称', dataIndex: 'name', key: 'name' },
{ title: '销售额', dataIndex: 'sales', key: 'sales', render: (v: number) => `$${v.toLocaleString()}` },
{ title: '销量', dataIndex: 'quantity', key: 'quantity' },
{ title: '占比', key: 'percentage', render: (_: any, record: ProductData) => {
const total = PRODUCT_DATA.reduce((sum, p) => sum + p.sales, 0);
return `${((record.sales / total) * 100).toFixed(1)}%`;
}},
];
const tabItems = [
{
key: 'sales',
label: '销售分析',
children: (
<Row gutter={16}>
<Col span={24}>
<Card title="销售趋势">
<Line {...salesTrendConfig} height={300} />
</Card>
</Col>
<Col xs={24} lg={16} style={{ marginTop: 16 }}>
<Card title="商品销售排行">
<Column {...productSalesConfig} height={280} />
</Card>
</Col>
<Col xs={24} lg={8} style={{ marginTop: 16 }}>
<Card title="转化漏斗">
<Pie {...conversionConfig} height={280} />
</Card>
</Col>
<Col span={24} style={{ marginTop: 16 }}>
<Card title="商品销售明细">
<Table columns={productColumns} dataSource={PRODUCT_DATA} rowKey="name" pagination={false} />
</Card>
</Col>
</Row>
),
},
{
key: 'traffic',
label: '流量分析',
children: (
<Row gutter={16}>
<Col span={24}>
<Card title="流量趋势">
<DualAxes {...trafficConfig} height={300} />
</Card>
</Col>
<Col xs={24} lg={12} style={{ marginTop: 16 }}>
<Card title="设备分布">
<Pie {...deviceConfig} height={280} />
</Card>
</Col>
<Col xs={24} lg={12} style={{ marginTop: 16 }}>
<Card title="流量来源">
<Table
columns={[
{ title: '来源', dataIndex: 'source', key: 'source' },
{ title: '访客数', dataIndex: 'visitors', key: 'visitors' },
{ title: '占比', dataIndex: 'percentage', key: 'percentage', render: (v: number) => `${v}%` },
]}
dataSource={[
{ key: '1', source: '搜索引擎', visitors: 3500, percentage: 42 },
{ key: '2', source: '社交媒体', visitors: 2100, percentage: 25 },
{ key: '3', source: '直接访问', visitors: 1500, percentage: 18 },
{ key: '4', source: '广告投放', visitors: 800, percentage: 10 },
{ key: '5', source: '其他', visitors: 400, percentage: 5 },
]}
pagination={false}
/>
</Card>
</Col>
</Row>
),
},
{
key: 'customer',
label: '客户分析',
children: (
<Row gutter={16}>
<Col xs={24} lg={16}>
<Card title="客户地域分布">
<Column {...regionConfig} height={300} />
</Card>
</Col>
<Col xs={24} lg={8}>
<Card title="客户类型">
<Pie
data={[
{ type: '新客户', value: 45 },
{ type: '回头客', value: 35 },
{ type: 'VIP客户', value: 20 },
]}
angleField="value"
colorField="type"
radius={0.8}
innerRadius={0.6}
label={{ text: 'type', position: 'outside' }}
height={280}
/>
</Card>
</Col>
<Col span={24} style={{ marginTop: 16 }}>
<Card title="客户购买行为分析">
<Row gutter={16}>
<Col span={6}>
<Statistic title="平均订单金额" value={125} prefix={<DollarOutlined />} suffix="USD" />
</Col>
<Col span={6}>
<Statistic title="复购率" value={35} suffix="%" valueStyle={{ color: '#52c41a' }} prefix={<RiseOutlined />} />
</Col>
<Col span={6}>
<Statistic title="平均购买频次" value={2.3} suffix="次/月" />
</Col>
<Col span={6}>
<Statistic title="客户满意度" value={4.8} suffix="/ 5.0" valueStyle={{ color: '#faad14' }} />
</Col>
</Row>
</Card>
</Col>
</Row>
),
},
];
return (
<div className="independent-site-analytics" style={{ padding: 24 }}>
<Row justify="space-between" align="middle" style={{ marginBottom: 24 }}>
<Col>
<Title level={4}></Title>
<Text type="secondary">ID: {id}</Text>
</Col>
<Col>
<Space>
<Select value={timeRange} onChange={setTimeRange} 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={setDateRange} />
<Button icon={<ReloadOutlined />} onClick={fetchAnalyticsData}></Button>
<Button type="primary" icon={<ExportOutlined />}></Button>
</Space>
</Col>
</Row>
{loading ? (
<div style={{ textAlign: 'center', padding: '50px 0' }}>
<Spin size="large" />
</div>
) : (
<>
<Row gutter={16} style={{ marginBottom: 24 }}>
<Col xs={12} sm={6}>
<Card>
<Statistic title="总销售额" value={73800} precision={2} prefix={<DollarOutlined />} suffix="USD" valueStyle={{ color: '#3f8600' }} />
</Card>
</Col>
<Col xs={12} sm={6}>
<Card>
<Statistic title="总订单数" value={607} prefix={<ShoppingCartOutlined />} />
</Card>
</Col>
<Col xs={12} sm={6}>
<Card>
<Statistic title="总客户数" value={480} prefix={<TeamOutlined />} />
</Card>
</Col>
<Col xs={12} sm={6}>
<Card>
<Statistic title="转化率" value={4.2} precision={2} suffix="%" valueStyle={{ color: '#722ed1' }} prefix={<RiseOutlined />} />
</Card>
</Col>
</Row>
<Tabs activeKey={activeTab} onChange={setActiveTab} items={tabItems} />
</>
)}
</div>
);
};
export default IndependentSiteAnalytics;