Files
affiliate_dash/frontend/src/pages/platformRock/PurchaseTokenModal.tsx
T

226 lines
7.3 KiB
TypeScript

import { Button, Col, Divider, InputNumber, Modal, Row, Space } from 'antd'
import { ShoppingCartOutlined } from '@ant-design/icons'
import type { RockStats } from '../../api'
export interface PurchaseTokenModalProps {
open: boolean
stats: RockStats | null
purchaseCount: number
purchasing: boolean
maxPurchasable: number
upstreamPoints: number
estimatedCost: number
isPointsInsufficient: boolean
tokenCost: number
upstreamStock: number | null
onPurchaseCountChange: (value: number) => void
onCancel: () => void
onConfirm: (count: number) => void
}
export function PurchaseTokenModal({
open,
stats,
purchaseCount,
purchasing,
maxPurchasable,
upstreamPoints,
estimatedCost,
isPointsInsufficient,
tokenCost,
upstreamStock,
onPurchaseCountChange,
onCancel,
onConfirm,
}: PurchaseTokenModalProps) {
const isStockInsufficient = upstreamStock !== null && purchaseCount > upstreamStock
return (
<Modal
title={
<Space>
<ShoppingCartOutlined style={{ color: '#2563eb' }} />
<span>手动采购通用黑色 Token (0666.pro)</span>
</Space>
}
open={open}
onCancel={onCancel}
footer={[
<Button key="cancel" onClick={onCancel}>
取消
</Button>,
<Button
key="submit"
type="primary"
icon={<ShoppingCartOutlined />}
loading={purchasing}
disabled={isPointsInsufficient || isStockInsufficient || purchaseCount <= 0}
onClick={() => onConfirm(purchaseCount)}
style={{
background:
isPointsInsufficient || isStockInsufficient
? undefined
: 'linear-gradient(135deg, #2563eb 0%, #1d4ed8 100%)',
}}
>
确认采购 {purchaseCount} 个 ({estimatedCost.toFixed(2)} 点)
</Button>,
]}
destroyOnClose
width={500}
>
<div style={{ marginTop: 16 }}>
{/* 账户与资产概览 */}
<div
style={{
background: '#f8fafc',
border: '1px solid #e2e8f0',
borderRadius: 8,
padding: '12px 16px',
marginBottom: 18,
}}
>
<Row gutter={16}>
<Col span={12}>
<div style={{ fontSize: 12, color: '#64748b' }}>0666.pro 采购商户</div>
<div style={{ fontSize: 15, fontWeight: 600, color: '#0f172a', marginTop: 2 }}>
{stats?.username || '正常商户'}
</div>
</Col>
<Col span={12}>
<div style={{ fontSize: 12, color: '#64748b' }}>当前实时积分余额</div>
<div style={{ fontSize: 15, fontWeight: 700, color: '#7c3aed', marginTop: 2 }}>
{upstreamPoints.toFixed(1)} 点
</div>
</Col>
</Row>
<Divider style={{ margin: '10px 0' }} />
<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' }}>
采购数量:
</div>
<InputNumber
min={1}
max={maxPurchasable > 0 ? maxPurchasable : 10000}
value={purchaseCount}
onChange={(val) => onPurchaseCountChange(val || 1)}
style={{ width: '100%' }}
addonAfter="个 Token"
size="large"
/>
</div>
{/* 快捷数量选择胶囊 */}
<div style={{ marginBottom: 18 }}>
<div style={{ fontSize: 12, color: '#64748b', marginBottom: 8 }}>快捷选择:</div>
<Space wrap size={[8, 8]}>
{[10, 20, 50, 100].map((num) => (
<Button
key={num}
size="small"
type={purchaseCount === num ? 'primary' : 'default'}
onClick={() => onPurchaseCountChange(num)}
disabled={maxPurchasable > 0 && num > maxPurchasable}
>
{num} 个
</Button>
))}
{maxPurchasable > 0 && (
<Button
size="small"
type={purchaseCount === maxPurchasable ? 'primary' : 'dashed'}
onClick={() => onPurchaseCountChange(maxPurchasable)}
style={{ color: '#2563eb', borderColor: '#bfdbfe' }}
>
全部可购 ({maxPurchasable} 个)
</Button>
)}
</Space>
</div>
{/* 实时消耗与结余试算 */}
<div
style={{
background: isPointsInsufficient ? '#fef2f2' : '#f0fdf4',
border: `1px solid ${isPointsInsufficient ? '#fecaca' : '#bbf7d0'}`,
borderRadius: 8,
padding: '12px 16px',
fontSize: 13,
}}
>
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
<span style={{ color: isPointsInsufficient ? '#991b1b' : '#166534' }}>
本次采购消耗积分:
</span>
<strong style={{ color: isPointsInsufficient ? '#dc2626' : '#16a34a', fontSize: 16 }}>
{estimatedCost.toFixed(2)} 点
</strong>
</div>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 6,
fontSize: 12,
}}
>
<span style={{ color: isPointsInsufficient ? '#b91c1c' : '#15803d' }}>
{isPointsInsufficient
? '⚠️ 账户积分不足,请先充值或减少采购数量'
: '采购后预计剩余积分:'}
</span>
{!isPointsInsufficient && (
<span style={{ color: '#64748b' }}>
{(upstreamPoints - estimatedCost).toFixed(1)} 点
</span>
)}
</div>
</div>
</div>
</Modal>
)
}