diff --git a/frontend/src/features/listings/components/ListingCard.vue b/frontend/src/features/listings/components/ListingCard.vue index 435a45e..84cd862 100644 --- a/frontend/src/features/listings/components/ListingCard.vue +++ b/frontend/src/features/listings/components/ListingCard.vue @@ -21,6 +21,7 @@ import { getCoinWan, formatHafCoinM, readAssetString, + isTopTierInsurance, type ListingDisplayResource, } from '@/shared/utils/listingDisplay' @@ -194,9 +195,15 @@ function formatStatNumber(value: number) { 哈夫币 {{ hafCoinDisplay }} -
+
保险格数 - {{ insuranceSlot }} + {{ insuranceSlot }}
折算币比 @@ -577,6 +584,11 @@ function formatStatNumber(value: number) { border-color: #e2e8f0; } +.slot-card.top-insurance { + background: #fff9f5; + border-color: #fed7aa; +} + .ratio-card { background: #f8fafc; border-color: #e2e8f0; diff --git a/frontend/src/features/listings/views/ListingDetailView.css b/frontend/src/features/listings/views/ListingDetailView.css index 274523e..467806d 100644 --- a/frontend/src/features/listings/views/ListingDetailView.css +++ b/frontend/src/features/listings/views/ListingDetailView.css @@ -307,6 +307,11 @@ border-color: #fed7aa; } +.slot-tile.top-insurance { + background: #fff9f5; + border-color: #fed7aa; +} + .tile-label { font-size: 12px; font-weight: 500; diff --git a/frontend/src/features/listings/views/ListingDetailView.vue b/frontend/src/features/listings/views/ListingDetailView.vue index 9b1f2fc..dfdb9ee 100644 --- a/frontend/src/features/listings/views/ListingDetailView.vue +++ b/frontend/src/features/listings/views/ListingDetailView.vue @@ -39,6 +39,7 @@ import { readAssetNumber, readAssetString, getRatioValue, + isTopTierInsurance, type ListingDisplayResource, } from '@/shared/utils/listingDisplay' @@ -112,6 +113,8 @@ const insuranceSlot = computed(() => { return readAssetString(listing.value, 'season_insurance') || '--' }) +const isTopTierInsuranceSlot = computed(() => isTopTierInsurance(insuranceSlot.value)) + const ratioValue = computed(() => { if (!listing.value) return 0 return getRatioValue(listing.value) @@ -479,9 +482,15 @@ function goBack() {
-
+
安全箱格数 - {{ insuranceSlot }} + {{ insuranceSlot }}
diff --git a/frontend/src/features/listings/views/MobileHomeView.css b/frontend/src/features/listings/views/MobileHomeView.css index 505b2c7..33e1df3 100644 --- a/frontend/src/features/listings/views/MobileHomeView.css +++ b/frontend/src/features/listings/views/MobileHomeView.css @@ -589,149 +589,57 @@ } .mobile-card { - display: grid; - grid-template-columns: 104px minmax(0, 1fr); - column-gap: 8px; - row-gap: 8px; - overflow: hidden; - padding: 8px; - border: 1px solid #e2e8f0; - border-radius: 8px; - background: #fff; - color: inherit; - text-decoration: none; -} - -.card-cover { - position: relative; - display: grid; - width: 104px; - height: 104px; - place-items: center; - overflow: hidden; - border-radius: 8px; - background: #e2e8f0; - color: #64748b; - font-weight: 800; -} - -.card-cover img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.card-cover-labels { - position: absolute; - left: 6px; - top: 6px; display: flex; - gap: 6px; -} - -.card-cover-labels em { - padding: 2px 6px; - border-radius: 999px; - background: rgba(255, 106, 0, 0.92); - color: #fff; - font-size: 10px; - font-style: normal; - font-weight: 800; -} - -.card-main { - display: flex; - min-width: 0; flex-direction: column; - padding: 0; + gap: 8px; + padding: 10px 12px; + border-radius: 12px; + background: #ffffff; + border: 1px solid #e8edf5; + box-shadow: 0 2px 8px rgba(15, 23, 42, 0.03); + text-decoration: none; + color: inherit; + transition: transform 0.15s ease, border-color 0.15s ease, box-shadow 0.15s ease; } -.card-title-row h2 { - margin: 0; - display: -webkit-box; - min-height: 35px; - overflow: hidden; - color: #0f172a; - font-size: 13px; - line-height: 1.34; - -webkit-box-orient: vertical; - -webkit-line-clamp: 2; +.mobile-card:active { + transform: scale(0.99); + border-color: #fed7aa; } -.card-meta-row { +/* 顶部精简元信息栏 */ +.mobile-card-header { display: flex; - flex-wrap: wrap; - gap: 4px; - margin-top: 3px; -} - -.mobile-listing-no, -.rental-meta-badge { - display: inline-flex; - width: fit-content; align-items: center; - min-height: 17px; - padding: 1px 6px; - border-radius: 999px; - background: #f5f7fa; - color: #64748b; - font-size: 10px; - font-weight: 800; - line-height: 1.2; + justify-content: space-between; + gap: 8px; } -.rental-meta-badge { - gap: 2px; - border: none; - background: #f5f7fa; - color: #64748b; - white-space: nowrap; -} - -.meta-separator { - color: #cbd5e1; -} - -.card-subtitle { - width: fit-content; - margin: 0; - padding: 1px 6px; - border-radius: 999px; - background: #fff7ed; - color: #ea580c; - font-size: 10px; - font-weight: 900; - line-height: 1.35; -} - -.card-action-row { +.header-tags-left { display: flex; - flex-wrap: wrap; align-items: center; - gap: 4px; - margin-top: 4px; -} - -.card-badges-row, -.card-chip-row { - display: flex; + gap: 5px; flex-wrap: wrap; - gap: 4px; } -.server-badge, -.card-chip-row span { +.code-badge { + padding: 1px 6px; + border-radius: 4px; + background: #eff6ff; + color: #2563eb; + font-size: 10px; + font-weight: 700; + letter-spacing: 0.2px; +} + +.server-badge { display: inline-flex; align-items: center; gap: 3px; - padding: 1px 5px; - border-radius: 999px; - border: 1px solid transparent; - background: #f1f5f9; - color: #475569; + padding: 1px 6px; + border-radius: 4px; font-size: 10px; - font-weight: 700; - line-height: 1.35; + font-weight: 600; } .server-badge.tone-wechat { @@ -741,7 +649,7 @@ .server-badge.tone-qq { background: #eff6ff; - color: #1683ff; + color: #0284c7; } .server-badge.tone-scan { @@ -759,80 +667,345 @@ color: #4f46e5; } -.card-chip-row span.tone-coin { - border-color: #fed7aa; - background: #fff7ed; - color: #c2410c; +.server-badge.tone-default { + background: #f1f5f9; + color: #475569; } -.card-chip-row span.tone-insurance { - border-color: #fed7aa; - background: #fff7ed; - color: #c2410c; +.header-tags-right { + display: flex; + align-items: center; + gap: 4px; + flex-shrink: 0; } -.card-chip-row span.tone-stamina { - border-color: #fed7aa; - background: #fff7ed; - color: #c2410c; +.aux-pill { + padding: 1px 6px; + border-radius: 4px; + font-size: 10px; + font-weight: 700; + white-space: nowrap; } -.card-chip-row span.tone-load { - border-color: #fed7aa; - background: #fff7ed; - color: #c2410c; +.aux-pill.rental { + background: #f0fdf4; + color: #15803d; + border: 1px solid #dcfce7; } -.card-chip-row span.tone-rank { - border-color: #fed7aa; - background: #fff7ed; - color: #c2410c; +.aux-pill.loss { + background: #fffbeb; + color: #b45309; + border: 1px solid #fef3c7; } -.card-chip-row span.tone-weapon { - border-color: #fed7aa; - background: #fff7ed; - color: #c2410c; +/* 中部:封面 + 信息流 */ +.mobile-card-body { + display: grid; + grid-template-columns: 100px minmax(0, 1fr); + gap: 10px; + align-items: center; } -.card-chip-row span.tone-asset { - border-color: #fed7aa; - background: #fff7ed; - color: #c2410c; +/* 左侧封面 */ +.mobile-card-media { + position: relative; + width: 100px; + height: 90px; + border-radius: 8px; + overflow: hidden; + background: #f1f5f9; + flex-shrink: 0; } -.card-chip-row span.tone-time { - border-color: #fed7aa; - background: #fff7ed; - color: #c2410c; +.mobile-card-media img { + width: 100%; + height: 100%; + object-fit: cover; + display: block; } -.card-footer { - margin-top: 5px; +.media-empty { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + font-size: 16px; + font-weight: 800; + color: #cbd5e1; } -.price-col { +.media-badges { + position: absolute; + top: 4px; + left: 4px; + display: flex; + gap: 3px; + z-index: 2; +} + +.media-badges .badge { + padding: 1px 4px; + border-radius: 4px; + font-size: 9px; + font-weight: 700; + color: #ffffff; +} + +.media-badges .badge.sale { + background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); +} + +.media-badges .badge.gift { + background: linear-gradient(135deg, #10b981 0%, #059669 100%); +} + +.media-ratio { + position: absolute; + bottom: 0; + left: 0; + right: 0; + padding: 2px 4px; + background: linear-gradient(to top, rgba(15, 23, 42, 0.88), rgba(15, 23, 42, 0.4)); + color: #fcd34d; + font-size: 10px; + font-weight: 700; + text-align: center; + backdrop-filter: blur(2px); + z-index: 2; +} + +/* 右侧信息流 */ +.mobile-card-info { + display: flex; + flex-direction: column; + gap: 5px; + min-width: 0; +} + +/* 核心资产微卡 */ +.card-core-assets { + display: flex; + align-items: center; + gap: 4px; + flex-wrap: wrap; +} + +.core-asset-card { display: inline-flex; - width: fit-content; - max-width: 100%; - align-items: baseline; - gap: 6px; + align-items: center; + gap: 3px; + padding: 2px 6px; + border-radius: 6px; + background: #f8fafc; + border: 1px solid #e2e8f0; } -.price-col strong { - color: #ef4444; - font-size: 20px; +.core-asset-card.coin-card { + background: #fff9f5; + border-color: #fed7aa; +} + +.core-asset-card.slot-card.top-insurance { + background: #fff9f5; + border-color: #fed7aa; +} + +.c-label { + font-size: 10px; + color: #64748b; + font-weight: 500; +} + +.c-val { + font-size: 12px; + font-weight: 800; + line-height: 1; +} + +.c-val.orange { + color: #ea580c; +} + +.c-val.slate { + color: #0f172a; +} + +.c-val.amber { + color: #b45309; +} + +/* 高光皮肤与物资 */ +.card-highlights { + display: flex; + align-items: center; + gap: 4px; + flex-wrap: wrap; +} + +.mobile-skin-pill { + display: inline-flex; + align-items: center; + gap: 3px; + padding: 1px 5px; + border-radius: 4px; + font-size: 10px; + font-weight: 600; + background: #f8fafc; + color: #334155; + border: 1px solid #e2e8f0; + white-space: nowrap; + max-width: 120px; + overflow: hidden; + text-overflow: ellipsis; +} + +.mobile-skin-pill .quality-dot { + width: 5px; + height: 5px; + border-radius: 999px; +} + +.mobile-skin-pill.skin-type-melee { + background: #faf5ff; + color: #7e22ce; + border-color: #f3e8ff; +} +.mobile-skin-pill.skin-type-melee .quality-dot { + background: #a855f7; +} + +.mobile-skin-pill.skin-type-operatorGold, +.mobile-skin-pill.skin-type-weaponGold { + background: #fffbeb; + color: #b45309; + border-color: #fef3c7; +} +.mobile-skin-pill.skin-type-operatorGold .quality-dot, +.mobile-skin-pill.skin-type-weaponGold .quality-dot { + background: #f59e0b; +} + +.mobile-skin-pill.skin-type-operatorRed { + background: #fff1f2; + color: #be123c; + border-color: #ffe4e6; +} +.mobile-skin-pill.skin-type-operatorRed .quality-dot { + background: #f43f5e; +} + +.mobile-res-pill { + display: inline-flex; + align-items: center; + gap: 3px; + padding: 1px 5px; + border-radius: 4px; + background: #f8fafc; + border: 1px solid #e2e8f0; + font-size: 10px; + color: #334155; +} + +.mobile-res-pill .r-label { + color: #64748b; +} + +.mobile-res-pill .r-qty { + color: #ea580c; + font-weight: 700; +} + +.mobile-res-pill .r-gift { + padding: 0 3px; + border-radius: 2px; + background: #10b981; + color: #ffffff; + font-size: 9px; + font-style: normal; + font-weight: 700; +} + +/* 价格与租用行动区 */ +.card-price-action { + display: flex; + align-items: center; + justify-content: space-between; + gap: 6px; + margin-top: 2px; +} + +.price-wrap { + display: flex; + align-items: baseline; + gap: 5px; +} + +.price-main { + display: flex; + align-items: baseline; + color: #ff5500; +} + +.price-main small { + font-size: 12px; + font-weight: 700; +} + +.price-main strong { + font-size: 18px; font-weight: 900; line-height: 1; } -.rent-sub { - padding: 1px 6px; - border-radius: 999px; - background: #fff7ed; - color: #9a3412; +.deposit-text { font-size: 10px; + color: #94a3b8; + font-weight: 500; +} + +.rent-cta-btn { + display: inline-flex; + align-items: center; + gap: 2px; + height: 24px; + padding: 0 9px; + border-radius: 999px; + background: linear-gradient(135deg, #ff6a00 0%, #ff8533 100%); + color: #ffffff; + font-size: 11px; font-weight: 700; + box-shadow: 0 2px 6px rgba(255, 106, 0, 0.25); +} + +/* 底部次要规格流 */ +.card-specs-row { + display: flex; + align-items: center; + gap: 4px; + flex-wrap: wrap; + padding-top: 6px; + border-top: 1px dashed #f1f5f9; +} + +.spec-chip { + display: inline-flex; + align-items: center; + gap: 3px; + padding: 1px 5px; + border-radius: 4px; + background: #f8fafc; + border: 1px solid #edf2f7; + font-size: 10px; +} + +.spec-chip .k { + color: #94a3b8; +} + +.spec-chip .v { + color: #334155; + font-weight: 600; } .card-chip-row { diff --git a/frontend/src/features/listings/views/MobileHomeView.vue b/frontend/src/features/listings/views/MobileHomeView.vue index 9c1bbb9..25c9412 100644 --- a/frontend/src/features/listings/views/MobileHomeView.vue +++ b/frontend/src/features/listings/views/MobileHomeView.vue @@ -53,6 +53,12 @@ import { assetRegions, hasAcceleratedSaleRatio, hasGiftResources, + isTopTierInsurance, + getCoinWan, + formatHafCoinM, + readAssetString, + getListingSkinGroups, + getListingResources, } from '@/shared/utils/listingDisplay' import { buildMobileHomeListSignature, useMobileHomeCacheStore } from '@/stores/mobileHomeCache' import { useSessionStore } from '@/stores/session' @@ -775,16 +781,41 @@ function getMobileRatioText(item: Listing) { return `1元=${formatted}w哈夫币` } -function chipTone(label: string) { - if (label.includes('哈夫币')) return 'coin' - if (label.includes('保险')) return 'insurance' - if (label.includes('体力')) return 'stamina' - if (label.includes('负重')) return 'load' - if (label.includes('段位')) return 'rank' - if (label.includes('AWM')) return 'weapon' - if (label.includes('总资产')) return 'asset' - if (label.includes('方便')) return 'time' - return 'default' +function getItemHafCoin(item: Listing) { + return formatHafCoinM(getCoinWan(item)) +} + +function getItemInsurance(item: Listing) { + return readAssetString(item, 'season_insurance') || '--' +} + +function getItemRatioText(item: Listing) { + const val = getRatioValue(item) + if (val <= 0) return null + return `1元=${val.toFixed(0)}w` +} + +function getItemSecondaryChips(item: Listing) { + return getListingChips(item).filter( + c => c.label !== '哈夫币' && c.label !== '保险格数' && c.label !== 'AWM' + ) +} + +function getItemSkinPreview(item: Listing) { + const groups = getListingSkinGroups(item) + const list: Array<{ name: string; type: string }> = [] + for (const group of groups) { + for (const opt of group.options) { + list.push({ name: opt, type: group.key }) + if (list.length >= 3) return list + } + } + return list +} + +function getItemResourcePreview(item: Listing) { + const resources = getListingResources(item) + return resources.slice(0, 2) } syncMobileHomeQuery() @@ -979,7 +1010,7 @@ syncMobileHomeQuery() 重置条件 - +
-
- - 图 -
- 有赠送 - 特惠 -
-
-
-
-

{{ getListingTitle(item) }}

-
-
-
{{ formatListingCode(item) }}
-
+
+
+ {{ formatListingCode(item) }} + - 消耗 {{ getDailyLoss(item) }}/天 - - · - - - 租期 {{ formatEstimatedRentalDuration(item) }} - + + {{ getServerRegion(item) }} + + + + {{ getLoginMethod(item) }} + +
+
+ + 可租 {{ formatEstimatedRentalDuration(item) }} + + + 耗 {{ getDailyLoss(item) }}/天 + +
+
+ + +
+ +
+ +
HFB
+ +
+ 特惠 + 有赠送 +
+ +
+ 比例 1:{{ getRatioValue(item).toFixed(1) }}
-
-

- {{ getMobileRatioText(item) }} -

-
- +
+ +
+
+ 哈夫币 + {{ getItemHafCoin(item) }} +
+
- - {{ getServerRegion(item) }} + 保险 + {{ getItemInsurance(item) }} +
+
+ 币比 + {{ getItemRatioText(item) }} +
+
+ + +
+ + + {{ skin.name }} - - {{ getLoginMethod(item) }} + {{ res.label }} + {{ res.quantity }} + 送
-
- -
+ + +
- {{ chip.label }}:{{ chip.value }} + {{ chip.label }} + {{ chip.value }}
diff --git a/frontend/src/features/listings/views/MobileListingDetailView.css b/frontend/src/features/listings/views/MobileListingDetailView.css index 0fe21ed..6d1d172 100644 --- a/frontend/src/features/listings/views/MobileListingDetailView.css +++ b/frontend/src/features/listings/views/MobileListingDetailView.css @@ -1,28 +1,36 @@ +/* ========================================================================== + 移动端商品详情页全新重构样式 (完全对标 PC 端设计语言规范) + ========================================================================== */ + .mobile-detail { min-height: 100dvh; - background: #f5f7fa; - padding-bottom: calc(80px + env(safe-area-inset-bottom)); + background: #f1f5f9; + padding-bottom: calc(84px + env(safe-area-inset-bottom)); + box-sizing: border-box; } /* ========== 顶部导航 ========== */ .page-header { position: sticky; top: 0; - z-index: 10; + z-index: 50; display: flex; align-items: center; + justify-content: space-between; height: 48px; padding: 0 12px; - background: #fff; - border-bottom: 1px solid #eee; + background: #ffffff; + border-bottom: 1px solid #e2e8f0; } .page-header h1 { flex: 1; margin: 0; - font-size: 17px; - font-weight: 700; + font-size: 16px; + font-weight: 800; + color: #0f172a; text-align: center; + letter-spacing: -0.01em; } .back-btn, @@ -33,324 +41,759 @@ place-items: center; border: none; background: none; - color: #333; + color: #334155; cursor: pointer; + border-radius: 8px; +} + +.back-btn:active, +.favorite-header-btn:active { + background: #f1f5f9; } .favorite-header-btn { color: #ff6a00; } -.header-spacer { - width: 36px; -} - .center-loading { display: flex; justify-content: center; - padding: 60px 0; + padding: 80px 0; } -/* ========== 防骗提示 ========== */ +/* ========== 平台官方安全保障提示条 ========== */ .fraud-tip { display: flex; align-items: center; - gap: 6px; - padding: 8px 16px; - background: #fff8e1; - font-size: 11px; - color: #e65100; - line-height: 1.4; + gap: 8px; + margin: 10px 12px 6px; + padding: 8px 12px; + background: #fffcf5; + border: 1px solid #fef3c7; + border-radius: 10px; + font-size: 11.5px; + color: #92400e; + line-height: 1.45; + box-shadow: 0 1px 3px rgba(245, 158, 11, 0.05); } -/* ========== 封面图 ========== */ -.cover-area { - position: relative; - width: 100%; - aspect-ratio: 16 / 9; - background: #e8e8e8; - overflow: hidden; +.fraud-tip span { + font-weight: 500; } -.cover-img { - width: 100%; - height: 100%; - cursor: zoom-in; -} - -.cover-img :deep(.el-image__inner) { - width: 100%; - height: 100%; - object-fit: cover; -} - -.cover-placeholder { - width: 100%; - height: 100%; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - gap: 6px; - color: #999; - font-size: 12px; -} - -.cover-count { - position: absolute; - right: 10px; - bottom: 10px; - padding: 2px 8px; - border-radius: 8px; - background: rgba(0, 0, 0, 0.5); - color: #fff; - font-size: 11px; -} - -/* ========== 信息卡片 ========== */ +/* ========== 通用信息卡片 (对齐 PC detail-card) ========== */ .info-card { margin: 10px 12px; - padding: 14px; - background: #fff; - border-radius: 12px; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); + padding: 16px; + background: #ffffff; + border-radius: 16px; + border: 1px solid #e8edf5; + box-shadow: 0 3px 12px rgba(15, 23, 42, 0.03); } -.info-tag-row { +.card-header-row { display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 12px; +} + +.header-left { + display: flex; + flex-direction: column; + gap: 2px; +} + +.card-subtitle { + margin: 0; + font-size: 15px; + font-weight: 800; + color: #0f172a; +} + +.card-subtitle-tip { + font-size: 11px; + color: #64748b; +} + +.gallery-count { + font-size: 11px; + font-weight: 600; + color: #94a3b8; + flex-shrink: 0; +} + +/* ========== 1. 账号抬头卡片 (Header Card) ========== */ +.header-meta-row { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 10px; +} + +.meta-tags-wrap { + display: flex; + align-items: center; + flex-wrap: wrap; gap: 6px; - margin-bottom: 8px; } .mobile-code-chip { - margin-bottom: 8px; - padding: 4px 9px; - border: 1px solid #dbeafe; - border-radius: 999px; + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 8px; + border-radius: 6px; background: #eff6ff; + border: 1px solid #bfdbfe; color: #2563eb; - font-size: 12px; - font-weight: 800; + font-size: 11px; + font-weight: 700; + cursor: pointer; +} + +.platform-pill { + display: inline-flex; + align-items: center; + gap: 3px; + padding: 2px 7px; + border-radius: 5px; + font-size: 11px; + font-weight: 600; +} + +.platform-pill.wechat { + background: #ecfdf5; + color: #059669; +} + +.platform-pill.qq { + background: #eff6ff; + color: #0284c7; +} + +.platform-pill.scan { + background: #fff7ed; + color: #ea580c; +} + +.platform-pill.password, +.platform-pill.default { + background: #f1f5f9; + color: #475569; +} + +.platform-pill.steam { + background: #eef2ff; + color: #4f46e5; +} + +.platform-pill.rank { + background: #f5f3ff; + color: #7c3aed; +} + +.status-pill { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 2px 8px; + border-radius: 5px; + background: #f0fdf4; + border: 1px solid #dcfce7; + color: #15803d; + font-size: 11px; + font-weight: 700; +} + +.status-dot { + width: 6px; + height: 6px; + border-radius: 999px; + background: #16a34a; +} + +.status-pill.is-busy { + background: #fff7ed; + border-color: #fed7aa; + color: #ea580c; +} + +.status-pill.is-busy .status-dot { + background: #ea580c; } .mobile-favorite-chip { display: inline-flex; align-items: center; - gap: 4px; - margin-left: 6px; - margin-bottom: 8px; - padding: 4px 9px; - border: 1px solid #fed7aa; + gap: 3px; + padding: 3px 9px; + border: 1px solid #e2e8f0; border-radius: 999px; + background: #ffffff; + color: #64748b; + font-size: 11px; + font-weight: 600; + cursor: pointer; + flex-shrink: 0; +} + +.mobile-favorite-chip.is-active { + border-color: #fed7aa; background: #fff7ed; - color: #ff6a00; - font-size: 12px; - font-weight: 800; + color: #ea580c; } .detail-title { margin: 0 0 6px; - font-size: 20px; - font-weight: 700; - color: #1a1a1a; - line-height: 1.35; + font-size: 17px; + font-weight: 800; + color: #0f172a; + line-height: 1.4; + letter-spacing: -0.015em; } .detail-desc { - margin: 0; - font-size: 13px; - color: #666; + margin: 0 0 12px; + font-size: 12px; + color: #64748b; line-height: 1.5; } -/* ========== 资产指标 ========== */ -.metric-row { - display: grid; - grid-template-columns: repeat(3, minmax(0, 1fr)); - gap: 8px; - padding: 0 12px; -} - -.metric-item { - background: #fff; - border-radius: 10px; - padding: 10px 4px; - text-align: center; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04); -} - -.metric-label { - display: block; - font-size: 11px; - color: #999; - margin-bottom: 4px; -} - -.metric-value { - display: block; - overflow: hidden; - font-size: 15px; - color: #1a1a1a; - text-overflow: ellipsis; - white-space: nowrap; -} - -.metric-value.coin { - color: #1477ff; -} - -.metric-value.price { - color: #ff5f00; -} - -/* ========== 账号资料 ========== */ -.info-line { - display: flex; - justify-content: space-between; - padding: 8px 0; - border-bottom: 1px solid #f5f5f5; - font-size: 13px; -} - -.info-line:last-child { - border-bottom: none; -} - -.info-label { - color: #999; -} - -.info-text { - color: #333; - font-weight: 500; -} - -/* ========== 截图列表 ========== */ -.card-subtitle { - margin: 0 0 10px; - font-size: 14px; - font-weight: 700; -} - -.detail-chip-row { - display: flex; - flex-wrap: wrap; - gap: 7px; - margin-bottom: 10px; -} - -.detail-chip-row span { - display: inline-flex; - align-items: center; - min-height: 28px; - border: 1px solid #ff8a1f; - border-radius: 5px; - color: #ff7900; - padding: 0 7px; - font-size: 12px; - font-weight: 800; -} - -.resource-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 8px; -} - -.resource-pill { - display: grid; - grid-template-columns: minmax(0, 1fr) auto; - gap: 4px 8px; - border-radius: 8px; - background: #f7f9fc; - padding: 10px; -} - -.resource-pill span { - min-width: 0; - color: #5f6b7a; - font-size: 12px; - font-weight: 700; -} - -.resource-pill strong { - color: #17233d; - font-size: 14px; -} - -.resource-pill em { - display: flex; - align-items: center; - justify-content: space-between; - gap: 6px; - grid-column: 1 / -1; - font-size: 11px; - font-style: normal; - font-weight: 800; -} - -.resource-pill em b { - color: #ff7900; -} - -.resource-pill em small { - min-width: 0; - color: #17233d; - font-size: 11px; - font-weight: 800; - text-align: right; -} - -.skin-detail-group + .skin-detail-group { - margin-top: 12px; -} - -.skin-detail-group p { - margin: 0 0 8px; - color: #5f6b7a; - font-size: 12px; - font-weight: 900; -} - -.screenshot-grid { +/* 核心资产四大微卡 (2x2 网格对齐移动端体验) */ +.core-assets-bar { display: grid; grid-template-columns: repeat(2, 1fr); gap: 8px; + margin-top: 10px; } -.screenshot-item { - margin: 0; -} - -.screenshot-thumb { - width: 100%; - aspect-ratio: 4 / 3; - overflow: hidden; - border-radius: 8px; +.asset-tile { + display: flex; + flex-direction: column; + gap: 4px; + padding: 10px 12px; + border-radius: 10px; background: #f8fafc; - cursor: pointer; + border: 1px solid #e2e8f0; } -.screenshot-thumb :deep(.el-image__inner) { +.coin-tile { + background: #fff9f5; + border-color: #fed7aa; +} + +.slot-tile.top-insurance { + background: #fff9f5; + border-color: #fed7aa; +} + +.tile-label { + font-size: 11px; + font-weight: 500; + color: #64748b; +} + +.tile-value { + font-size: 19px; + font-weight: 800; + line-height: 1.15; + letter-spacing: -0.02em; +} + +.text-orange { + color: #ea580c; +} + +.text-slate { + color: #0f172a; +} + +.tile-subvalues { + display: flex; + flex-wrap: wrap; + gap: 4px; + align-items: center; +} + +.val-pill { + display: inline-flex; + align-items: center; + padding: 2px 6px; + border-radius: 4px; + font-size: 11px; + font-weight: 700; +} + +.val-pill.green { + background: #f0fdf4; + border: 1px solid #dcfce7; + color: #15803d; +} + +.val-pill.warm { + background: #fffbeb; + border: 1px solid #fef3c7; + color: #b45309; +} + +/* ========== 2. 验号多图互动展台 (Gallery Stage) ========== */ +.gallery-stage { + position: relative; + width: 100%; + aspect-ratio: 16 / 10; + border-radius: 10px; + overflow: hidden; + background: #0f172a; +} + +.stage-viewer { + width: 100%; + height: 100%; + position: relative; + display: flex; + align-items: center; + justify-content: center; +} + +.stage-image { + width: 100%; + height: 100%; + object-fit: contain; +} + +.stage-viewer :deep(.el-image) { + width: 100%; + height: 100%; +} + +.stage-viewer :deep(.el-image__inner) { + width: 100%; + height: 100%; + object-fit: contain; +} + +.stage-tag { + position: absolute; + top: 8px; + left: 8px; + padding: 3px 8px; + border-radius: 5px; + background: rgba(15, 23, 42, 0.75); + backdrop-filter: blur(4px); + color: #ffffff; + font-size: 11px; + font-weight: 600; + z-index: 2; +} + +.stage-zoom-hint { + position: absolute; + bottom: 8px; + right: 8px; + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 8px; + border-radius: 999px; + background: rgba(15, 23, 42, 0.65); + backdrop-filter: blur(4px); + color: #f1f5f9; + font-size: 10px; + z-index: 2; +} + +.stage-empty { + display: flex; + align-items: center; + justify-content: center; + height: 100%; + color: #64748b; + font-size: 12px; +} + +/* 胶卷导轨缩略图 */ +.thumbnail-filmstrip { + display: flex; + gap: 8px; + margin-top: 10px; + overflow-x: auto; + padding-bottom: 2px; +} + +.thumbnail-item { + flex: 0 0 76px; + height: 52px; + position: relative; + border-radius: 6px; + overflow: hidden; + border: 2px solid transparent; + background: #0f172a; + cursor: pointer; + padding: 0; + transition: all 0.15s ease; +} + +.thumbnail-item.is-active { + border-color: #ff6a00; + box-shadow: 0 0 0 1px rgba(255, 106, 0, 0.3); +} + +.thumb-img { width: 100%; height: 100%; object-fit: cover; } -.screenshot-item figcaption { - margin-top: 4px; - color: #6b7280; - font-size: 11px; - font-weight: 700; +.thumbnail-item :deep(.el-image) { + width: 100%; + height: 100%; +} + +.thumbnail-item :deep(.el-image__inner) { + width: 100%; + height: 100%; + object-fit: cover; +} + +.thumb-label { + position: absolute; + bottom: 0; + left: 0; + right: 0; + background: rgba(15, 23, 42, 0.78); + color: #ffffff; + font-size: 9.5px; + font-weight: 500; text-align: center; + padding: 1px 2px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* ========== 3. 账号核心档案矩阵 (Specs Card) ========== */ +.secondary-chips-flow { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-bottom: 12px; +} + +.secondary-spec-pill { + display: inline-flex; + align-items: center; + gap: 4px; + padding: 3px 8px; + border-radius: 6px; + background: #f8fafc; + border: 1px solid #e2e8f0; + font-size: 11px; +} + +.pill-k { + color: #64748b; +} + +.pill-v { + color: #0f172a; + font-weight: 700; +} + +.specs-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 8px; +} + +.spec-cell { + display: flex; + flex-direction: column; + gap: 2px; + padding: 8px 10px; + border-radius: 8px; + background: #f8fafc; + border: 1px solid #edf2f7; +} + +.cell-label { + font-size: 11px; + font-weight: 500; + color: #64748b; +} + +.cell-val { + font-size: 13px; + font-weight: 700; + color: #0f172a; + overflow-wrap: anywhere; +} + +/* ========== 4. 精选皮肤展台 (Skin Card) ========== */ +.skin-groups-container { + display: flex; + flex-direction: column; + gap: 10px; +} + +.skin-group-block { + display: flex; + flex-direction: column; + gap: 6px; +} + +.skin-group-header { + display: flex; + align-items: center; + gap: 6px; +} + +.skin-group-title { + font-size: 12px; + font-weight: 700; + color: #334155; +} + +.skin-group-count { + padding: 1px 6px; + border-radius: 999px; + background: #f1f5f9; + color: #64748b; + font-size: 10px; + font-weight: 600; +} + +.skin-pill-wrap { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.skin-pill { + display: inline-flex; + align-items: center; + gap: 5px; + padding: 3px 8px; + border-radius: 6px; + background: #f8fafc; + border: 1px solid #e2e8f0; + font-size: 11.5px; + font-weight: 600; + color: #1e293b; +} + +.quality-dot { + width: 6px; + height: 6px; + border-radius: 999px; + background: #94a3b8; + flex-shrink: 0; +} + +/* 皮肤各品级颜色 */ +.skin-type-melee { + background: #fff7ed; + border-color: #fed7aa; + color: #c2410c; +} +.skin-type-melee .quality-dot { + background: #ea580c; +} + +.skin-type-operatorRed { + background: #fef2f2; + border-color: #fecaca; + color: #b91c1c; +} +.skin-type-operatorRed .quality-dot { + background: #ef4444; +} + +.skin-type-operatorGold { + background: #fffbeb; + border-color: #fde68a; + color: #b45309; +} +.skin-type-operatorGold .quality-dot { + background: #f59e0b; +} + +.skin-type-weapon { + background: #f0fdf4; + border-color: #bbf7d0; + color: #15803d; +} +.skin-type-weapon .quality-dot { + background: #22c55e; +} + +/* ========== 5. 仓库物资与额外消耗品 (Resources Card) ========== */ +.resources-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 8px; +} + +.resource-tile { + display: flex; + flex-direction: column; + gap: 6px; + padding: 10px; + border-radius: 8px; + background: #f8fafc; + border: 1px solid #edf2f7; +} + +.res-top { + display: flex; + justify-content: space-between; + align-items: baseline; + gap: 4px; +} + +.res-name { + font-size: 12px; + font-weight: 600; + color: #334155; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.res-qty { + font-size: 14px; + font-weight: 800; + color: #0f172a; + flex-shrink: 0; +} + +.res-bottom { + display: flex; +} + +.res-badge { + display: inline-flex; + align-items: center; + gap: 3px; + padding: 2px 6px; + border-radius: 4px; + font-size: 10px; + font-weight: 700; +} + +.res-badge.gift { + background: #ecfdf5; + color: #059669; +} + +.res-badge.charge { + background: #fff7ed; + color: #ea580c; +} + +.res-badge.charge small { + font-weight: 800; +} + +.res-badge.standard { + background: #f1f5f9; + color: #64748b; +} + +/* ========== 6. 号主留言卡片 (Note Card) ========== */ +.seller-note-box { + display: flex; + gap: 10px; + align-items: flex-start; + padding: 12px 14px; + border-radius: 8px; + background: #f8fafc; + border-left: 3px solid #ff8533; +} + +.note-quote-icon { + color: #ff8533; + flex-shrink: 0; + margin-top: 2px; +} + +.note-content { + margin: 0; + font-size: 13px; + line-height: 1.6; + color: #334155; +} + +/* ========== 7. 平台官方保障卡片 (Trust Card) ========== */ +.trust-features-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); + gap: 8px; +} + +.trust-item { + display: flex; + gap: 8px; + align-items: flex-start; + padding: 10px; + border-radius: 8px; + background: #f8fafc; + border: 1px solid #f1f5f9; +} + +.trust-icon-box { + width: 32px; + height: 32px; + border-radius: 6px; + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; +} + +.trust-icon-box.orange { + background: #fff7ed; + color: #ea580c; +} + +.trust-icon-box.blue { + background: #eff6ff; + color: #2563eb; +} + +.trust-icon-box.green { + background: #ecfdf5; + color: #059669; +} + +.trust-icon-box.amber { + background: #fffbeb; + color: #d97706; +} + +.trust-text strong { + display: block; + font-size: 11.5px; + font-weight: 700; + color: #0f172a; + margin-bottom: 2px; +} + +.trust-text p { + margin: 0; + font-size: 10px; + color: #64748b; + line-height: 1.35; } /* ========== 底部留白 ========== */ .bottom-spacer { - height: 20px; + height: 24px; } -/* ========== 底部下单栏 ========== */ +/* ========== 底部下单栏 (Fixed Bar) ========== */ .order-bar { position: fixed; left: 0; @@ -359,18 +802,19 @@ z-index: 100; display: flex; align-items: center; - gap: 10px; - padding: 8px 12px calc(8px + env(safe-area-inset-bottom)); - background: #fff; - border-top: 1px solid #eee; - box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.04); + justify-content: space-between; + gap: 12px; + padding: 10px 14px calc(10px + env(safe-area-inset-bottom)); + background: #ffffff; + border-top: 1px solid #e2e8f0; + box-shadow: 0 -4px 16px rgba(15, 23, 42, 0.06); } .order-bar-left { - flex: 1; display: flex; flex-direction: column; gap: 2px; + min-width: 0; } .order-price { @@ -379,37 +823,74 @@ gap: 4px; } -.price-label { +.price-caption { font-size: 11px; - color: #999; + font-weight: 500; + color: #64748b; +} + +.price-total-wrap { + display: inline-flex; + align-items: baseline; + gap: 1px; +} + +.price-currency { + font-size: 14px; + font-weight: 800; + color: #ff5f00; } .price-amount { - font-size: 18px; + font-size: 20px; font-weight: 900; color: #ff5f00; + letter-spacing: -0.02em; } .order-price-detail { display: flex; - flex-wrap: wrap; - gap: 4px 8px; - color: #8a5a12; + align-items: center; + gap: 6px; + font-size: 10.5px; + font-weight: 600; + color: #64748b; +} + +.deposit-tag { + display: inline-block; + padding: 1px 5px; + border-radius: 4px; + background: #f1f5f9; + color: #475569; font-size: 10px; - font-weight: 700; - line-height: 1.3; } .order-btn { flex-shrink: 0; + display: inline-flex; + align-items: center; + justify-content: center; + gap: 6px; + height: 42px; padding: 0 20px; - height: 40px; font-size: 14px; - font-weight: 700; - background: #ff6a00 !important; + font-weight: 800; + background: linear-gradient(135deg, #ff8533 0%, #ff5f00 100%) !important; border: none !important; + box-shadow: 0 4px 12px rgba(255, 95, 0, 0.25); } +.order-btn:active { + opacity: 0.9; + transform: scale(0.98); +} + +.order-btn-icon { + margin-right: 2px; +} + +/* ========== 协议弹窗 ========== */ .agreement-popup { max-height: 92vh; } diff --git a/frontend/src/features/listings/views/MobileListingDetailView.vue b/frontend/src/features/listings/views/MobileListingDetailView.vue index 12bf339..5f29c0e 100644 --- a/frontend/src/features/listings/views/MobileListingDetailView.vue +++ b/frontend/src/features/listings/views/MobileListingDetailView.vue @@ -13,7 +13,7 @@ import { } from '@/features/orders/api/orders' import { useSessionStore } from '@/stores/session' import AuthImage from '@/shared/components/business/AuthImage.vue' -import { formatCent, formatMoney } from '@/shared/utils/money' +import { formatCent, formatMoney, roundMoney } from '@/shared/utils/money' import { assetRegions, formatAssetNumber, @@ -31,9 +31,12 @@ import { getListingSubtitle, getListingTitle, getLoginMethod, + getOnlineTimeText, getServerRegion, readAssetNumber, readAssetString, + getRatioValue, + isTopTierInsurance, } from '@/shared/utils/listingDisplay' const route = useRoute() @@ -75,6 +78,7 @@ onMounted(async () => { const listingFavorited = computed(() => (listing.value ? isFavorite(listing.value.id) : false)) +// 价格拆解与格式化 const orderTotal = computed(() => { if (!listing.value) return '0.0' return formatMoney(getListingDisplayPrice(listing.value)) @@ -85,44 +89,71 @@ const orderPriceBreakdown = computed(() => { return { rent: 0, consumable: 0, + total: 0, } } return { rent: getListingRentPrice(listing.value), consumable: getListingConsumablePrice(listing.value), + total: roundMoney(getListingDisplayPrice(listing.value)), } }) -const detailMetrics = computed(() => { - if (!listing.value) return [] - const dailyLoss = getDailyLoss(listing.value) - const secretKD = readAssetNumber(listing.value, 'secret_kd') - return [ - { label: '纯币', value: formatHafCoinM(getCoinWan(listing.value)), tone: 'coin' }, - { - label: '绝密KD', - value: secretKD > 0 ? formatAssetNumber(secretKD) : '--', - tone: 'coin', - }, - { - label: '日损耗', - value: dailyLoss ? `${dailyLoss}/天` : '--', - tone: 'coin', - }, - { - label: '预计可租', - value: formatEstimatedRentalDuration(listing.value), - tone: 'coin', - }, - { - label: '价格', - value: `¥${formatMoney(getListingDisplayPrice(listing.value))}`, - tone: 'price', - }, - { label: '押金', value: `¥${formatCent(listing.value.deposit_amount_cent)}`, tone: '' }, - ] +// 核心资产微卡计算(完全对齐 PC 首页及详情页设计标准) +const hafCoinDisplay = computed(() => { + if (!listing.value) return '--' + return formatHafCoinM(getCoinWan(listing.value)) }) +const insuranceSlot = computed(() => { + if (!listing.value) return '--' + return readAssetString(listing.value, 'season_insurance') || '--' +}) + +const isTopTierInsuranceSlot = computed(() => isTopTierInsurance(insuranceSlot.value)) + +const ratioValue = computed(() => { + if (!listing.value) return 0 + return getRatioValue(listing.value) +}) + +const ratioDisplay = computed(() => { + if (ratioValue.value <= 0) return null + return `1元 = ${ratioValue.value.toFixed(1)}w` +}) + +const rentalDuration = computed(() => { + if (!listing.value) return '--' + return formatEstimatedRentalDuration(listing.value) +}) + +const dailyLoss = computed(() => { + if (!listing.value) return '' + return getDailyLoss(listing.value) +}) + +// 平台/区服特性徽标 +const accessTags = computed(() => { + if (!listing.value) return [] + return [getServerRegion(listing.value), getLoginMethod(listing.value)] + .filter(Boolean) + .map(item => accessBadgeMeta(item)) +}) + +function accessBadgeMeta(value: string) { + const text = value.trim() + if (text.includes('微信')) return { label: text, icon: 'wechat', tone: 'wechat' } + if (text.toUpperCase().includes('QQ')) return { label: text, icon: 'qq', tone: 'qq' } + if (text.includes('扫码')) return { label: text, icon: 'scan', tone: 'scan' } + if (text.includes('账') || text.includes('密码')) + return { label: text, icon: 'lock', tone: 'password' } + if (text.includes('Steam')) return { label: text, icon: 'desktop-o', tone: 'steam' } + return { label: text, icon: 'bookmark-o', tone: 'default' } +} + +// 截图多图展台与当前选中的大图 +const activeImageIndex = ref(0) + const detailScreenshots = computed(() => { if (!listing.value) return [] const groupedScreenshots = readGroupedScreenshots(listing.value) @@ -135,15 +166,20 @@ const detailScreenshots = computed(() => { }) const detailScreenshotUrls = computed(() => detailScreenshots.value.map(shot => shot.url)) +const activeScreenshot = computed(() => { + if (detailScreenshots.value.length === 0) return null + return detailScreenshots.value[activeImageIndex.value] || detailScreenshots.value[0] +}) + function readGroupedScreenshots(item: Listing) { const groups = item.asset_summary?.screenshot_groups if (typeof groups !== 'object' || groups === null) return [] const slots = [ { key: 'coin', label: '纯币截图' }, - { key: 'gameId', label: '游戏ID截图' }, - { key: 'totalAsset', label: '总资产截图' }, { key: 'tencentSecurity', label: '腾讯安全中心截图' }, { key: 'skin', label: '皮肤截图' }, + { key: 'gameId', label: '游戏ID截图' }, + { key: 'totalAsset', label: '总资产截图' }, ] return slots.flatMap(slot => { const urls = (groups as Record)[slot.key] @@ -156,30 +192,84 @@ function readGroupedScreenshots(item: Listing) { }) } +// 皮肤清单(带高光品级) const detailSkinGroups = computed(() => { if (!listing.value) return [] const groups = listing.value.asset_summary?.skin_groups if (typeof groups !== 'object' || groups === null) return [] - const titles: Record = { - melee: '近战皮肤', - operator: '干员皮肤', - operatorGold: '干员金皮', - operatorRed: '干员红皮', - weapon: '武器皮肤', - other: '其他皮肤', - imported: '其他皮肤', + const titles: Record = { + melee: { title: '近战皮肤', type: 'melee' }, + operatorRed: { title: '干员红皮', type: 'operatorRed' }, + operatorGold: { title: '干员金皮', type: 'operatorGold' }, + operator: { title: '干员皮肤', type: 'operator' }, + weapon: { title: '武器皮肤', type: 'weapon' }, + other: { title: '其他皮肤', type: 'other' }, + imported: { title: '其他皮肤', type: 'other' }, } return Object.entries(groups as Record) - .map(([key, value]) => ({ - key, - title: titles[key] || key, - options: Array.isArray(value) - ? value.filter((skin): skin is string => typeof skin === 'string') - : [], - })) + .map(([key, value]) => { + const meta = titles[key] || { title: key, type: 'other' } + return { + key, + type: meta.type, + title: meta.title, + options: Array.isArray(value) + ? value.filter((skin): skin is string => typeof skin === 'string') + : [], + } + }) .filter(group => group.options.length) }) +const totalSkinCount = computed(() => { + return detailSkinGroups.value.reduce((acc, g) => acc + g.options.length, 0) +}) + +// 次要规格胶囊(过滤已展示的哈夫币、保险、AWM) +const secondaryChips = computed(() => { + if (!listing.value) return [] + return getListingChips(listing.value).filter( + chip => + chip.label !== '哈夫币' && + chip.label !== '保险格数' && + chip.label !== 'AWM' + ) +}) + +// 账号基础档案(与 PC 对齐) +const accountRows = computed(() => { + if (!listing.value) return [] + const regions = assetRegions(listing.value) + const secretKD = readAssetNumber(listing.value, 'secret_kd') + const fireLevel = readAssetNumber(listing.value, 'fire_level') + return [ + { label: '所属区服', value: getServerRegion(listing.value) || '--' }, + { label: '上号方式', value: getLoginMethod(listing.value) || '--' }, + { label: '游戏段位', value: listing.value.rank_level || '--' }, + { label: '烽火等级', value: fireLevel > 0 ? `${fireLevel}级` : '--' }, + { label: '体力等级', value: readAssetString(listing.value, 'stamina_level') || '--' }, + { label: '负重等级', value: readAssetString(listing.value, 'load_level') || '--' }, + { label: '绝密KD', value: secretKD > 0 ? formatAssetNumber(secretKD) : '--' }, + { label: 'M单价', value: formatRatio(listing.value) }, + { label: '方便上号', value: getOnlineTimeText(listing.value) || '--' }, + { label: '预计可租', value: formatEstimatedRentalDuration(listing.value) }, + { label: '常用登录地', value: regions.length ? regions.join('、') : '--' }, + { label: '封禁记录', value: readAssetString(listing.value, 'ban_record') || '无封禁记录' }, + ] +}) + +// 仓库物资 +const resourceItems = computed(() => { + if (!listing.value) return [] + return getListingResources(listing.value) +}) + +function formatResourceQuantity(resource: any) { + const suffix = resource.key === 'awmAmmo' ? '发' : '' + const val = Number.isInteger(resource.quantity) ? String(resource.quantity) : resource.quantity.toFixed(1) + return `${val}${suffix}` +} + /* 下单 */ async function handleCreateOrder() { if (!listing.value) return @@ -318,174 +408,307 @@ function handleToggleFavorite() {