Files
hfb_jysh/frontend/src/features/admin/listingDisplay.ts
T

230 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { AdminListing } from '@/features/admin/types'
import { formatCoinM, formatMoney } from '@/shared/utils/format'
type JsonRecord = Record<string, unknown>
function recordOf(value: unknown): JsonRecord {
return typeof value === 'object' && value !== null ? (value as JsonRecord) : {}
}
function textOf(value: unknown) {
return value == null || value === '' ? '' : String(value)
}
function numberOf(value: unknown) {
const parsed = Number(value)
return Number.isFinite(parsed) ? parsed : 0
}
function compactNumber(value: number) {
const rounded = Math.round(value * 10) / 10
return Number.isInteger(rounded) ? String(rounded) : rounded.toFixed(1)
}
export function listingCodeText(row: AdminListing) {
return row.listing_no || `SP${String(row.id).padStart(6, '0')}`
}
export function listingCoinText(row: AdminListing) {
return formatCoinM(row.haf_coin_amount)
}
export function listingRegionText(row: AdminListing) {
const regions = row.asset_summary?.common_regions
if (Array.isArray(regions)) {
const names = regions.filter(
(region): region is string => typeof region === 'string' && Boolean(region.trim())
)
if (names.length > 0) return names.join('、')
}
return row.server_region || '—'
}
export function listingAssetText(row: AdminListing, key: string) {
const value = row.asset_summary?.[key]
if (value == null || value === '') return '—'
if (Array.isArray(value)) {
const names = value.filter(
(item): item is string => typeof item === 'string' && Boolean(item.trim())
)
return names.length > 0 ? names.join('、') : '—'
}
if (typeof value === 'object') return '—'
return String(value)
}
export function listingLevelText(row: AdminListing, key: string) {
const value = listingAssetText(row, key)
return value === '—' ? value : value.replace(/级$/, '')
}
export function listingQuantity(row: AdminListing, key: string) {
const resources = row.asset_summary?.resources
if (!Array.isArray(resources)) return 0
const match = resources.find(item => {
const resource = recordOf(item)
return textOf(resource.key) === key
})
return numberOf(recordOf(match).quantity)
}
export function listingResourceText(row: AdminListing, key: string) {
const quantity = listingQuantity(row, key)
return quantity > 0 ? compactNumber(quantity) : '—'
}
export function listingSkinGroupText(row: AdminListing, groupKey: string) {
const groups = recordOf(row.asset_summary?.skin_groups)
const names = groups[groupKey]
if (!Array.isArray(names)) return '—'
const values = names.filter(
(name): name is string => typeof name === 'string' && Boolean(name.trim())
)
return values.length > 0 ? values.join('、') : '—'
}
export function listingCharacterSkinText(row: AdminListing) {
const groups = [
{ label: '红皮', key: 'operatorRed' },
{ label: '金皮', key: 'operatorGold' },
{ label: '武器', key: 'weapon' },
]
const parts = groups
.map(group => {
const names = listingSkinGroupText(row, group.key)
return names === '—' ? '' : `${group.label}:${names}`
})
.filter(Boolean)
return parts.length > 0 ? parts.join(' / ') : '—'
}
export function listingAllHeroText(row: AdminListing) {
const value =
listingAssetText(row, 'all_hero') !== '—'
? listingAssetText(row, 'all_hero')
: listingAssetText(row, 'all_heroes')
return value === '是' || value === '否' ? value : '—'
}
export function listingOwnerRemark(row: AdminListing) {
return row.description?.trim() || '—'
}
export function listingPureCoinPrice(row: AdminListing) {
const summary = row.asset_summary ?? {}
const breakdown = recordOf(summary.price_breakdown)
const buyerCent = numberOf(breakdown.buyer_coin_base_price_cent)
if (buyerCent > 0) return Math.round(buyerCent)
const buyerYuan = numberOf(breakdown.buyer_coin_base_price)
if (buyerYuan > 0) return Math.round(buyerYuan * 100)
const consumableCent =
numberOf(breakdown.consumable_price_cent) || numberOf(breakdown.consumable_price) * 100
return Math.max(0, Math.round(row.price_cent - consumableCent))
}
export function listingPureCoinPriceText(row: AdminListing) {
const cents = listingPureCoinPrice(row)
return cents > 0 ? formatMoney(cents) : '—'
}
export function listingRentalDuration(row: AdminListing) {
const coinM = numberOf(row.haf_coin_amount) / 1_000_000
const dailyLoss = numberOf(row.asset_summary?.daily_loss_m)
if (coinM <= 0 || dailyLoss <= 0) {
return { days: '—', dailyLoss: '' }
}
return {
days: `${Math.max(1, Math.ceil(coinM / dailyLoss))}天`,
dailyLoss: `日耗 ${compactNumber(dailyLoss)}M`,
}
}
export function listingRentDepositText(row: AdminListing) {
return `${formatMoney(row.price_cent)} / ${formatMoney(row.deposit_amount_cent)}`
}
export function listingRatioText(row: AdminListing) {
const ratio = numberOf(row.asset_summary?.publish_ratio)
if (ratio <= 0) return '—'
return `1:${Math.round(ratio)}`
}
export function listingAccountLevel(row: AdminListing) {
const fireLevel = listingAssetText(row, 'fire_level')
if (fireLevel !== '—') return fireLevel
return row.rank_level || '—'
}
function breakdownCent(row: AdminListing, key: string) {
const breakdown = recordOf(row.asset_summary?.price_breakdown)
const cent = numberOf(breakdown[`${key}_cent`])
if (cent > 0) return Math.round(cent)
const yuan = numberOf(breakdown[key])
return yuan > 0 ? Math.round(yuan * 100) : 0
}
export function listingConsumablePriceCent(row: AdminListing) {
return breakdownCent(row, 'consumable_price')
}
export function listingSellerCoinBasePriceCent(row: AdminListing) {
const stored = breakdownCent(row, 'seller_coin_base_price')
if (stored > 0) return stored
const fallback = row.price_cent - listingConsumablePriceCent(row)
return Math.max(0, Math.round(fallback))
}
export function listingSellerTotalPriceCent(row: AdminListing) {
const stored = breakdownCent(row, 'seller_total_price')
if (stored > 0) return stored
return listingSellerCoinBasePriceCent(row) + listingConsumablePriceCent(row)
}
export function listingBuyerCoinBasePriceCent(row: AdminListing) {
return listingPureCoinPrice(row)
}
export function listingBuyerTotalPriceCent(row: AdminListing) {
const stored = breakdownCent(row, 'buyer_total_price')
if (stored > 0) return stored
return listingBuyerCoinBasePriceCent(row) + listingConsumablePriceCent(row)
}
export function listingPlatformMarkupCent(row: AdminListing) {
const stored = breakdownCent(row, 'platform_markup_amount')
if (stored > 0) return stored
return Math.max(0, listingBuyerCoinBasePriceCent(row) - listingSellerCoinBasePriceCent(row))
}
export function listingSellerRatio(row: AdminListing) {
const breakdown = recordOf(row.asset_summary?.price_breakdown)
const stored = numberOf(breakdown.seller_ratio) || numberOf(breakdown.seller_reference_ratio)
if (stored > 0) return stored
const baseYuan = listingSellerCoinBasePriceCent(row) / 100
const coinWan = numberOf(row.haf_coin_amount) / 10000
return baseYuan > 0 ? coinWan / baseYuan : 0
}
export function listingBuyerRatio(row: AdminListing) {
const breakdown = recordOf(row.asset_summary?.price_breakdown)
const stored = numberOf(breakdown.buyer_ratio)
if (stored > 0) return stored
const baseYuan = listingBuyerCoinBasePriceCent(row) / 100
const coinWan = numberOf(row.haf_coin_amount) / 10000
return baseYuan > 0 ? coinWan / baseYuan : 0
}
export function listingSkinNames(row: AdminListing): string[] {
const groups = recordOf(row.asset_summary?.skin_groups)
const names: string[] = []
Object.values(groups).forEach(value => {
if (!Array.isArray(value)) return
value.forEach(name => {
if (typeof name === 'string' && name.trim()) names.push(name.trim())
})
})
return Array.from(new Set(names))
}