增加异步日志和库存展示
This commit is contained in:
+94
-11
@@ -107,6 +107,7 @@ type PrizeInfo = {
|
||||
left_num?: number
|
||||
status?: number
|
||||
percent?: string
|
||||
is_show_num?: number
|
||||
img?: string
|
||||
}
|
||||
|
||||
@@ -208,9 +209,77 @@ function priceOf(row?: PrizeInfo): number {
|
||||
return row.new_score || row.score || 0
|
||||
}
|
||||
|
||||
function leftOf(row?: PrizeInfo): number {
|
||||
if (!row) return 0
|
||||
return row.left_num || 0
|
||||
type InventoryInfo = {
|
||||
left?: number
|
||||
total?: number
|
||||
percent?: string
|
||||
estimated: boolean
|
||||
}
|
||||
|
||||
function inventoryOf(row?: PrizeInfo): InventoryInfo {
|
||||
if (!row) return { estimated: false }
|
||||
const total = Number(row.num || 0)
|
||||
const percent = normalizePercent(row.percent)
|
||||
const rawLeft = Number(row.left_num || 0)
|
||||
if (rawLeft > 0 || percent === '0%') {
|
||||
return {
|
||||
left: rawLeft,
|
||||
total: total > 0 ? total : undefined,
|
||||
percent,
|
||||
estimated: false,
|
||||
}
|
||||
}
|
||||
if (total > 0 && percent) {
|
||||
const percentValue = Number(percent.replace('%', ''))
|
||||
if (Number.isFinite(percentValue)) {
|
||||
return {
|
||||
left: Math.floor((total * percentValue) / 100),
|
||||
total,
|
||||
percent,
|
||||
estimated: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
return {
|
||||
total: total > 0 ? total : undefined,
|
||||
percent,
|
||||
estimated: false,
|
||||
}
|
||||
}
|
||||
|
||||
function normalizePercent(value?: string): string | undefined {
|
||||
const text = String(value || '').trim()
|
||||
if (!text) return undefined
|
||||
const numeric = Number(text.replace('%', ''))
|
||||
if (!Number.isFinite(numeric)) return text
|
||||
return `${numeric}%`
|
||||
}
|
||||
|
||||
function inventoryText(row?: PrizeInfo): string {
|
||||
const info = inventoryOf(row)
|
||||
const left = info.left === undefined ? '-' : `${info.estimated ? '约 ' : ''}${info.left}`
|
||||
const total = info.total === undefined ? '-' : String(info.total)
|
||||
return `剩余 ${left} / 总量 ${total}${info.percent ? ` · ${info.percent}` : ''}`
|
||||
}
|
||||
|
||||
function summarizeInventory(items: PrizeInfo[]): string {
|
||||
let left = 0
|
||||
let total = 0
|
||||
let leftKnown = 0
|
||||
let totalKnown = 0
|
||||
items.forEach((item) => {
|
||||
const info = inventoryOf(item)
|
||||
if (info.left !== undefined) {
|
||||
left += info.left
|
||||
leftKnown += 1
|
||||
}
|
||||
if (info.total !== undefined) {
|
||||
total += info.total
|
||||
totalKnown += 1
|
||||
}
|
||||
})
|
||||
if (leftKnown === 0 && totalKnown === 0) return '库存未公开'
|
||||
return `剩余 ${leftKnown ? left : '-'} / 总量 ${totalKnown ? total : '-'}`
|
||||
}
|
||||
|
||||
function statusTag(status?: number, error?: string): ReactNode {
|
||||
@@ -550,6 +619,7 @@ export default function Dashboard() {
|
||||
String(priceOf(item)).includes(keyword),
|
||||
)
|
||||
}, [prizes, productSearch])
|
||||
const inventorySummary = useMemo(() => summarizeInventory(filteredPrizes), [filteredPrizes])
|
||||
|
||||
const config = () => ({
|
||||
...pickConfig(form.getFieldsValue(true)),
|
||||
@@ -911,10 +981,9 @@ export default function Dashboard() {
|
||||
|
||||
const prizeColumns: ColumnsType<PrizeInfo> = useMemo(
|
||||
() => [
|
||||
{ title: 'num', dataIndex: 'id', width: 92 },
|
||||
{ title: 'name', dataIndex: 'name', minWidth: 220, ellipsis: true },
|
||||
{ title: '剩余', width: 84, render: (_, row) => leftOf(row) || '-' },
|
||||
{ title: '总量', dataIndex: 'num', width: 84 },
|
||||
{ title: 'PID', dataIndex: 'id', width: 92 },
|
||||
{ title: '名称', dataIndex: 'name', minWidth: 220, ellipsis: true },
|
||||
{ title: '库存', minWidth: 190, render: (_, row) => inventoryText(row) },
|
||||
{ title: '价格', width: 84, render: (_, row) => priceOf(row) || '-' },
|
||||
],
|
||||
[],
|
||||
@@ -1065,6 +1134,7 @@ export default function Dashboard() {
|
||||
|
||||
<div className="inventory-line">
|
||||
<span>商品缓存 {filteredPrizes.length} 个</span>
|
||||
<span>{inventorySummary}</span>
|
||||
<span>{productListUpdatedAt ? `更新 ${productListUpdatedAt}` : '未缓存'}</span>
|
||||
</div>
|
||||
|
||||
@@ -1091,9 +1161,9 @@ export default function Dashboard() {
|
||||
</div>
|
||||
<div className="product-meta">
|
||||
<strong>{item.name}</strong>
|
||||
<div>
|
||||
<div className="product-tags">
|
||||
<Tag color="gold">{priceOf(item)} 积分</Tag>
|
||||
<Tag color="green">库存 {leftOf(item) || '-'}</Tag>
|
||||
<InventoryTags prize={item} compact />
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
@@ -1425,18 +1495,31 @@ export default function Dashboard() {
|
||||
)
|
||||
}
|
||||
|
||||
function InventoryTags({ prize, compact = false }: { prize?: PrizeInfo; compact?: boolean }) {
|
||||
const info = inventoryOf(prize)
|
||||
const left = info.left === undefined ? '-' : `${info.estimated ? '约 ' : ''}${info.left}`
|
||||
const total = info.total === undefined ? '-' : String(info.total)
|
||||
return (
|
||||
<span className={`inventory-tags${compact ? ' compact' : ''}`}>
|
||||
<Tag color={info.left === 0 ? 'red' : 'green'}>剩余 {left}</Tag>
|
||||
<Tag>总量 {total}</Tag>
|
||||
{info.percent ? <Tag color="blue">{info.percent}</Tag> : null}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
|
||||
function CurrentProduct({ prize }: { prize?: PrizeInfo }) {
|
||||
const name = prize?.name || '未从 CK 列表选择'
|
||||
return (
|
||||
<div className="current-product">
|
||||
<span>当前商品</span>
|
||||
<strong>{name}</strong>
|
||||
<div>
|
||||
<div className="current-product-tags">
|
||||
{prize ? (
|
||||
<>
|
||||
<Tag color="gold">{priceOf(prize)} 积分</Tag>
|
||||
<Tag color="green">库存 {leftOf(prize) || '-'}</Tag>
|
||||
<Tag>PID {prize.id}</Tag>
|
||||
<InventoryTags prize={prize} />
|
||||
</>
|
||||
) : (
|
||||
<Tag>CK 商品源</Tag>
|
||||
|
||||
@@ -73,8 +73,32 @@ body {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.current-product-tags,
|
||||
.product-tags,
|
||||
.inventory-tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.inventory-tags {
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.inventory-tags.compact {
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.inventory-tags .ant-tag,
|
||||
.product-tags .ant-tag,
|
||||
.current-product-tags .ant-tag {
|
||||
margin-inline-end: 0;
|
||||
}
|
||||
|
||||
.inventory-line {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 8px;
|
||||
|
||||
Reference in New Issue
Block a user