Files
hfb_sys/distribution-admin/src/pages/Dashboard.tsx
T

257 lines
9.2 KiB
TypeScript

import { useEffect, useState, useCallback } from 'react'
import {
Card,
Table,
Typography,
Statistic,
Space,
Row,
Col,
Select,
Tag,
Button,
message,
} from 'antd'
import {
ShoppingCartOutlined,
DollarCircleOutlined,
WalletOutlined,
AppstoreOutlined,
LineChartOutlined,
ArrowRightOutlined,
SkinOutlined,
} from '@ant-design/icons'
import { Link } from 'react-router-dom'
import {
listOrders,
listSites,
fetchDashboardAnalytics,
formatMoney,
type Site,
type DistributionAnalytics,
type DistributionOrder,
} from '../api'
export default function Dashboard() {
const [sites, setSites] = useState<Site[]>([])
const [siteID, setSiteID] = useState<number>()
const [analytics, setAnalytics] = useState<DistributionAnalytics | null>(null)
const [recentOrders, setRecentOrders] = useState<DistributionOrder[]>([])
const [loading, setLoading] = useState(false)
const loadData = useCallback(async (sid: number) => {
setLoading(true)
try {
const [anaRes, ordRes] = await Promise.all([
fetchDashboardAnalytics(sid),
listOrders({ siteID: sid, pageSize: 8 }),
])
setAnalytics(anaRes)
setRecentOrders(ordRes.items)
} catch {
message.error('加载大盘数据失败')
} finally {
setLoading(false)
}
}, [])
useEffect(() => {
listSites()
.then((rows) => {
setSites(rows)
if (rows.length > 0) {
setSiteID(rows[0].id)
void loadData(rows[0].id)
}
})
.catch(() => message.error('站点加载失败'))
}, [loadData])
const selectedSite = sites.find((s) => s.id === siteID)
return (
<Space direction="vertical" size="large" className="page" style={{ width: '100%' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<Typography.Title level={3} style={{ marginBottom: 4 }}>
运营数据大盘
</Typography.Title>
<Typography.Text type="secondary">
当前站点:{selectedSite?.site_name || '默认分销站'} ({selectedSite?.site_code}) · 实时聚合收号、货架、订单与结算收益
</Typography.Text>
</div>
<Space>
<Select
placeholder="切换分销网站"
style={{ width: 220 }}
value={siteID}
options={sites.map((site) => ({ label: `${site.site_name} (${site.site_code})`, value: site.id }))}
onChange={(val) => {
setSiteID(val)
void loadData(val)
}}
/>
<Link to="/wallet">
<Button type="primary" icon={<WalletOutlined />}>
去提现
</Button>
</Link>
<Link to="/settings">
<Button icon={<SkinOutlined />}>
站点装修
</Button>
</Link>
</Space>
</div>
{/* 核心业务指标看板 */}
<Row gutter={[16, 16]}>
<Col xs={24} sm={12} md={8} lg={4}>
<Card bordered={false} style={{ background: '#fafafa' }} loading={loading}>
<Statistic
title="今日收号 / 累计收号"
value={analytics?.today_received_count ?? 0}
suffix={`/ ${analytics?.total_received_count ?? 0} 个`}
valueStyle={{ color: '#1677ff', fontWeight: 'bold' }}
/>
<div style={{ marginTop: 8 }}>
<Link to="/receive" style={{ fontSize: 12 }}>查看收号资产 <ArrowRightOutlined /></Link>
</div>
</Card>
</Col>
<Col xs={24} sm={12} md={8} lg={4}>
<Card bordered={false} style={{ background: '#fafafa' }} loading={loading}>
<Statistic
title="在售可租货源"
value={analytics?.active_listing_count ?? 0}
prefix={<AppstoreOutlined style={{ color: '#52c41a' }} />}
suffix="个"
valueStyle={{ color: '#52c41a', fontWeight: 'bold' }}
/>
<div style={{ marginTop: 8 }}>
<Link to="/accounts" style={{ fontSize: 12 }}>管理商品货架 <ArrowRightOutlined /></Link>
</div>
</Card>
</Col>
<Col xs={24} sm={12} md={8} lg={4}>
<Card bordered={false} style={{ background: '#fafafa' }} loading={loading}>
<Statistic
title="今日订单量"
value={analytics?.today_order_count ?? 0}
prefix={<ShoppingCartOutlined style={{ color: '#722ed1' }} />}
suffix="单"
valueStyle={{ color: '#722ed1', fontWeight: 'bold' }}
/>
<div style={{ marginTop: 8 }}>
<Link to="/sold" style={{ fontSize: 12 }}>查看交易订单 <ArrowRightOutlined /></Link>
</div>
</Card>
</Col>
<Col xs={24} sm={12} md={8} lg={4}>
<Card bordered={false} style={{ background: '#fafafa' }} loading={loading}>
<Statistic
title="今日成交 GMV"
value={((analytics?.today_gmv_cent ?? 0) / 100).toFixed(2)}
suffix="元"
valueStyle={{ color: '#fa8c16', fontWeight: 'bold' }}
/>
<div style={{ marginTop: 8, fontSize: 12, color: '#888' }}>
今日收益: {formatMoney(analytics?.today_profit_cent)}
</div>
</Card>
</Col>
<Col xs={24} sm={12} md={8} lg={4}>
<Card bordered={false} style={{ background: '#fafafa' }} loading={loading}>
<Statistic
title="本月预估分润收益"
value={((analytics?.month_profit_cent ?? 0) / 100).toFixed(2)}
prefix={<DollarCircleOutlined style={{ color: '#eb2f96' }} />}
suffix="元"
valueStyle={{ color: '#eb2f96', fontWeight: 'bold' }}
/>
<div style={{ marginTop: 8 }}>
<Link to="/settlements" style={{ fontSize: 12 }}>结算对账 <ArrowRightOutlined /></Link>
</div>
</Card>
</Col>
<Col xs={24} sm={12} md={8} lg={4}>
<Card bordered={false} style={{ background: '#f6ffed', border: '1px solid #b7eb8f' }} loading={loading}>
<Statistic
title="钱包可提现余额"
value={((analytics?.available_balance_cent ?? 0) / 100).toFixed(2)}
prefix={<WalletOutlined style={{ color: '#52c41a' }} />}
suffix="元"
valueStyle={{ color: '#52c41a', fontWeight: 'bold' }}
/>
<div style={{ marginTop: 8 }}>
<Link to="/wallet" style={{ fontSize: 12 }}>立即提现 <ArrowRightOutlined /></Link>
</div>
</Card>
</Col>
</Row>
{/* 近7天运营走势 */}
<Card
title={
<Space>
<LineChartOutlined />
<span>近 7 天交易与收号走势</span>
</Space>
}
loading={loading}
>
<Table
rowKey="date"
dataSource={analytics?.trend_days ?? []}
pagination={false}
columns={[
{ title: '日期', dataIndex: 'date', width: 140, render: (v: string) => <strong>{v}</strong> },
{
title: '收号数量',
dataIndex: 'received_count',
render: (v: number) => <Tag color={v > 0 ? 'blue' : 'default'}>{v} 个</Tag>,
},
{
title: '成交订单',
dataIndex: 'order_count',
render: (v: number) => <Tag color={v > 0 ? 'purple' : 'default'}>{v} 单</Tag>,
},
{
title: '成交 GMV',
dataIndex: 'gmv_cent',
render: (v: number) => <span style={{ color: '#cf1322', fontWeight: 'bold' }}>{formatMoney(v)}</span>,
},
{
title: '预计分成收益',
dataIndex: 'profit_cent',
render: (v: number) => <span style={{ color: '#52c41a', fontWeight: 'bold' }}>{formatMoney(v)}</span>,
},
]}
/>
</Card>
{/* 最近交易订单 */}
<Card
title="最近分销订单"
extra={<Link to="/sold">查看全部订单 <ArrowRightOutlined /></Link>}
loading={loading}
>
<Table
rowKey="order_id"
dataSource={recentOrders}
pagination={false}
columns={[
{ title: '订单号', dataIndex: 'order_no', render: (v: string) => <Typography.Text copyable>{v}</Typography.Text> },
{ title: '状态', dataIndex: 'status', render: (v: string) => <Tag>{v}</Tag> },
{ title: '净支付', dataIndex: 'net_paid_amount_cent', render: (v: number) => formatMoney(v) },
{ title: '手续费', dataIndex: 'payment_fee_cent', render: (v: number) => formatMoney(v) },
{ title: '分成基数', dataIndex: 'split_base_amount_cent', render: (v: number) => formatMoney(v) },
{ title: '结算状态', dataIndex: 'settlement_status', render: (v: string) => <Tag color={v === 'settled' ? 'success' : 'default'}>{v}</Tag> },
{ title: '创建时间', dataIndex: 'created_at' },
]}
/>
</Card>
</Space>
)
}