feat(distribution): 完善分销收号资产双视图、商户提现、自主加价、品牌装修与数据大盘
This commit is contained in:
@@ -1,43 +1,252 @@
|
||||
import { useEffect, useState } from 'react'
|
||||
import { Card, Table, Typography, Statistic, Space, message } from 'antd'
|
||||
import { listOrders, listSites, formatMoney, type Site } from '../api'
|
||||
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 [orders, setOrders] = useState<Awaited<ReturnType<typeof listOrders>>['items']>([])
|
||||
const [total, setTotal] = useState(0)
|
||||
const [siteID, setSiteID] = useState<number>()
|
||||
const [analytics, setAnalytics] = useState<DistributionAnalytics | null>(null)
|
||||
const [recentOrders, setRecentOrders] = useState<DistributionOrder[]>([])
|
||||
const [loading, setLoading] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
Promise.all([listSites(), listOrders({ pageSize: 100 })])
|
||||
.then(([siteItems, page]) => {
|
||||
setSites(siteItems)
|
||||
setOrders(page.items)
|
||||
setTotal(page.total)
|
||||
})
|
||||
.catch(() => message.error('数据加载失败'))
|
||||
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)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const netPaid = orders.reduce((sum, item) => sum + item.net_paid_amount_cent, 0)
|
||||
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">
|
||||
<Typography.Title level={3}>分销运营概览</Typography.Title>
|
||||
<div className="stats">
|
||||
<Card><Statistic title="分销网站" value={sites.length} /></Card>
|
||||
<Card><Statistic title="分销订单" value={total} /></Card>
|
||||
<Card><Statistic title="最近净支付" value={(netPaid / 100).toFixed(2)} suffix="元" /></Card>
|
||||
<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>
|
||||
<Card title="最近订单">
|
||||
|
||||
{/* 核心业务指标看板 */}
|
||||
<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="order_id"
|
||||
dataSource={orders.slice(0, 10)}
|
||||
rowKey="date"
|
||||
dataSource={analytics?.trend_days ?? []}
|
||||
pagination={false}
|
||||
columns={[
|
||||
{ title: '订单号', dataIndex: 'order_no' },
|
||||
{ title: '状态', dataIndex: 'status' },
|
||||
{ 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' },
|
||||
{ title: '结算状态', dataIndex: 'settlement_status', render: (v: string) => <Tag color={v === 'settled' ? 'success' : 'default'}>{v}</Tag> },
|
||||
{ title: '创建时间', dataIndex: 'created_at' },
|
||||
]}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user