fix(distribution-admin): 全面优化全站表格列宽、单号防折行、平台与状态中文化体验

This commit is contained in:
yml2213
2026-09-09 19:57:06 +08:00
parent 1c16f26ae0
commit 423672e47b
10 changed files with 292 additions and 57 deletions
+25 -2
View File
@@ -94,9 +94,31 @@ function formatGameName(name?: string) {
) {
return '三角洲'
}
if (lower.includes('honor') || name.includes('王者')) {
return '王者荣耀'
}
if (lower.includes('genshin') || name.includes('原神')) {
return '原神'
}
if (lower.includes('cs') || lower.includes('csgo')) {
return 'CS2'
}
return name
}
function formatLoginPlatform(platform?: string): string {
if (!platform) return ''
const lower = platform.toLowerCase().trim()
if (lower === 'qq') return 'QQ'
if (lower === 'wechat' || lower === 'wx') return '微信'
if (lower === 'steam') return 'Steam'
if (lower === 'epic') return 'Epic'
if (lower === 'wegame') return 'WeGame'
if (lower === 'mobile') return '手游端'
if (lower === 'pc') return 'PC端'
return platform
}
export default function Accounts() {
const member = useMember()
const [sites, setSites] = useState<Site[]>([])
@@ -734,11 +756,12 @@ export default function Accounts() {
<Typography.Text
copyable={{ text: v || String(row.listing_id), tooltips: ['复制商品编号', '已复制'] }}
strong
className="mono-code"
style={{
fontSize: 13,
color: '#1677ff',
fontFamily: 'monospace',
whiteSpace: 'nowrap',
wordBreak: 'keep-all',
display: 'inline-flex',
alignItems: 'center',
}}
@@ -801,7 +824,7 @@ export default function Accounts() {
<Space size={4} style={{ marginTop: 4 }} wrap>
<Tag color="blue">{formatGameName(row.game_name)}</Tag>
{row.server_region && <Tag>{row.server_region}</Tag>}
{row.login_platform && <Tag color="cyan">{row.login_platform}</Tag>}
{row.login_platform && <Tag color="cyan">{formatLoginPlatform(row.login_platform)}</Tag>}
</Space>
</div>
),
+41 -9
View File
@@ -47,6 +47,7 @@ const ORDER_STATUS_MAP: Record<string, { label: string; color: string }> = {
completed: { label: '已完成', color: 'success' },
refunded: { label: '已退款', color: 'purple' },
cancelled: { label: '已取消', color: 'default' },
closed: { label: '已关闭', color: 'default' },
}
const SETTLEMENT_STATUS_MAP: Record<string, { label: string; color: string }> = {
@@ -56,6 +57,8 @@ const SETTLEMENT_STATUS_MAP: Record<string, { label: string; color: string }> =
settling: { label: '结算中', color: 'processing' },
disputed: { label: '争议冻结', color: 'magenta' },
failed: { label: '结算异常', color: 'error' },
arbitrated: { label: '已仲裁结算', color: 'purple' },
closed: { label: '已关闭', color: 'default' },
}
function formatGameName(gameName?: string): string {
@@ -71,9 +74,31 @@ function formatGameName(gameName?: string): string {
) {
return '三角洲'
}
if (lower.includes('honor') || gameName.includes('王者')) {
return '王者荣耀'
}
if (lower.includes('genshin') || gameName.includes('原神')) {
return '原神'
}
if (lower.includes('cs') || lower.includes('csgo')) {
return 'CS2'
}
return gameName
}
function formatLoginPlatform(platform?: string): string {
if (!platform) return ''
const lower = platform.toLowerCase().trim()
if (lower === 'qq') return 'QQ'
if (lower === 'wechat' || lower === 'wx') return '微信'
if (lower === 'steam') return 'Steam'
if (lower === 'epic') return 'Epic'
if (lower === 'wegame') return 'WeGame'
if (lower === 'mobile') return '手游端'
if (lower === 'pc') return 'PC端'
return platform
}
export default function Dashboard() {
const member = useMember()
const [sites, setSites] = useState<Site[]>([])
@@ -270,26 +295,31 @@ export default function Dashboard() {
rowKey="date"
dataSource={analytics?.trend_days ?? []}
pagination={false}
scroll={{ x: 720 }}
columns={[
{ title: '日期', dataIndex: 'date', width: 140, render: (v: string) => <strong>{v}</strong> },
{ title: '日期', dataIndex: 'date', width: 130, render: (v: string) => <strong>{v}</strong> },
{
title: '收号数量',
dataIndex: 'received_count',
width: 120,
render: (v: number) => <Tag color={v > 0 ? 'blue' : 'default'}>{v} 个</Tag>,
},
{
title: '成交订单',
dataIndex: 'order_count',
width: 120,
render: (v: number) => <Tag color={v > 0 ? 'purple' : 'default'}>{v} 单</Tag>,
},
{
title: '成交 GMV',
dataIndex: 'gmv_cent',
width: 160,
render: (v: number) => <span style={{ color: '#cf1322', fontWeight: 'bold' }}>{formatMoney(v)}</span>,
},
{
title: '预计分成收益',
dataIndex: 'profit_cent',
width: 160,
render: (v: number) => <span style={{ color: '#52c41a', fontWeight: 'bold' }}>{formatMoney(v)}</span>,
},
]}
@@ -306,14 +336,14 @@ export default function Dashboard() {
rowKey="order_id"
dataSource={recentOrders}
pagination={false}
scroll={{ x: 1050 }}
scroll={{ x: 1100 }}
columns={[
{
title: '订单与商品编号',
width: 260,
width: 280,
render: (_: unknown, row: DistributionOrder) => (
<div style={{ whiteSpace: 'nowrap' }}>
<div style={{ display: 'flex', alignItems: 'center', whiteSpace: 'nowrap', marginBottom: 4 }}>
<div style={{ whiteSpace: 'nowrap', display: 'flex', flexDirection: 'column', gap: 4 }}>
<div style={{ display: 'inline-flex', alignItems: 'center', whiteSpace: 'nowrap' }}>
<Tag
color="default"
style={{
@@ -330,10 +360,11 @@ export default function Dashboard() {
<Typography.Text
copyable={{ text: row.order_no, tooltips: ['复制订单号', '已复制'] }}
strong
className="mono-code"
style={{
fontSize: 13,
fontFamily: 'monospace',
whiteSpace: 'nowrap',
wordBreak: 'keep-all',
display: 'inline-flex',
alignItems: 'center',
}}
@@ -342,7 +373,7 @@ export default function Dashboard() {
</Typography.Text>
</div>
{row.listing_no ? (
<div style={{ display: 'flex', alignItems: 'center', whiteSpace: 'nowrap' }}>
<div style={{ display: 'inline-flex', alignItems: 'center', whiteSpace: 'nowrap' }}>
<Tag
color="blue"
style={{
@@ -358,12 +389,13 @@ export default function Dashboard() {
</Tag>
<Typography.Text
copyable={{ text: row.listing_no, tooltips: ['复制商品编号', '已复制'] }}
className="mono-code"
style={{
fontSize: 13,
color: '#1677ff',
fontWeight: 600,
fontFamily: 'monospace',
whiteSpace: 'nowrap',
wordBreak: 'keep-all',
display: 'inline-flex',
alignItems: 'center',
}}
@@ -390,7 +422,7 @@ export default function Dashboard() {
<Space size={4} style={{ marginTop: 4 }} wrap>
{row.game_name && <Tag color="blue">{formatGameName(row.game_name)}</Tag>}
{row.server_region && <Tag>{row.server_region}</Tag>}
{row.login_platform && <Tag color="cyan">{row.login_platform}</Tag>}
{row.login_platform && <Tag color="cyan">{formatLoginPlatform(row.login_platform)}</Tag>}
</Space>
</div>
),
+46 -5
View File
@@ -53,6 +53,7 @@ const ORDER_STATUS_MAP: Record<string, { label: string; color: string }> = {
completed: { label: '已完成', color: 'success' },
refunded: { label: '已退款', color: 'purple' },
cancelled: { label: '已取消', color: 'default' },
closed: { label: '已关闭', color: 'default' },
}
const SETTLEMENT_STATUS_MAP: Record<string, { label: string; color: string }> = {
@@ -62,10 +63,14 @@ const SETTLEMENT_STATUS_MAP: Record<string, { label: string; color: string }> =
settling: { label: '结算中', color: 'processing' },
disputed: { label: '争议冻结', color: 'magenta' },
failed: { label: '结算异常', color: 'error' },
arbitrated: { label: '已仲裁结算', color: 'purple' },
closed: { label: '已关闭', color: 'default' },
}
const REFUND_STATUS_MAP: Record<string, { label: string; color: string }> = {
none: { label: '未退款', color: 'default' },
pending: { label: '退款审核中', color: 'orange' },
pending_review: { label: '待退款审核', color: 'orange' },
refunding: { label: '退款中', color: 'orange' },
refunded: { label: '已退押金/差额', color: 'blue' },
failed: { label: '退款失败', color: 'error' },
@@ -107,9 +112,31 @@ function formatGameName(gameName?: string): string {
) {
return '三角洲'
}
if (lower.includes('honor') || gameName.includes('王者')) {
return '王者荣耀'
}
if (lower.includes('genshin') || gameName.includes('原神')) {
return '原神'
}
if (lower.includes('cs') || lower.includes('csgo')) {
return 'CS2'
}
return gameName
}
function formatLoginPlatform(platform?: string): string {
if (!platform) return ''
const lower = platform.toLowerCase().trim()
if (lower === 'qq') return 'QQ'
if (lower === 'wechat' || lower === 'wx') return '微信'
if (lower === 'steam') return 'Steam'
if (lower === 'epic') return 'Epic'
if (lower === 'wegame') return 'WeGame'
if (lower === 'mobile') return '手游端'
if (lower === 'pc') return 'PC端'
return platform
}
export default function Orders({ kind }: { kind: 'all' | 'sold' | 'receive' }) {
const member = useMember()
const [sites, setSites] = useState<Site[]>([])
@@ -337,7 +364,7 @@ export default function Orders({ kind }: { kind: 'all' | 'sold' | 'receive' }) {
<Space size={4} style={{ marginTop: 4 }} wrap>
{row.game_name && <Tag color="blue">{formatGameName(row.game_name)}</Tag>}
{row.server_region && <Tag>{row.server_region}</Tag>}
{row.login_platform && <Tag color="cyan">{row.login_platform}</Tag>}
{row.login_platform && <Tag color="cyan">{formatLoginPlatform(row.login_platform)}</Tag>}
{row.rent_duration_hours ? <Tag color="purple">租期: {row.rent_duration_hours}h</Tag> : null}
</Space>
</div>
@@ -589,7 +616,7 @@ export default function Orders({ kind }: { kind: 'all' | 'sold' | 'receive' }) {
<Space size={4} style={{ marginTop: 4 }} wrap>
<Tag color="blue">{formatGameName(row.game_name)}</Tag>
{row.server_region && <Tag>{row.server_region}</Tag>}
{row.login_platform && <Tag color="cyan">{row.login_platform}</Tag>}
{row.login_platform && <Tag color="cyan">{formatLoginPlatform(row.login_platform)}</Tag>}
{row.rank_level && <Tag color="purple">{row.rank_level}</Tag>}
</Space>
</div>
@@ -865,13 +892,27 @@ export default function Orders({ kind }: { kind: 'all' | 'sold' | 'receive' }) {
{
key: 'order_no',
label: '订单编号',
children: <Typography.Text copyable>{selectedOrder.order_no}</Typography.Text>,
children: (
<Typography.Text
copyable={{ text: selectedOrder.order_no, tooltips: ['复制订单号', '已复制'] }}
strong
className="mono-code"
style={{ fontSize: 13, whiteSpace: 'nowrap', wordBreak: 'keep-all', display: 'inline-flex', alignItems: 'center' }}
>
{selectedOrder.order_no}
</Typography.Text>
),
},
{
key: 'listing_no',
label: '商品编号',
children: selectedOrder.listing_no ? (
<Typography.Text copyable={{ text: selectedOrder.listing_no }} strong style={{ color: '#1677ff' }}>
<Typography.Text
copyable={{ text: selectedOrder.listing_no, tooltips: ['复制商品编号', '已复制'] }}
strong
className="mono-code"
style={{ color: '#1677ff', fontSize: 13, whiteSpace: 'nowrap', wordBreak: 'keep-all', display: 'inline-flex', alignItems: 'center' }}
>
{selectedOrder.listing_no}
</Typography.Text>
) : (
@@ -892,7 +933,7 @@ export default function Orders({ kind }: { kind: 'all' | 'sold' | 'receive' }) {
{
key: 'platform',
label: '登录平台',
children: selectedOrder.login_platform || '-',
children: formatLoginPlatform(selectedOrder.login_platform) || '-',
},
{
key: 'renter',
+98 -32
View File
@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'
import { Card, Table, Typography, Space, Select, Tag, InputNumber, Button, message } from 'antd'
import { Card, Table, Typography, Space, Select, Tag, InputNumber, Button, message, Modal } from 'antd'
import { bindPaymentChannel, listPaymentChannels, listSites, unbindPaymentChannel, getMe, type PaymentChannel, type Site } from '../api'
import { useMember } from '../MemberContext'
@@ -71,49 +71,115 @@ export default function Payments() {
}
}
async function handleUnbind(id: number) {
try {
await unbindPaymentChannel(id)
message.success('已解绑')
if (siteID) void load(siteID)
} catch {
message.error('解绑失败')
}
async function handleUnbind(channel: PaymentChannel) {
Modal.confirm({
title: '确认解绑该支付通道?',
content: `通道 ID: ${channel.config_id},解绑后该站点将无法继续使用此通道发起收款。`,
okText: '确认解绑',
okType: 'danger',
cancelText: '取消',
onOk: async () => {
try {
await unbindPaymentChannel(channel.id)
message.success('已成功解绑支付通道')
if (siteID) void load(siteID)
} catch {
message.error('解绑失败')
}
},
})
}
return (
<Space direction="vertical" className="page">
<Typography.Title level={3}>支付通道</Typography.Title>
<Space>
{member?.is_superadmin && sites.length > 1 && (
<Select
placeholder="分销网站"
style={{ width: 200 }}
value={siteID}
options={sites.map((site) => ({ label: site.site_name, value: site.id }))}
onChange={(value) => {
setSiteID(value)
void load(value)
}}
<Space direction="vertical" className="page" style={{ width: '100%' }} size="middle">
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<Typography.Title level={3} style={{ marginBottom: 4 }}>
支付通道配置
</Typography.Title>
<Typography.Text type="secondary">
为分销独立站绑定专属收单商户号与收银台支付通道,支持设置多通道优先级调度。
</Typography.Text>
</div>
</div>
<Card size="small" style={{ background: '#f8fafc' }}>
<Space wrap>
{member?.is_superadmin && sites.length > 1 && (
<Select
placeholder="切换分销网站"
style={{ width: 220 }}
value={siteID}
options={sites.map((site) => ({ label: `${site.site_name} (${site.site_code})`, value: site.id }))}
onChange={(value) => {
setSiteID(value)
void load(value)
}}
/>
)}
<InputNumber
min={1}
placeholder="商户配置 ID"
style={{ width: 160 }}
value={configID ?? undefined}
onChange={(v) => setConfigID(v ?? null)}
/>
)}
<InputNumber min={1} placeholder="商户配置 ID" value={configID ?? undefined} onChange={(v) => setConfigID(v ?? null)} />
<InputNumber min={0} placeholder="优先级" value={priority} onChange={(v) => setPriority(v ?? 0)} />
<Button type="primary" onClick={handleBind}>绑定</Button>
</Space>
<Card>
<InputNumber
min={0}
placeholder="调度优先级"
style={{ width: 140 }}
value={priority}
onChange={(v) => setPriority(v ?? 0)}
/>
<Button type="primary" onClick={handleBind}>
绑定支付通道
</Button>
</Space>
</Card>
<Card title="已绑定的支付通道列表">
<Table
rowKey="id"
loading={loading}
dataSource={channels}
pagination={false}
scroll={{ x: 650 }}
columns={[
{ title: '商户配置 ID', dataIndex: 'config_id' },
{ title: '优先级', dataIndex: 'priority' },
{ title: '状态', dataIndex: 'status', render: (v: string) => <Tag color={v === 'active' ? 'green' : 'default'}>{v === 'active' ? '正常生效' : (v || '已停用')}</Tag> },
{
title: '商户配置 ID',
dataIndex: 'config_id',
width: 160,
render: (v: number) => <strong>#{v}</strong>,
},
{
title: '调度优先级',
dataIndex: 'priority',
width: 120,
render: (v: number) => <Tag color="blue">{v}</Tag>,
},
{
title: '运行状态',
dataIndex: 'status',
width: 140,
render: (v: string) => (
<Tag color={v === 'active' ? 'green' : 'default'}>
{v === 'active' ? '正常生效' : v || '已停用'}
</Tag>
),
},
{
title: '操作',
render: (_: unknown, row: PaymentChannel) => <a onClick={() => handleUnbind(row.id)}>解绑</a>,
width: 100,
render: (_: unknown, row: PaymentChannel) => (
<Button
type="link"
danger
size="small"
onClick={() => void handleUnbind(row)}
>
解绑通道
</Button>
),
},
]}
/>
+2
View File
@@ -215,10 +215,12 @@ export default function Rules() {
loading={loading}
dataSource={rules}
pagination={false}
scroll={{ x: 950 }}
columns={[
{
title: '业务交易模式',
dataIndex: 'mode',
width: 280,
render: (v: string) => (
<div>
<strong>{MODE_LABELS[v] ?? v}</strong>
+21 -3
View File
@@ -227,8 +227,25 @@ export default function Settlements() {
{
title: '账单批次号',
dataIndex: 'batch_no',
width: 200,
render: (v: string) => <Typography.Text strong copyable>{v}</Typography.Text>,
width: 220,
render: (v: string) => (
<div style={{ whiteSpace: 'nowrap' }}>
<Typography.Text
strong
copyable={{ text: v, tooltips: ['复制批次号', '已复制'] }}
className="mono-code"
style={{
fontSize: 13,
whiteSpace: 'nowrap',
wordBreak: 'keep-all',
display: 'inline-flex',
alignItems: 'center',
}}
>
{v}
</Typography.Text>
</div>
),
},
{
title: '结算周期开始',
@@ -354,11 +371,12 @@ export default function Settlements() {
loading={itemsLoading}
dataSource={items}
pagination={false}
scroll={{ x: 550 }}
columns={[
{
title: '关联订单 ID',
dataIndex: 'order_id',
render: (v: number) => <Typography.Text strong>订单 #{v}</Typography.Text>,
render: (v: number) => <Typography.Text strong className="mono-code">订单 #{v}</Typography.Text>,
},
{
title: '分润角色 / 收益类型',
+23 -3
View File
@@ -85,7 +85,7 @@ export default function Sites({ isSuperadmin = true }: { isSuperadmin?: boolean
}
return (
<Space direction="vertical" className="page" size="large">
<Space direction="vertical" className="page" size="large" style={{ width: '100%' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<div>
<Typography.Title level={3} style={{ margin: 0 }}>
@@ -108,10 +108,12 @@ export default function Sites({ isSuperadmin = true }: { isSuperadmin?: boolean
dataSource={items}
loading={loading}
pagination={false}
scroll={{ x: 950 }}
columns={[
{
title: '站点名称',
dataIndex: 'site_name',
width: 220,
render: (v: string) => (
<Space>
<ShopOutlined style={{ color: '#1677ff' }} />
@@ -119,11 +121,26 @@ export default function Sites({ isSuperadmin = true }: { isSuperadmin?: boolean
</Space>
),
},
{ title: '站点标识', dataIndex: 'site_code', render: (v: string) => <Tag>{v}</Tag> },
{ title: '绑定域名', dataIndex: 'domain' },
{
title: '站点标识',
dataIndex: 'site_code',
width: 140,
render: (v: string) => <Tag color="blue">{v}</Tag>,
},
{
title: '绑定域名',
dataIndex: 'domain',
width: 220,
render: (v: string) => (
<Typography.Text copyable={{ text: v }} className="mono-code" style={{ fontSize: 13 }}>
{v}
</Typography.Text>
),
},
{
title: '运行状态',
dataIndex: 'status',
width: 110,
render: (v: string) => (
<Tag color={v === 'active' ? 'green' : 'default'}>
{v === 'active' ? '正常运营' : v}
@@ -133,16 +150,19 @@ export default function Sites({ isSuperadmin = true }: { isSuperadmin?: boolean
{
title: '账号展示',
dataIndex: 'display_account_enabled',
width: 100,
render: (v: boolean) => (v ? <Tag color="green">开启</Tag> : <Tag>关闭</Tag>),
},
{
title: '税费设定',
width: 110,
render: (_: unknown, row: Site) =>
row.tax_enabled ? <Tag color="orange">{row.tax_rate_bps / 100}%</Tag> : <Tag>不征税</Tag>,
},
{
title: '创建时间',
dataIndex: 'created_at',
width: 140,
render: (v: string) => (v ? new Date(v).toLocaleDateString() : '-'),
},
]}
+24 -2
View File
@@ -48,9 +48,31 @@ function formatGameName(gameName?: string): string {
) {
return '三角洲'
}
if (lower.includes('honor') || gameName.includes('王者')) {
return '王者荣耀'
}
if (lower.includes('genshin') || gameName.includes('原神')) {
return '原神'
}
if (lower.includes('cs') || lower.includes('csgo')) {
return 'CS2'
}
return gameName
}
function formatLoginPlatform(platform?: string): string {
if (!platform) return ''
const lower = platform.toLowerCase().trim()
if (lower === 'qq') return 'QQ'
if (lower === 'wechat' || lower === 'wx') return '微信'
if (lower === 'steam') return 'Steam'
if (lower === 'epic') return 'Epic'
if (lower === 'wegame') return 'WeGame'
if (lower === 'mobile') return '手游端'
if (lower === 'pc') return 'PC端'
return platform
}
export default function Storefront() {
const { siteCode = 'demo' } = useParams<{ siteCode: string }>()
const [loading, setLoading] = useState(true)
@@ -370,7 +392,7 @@ export default function Storefront() {
)}
{item.login_platform && (
<Tag style={{ margin: 0, background: 'rgba(255,255,255,0.06)', color: '#cbd5e1', border: 'none' }}>
{item.login_platform}
{formatLoginPlatform(item.login_platform)}
</Tag>
)}
{item.rank_level && (
@@ -465,7 +487,7 @@ export default function Storefront() {
{formatGameName(selectedListing.game_name)}
</Descriptions.Item>
<Descriptions.Item label="游戏大区">{selectedListing.server_region || '全区通用'}</Descriptions.Item>
<Descriptions.Item label="登录平台">{selectedListing.login_platform || 'PC端'}</Descriptions.Item>
<Descriptions.Item label="登录平台">{formatLoginPlatform(selectedListing.login_platform) || 'PC端'}</Descriptions.Item>
<Descriptions.Item label="段位天梯">{selectedListing.rank_level || '暂未定级'}</Descriptions.Item>
<Descriptions.Item label="哈夫币资产">
{selectedListing.haf_coin_amount > 0
+1 -1
View File
@@ -316,7 +316,7 @@ export default function Wallet() {
},
{
title: '审核 / 付款备注',
minWidth: 160,
width: 180,
render: (_: unknown, row: SiteWithdrawal) => (
<div>
{row.review_remark && (
+11
View File
@@ -149,6 +149,17 @@ body,
align-items: center !important;
vertical-align: middle !important;
margin-inline-start: 4px !important;
flex-shrink: 0 !important;
}
.ant-typography {
word-break: keep-all;
}
.mono-code {
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace !important;
white-space: nowrap !important;
word-break: keep-all !important;
}
.stats {