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) { const assetCoin = readAssetNumber(item, "haf_coin_wan"); if (assetCoin > 0) return assetCoin; 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) { const assetPrice = readAssetNumber(item, "price"); if (assetPrice > 0) return assetPrice; const rentDays = getRentDays(item); if (rentDays > 0 && item.price_daily > 0) { return roundMoney(item.price_daily * rentDays); } return Number(item.price_daily || item.price_hourly || 0); } export function getRentDays(item: Listing) { const assetDays = readAssetNumber(item, "rent_days"); if (assetDays > 0) return assetDays; if (item.max_rent_hours >= 24) return Math.max(Math.round(item.max_rent_hours / 24), 1); return 1; } export function getRatioValue(item: Listing) { const ratio = readAssetNumber(item, "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:${Math.round(ratio)}` : "--"; } export function getValuePerYuanText(item: Listing) { const price = getListingDisplayPrice(item); if (price <= 0) return ""; return `1元=${Math.round(getCoinWan(item) / price)}w哈夫币`; } export function getLoginMethod(item: Listing) { return normalizeLoginMethod(readAssetString(item, "login_method") || item.login_platform); } export function getServerRegion(item: Listing) { return normalizeServerRegion(item.server_region); } 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), `租期${getRentDays(item)}天`] .filter(Boolean) .join(" "); } 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) { const keys = getResourceKeyAliases(resourceKey); return getListingResources(item).find((resource) => keys.includes(resource.key))?.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 Object.values(skinGroups as Record) .flatMap((group) => (Array.isArray(group) ? group : [])) .filter((skin): skin is string => typeof skin === "string"); } const skins = item.asset_summary?.skins; return Array.isArray(skins) ? skins.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 getScreenshotMap(item: Listing) { const screenshots = item.asset_summary?.screenshots; return typeof screenshots === "object" && screenshots !== null ? (screenshots as Record) : {}; } 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 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; } export function normalizeServerRegion(value: string) { const text = value.trim(); const upper = text.toUpperCase(); if (upper.includes("STEAM")) return "Steam"; if (upper.includes("QQ")) return "QQ"; if (text.includes("微信")) return "微信"; return text; } export function normalizeLoginMethod(value: string) { const text = value.trim(); if (!text) return ""; if (text.includes("扫码")) return "扫码"; if (text.includes("账密") || text.includes("账号密码")) return "账号密码"; return text; } 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 getResourceKeyAliases(key: string) { if (key === "level6Ammo") return ["level6Ammo", "armor6Ammo"]; if (key === "armor6Ammo") return ["armor6Ammo", "level6Ammo"]; return [key]; } function roundMoney(value: number) { return Math.round(value * 100) / 100; }