feat:自动补货/手动采购逻辑修改:批量获取-》打包-》获取Token列表
This commit is contained in:
@@ -11,6 +11,8 @@ export interface PurchaseTokenModalProps {
|
||||
upstreamPoints: number
|
||||
estimatedCost: number
|
||||
isPointsInsufficient: boolean
|
||||
tokenCost: number
|
||||
upstreamStock: number | null
|
||||
onPurchaseCountChange: (value: number) => void
|
||||
onCancel: () => void
|
||||
onConfirm: (count: number) => void
|
||||
@@ -25,10 +27,13 @@ export function PurchaseTokenModal({
|
||||
upstreamPoints,
|
||||
estimatedCost,
|
||||
isPointsInsufficient,
|
||||
tokenCost,
|
||||
upstreamStock,
|
||||
onPurchaseCountChange,
|
||||
onCancel,
|
||||
onConfirm,
|
||||
}: PurchaseTokenModalProps) {
|
||||
const isStockInsufficient = upstreamStock !== null && purchaseCount > upstreamStock
|
||||
return (
|
||||
<Modal
|
||||
title={
|
||||
@@ -48,15 +53,16 @@ export function PurchaseTokenModal({
|
||||
type="primary"
|
||||
icon={<ShoppingCartOutlined />}
|
||||
loading={purchasing}
|
||||
disabled={isPointsInsufficient || purchaseCount <= 0}
|
||||
disabled={isPointsInsufficient || isStockInsufficient || purchaseCount <= 0}
|
||||
onClick={() => onConfirm(purchaseCount)}
|
||||
style={{
|
||||
background: isPointsInsufficient
|
||||
? undefined
|
||||
: 'linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%)',
|
||||
background:
|
||||
isPointsInsufficient || isStockInsufficient
|
||||
? undefined
|
||||
: 'linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%)',
|
||||
}}
|
||||
>
|
||||
确认采购 {purchaseCount} 个 ({estimatedCost} 点)
|
||||
确认采购 {purchaseCount} 个 ({estimatedCost.toFixed(2)} 点)
|
||||
</Button>,
|
||||
]}
|
||||
destroyOnClose
|
||||
@@ -88,12 +94,48 @@ export function PurchaseTokenModal({
|
||||
</Col>
|
||||
</Row>
|
||||
<Divider style={{ margin: '10px 0' }} />
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 12, color: '#64748b' }}>
|
||||
<span>采购单价:<strong style={{ color: '#0f172a' }}>21 点 / 个</strong></span>
|
||||
<span>最大可采购:<strong style={{ color: '#2563eb' }}>{maxPurchasable} 个</strong></span>
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
flexWrap: 'wrap',
|
||||
gap: 8,
|
||||
fontSize: 12,
|
||||
color: '#64748b',
|
||||
}}
|
||||
>
|
||||
<span>
|
||||
采购单价:<strong style={{ color: '#0f172a' }}>{tokenCost.toFixed(2)} 点 / 个</strong>
|
||||
</span>
|
||||
<span>
|
||||
上游可用库存:
|
||||
<strong style={{ color: isStockInsufficient ? '#dc2626' : '#0f172a' }}>
|
||||
{upstreamStock === null ? '未获取' : `${upstreamStock} 个`}
|
||||
</strong>
|
||||
</span>
|
||||
<span>
|
||||
最大可采购:<strong style={{ color: '#2563eb' }}>{maxPurchasable} 个</strong>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 上游库存不足提示 */}
|
||||
{isStockInsufficient && (
|
||||
<div
|
||||
style={{
|
||||
background: '#fef2f2',
|
||||
border: '1px solid #fecaca',
|
||||
borderRadius: 8,
|
||||
padding: '10px 16px',
|
||||
marginBottom: 14,
|
||||
fontSize: 13,
|
||||
color: '#b91c1c',
|
||||
}}
|
||||
>
|
||||
⚠️ 上游可用库存不足:当前 {upstreamStock} 个,本次需要 {purchaseCount} 个,请减少采购数量。
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 采购数量输入 */}
|
||||
<div style={{ marginBottom: 14 }}>
|
||||
<div style={{ fontWeight: 600, marginBottom: 8, color: '#0f172a' }}>
|
||||
@@ -153,7 +195,7 @@ export function PurchaseTokenModal({
|
||||
本次采购消耗积分:
|
||||
</span>
|
||||
<strong style={{ color: isPointsInsufficient ? '#dc2626' : '#16a34a', fontSize: 16 }}>
|
||||
{estimatedCost} 点
|
||||
{estimatedCost.toFixed(2)} 点
|
||||
</strong>
|
||||
</div>
|
||||
<div
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Col, Row, Tag } from 'antd'
|
||||
import {
|
||||
CheckCircleOutlined,
|
||||
CloudServerOutlined,
|
||||
ExclamationCircleOutlined,
|
||||
FireOutlined,
|
||||
ShoppingCartOutlined,
|
||||
@@ -9,6 +10,8 @@ import type { RockStats } from '../../api'
|
||||
|
||||
export interface RockMetricCardsProps {
|
||||
stats: RockStats | null
|
||||
upstreamStock: number | null
|
||||
blackTokenCost: number
|
||||
availableStock: number
|
||||
warningStock: number
|
||||
isStockLow: boolean
|
||||
@@ -20,6 +23,8 @@ export interface RockMetricCardsProps {
|
||||
|
||||
export function RockMetricCards({
|
||||
stats,
|
||||
upstreamStock,
|
||||
blackTokenCost,
|
||||
availableStock,
|
||||
warningStock,
|
||||
isStockLow,
|
||||
@@ -30,7 +35,38 @@ export function RockMetricCards({
|
||||
}: RockMetricCardsProps) {
|
||||
return (
|
||||
<Row gutter={[16, 16]} style={{ marginBottom: 20 }}>
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Col flex="1 1 200px">
|
||||
<div className="metric-card-box">
|
||||
<div className="metric-card-header">
|
||||
<span className="metric-card-title">上游可用库存 (0666.pro)</span>
|
||||
<div className="metric-card-icon" style={{ background: '#e0f2fe', color: '#0284c7' }}>
|
||||
<CloudServerOutlined />
|
||||
</div>
|
||||
</div>
|
||||
<div className="metric-card-value" style={{ color: upstreamStock === null ? '#94a3b8' : '#0369a1' }}>
|
||||
{upstreamStock === null ? '-' : upstreamStock}
|
||||
<span style={{ fontSize: 13, fontWeight: 500, marginLeft: 4, color: '#64748b' }}>个</span>
|
||||
</div>
|
||||
<div className="metric-card-footer">
|
||||
<span>采购单价: {blackTokenCost.toFixed(2)} 点/个</span>
|
||||
{upstreamStock === null ? (
|
||||
<Tag color="warning" style={{ margin: 0, borderRadius: 4 }}>
|
||||
未获取
|
||||
</Tag>
|
||||
) : upstreamStock === 0 ? (
|
||||
<Tag color="error" style={{ margin: 0, borderRadius: 4 }}>
|
||||
无库存
|
||||
</Tag>
|
||||
) : (
|
||||
<Tag color="processing" style={{ margin: 0, borderRadius: 4 }}>
|
||||
实时
|
||||
</Tag>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col flex="1 1 200px">
|
||||
<div className="metric-card-box">
|
||||
<div className="metric-card-header">
|
||||
<span className="metric-card-title">可用通用库存 (Available)</span>
|
||||
@@ -45,7 +81,9 @@ export function RockMetricCards({
|
||||
<div className="metric-card-footer">
|
||||
<span>警戒水位: {warningStock} 个</span>
|
||||
{isStockLow ? (
|
||||
<Tag color="error" style={{ margin: 0, borderRadius: 4 }}>库存紧缺</Tag>
|
||||
<Tag color="error" style={{ margin: 0, borderRadius: 4 }}>
|
||||
库存紧缺
|
||||
</Tag>
|
||||
) : (
|
||||
<span style={{ color: '#10b981', fontWeight: 600 }}>储备充足</span>
|
||||
)}
|
||||
@@ -53,7 +91,7 @@ export function RockMetricCards({
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Col flex="1 1 200px">
|
||||
<div className="metric-card-box">
|
||||
<div className="metric-card-header">
|
||||
<span className="metric-card-title">绑定错误阻断 (Mismatch)</span>
|
||||
@@ -68,7 +106,9 @@ export function RockMetricCards({
|
||||
<div className="metric-card-footer">
|
||||
<span>{hasMismatch ? 'UID强一致校验拦截保护' : '暂无异常阻断'}</span>
|
||||
{hasMismatch ? (
|
||||
<Tag color="error" style={{ margin: 0, borderRadius: 4 }}>需核对处理</Tag>
|
||||
<Tag color="error" style={{ margin: 0, borderRadius: 4 }}>
|
||||
需核对处理
|
||||
</Tag>
|
||||
) : (
|
||||
<span style={{ color: '#10b981', fontWeight: 600 }}>状态正常</span>
|
||||
)}
|
||||
@@ -76,7 +116,7 @@ export function RockMetricCards({
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Col flex="1 1 200px">
|
||||
<div className="metric-card-box">
|
||||
<div className="metric-card-header">
|
||||
<span className="metric-card-title">已履约交付 (Redeemed)</span>
|
||||
@@ -95,7 +135,7 @@ export function RockMetricCards({
|
||||
</div>
|
||||
</Col>
|
||||
|
||||
<Col xs={24} sm={12} lg={6}>
|
||||
<Col flex="1 1 200px">
|
||||
<div className="metric-card-box">
|
||||
<div className="metric-card-header">
|
||||
<span className="metric-card-title">0666.pro 账号积分</span>
|
||||
|
||||
@@ -27,9 +27,12 @@ const { Text } = Typography
|
||||
export interface RockStrategyCardProps {
|
||||
form: FormInstance
|
||||
autoDelivery: boolean
|
||||
autoPurchaseEnabled: boolean
|
||||
togglingAutoPurchase: boolean
|
||||
testingConn: boolean
|
||||
savingConfig: boolean
|
||||
onToggleAutoDelivery: (checked: boolean) => void
|
||||
onToggleAutoPurchase: (checked: boolean) => void
|
||||
onTestConnection: () => void
|
||||
onSaveConfig: () => void
|
||||
}
|
||||
@@ -37,9 +40,12 @@ export interface RockStrategyCardProps {
|
||||
export function RockStrategyCard({
|
||||
form,
|
||||
autoDelivery,
|
||||
autoPurchaseEnabled,
|
||||
togglingAutoPurchase,
|
||||
testingConn,
|
||||
savingConfig,
|
||||
onToggleAutoDelivery,
|
||||
onToggleAutoPurchase,
|
||||
onTestConnection,
|
||||
onSaveConfig,
|
||||
}: RockStrategyCardProps) {
|
||||
@@ -227,13 +233,24 @@ export function RockStrategyCard({
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="auto_purchase_enabled" label="自动补货触发开关" valuePropName="checked">
|
||||
<Switch checkedChildren="开启自动补货" unCheckedChildren="关闭" />
|
||||
<Form.Item label="自动补货触发开关">
|
||||
<Space direction="vertical" size={2}>
|
||||
<Switch
|
||||
checked={autoPurchaseEnabled}
|
||||
onChange={onToggleAutoPurchase}
|
||||
loading={togglingAutoPurchase}
|
||||
checkedChildren="开启自动补货"
|
||||
unCheckedChildren="关闭"
|
||||
/>
|
||||
<Text type="secondary" style={{ fontSize: 11 }}>
|
||||
切换后立即保存
|
||||
</Text>
|
||||
</Space>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item name="auto_purchase_count" label="单次自动补货数量">
|
||||
<InputNumber min={5} max={200} style={{ width: '100%' }} addonAfter="个" />
|
||||
<InputNumber min={1} max={200} style={{ width: '100%' }} addonAfter="个" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
|
||||
@@ -34,12 +34,12 @@ export function RockTokenFilterBar({
|
||||
<Col xs={24} md={16}>
|
||||
<Space size="middle" wrap>
|
||||
<Input
|
||||
placeholder="搜索 0666 编号 / 提取码 / 订单号 / UID"
|
||||
placeholder="搜索 包ID / 0666 编号 / 提取码 / 兑换码 / 订单号 / UID"
|
||||
prefix={<SearchOutlined style={{ color: '#94a3b8' }} />}
|
||||
value={searchKeyword}
|
||||
onChange={(e) => onSearchKeywordChange(e.target.value)}
|
||||
onPressEnter={onRefresh}
|
||||
style={{ width: 340 }}
|
||||
style={{ width: 400 }}
|
||||
allowClear
|
||||
/>
|
||||
<Radio.Group
|
||||
@@ -72,7 +72,7 @@ export function RockTokenFilterBar({
|
||||
</Col>
|
||||
<Col xs={24} md={8} style={{ textAlign: 'right' }}>
|
||||
<Text type="secondary" style={{ fontSize: 13 }}>
|
||||
共检索到 <strong style={{ color: '#0f172a' }}>{totalTokens}</strong> 条库存记录
|
||||
共检索到 <strong style={{ color: '#0f172a' }}>{totalTokens}</strong> 条 Token 记录
|
||||
</Text>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
@@ -18,6 +18,7 @@ export interface RockTokenTableProps {
|
||||
setPreviewVoucherUrl: Dispatch<SetStateAction<string>>
|
||||
setPreviewVoucherOpen: Dispatch<SetStateAction<boolean>>
|
||||
onEditUid: (state: UidEditState) => void
|
||||
onViewRawData: (record: RockTokenItem) => void
|
||||
onPageChange: (page: number, pageSize: number) => void
|
||||
onReloadData: () => void
|
||||
onReloadTokens: () => void
|
||||
@@ -34,6 +35,7 @@ export function RockTokenTable({
|
||||
setPreviewVoucherUrl,
|
||||
setPreviewVoucherOpen,
|
||||
onEditUid,
|
||||
onViewRawData,
|
||||
onPageChange,
|
||||
onReloadData,
|
||||
onReloadTokens,
|
||||
@@ -153,6 +155,7 @@ export function RockTokenTable({
|
||||
onCopyVoucher: handleCopyVoucher,
|
||||
onPreviewVoucher: handlePreviewVoucher,
|
||||
onEditUid,
|
||||
onViewRawData,
|
||||
onResetToken: handleResetToken,
|
||||
onReleaseToken: handleReleaseToken,
|
||||
onUpdateRemark: handleUpdateRemark,
|
||||
@@ -165,14 +168,14 @@ export function RockTokenTable({
|
||||
columns={columns}
|
||||
dataSource={tokens}
|
||||
loading={loadingTokens}
|
||||
scroll={{ x: 1300 }}
|
||||
scroll={{ x: 1700 }}
|
||||
pagination={{
|
||||
current: page,
|
||||
pageSize,
|
||||
total: totalTokens,
|
||||
showSizeChanger: true,
|
||||
pageSizeOptions: ['10', '20', '50', '100'],
|
||||
showTotal: (total) => `共 ${total} 条库存记录`,
|
||||
showTotal: (total) => `共 ${total} 条 Token 记录`,
|
||||
onChange: (p, ps) => {
|
||||
onPageChange(p, ps)
|
||||
},
|
||||
|
||||
@@ -13,6 +13,7 @@ export interface TokenColumnHandlers {
|
||||
onCopyVoucher: (record: RockTokenItem) => void
|
||||
onPreviewVoucher: (record: RockTokenItem) => void
|
||||
onEditUid: (state: UidEditState) => void
|
||||
onViewRawData: (record: RockTokenItem) => void
|
||||
onResetToken: (id: number) => void
|
||||
onReleaseToken: (id: number) => void
|
||||
onUpdateRemark: (id: number, remark: string, status?: string) => void
|
||||
@@ -24,6 +25,7 @@ export function buildTokenColumns({
|
||||
onCopyVoucher,
|
||||
onPreviewVoucher,
|
||||
onEditUid,
|
||||
onViewRawData,
|
||||
onResetToken,
|
||||
onReleaseToken,
|
||||
onUpdateRemark,
|
||||
@@ -31,7 +33,7 @@ export function buildTokenColumns({
|
||||
}: TokenColumnHandlers): ColumnsType<RockTokenItem> {
|
||||
return [
|
||||
{
|
||||
title: '0666 编号 / 提取码',
|
||||
title: '0666 编号 / 兑换码',
|
||||
dataIndex: 'upstream_token_id',
|
||||
width: 250,
|
||||
render: (_, record) => (
|
||||
@@ -53,6 +55,32 @@ export function buildTokenColumns({
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '包ID / 提取码',
|
||||
width: 230,
|
||||
render: (_, record) => (
|
||||
<Space direction="vertical" size={2}>
|
||||
<div style={{ fontSize: 12, color: '#334155' }}>
|
||||
<span style={{ color: '#94a3b8' }}>包ID: </span>
|
||||
{record.package_id ? `#${record.package_id}` : '-'}
|
||||
</div>
|
||||
<div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
|
||||
<span style={{ color: '#94a3b8', fontSize: 12 }}>提取码:</span>
|
||||
<Text
|
||||
copyable={record.token ? { text: record.token } : false}
|
||||
style={{ fontSize: 12, fontFamily: 'monospace', fontWeight: 600 }}
|
||||
>
|
||||
{record.token || '-'}
|
||||
</Text>
|
||||
</div>
|
||||
{record.source === 'package' ? (
|
||||
<Tag color="purple" style={{ margin: 0, width: 'fit-content', fontSize: 10 }}>
|
||||
打包导入
|
||||
</Tag>
|
||||
) : null}
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '规格与适用范围',
|
||||
dataIndex: 'color',
|
||||
@@ -338,6 +366,18 @@ export function buildTokenColumns({
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '原始数据',
|
||||
width: 100,
|
||||
render: (_, record) =>
|
||||
record.raw_data ? (
|
||||
<Button size="small" onClick={() => onViewRawData(record)}>
|
||||
查看
|
||||
</Button>
|
||||
) : (
|
||||
<Text type="secondary">-</Text>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
|
||||
Reference in New Issue
Block a user