perf(listings): 首页仅展示单张纯币截图,去除悬停轮播以降低OSS并发请求
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { computed, ref } from 'vue'
|
import { computed } from 'vue'
|
||||||
import { RouterLink } from 'vue-router'
|
import { RouterLink } from 'vue-router'
|
||||||
import type { Listing } from '@/features/listings'
|
import type { Listing } from '@/features/listings'
|
||||||
import { formatCent, formatMoney } from '@/shared/utils/money'
|
import { formatCent, formatMoney } from '@/shared/utils/money'
|
||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
formatHafCoinM,
|
formatHafCoinM,
|
||||||
readAssetString,
|
readAssetString,
|
||||||
isTopTierInsurance,
|
isTopTierInsurance,
|
||||||
|
getCoinScreenshotURL,
|
||||||
type ListingDisplayResource,
|
type ListingDisplayResource,
|
||||||
} from '@/shared/utils/listingDisplay'
|
} from '@/shared/utils/listingDisplay'
|
||||||
|
|
||||||
@@ -31,37 +32,8 @@ interface Props {
|
|||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
// 多图悬浮轮播(Hover Carousel)
|
// 封面图片:仅展示纯币截图单张,避免 OSS 爆发高频请求错误
|
||||||
const activeImageIndex = ref(0)
|
const coverURL = computed(() => getCoinScreenshotURL(props.listing))
|
||||||
const screenshotList = computed(() => {
|
|
||||||
const urls = props.listing.screenshot_urls
|
|
||||||
if (Array.isArray(urls) && urls.length > 0) {
|
|
||||||
return urls.filter(Boolean)
|
|
||||||
}
|
|
||||||
if (props.listing.cover_url) {
|
|
||||||
return [props.listing.cover_url]
|
|
||||||
}
|
|
||||||
return []
|
|
||||||
})
|
|
||||||
|
|
||||||
const activeCoverURL = computed(() => {
|
|
||||||
if (screenshotList.value.length === 0) return ''
|
|
||||||
return screenshotList.value[activeImageIndex.value] || screenshotList.value[0] || ''
|
|
||||||
})
|
|
||||||
|
|
||||||
function handleCoverMouseMove(e: MouseEvent) {
|
|
||||||
const total = screenshotList.value.length
|
|
||||||
if (total <= 1) return
|
|
||||||
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
|
|
||||||
const offsetX = Math.max(0, Math.min(e.clientX - rect.left, rect.width))
|
|
||||||
const ratio = offsetX / rect.width
|
|
||||||
const nextIdx = Math.min(total - 1, Math.floor(ratio * total))
|
|
||||||
activeImageIndex.value = nextIdx
|
|
||||||
}
|
|
||||||
|
|
||||||
function handleCoverMouseLeave() {
|
|
||||||
activeImageIndex.value = 0
|
|
||||||
}
|
|
||||||
|
|
||||||
// 基础元信息
|
// 基础元信息
|
||||||
const listingCode = computed(() => formatListingCode(props.listing))
|
const listingCode = computed(() => formatListingCode(props.listing))
|
||||||
@@ -129,11 +101,11 @@ function formatStatNumber(value: number) {
|
|||||||
|
|
||||||
<template>
|
<template>
|
||||||
<RouterLink class="hfb-listing-card" :to="`/listings/${listing.id}`">
|
<RouterLink class="hfb-listing-card" :to="`/listings/${listing.id}`">
|
||||||
<!-- 左侧:4:3 黄金比例封面 + 悬浮多图轮播 -->
|
<!-- 左侧:4:3 黄金比例单张封面(展示纯币截图) -->
|
||||||
<div class="card-media" @mousemove="handleCoverMouseMove" @mouseleave="handleCoverMouseLeave">
|
<div class="card-media">
|
||||||
<img
|
<img
|
||||||
v-if="activeCoverURL"
|
v-if="coverURL"
|
||||||
:src="activeCoverURL"
|
:src="coverURL"
|
||||||
:alt="getListingTitle(listing)"
|
:alt="getListingTitle(listing)"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
@@ -148,17 +120,6 @@ function formatStatNumber(value: number) {
|
|||||||
|
|
||||||
<!-- 左下角黑金比例浮层 -->
|
<!-- 左下角黑金比例浮层 -->
|
||||||
<div v-if="ratioValue > 0" class="media-ratio">比例 1:{{ ratioValue.toFixed(1) }}</div>
|
<div v-if="ratioValue > 0" class="media-ratio">比例 1:{{ ratioValue.toFixed(1) }}</div>
|
||||||
|
|
||||||
<!-- 多图小圆点指示器 -->
|
|
||||||
<div v-if="screenshotList.length > 1" class="media-indicators">
|
|
||||||
<span
|
|
||||||
v-for="(_, index) in screenshotList"
|
|
||||||
:key="index"
|
|
||||||
class="indicator-dot"
|
|
||||||
:class="{ active: index === activeImageIndex }"
|
|
||||||
@mouseenter.stop="activeImageIndex = index"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 中间:双栏结构化信息区(左基础指标 + 右高光装备物资展台) -->
|
<!-- 中间:双栏结构化信息区(左基础指标 + 右高光装备物资展台) -->
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ import {
|
|||||||
hasAcceleratedSaleRatio,
|
hasAcceleratedSaleRatio,
|
||||||
hasGiftResources,
|
hasGiftResources,
|
||||||
isTopTierInsurance,
|
isTopTierInsurance,
|
||||||
|
getCoinScreenshotURL,
|
||||||
getCoinWan,
|
getCoinWan,
|
||||||
formatHafCoinM,
|
formatHafCoinM,
|
||||||
readAssetString,
|
readAssetString,
|
||||||
@@ -1046,8 +1047,8 @@ syncMobileHomeQuery()
|
|||||||
<!-- 左侧封面 -->
|
<!-- 左侧封面 -->
|
||||||
<div class="mobile-card-media">
|
<div class="mobile-card-media">
|
||||||
<img
|
<img
|
||||||
v-if="item.cover_url || item.screenshot_urls?.[0]"
|
v-if="getCoinScreenshotURL(item)"
|
||||||
:src="item.cover_url || item.screenshot_urls?.[0]"
|
:src="getCoinScreenshotURL(item)"
|
||||||
:alt="formatListingCode(item)"
|
:alt="formatListingCode(item)"
|
||||||
loading="lazy"
|
loading="lazy"
|
||||||
decoding="async"
|
decoding="async"
|
||||||
|
|||||||
@@ -409,3 +409,18 @@ export function isTopTierInsurance(value: string | undefined | null): boolean {
|
|||||||
s.includes('3*3') || s.includes('3x3') || s.includes('3×3') || s === '9格' || s.includes('9格')
|
s.includes('3*3') || s.includes('3x3') || s.includes('3×3') || s === '9格' || s.includes('9格')
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取账号封面/纯币截图(仅展示一张,优先纯币截图,其次封面图,其次首张截图)
|
||||||
|
*/
|
||||||
|
export function getCoinScreenshotURL(item: Listing): string {
|
||||||
|
const groups = item.asset_summary?.screenshot_groups
|
||||||
|
if (typeof groups === 'object' && groups !== null) {
|
||||||
|
const coinUrls = (groups as Record<string, unknown>)['coin']
|
||||||
|
if (Array.isArray(coinUrls)) {
|
||||||
|
const firstCoin = coinUrls.find(url => typeof url === 'string' && Boolean(url))
|
||||||
|
if (firstCoin) return firstCoin
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return item.cover_url || item.screenshot_urls?.[0] || ''
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user