style(platform-rock): 深度重构 Rock 管理大屏 UI,全面对齐平台设计规范
1. 页面容器对标 PlatformProducts 规范,移除内层冗余白底外壳,优化整体边距与排版; 2. 废弃原生低质感 Statistic 标签,全面改用 metric-card 高质感统计看板(可用库存、错单阻断、交付总量、上游积分); 3. 重构通道控制与 0666.pro 凭据面板,增加通道呼吸状态条与软熔断一键切换 Banner,表单分栏结构化展示; 4. 筛选工具栏与库存审计表格全面升级,增加 UID 强一致校验高亮对比与错单解冻重置操作。
This commit is contained in:
+453
-295
@@ -1,6 +1,5 @@
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
import {
|
||||
Alert,
|
||||
Badge,
|
||||
Button,
|
||||
Card,
|
||||
@@ -14,7 +13,6 @@ import {
|
||||
Row,
|
||||
Radio,
|
||||
Space,
|
||||
Statistic,
|
||||
Switch,
|
||||
Table,
|
||||
Tag,
|
||||
@@ -24,11 +22,12 @@ import {
|
||||
} from 'antd'
|
||||
import {
|
||||
CheckCircleOutlined,
|
||||
ControlOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
FireOutlined,
|
||||
KeyOutlined,
|
||||
PlusCircleOutlined,
|
||||
ReloadOutlined,
|
||||
SafetyCertificateOutlined,
|
||||
SearchOutlined,
|
||||
ShoppingCartOutlined,
|
||||
ThunderboltOutlined,
|
||||
@@ -113,16 +112,18 @@ export default function PlatformRock() {
|
||||
Modal.confirm({
|
||||
title: checked ? '确认开启 Rock 自动发货通道?' : '确认一键关闭 Rock 自动发货通道(软熔断)?',
|
||||
content: checked
|
||||
? '开启后,黑色 ROCK 与蓝色运动服两款商品将恢复全自动反向代理发货。'
|
||||
: '关闭后,两款商品将彻底脱离自动发货流程,订单将转交人工打手或客服处理,避免错单穿透。',
|
||||
okText: '确认更改',
|
||||
? '开启后,黑色 ROCK 与蓝色运动服两款商品将恢复 0666.pro 全自动反代发货。'
|
||||
: '关闭后,两款商品将脱离自动发货通道,进单将转交人工打手大厅处理,防止错单穿透。',
|
||||
okText: checked ? '确认开启' : '立即熔断关闭',
|
||||
cancelText: '取消',
|
||||
okType: checked ? 'primary' : 'danger',
|
||||
okButtonProps: {
|
||||
danger: !checked,
|
||||
},
|
||||
onOk: async () => {
|
||||
try {
|
||||
const updated = await rockApi.updateConfig({ auto_delivery_enabled: checked })
|
||||
setConfig(updated)
|
||||
message.success(checked ? '自动发货已开启' : '自动发货已成功熔断关闭')
|
||||
message.success(checked ? '自动发货通道已开启' : '自动发货通道已成功熔断关闭')
|
||||
loadData()
|
||||
} catch (e) {
|
||||
message.error('更新总开关失败: ' + (e instanceof Error ? e.message : '未知错误'))
|
||||
@@ -141,7 +142,7 @@ export default function PlatformRock() {
|
||||
setTestingConn(true)
|
||||
try {
|
||||
const res = await rockApi.testConnection(bearerToken)
|
||||
message.success(`连通成功!采购商户:${res.user?.username || '正常'},可用积分:${res.user?.points || 0}`)
|
||||
message.success(`连通成功!商户:${res.user?.username || '正常'},可用积分:${res.user?.points || 0}`)
|
||||
loadData()
|
||||
} catch (e) {
|
||||
message.error('连通性测试失败:' + (e instanceof Error ? e.message : '未知错误'))
|
||||
@@ -157,7 +158,7 @@ export default function PlatformRock() {
|
||||
setSavingConfig(true)
|
||||
const updated = await rockApi.updateConfig(values)
|
||||
setConfig(updated)
|
||||
message.success('配置已保存生效')
|
||||
message.success('配置已成功保存生效')
|
||||
loadData()
|
||||
} catch (e) {
|
||||
if (e instanceof Error) {
|
||||
@@ -187,7 +188,7 @@ export default function PlatformRock() {
|
||||
const handleResetToken = async (id: number) => {
|
||||
try {
|
||||
await rockApi.resetToken(id)
|
||||
message.success('已解冻重置为可用库存')
|
||||
message.success('已解除绑定异常,重置为可用库存')
|
||||
loadData()
|
||||
loadTokens()
|
||||
} catch (e) {
|
||||
@@ -199,57 +200,83 @@ export default function PlatformRock() {
|
||||
{
|
||||
title: 'ID / 提取码',
|
||||
dataIndex: 'id',
|
||||
width: 140,
|
||||
width: 170,
|
||||
render: (id, record) => (
|
||||
<Space direction="vertical" size={2}>
|
||||
<Text strong>#{id}</Text>
|
||||
<Text copyable={{ text: record.redeem_code }} type="secondary" style={{ fontSize: 12 }}>
|
||||
{record.redeem_code?.substring(0, 10)}...
|
||||
<Text code style={{ fontWeight: 600 }}>#{id}</Text>
|
||||
<Text
|
||||
copyable={{ text: record.redeem_code }}
|
||||
type="secondary"
|
||||
style={{ fontSize: 12, fontFamily: 'monospace' }}
|
||||
>
|
||||
{record.redeem_code ? `${record.redeem_code.substring(0, 10)}...` : '-'}
|
||||
</Text>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '颜色与适用范围',
|
||||
title: '规格与适用范围',
|
||||
dataIndex: 'color',
|
||||
width: 130,
|
||||
width: 150,
|
||||
render: (_, record) => (
|
||||
<Space direction="vertical" size={2}>
|
||||
<Tag color="purple">通用黑色 Token</Tag>
|
||||
<Tag color="geekblue" style={{ borderRadius: 4, fontWeight: 500 }}>
|
||||
通用黑色 Token
|
||||
</Tag>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{record.region || 'Q/V可用'}
|
||||
{record.region || 'Q/V区通用 · 自动分发'}
|
||||
</Text>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
title: '库存状态',
|
||||
dataIndex: 'status',
|
||||
width: 120,
|
||||
width: 140,
|
||||
render: (status) => {
|
||||
switch (status) {
|
||||
case 'available':
|
||||
return <Tag color="green">可用库存</Tag>
|
||||
return (
|
||||
<Tag color="success" style={{ fontWeight: 600, padding: '2px 8px' }}>
|
||||
● 可用库存
|
||||
</Tag>
|
||||
)
|
||||
case 'using':
|
||||
return <Tag color="blue">使用中 (扫码)</Tag>
|
||||
return (
|
||||
<Tag color="processing" style={{ fontWeight: 600, padding: '2px 8px' }}>
|
||||
● 扫码使用中
|
||||
</Tag>
|
||||
)
|
||||
case 'bound':
|
||||
return <Tag color="cyan">已绑定授权</Tag>
|
||||
return (
|
||||
<Tag color="cyan" style={{ fontWeight: 600, padding: '2px 8px' }}>
|
||||
● 已授权绑定
|
||||
</Tag>
|
||||
)
|
||||
case 'redeemed':
|
||||
return <Tag color="purple">已交付发货</Tag>
|
||||
return (
|
||||
<Tag color="purple" style={{ fontWeight: 600, padding: '2px 8px' }}>
|
||||
● 已交付发货
|
||||
</Tag>
|
||||
)
|
||||
case 'mismatch':
|
||||
return <Tag color="red">绑定错误 (阻断)</Tag>
|
||||
return (
|
||||
<Tag color="error" style={{ fontWeight: 600, padding: '2px 8px', border: '1px solid #ff4d4f' }}>
|
||||
⚠️ 绑定错误 (阻断)
|
||||
</Tag>
|
||||
)
|
||||
default:
|
||||
return <Tag>{status}</Tag>
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '关联订单',
|
||||
title: '关联业务工单',
|
||||
dataIndex: 'assigned_order_no',
|
||||
width: 170,
|
||||
width: 190,
|
||||
render: (orderNo) =>
|
||||
orderNo ? (
|
||||
<Text copyable style={{ fontSize: 12 }}>
|
||||
<Text code copyable={{ text: orderNo }} style={{ fontWeight: 600, color: '#1d4ed8' }}>
|
||||
{orderNo}
|
||||
</Text>
|
||||
) : (
|
||||
@@ -257,46 +284,57 @@ export default function PlatformRock() {
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '预期 UID vs 扫码角色',
|
||||
width: 220,
|
||||
title: '预期 UID vs 扫码角色 (风控门禁)',
|
||||
width: 250,
|
||||
render: (_, record) => {
|
||||
const hasMismatch = record.status === 'mismatch'
|
||||
const isMismatch = record.status === 'mismatch'
|
||||
return (
|
||||
<Space direction="vertical" size={2} style={{ width: '100%' }}>
|
||||
<div>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>预期UID: </Text>
|
||||
<Text strong>{record.expected_uid || '-'}</Text>
|
||||
<div
|
||||
style={{
|
||||
padding: isMismatch ? '6px 10px' : '0',
|
||||
background: isMismatch ? '#fff1f0' : 'transparent',
|
||||
borderRadius: isMismatch ? 6 : 0,
|
||||
border: isMismatch ? '1px dashed #ffa39e' : 'none',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12 }}>
|
||||
<span style={{ color: '#64748b' }}>预期 UID:</span>
|
||||
<Text strong style={{ color: '#0f172a' }}>{record.expected_uid || '-'}</Text>
|
||||
</div>
|
||||
<div>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>扫码角色: </Text>
|
||||
<Text type={hasMismatch ? 'danger' : undefined}>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6, fontSize: 12, marginTop: 2 }}>
|
||||
<span style={{ color: '#64748b' }}>扫码反查:</span>
|
||||
<Text type={isMismatch ? 'danger' : undefined} strong={isMismatch}>
|
||||
{record.actual_role_name ? `${record.actual_role_name} (${record.actual_role_id || '-'})` : '-'}
|
||||
</Text>
|
||||
</div>
|
||||
{hasMismatch && record.mismatch_reason && (
|
||||
<Tooltip title={record.mismatch_reason}>
|
||||
<Tag color="error" style={{ fontSize: 11, maxWidth: 200, overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||
{record.mismatch_reason}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
{isMismatch && record.mismatch_reason && (
|
||||
<div style={{ marginTop: 4 }}>
|
||||
<Tooltip title={record.mismatch_reason}>
|
||||
<Tag color="error" style={{ fontSize: 11, margin: 0, maxWidth: 220, overflow: 'hidden', textOverflow: 'ellipsis' }}>
|
||||
{record.mismatch_reason}
|
||||
</Tag>
|
||||
</Tooltip>
|
||||
</div>
|
||||
)}
|
||||
</Space>
|
||||
</div>
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '官方发货凭证 (FulfillId)',
|
||||
dataIndex: 'upstream_fulfill_id',
|
||||
width: 150,
|
||||
width: 180,
|
||||
render: (fulfillId, record) =>
|
||||
fulfillId ? (
|
||||
<Space direction="vertical" size={2}>
|
||||
<Text copyable strong style={{ color: '#52c41a' }}>
|
||||
<Text code copyable strong style={{ color: '#059669', fontSize: 12 }}>
|
||||
{fulfillId}
|
||||
</Text>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{record.target_skin || '和平精英道具'}
|
||||
</Text>
|
||||
<div>
|
||||
<Tag color="default" style={{ fontSize: 11 }}>
|
||||
{record.target_skin || '和平精英道具'}
|
||||
</Tag>
|
||||
</div>
|
||||
</Space>
|
||||
) : (
|
||||
<Text type="secondary">-</Text>
|
||||
@@ -305,16 +343,19 @@ export default function PlatformRock() {
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 100,
|
||||
width: 120,
|
||||
fixed: 'right',
|
||||
render: (_, record) => {
|
||||
if (record.status === 'mismatch') {
|
||||
return (
|
||||
<Popconfirm
|
||||
title="确定解冻重置该 Token?"
|
||||
description="重置后将清除订单关联并重新释放为可用库存。"
|
||||
description="重置后将清除原订单绑定与错误标记,重新释放为可用通用库存。"
|
||||
okText="确认解冻"
|
||||
cancelText="取消"
|
||||
onConfirm={() => handleResetToken(record.id)}
|
||||
>
|
||||
<Button size="small" type="link" danger icon={<UndoOutlined />}>
|
||||
<Button size="small" type="primary" danger ghost icon={<UndoOutlined />}>
|
||||
解冻重置
|
||||
</Button>
|
||||
</Popconfirm>
|
||||
@@ -327,283 +368,400 @@ export default function PlatformRock() {
|
||||
|
||||
const autoDelivery = config?.auto_delivery_enabled ?? true
|
||||
const availableStock = stats?.available_stock ?? 0
|
||||
const isStockLow = availableStock <= (config?.token_warning_stock ?? 10)
|
||||
const warningStock = config?.token_warning_stock ?? 10
|
||||
const isStockLow = availableStock <= warningStock
|
||||
const hasMismatch = (stats?.mismatch_stock ?? 0) > 0
|
||||
|
||||
return (
|
||||
<div style={{ padding: 24, maxWidth: 1400, margin: '0 auto' }}>
|
||||
<div style={{ padding: '0 4px' }}>
|
||||
<PageHeader
|
||||
title="Rock 发货平台 (0666.pro) 自动发货与库存风控"
|
||||
subtitle="管理和平精英「黑色ROCK长袖T恤」与「蓝色运动服上衣」通用库存池、采购策略、UID强一致防错门禁与软熔断机制"
|
||||
/>
|
||||
|
||||
{/* 熔断状态警示条 */}
|
||||
{!autoDelivery && (
|
||||
<Alert
|
||||
type="error"
|
||||
showIcon
|
||||
style={{ marginBottom: 20 }}
|
||||
message="【注意】Rock 自动发货全局总开关已关闭(已软熔断)"
|
||||
description="当前黑色 ROCK 和蓝色运动服两款商品已停止调用 0666.pro 自动发货,订单保留在待发货状态,将由人工打手在接单平台处理。"
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* 核心指标统计看板 */}
|
||||
<Row gutter={[16, 16]} style={{ marginBottom: 24 }}>
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Card bordered={false} style={{ background: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.06)' }}>
|
||||
<Statistic
|
||||
title="可用通用库存 (Available)"
|
||||
value={availableStock}
|
||||
valueStyle={{ color: isStockLow ? '#cf1322' : '#3f8600', fontWeight: 'bold' }}
|
||||
prefix={<ShoppingCartOutlined />}
|
||||
suffix="个"
|
||||
/>
|
||||
<div style={{ marginTop: 8, display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
警戒水位: {config?.token_warning_stock ?? 10} 个
|
||||
</Text>
|
||||
{isStockLow && <Tag color="error">库存紧缺</Tag>}
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Card bordered={false} style={{ background: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.06)' }}>
|
||||
<Statistic
|
||||
title="绑定错误阻断 (Mismatch)"
|
||||
value={stats?.mismatch_stock ?? 0}
|
||||
valueStyle={{ color: (stats?.mismatch_stock ?? 0) > 0 ? '#f5222d' : '#8c8c8c', fontWeight: 'bold' }}
|
||||
prefix={<ExclamationCircleOutlined />}
|
||||
suffix="个"
|
||||
/>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
{(stats?.mismatch_stock ?? 0) > 0 ? '存在 UID 校验不匹配的待排查记录' : '当前无绑定异常'}
|
||||
</Text>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Card bordered={false} style={{ background: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.06)' }}>
|
||||
<Statistic
|
||||
title="履约中 / 已交付"
|
||||
value={stats?.redeemed_stock ?? 0}
|
||||
valueStyle={{ color: '#1677ff', fontWeight: 'bold' }}
|
||||
prefix={<CheckCircleOutlined />}
|
||||
suffix={`(扫码中: ${stats?.using_stock ?? 0})`}
|
||||
/>
|
||||
<div style={{ marginTop: 8 }}>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
已完成发货交付总量
|
||||
</Text>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Card bordered={false} style={{ background: '#fff', borderRadius: 8, boxShadow: '0 2px 8px rgba(0,0,0,0.06)' }}>
|
||||
<Statistic
|
||||
title="0666 账号积分余额"
|
||||
value={stats?.upstream_points ?? 0}
|
||||
precision={1}
|
||||
valueStyle={{ color: (stats?.upstream_points ?? 0) < (config?.min_points_warning ?? 1050) ? '#faad14' : '#722ed1', fontWeight: 'bold' }}
|
||||
prefix={<FireOutlined />}
|
||||
suffix="点"
|
||||
/>
|
||||
<div style={{ marginTop: 8, display: 'flex', justifyContent: 'space-between' }}>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
账号: {stats?.username || '未同步'}
|
||||
</Text>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
约可采购 {Math.floor((stats?.upstream_points ?? 0) / 21)} 个
|
||||
</Text>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* 上游凭据与策略设置 */}
|
||||
<Card
|
||||
title={
|
||||
<Space>
|
||||
<KeyOutlined />
|
||||
<span>0666.pro 采购凭据与自动发货控制</span>
|
||||
</Space>
|
||||
}
|
||||
subtitle="和平精英「黑色ROCK长袖T恤」与「蓝色运动服上衣」通用库存池、上游采购、UID强一致防错门禁与软熔断机制"
|
||||
extra={
|
||||
<Space>
|
||||
<Button icon={<ReloadOutlined />} onClick={loadData} loading={loadingStats}>
|
||||
刷新状态
|
||||
</Button>
|
||||
<Space size="middle">
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<PlusCircleOutlined />}
|
||||
icon={<ShoppingCartOutlined />}
|
||||
onClick={() => handlePurchase(50)}
|
||||
loading={purchasing}
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%)',
|
||||
boxShadow: '0 2px 4px rgba(37,99,235,0.2)',
|
||||
}}
|
||||
>
|
||||
一键采购 50 个通用 Token
|
||||
</Button>
|
||||
<Button icon={<ReloadOutlined />} onClick={loadData} loading={loadingStats}>
|
||||
刷新状态
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
style={{ marginBottom: 24, borderRadius: 8 }}
|
||||
/>
|
||||
|
||||
{/* 顶部高质感指标卡片 */}
|
||||
<Row gutter={[16, 16]} style={{ marginBottom: 20 }}>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<div className="metric-card-box">
|
||||
<div className="metric-card-header">
|
||||
<span className="metric-card-title">可用通用库存 (Available)</span>
|
||||
<div className={`metric-card-icon ${isStockLow ? 'metric-card-icon--rose' : ''}`}>
|
||||
<ShoppingCartOutlined />
|
||||
</div>
|
||||
</div>
|
||||
<div className="metric-card-value" style={{ color: isStockLow ? '#e11d48' : '#0f172a' }}>
|
||||
{availableStock}
|
||||
<span style={{ fontSize: 13, fontWeight: 500, marginLeft: 4, color: '#64748b' }}>个</span>
|
||||
</div>
|
||||
<div className="metric-card-footer">
|
||||
<span>警戒水位: {warningStock} 个</span>
|
||||
{isStockLow ? (
|
||||
<Tag color="error" style={{ margin: 0, borderRadius: 4 }}>库存紧缺</Tag>
|
||||
) : (
|
||||
<span style={{ color: '#10b981', fontWeight: 600 }}>储备充足</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<div className="metric-card-box">
|
||||
<div className="metric-card-header">
|
||||
<span className="metric-card-title">绑定错误阻断 (Mismatch)</span>
|
||||
<div className={`metric-card-icon ${hasMismatch ? 'metric-card-icon--rose' : ''}`}>
|
||||
<ExclamationCircleOutlined />
|
||||
</div>
|
||||
</div>
|
||||
<div className="metric-card-value" style={{ color: hasMismatch ? '#e11d48' : '#0f172a' }}>
|
||||
{stats?.mismatch_stock ?? 0}
|
||||
<span style={{ fontSize: 13, fontWeight: 500, marginLeft: 4, color: '#64748b' }}>个</span>
|
||||
</div>
|
||||
<div className="metric-card-footer">
|
||||
<span>{hasMismatch ? 'UID强一致校验拦截保护' : '暂无异常阻断'}</span>
|
||||
{hasMismatch ? (
|
||||
<Tag color="error" style={{ margin: 0, borderRadius: 4 }}>需核对处理</Tag>
|
||||
) : (
|
||||
<span style={{ color: '#10b981', fontWeight: 600 }}>状态正常</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<div className="metric-card-box">
|
||||
<div className="metric-card-header">
|
||||
<span className="metric-card-title">已履约交付 (Redeemed)</span>
|
||||
<div className="metric-card-icon metric-card-icon--emerald">
|
||||
<CheckCircleOutlined />
|
||||
</div>
|
||||
</div>
|
||||
<div className="metric-card-value">
|
||||
{stats?.redeemed_stock ?? 0}
|
||||
<span style={{ fontSize: 13, fontWeight: 500, marginLeft: 4, color: '#64748b' }}>个</span>
|
||||
</div>
|
||||
<div className="metric-card-footer">
|
||||
<span>当前扫码中</span>
|
||||
<span style={{ fontWeight: 600, color: '#2563eb' }}>{stats?.using_stock ?? 0} 个</span>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<div className="metric-card-box">
|
||||
<div className="metric-card-header">
|
||||
<span className="metric-card-title">0666.pro 账号积分</span>
|
||||
<div className="metric-card-icon metric-card-icon--purple">
|
||||
<FireOutlined />
|
||||
</div>
|
||||
</div>
|
||||
<div className="metric-card-value" style={{ color: '#7c3aed' }}>
|
||||
{(stats?.upstream_points ?? 0).toFixed(1)}
|
||||
<span style={{ fontSize: 13, fontWeight: 500, marginLeft: 4, color: '#64748b' }}>点</span>
|
||||
</div>
|
||||
<div className="metric-card-footer">
|
||||
<span>账号: {stats?.username || '未同步'}</span>
|
||||
<span>约可采购 <strong style={{ color: '#0f172a' }}>{Math.floor((stats?.upstream_points ?? 0) / 21)}</strong> 个</span>
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
{/* 核心控制与上游策略看板 */}
|
||||
<Card
|
||||
style={{
|
||||
marginBottom: 20,
|
||||
borderRadius: 12,
|
||||
border: '1px solid #e2e8f0',
|
||||
boxShadow: 'var(--shadow-sm)',
|
||||
}}
|
||||
styles={{ body: { padding: '20px 24px' } }}
|
||||
>
|
||||
{/* 通道状态与软熔断横幅 */}
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
padding: '14px 18px',
|
||||
borderRadius: 8,
|
||||
marginBottom: 20,
|
||||
background: autoDelivery ? '#f0fdf4' : '#fef2f2',
|
||||
border: autoDelivery ? '1px solid #bbf7d0' : '1px solid #fecaca',
|
||||
transition: 'all 0.3s ease',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 12 }}>
|
||||
<div
|
||||
style={{
|
||||
width: 38,
|
||||
height: 38,
|
||||
borderRadius: 8,
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
fontSize: 18,
|
||||
background: autoDelivery ? '#dcfce7' : '#fee2e2',
|
||||
color: autoDelivery ? '#16a34a' : '#dc2626',
|
||||
}}
|
||||
>
|
||||
{autoDelivery ? <SafetyCertificateOutlined /> : <ControlOutlined />}
|
||||
</div>
|
||||
<div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
||||
<strong style={{ fontSize: 14, color: autoDelivery ? '#166534' : '#991b1b' }}>
|
||||
{autoDelivery
|
||||
? '自动发货通道正常运转 (Active)'
|
||||
: '自动发货已全局软熔断 (Kill Switch Engaged)'}
|
||||
</strong>
|
||||
<Tag color={autoDelivery ? 'success' : 'error'} style={{ borderRadius: 10 }}>
|
||||
{autoDelivery ? '反代履约中' : '已转交人工打手'}
|
||||
</Tag>
|
||||
</div>
|
||||
<div style={{ fontSize: 12, color: autoDelivery ? '#15803d' : '#b91c1c', marginTop: 2 }}>
|
||||
{autoDelivery
|
||||
? '和平精英「黑色 ROCK 长袖」与「蓝色运动服」开启 0666.pro 全自动反代发货,UID 强一致安全门禁全面守护。'
|
||||
: '两款商品已彻底切断上游 0666.pro 自动发货通道,所有新订单均保留在待发货状态,由人工接单平台处理。'}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Space size="middle">
|
||||
<Switch
|
||||
checked={autoDelivery}
|
||||
onChange={handleToggleAutoDelivery}
|
||||
checkedChildren="开启"
|
||||
unCheckedChildren="熔断"
|
||||
style={{
|
||||
backgroundColor: autoDelivery ? '#16a34a' : '#dc2626',
|
||||
}}
|
||||
/>
|
||||
<Button
|
||||
size="small"
|
||||
danger={autoDelivery}
|
||||
type={autoDelivery ? 'default' : 'primary'}
|
||||
onClick={() => handleToggleAutoDelivery(!autoDelivery)}
|
||||
>
|
||||
{autoDelivery ? '一键紧急熔断' : '恢复自动发货'}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
|
||||
{/* 策略表单 */}
|
||||
<Form form={form} layout="vertical">
|
||||
<Row gutter={24}>
|
||||
<Col xs={24} md={12}>
|
||||
<Form.Item
|
||||
label={
|
||||
<Space>
|
||||
<Text strong>自动发货总开关 (Kill Switch)</Text>
|
||||
<Tooltip title="关闭后黑色 ROCK 与蓝色运动服两款商品停走自动发货通道,平滑转人工打手接单">
|
||||
<ExclamationCircleOutlined />
|
||||
</Tooltip>
|
||||
</Space>
|
||||
}
|
||||
<Col xs={24} lg={12}>
|
||||
<div
|
||||
style={{
|
||||
background: '#f8fafc',
|
||||
padding: '16px 18px',
|
||||
borderRadius: 10,
|
||||
border: '1px solid #f1f5f9',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<Space size="middle">
|
||||
<Switch
|
||||
checked={autoDelivery}
|
||||
onChange={handleToggleAutoDelivery}
|
||||
checkedChildren="自动发货中"
|
||||
unCheckedChildren="已熔断转人工"
|
||||
/>
|
||||
<Text type={autoDelivery ? 'success' : 'danger'}>
|
||||
{autoDelivery ? '通道正常,全自动反代履约中' : '已熔断!商品已下线自动通道转交人工处理'}
|
||||
</Text>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 16 }}>
|
||||
<KeyOutlined style={{ color: '#2563eb', fontSize: 16 }} />
|
||||
<strong style={{ fontSize: 14, color: '#0f172a' }}>0666.pro 采购认证凭据</strong>
|
||||
</div>
|
||||
|
||||
<Col xs={24} md={12}>
|
||||
<Form.Item name="base_url" label="上游平台接口地址 (Base URL)">
|
||||
<Input placeholder="https://0666.pro" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Form.Item name="base_url" label="上游平台接口地址 (Base URL)" style={{ marginBottom: 14 }}>
|
||||
<Input placeholder="https://0666.pro" />
|
||||
</Form.Item>
|
||||
|
||||
<Col xs={24}>
|
||||
<Form.Item
|
||||
name="bearer_token"
|
||||
label={
|
||||
<Space>
|
||||
<Text strong>上游商户 Bearer Token (yd_token)</Text>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
(平台无密码,直接输入从 0666.pro 网页复制的 64 位 Token)
|
||||
</Text>
|
||||
</Space>
|
||||
}
|
||||
extra={
|
||||
<div style={{ marginTop: 4 }}>
|
||||
<Text type="secondary" style={{ fontSize: 12 }}>
|
||||
💡 极简提取指引:登录 0666.pro 网页后,按 F12 打开控制台,在 Console 执行{' '}
|
||||
<code>localStorage.getItem('yd_token')</code> 即可一键复制。
|
||||
</Text>
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Input.Password
|
||||
placeholder="粘贴 64 位十六进制 Bearer Token"
|
||||
addonAfter={
|
||||
<Button type="link" size="small" onClick={handleTestConnection} loading={testingConn}>
|
||||
测试连通性
|
||||
</Button>
|
||||
<Form.Item
|
||||
name="bearer_token"
|
||||
label={
|
||||
<Space>
|
||||
<span>上游商户 Bearer Token (yd_token)</span>
|
||||
<Tooltip title="0666.pro 采用无密码架构,直接从网页 localStorage 提取 64 位十六进制 Token 即可">
|
||||
<ExclamationCircleOutlined style={{ color: '#94a3b8' }} />
|
||||
</Tooltip>
|
||||
</Space>
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
style={{ marginBottom: 12 }}
|
||||
>
|
||||
<Input.Password
|
||||
placeholder="粘贴 64 位十六进制 Bearer Token"
|
||||
addonAfter={
|
||||
<Button
|
||||
type="link"
|
||||
size="small"
|
||||
onClick={handleTestConnection}
|
||||
loading={testingConn}
|
||||
style={{ padding: '0 4px', height: 'auto' }}
|
||||
>
|
||||
测试连通性
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<div
|
||||
style={{
|
||||
background: '#eff6ff',
|
||||
border: '1px dashed #bfdbfe',
|
||||
borderRadius: 6,
|
||||
padding: '8px 12px',
|
||||
fontSize: 12,
|
||||
color: '#1e40af',
|
||||
}}
|
||||
>
|
||||
💡 <strong>极简提取指引:</strong>登录 0666.pro 网页后,按 F12 打开控制台,在 Console 执行{' '}
|
||||
<Text code copyable={{ text: "localStorage.getItem('yd_token')" }}>
|
||||
localStorage.getItem('yd_token')
|
||||
</Text>{' '}
|
||||
即可一键复制。
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item name="token_warning_stock" label="库存告警安全水位">
|
||||
<InputNumber min={1} max={500} style={{ width: '100%' }} addonAfter="个" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col xs={24} lg={12}>
|
||||
<div
|
||||
style={{
|
||||
background: '#f8fafc',
|
||||
padding: '16px 18px',
|
||||
borderRadius: 10,
|
||||
border: '1px solid #f1f5f9',
|
||||
height: '100%',
|
||||
}}
|
||||
>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 16 }}>
|
||||
<ThunderboltOutlined style={{ color: '#7c3aed', fontSize: 16 }} />
|
||||
<strong style={{ fontSize: 14, color: '#0f172a' }}>库存风控与自动补货策略</strong>
|
||||
</div>
|
||||
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item name="auto_purchase_enabled" label="自动补货触发开关" valuePropName="checked">
|
||||
<Switch checkedChildren="开启自动采购" unCheckedChildren="关闭" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item name="auto_purchase_count" label="单次自动补货数量">
|
||||
<InputNumber min={5} max={200} style={{ width: '100%' }} addonAfter="个" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} md={6}>
|
||||
<Form.Item name="purchase_cooldown_minutes" label="补货防抖冷却时间">
|
||||
<InputNumber min={1} max={60} style={{ width: '100%' }} addonAfter="分钟" />
|
||||
</Form.Item>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item name="token_warning_stock" label="库存告警安全水位">
|
||||
<InputNumber min={1} max={500} style={{ width: '100%' }} addonAfter="个" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="auto_purchase_enabled" label="自动补货触发开关" valuePropName="checked">
|
||||
<Switch checkedChildren="开启自动补货" unCheckedChildren="关闭" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="auto_purchase_count" label="单次自动补货数量">
|
||||
<InputNumber min={5} max={200} style={{ width: '100%' }} addonAfter="个" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="purchase_cooldown_minutes" label="补货防抖冷却时间">
|
||||
<InputNumber min={1} max={60} style={{ width: '100%' }} addonAfter="分钟" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Divider style={{ margin: '12px 0 20px 0' }} />
|
||||
<Divider style={{ margin: '18px 0 16px 0' }} />
|
||||
|
||||
<Space>
|
||||
<Button type="primary" onClick={handleSaveConfig} loading={savingConfig}>
|
||||
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={handleSaveConfig}
|
||||
loading={savingConfig}
|
||||
style={{
|
||||
background: 'linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%)',
|
||||
minWidth: 130,
|
||||
}}
|
||||
>
|
||||
保存策略配置
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
</Form>
|
||||
</Card>
|
||||
|
||||
{/* 本地通用库存池明细 */}
|
||||
{/* 筛选与搜索工具条(对齐标杆页面) */}
|
||||
<Card
|
||||
title={
|
||||
<Space>
|
||||
<ThunderboltOutlined />
|
||||
<span>通用 Token 库存池与错单审计列表</span>
|
||||
</Space>
|
||||
}
|
||||
extra={
|
||||
<Space>
|
||||
<Radio.Group value={filterStatus} onChange={(e) => setFilterStatus(e.target.value)} buttonStyle="solid">
|
||||
<Radio.Button value="all">全部</Radio.Button>
|
||||
<Radio.Button value="available">可用</Radio.Button>
|
||||
<Radio.Button value="using">使用中</Radio.Button>
|
||||
<Radio.Button value="bound">已绑定</Radio.Button>
|
||||
<Radio.Button value="redeemed">已发货</Radio.Button>
|
||||
<Radio.Button value="mismatch">
|
||||
<Badge count={stats?.mismatch_stock ?? 0} offset={[8, -2]}>
|
||||
<span>绑定错误</span>
|
||||
</Badge>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
|
||||
<Input
|
||||
placeholder="搜索订单号 / UID / 角色名"
|
||||
allowClear
|
||||
prefix={<SearchOutlined />}
|
||||
style={{ width: 220 }}
|
||||
onPressEnter={(e) => setSearchKeyword(e.currentTarget.value)}
|
||||
onChange={(e) => {
|
||||
if (!e.target.value) setSearchKeyword('')
|
||||
}}
|
||||
/>
|
||||
<Button icon={<ReloadOutlined />} onClick={loadTokens} loading={loadingTokens}>
|
||||
刷新
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
style={{ borderRadius: 8 }}
|
||||
styles={{ body: { padding: '16px 20px' } }}
|
||||
style={{ marginBottom: 16, borderRadius: 10, border: '1px solid #e2e8f0' }}
|
||||
>
|
||||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={tokens}
|
||||
loading={loadingTokens}
|
||||
pagination={{
|
||||
current: page,
|
||||
pageSize,
|
||||
total: totalTokens,
|
||||
showSizeChanger: true,
|
||||
onChange: (p, ps) => {
|
||||
setPage(p)
|
||||
setPageSize(ps)
|
||||
},
|
||||
}}
|
||||
/>
|
||||
<Row gutter={[16, 12]} align="middle" justify="space-between">
|
||||
<Col xs={24} md={16}>
|
||||
<Space size="middle" wrap>
|
||||
<Input
|
||||
placeholder="搜索订单号 / UID / 角色名 / 提取码"
|
||||
prefix={<SearchOutlined style={{ color: '#94a3b8' }} />}
|
||||
value={searchKeyword}
|
||||
onChange={(e) => setSearchKeyword(e.target.value)}
|
||||
onPressEnter={loadTokens}
|
||||
style={{ width: 280 }}
|
||||
allowClear
|
||||
/>
|
||||
<Radio.Group
|
||||
value={filterStatus}
|
||||
onChange={(e) => {
|
||||
setFilterStatus(e.target.value)
|
||||
setPage(1)
|
||||
}}
|
||||
buttonStyle="solid"
|
||||
>
|
||||
<Radio.Button value="all">全部</Radio.Button>
|
||||
<Radio.Button value="available">可用</Radio.Button>
|
||||
<Radio.Button value="using">使用中</Radio.Button>
|
||||
<Radio.Button value="bound">已绑定</Radio.Button>
|
||||
<Radio.Button value="redeemed">已发货</Radio.Button>
|
||||
<Radio.Button value="mismatch">
|
||||
<Badge count={stats?.mismatch_stock ?? 0} offset={[6, -2]} size="small">
|
||||
<span>绑定错误</span>
|
||||
</Badge>
|
||||
</Radio.Button>
|
||||
</Radio.Group>
|
||||
<Button icon={<ReloadOutlined />} onClick={loadTokens} loading={loadingTokens}>
|
||||
刷新
|
||||
</Button>
|
||||
</Space>
|
||||
</Col>
|
||||
<Col xs={24} md={8} style={{ textAlign: 'right' }}>
|
||||
<Text type="secondary" style={{ fontSize: 13 }}>
|
||||
共检索到 <strong style={{ color: '#0f172a' }}>{totalTokens}</strong> 条库存记录
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
</Card>
|
||||
|
||||
{/* 主库存与错单审计表格 */}
|
||||
<Table
|
||||
rowKey="id"
|
||||
columns={columns}
|
||||
dataSource={tokens}
|
||||
loading={loadingTokens}
|
||||
scroll={{ x: 1100 }}
|
||||
pagination={{
|
||||
current: page,
|
||||
pageSize,
|
||||
total: totalTokens,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
showTotal: (total) => `共 ${total} 条库存记录`,
|
||||
onChange: (p, ps) => {
|
||||
setPage(p)
|
||||
setPageSize(ps)
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user