import type { Listing } from "@/api/listings"; export interface ListingDisplayChip { label: string; value: string; } export interface ListingDisplayResource { key: string; label: string; quantity: number; mode: string; } export function getCoinWan(item: Listing) { return Math.round(Number(item.haf_coin_amount || 0) / 10000); } export function getCoinM(item: Listing) { return getCoinWan(item) / 100; } export function formatHafCoinM(amountWan: number) { const amountM = amountWan / 100; const rounded = Math.round(amountM * 10) / 10; return `${Number.isInteger(rounded) ? rounded : rounded.toFixed(1)}M`; } export function getListingDisplayPrice(item: Listing) { return Number(item.price_daily || item.price_hourly || 0); } export function getRatioValue(item: Listing) { const ratio = readAssetNumber(item, "publish_ratio"); if (ratio > 0) return ratio; const price = getListingDisplayPrice(item); if (price <= 0) return 0; return getCoinWan(item) / price; } export function formatRatio(item: Listing) { const ratio = getRatioValue(item); return ratio > 0 ? `1:${formatRatioNumber(ratio)}` : "--"; } export function getValuePerYuanText(item: Listing) { return formatRatio(item); } export function getLoginMethod(item: Listing) { return item.login_platform.trim(); } export function getServerRegion(item: Listing) { return item.server_region.trim(); } export function getListingTitle(item: Listing) { const parts = [ `纯币${formatHafCoinM(getCoinWan(item))}`, formatInsuranceSlotText(readAssetString(item, "season_insurance")), formatLevelShort(readAssetString(item, "stamina_level"), "体"), formatLevelShort(readAssetString(item, "load_level"), "负"), formatResourceShort(item, "armor6", "六甲"), formatResourceShort(item, "helmet6", "六头"), ...getSkinNames(item).slice(0, 4), ].filter(Boolean); return parts.join("/"); } export function getListingSubtitle(item: Listing) { return getValuePerYuanText(item); } export function getListingChips(item: Listing): ListingDisplayChip[] { const totalAsset = readAssetNumber(item, "total_asset_wan"); const chips: ListingDisplayChip[] = [ { label: "哈夫币", value: formatHafCoinM(getCoinWan(item)) }, { label: "保险格数", value: readAssetString(item, "season_insurance") }, { label: "体力", value: readAssetString(item, "stamina_level") }, { label: "负重", value: readAssetString(item, "load_level") }, { label: "段位", value: item.rank_level }, ]; const awmAmmo = getResourceQuantity(item, "awmAmmo"); if (awmAmmo > 0) { chips.push({ label: "AWM", value: `${awmAmmo}发` }); } if (totalAsset > 0) { chips.push({ label: "总资产", value: formatHafCoinM(totalAsset) }); } const online = getOnlineTimeText(item); if (online) { chips.push({ label: "方便上号", value: online }); } return chips.filter((chip) => chip.value); } export function getListingResources(item: Listing): ListingDisplayResource[] { const resources = item.asset_summary?.resources; if (!Array.isArray(resources)) return []; return resources .map((resource) => { if (typeof resource !== "object" || resource === null) return null; const row = resource as Record; return { key: typeof row.key === "string" ? row.key : "", label: typeof row.label === "string" ? row.label : "", quantity: readUnknownNumber(row.quantity), mode: typeof row.mode === "string" ? row.mode : "", }; }) .filter((resource): resource is ListingDisplayResource => { return Boolean(resource?.key && resource.label && resource.quantity > 0); }); } export function getResourceQuantity(item: Listing, resourceKey: string) { return getListingResources(item).find((resource) => resource.key === resourceKey)?.quantity || 0; } export function hasGiftResources(item: Listing) { return getListingResources(item).some((resource) => resource.mode === "赠送"); } export function getSkinGroup(item: Listing, groupKey: string) { const skinGroups = item.asset_summary?.skin_groups; if ( typeof skinGroups !== "object" || skinGroups === null || !Array.isArray((skinGroups as Record)[groupKey]) ) { return []; } return ((skinGroups as Record)[groupKey] as unknown[]).filter( (skin): skin is string => typeof skin === "string" ); } export function getSkinNames(item: Listing) { const skinGroups = item.asset_summary?.skin_groups; if (typeof skinGroups !== "object" || skinGroups === null) return []; return Object.values(skinGroups as Record) .flatMap((group) => (Array.isArray(group) ? group : [])) .filter((skin): skin is string => typeof skin === "string"); } export function assetRegions(item: Listing) { const regions = item.asset_summary?.common_regions; return Array.isArray(regions) ? regions.filter((region): region is string => typeof region === "string") : []; } export function getOnlineTimeText(item: Listing) { const onlineTime = item.asset_summary?.online_time; if (typeof onlineTime !== "object" || onlineTime === null) return ""; const start = (onlineTime as Record).start; const end = (onlineTime as Record).end; if (typeof start !== "string" || typeof end !== "string" || !start || !end) return ""; return `${start.replace(":00", "")}-${end.replace(":00", "")}点`; } export function getDailyLoss(item: Listing) { const coinWan = getCoinWan(item); if (coinWan >= 30000) return "30M"; if (coinWan >= 10000) return "20M"; return "10M"; } export function readAssetString(item: Listing, key: string) { const value = item.asset_summary?.[key]; return typeof value === "string" ? value : ""; } export function readAssetNumber(item: Listing, key: string) { return readUnknownNumber(item.asset_summary?.[key]); } function readUnknownNumber(value: unknown) { if (typeof value === "number") return value; if (typeof value === "string" && value.trim()) { const parsed = Number(value); return Number.isFinite(parsed) ? parsed : 0; } return 0; } function formatInsuranceSlotText(value: string) { const parts = value.split("*").map((item) => Number(item)); const rows = parts[0] || 0; const cols = parts[1] || 0; if (!Number.isFinite(rows) || !Number.isFinite(cols) || rows <= 0 || cols <= 0) { return value; } return `${rows * cols}格`; } function formatLevelShort(value: string, suffix: string) { const level = value.match(/\d+/)?.[0]; return level ? `${level}${suffix}` : value; } function formatResourceShort(item: Listing, key: string, label: string) { const quantity = getResourceQuantity(item, key); return quantity > 0 ? `${quantity}${label}` : ""; } function roundMoney(value: number) { return Math.round(value * 100) / 100; } function formatRatioNumber(value: number) { const rounded = roundMoney(value); return Number.isInteger(rounded) ? `${rounded}` : rounded.toFixed(2); }