diff --git a/frontend/src/features/listings/components/ListingCard.vue b/frontend/src/features/listings/components/ListingCard.vue index fabc972..702c1c0 100644 --- a/frontend/src/features/listings/components/ListingCard.vue +++ b/frontend/src/features/listings/components/ListingCard.vue @@ -4,21 +4,18 @@ import { RouterLink } from 'vue-router' import type { Listing } from '@/features/listings' import { formatCent, formatMoney } from '@/shared/utils/money' import { - formatHafCoinM, - getCoinWan, + formatEstimatedRentalDuration, getDailyLoss, getListingDisplayPrice, formatListingCode, + getListingChips, getListingResources, getListingTitle, getLoginMethod, - getOnlineTimeText, getServerRegion, getSkinNames, hasAcceleratedSaleRatio, hasGiftResources, - readAssetNumber, - readAssetString, getRatioValue, type ListingDisplayResource, } from '@/shared/utils/listingDisplay' @@ -27,53 +24,48 @@ interface Props { listing: Listing } -interface ListingCardStat { - label: string - value: string - tone?: 'accent' | 'highlight' - title?: string -} - const props = defineProps() const coverURL = computed(() => props.listing.cover_url || props.listing.screenshot_urls?.[0] || '') const dailyLoss = computed(() => getDailyLoss(props.listing)) const skinNames = computed(() => getSkinNames(props.listing)) -const statItems = computed(() => buildStatItems(props.listing)) +const chipItems = computed(() => getListingChips(props.listing)) const resourceItems = computed(() => getListingResources(props.listing)) - -function buildStatItems(listing: Listing) { - const onlineTime = getOnlineTimeText(listing) - const secretKD = readAssetNumber(listing, 'secret_kd') - const skinText = skinNames.value.slice(0, 2).join('、') - const stats: ListingCardStat[] = [ - { label: '哈夫币', value: formatHafCoinM(getCoinWan(listing)), tone: 'accent' }, - { label: '保险格数', value: readAssetString(listing, 'season_insurance') }, - { label: '体力/负重', value: formatLevelPair(listing) }, - { label: '绝密KD', value: secretKD > 0 ? formatStatNumber(secretKD) : '' }, - { label: '游戏段位', value: listing.rank_level }, - { label: '所属区服', value: getServerRegion(listing) }, - { label: '上号方式', value: getLoginMethod(listing) }, - { label: '方便上号', value: onlineTime }, - { - label: '持有皮肤', - value: skinText, - title: skinNames.value.join('、'), - }, - ] - - return stats.filter(stat => Boolean(stat.value)) -} +const rentalDuration = computed(() => formatEstimatedRentalDuration(props.listing)) +const visibleSkinNames = computed(() => skinNames.value.slice(0, 4)) +const skinOverflowCount = computed(() => + Math.max(0, skinNames.value.length - visibleSkinNames.value.length) +) +const accessTags = computed(() => + [getServerRegion(props.listing), getLoginMethod(props.listing)] + .filter(Boolean) + .map(item => accessBadgeMeta(item)) +) +const metaBadges = computed(() => + [ + { label: formatListingCode(props.listing), tone: 'code' }, + dailyLoss.value ? { label: `消耗 ${dailyLoss.value}/天`, tone: 'muted' } : null, + rentalDuration.value !== '--' ? { label: `租期 ${rentalDuration.value}`, tone: 'muted' } : null, + getRatioValue(props.listing) > 0 + ? { label: `1元=${getRatioValue(props.listing).toFixed(0)}w哈夫币`, tone: 'ratio' } + : null, + ].filter((item): item is { label: string; tone: string } => Boolean(item)) +) function formatResourceQuantity(resource: ListingDisplayResource) { const suffix = resource.key === 'awmAmmo' ? '发' : '' return `${formatStatNumber(resource.quantity)}${suffix}` } -function formatLevelPair(listing: Listing) { - const staminaLevel = readAssetString(listing, 'stamina_level') - const loadLevel = readAssetString(listing, 'load_level') - return [staminaLevel, loadLevel].filter(Boolean).join('/') +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' } } function formatStatNumber(value: number) { @@ -100,29 +92,35 @@ function formatStatNumber(value: number) {
-
-

{{ getListingTitle(listing) }}

-
- 编号 {{ formatListingCode(listing) }} - 日耗 {{ dailyLoss }} -
+

{{ getListingTitle(listing) }}

+ +
+ + {{ badge.label }} +
-
-
基础资料
-
-
- - {{ stat.value }} - - {{ stat.value }}... - -
-
+
+ + + {{ tag.label }} +
-
-
额外物资
+
+ + {{ chip.label }}:{{ chip.value }} + +
+ +
+ 皮肤 + {{ visibleSkinNames.join(' / ') }} + +{{ skinOverflowCount }} +
+ +
+ 额外物资
{{ resource.label }} @@ -134,17 +132,15 @@ function formatStatNumber(value: number) {
-
+
总租金 ¥{{ formatMoney(getListingDisplayPrice(listing)) }}
-
+
押金 ¥{{ formatCent(listing.deposit_amount_cent) }}
-
- -
+
@@ -152,31 +148,35 @@ function formatStatNumber(value: number) {