revert(listings): 首页卡片恢复使用原生 img 与公开缩略图链路 (16KB秒开)
This commit is contained in:
@@ -2,7 +2,6 @@
|
|||||||
import { computed } 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 AuthImage from '@/shared/components/business/AuthImage.vue'
|
|
||||||
import { formatCent, formatMoney } from '@/shared/utils/money'
|
import { formatCent, formatMoney } from '@/shared/utils/money'
|
||||||
import {
|
import {
|
||||||
formatEstimatedRentalDuration,
|
formatEstimatedRentalDuration,
|
||||||
@@ -23,7 +22,6 @@ import {
|
|||||||
formatHafCoinM,
|
formatHafCoinM,
|
||||||
readAssetString,
|
readAssetString,
|
||||||
isTopTierInsurance,
|
isTopTierInsurance,
|
||||||
getCoinScreenshotURL,
|
|
||||||
type ListingDisplayResource,
|
type ListingDisplayResource,
|
||||||
} from '@/shared/utils/listingDisplay'
|
} from '@/shared/utils/listingDisplay'
|
||||||
|
|
||||||
@@ -33,8 +31,8 @@ interface Props {
|
|||||||
|
|
||||||
const props = defineProps<Props>()
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
// 封面图片:仅展示纯币截图单张,避免 OSS 爆发高频请求错误
|
// 封面图片:优先使用后端生成的公开缩略图(16KB秒开,免鉴权)
|
||||||
const coverURL = computed(() => getCoinScreenshotURL(props.listing))
|
const coverURL = computed(() => props.listing.cover_url || props.listing.screenshot_urls?.[0] || '')
|
||||||
|
|
||||||
// 基础元信息
|
// 基础元信息
|
||||||
const listingCode = computed(() => formatListingCode(props.listing))
|
const listingCode = computed(() => formatListingCode(props.listing))
|
||||||
@@ -102,15 +100,15 @@ 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">
|
<div class="card-media">
|
||||||
<AuthImage
|
<img
|
||||||
v-if="coverURL"
|
v-if="coverURL"
|
||||||
:source="coverURL"
|
:src="coverURL"
|
||||||
:alt="getListingTitle(listing)"
|
:alt="getListingTitle(listing)"
|
||||||
image-class="media-img"
|
loading="lazy"
|
||||||
fallback-class="media-fallback"
|
decoding="async"
|
||||||
fit="cover"
|
class="media-img"
|
||||||
/>
|
/>
|
||||||
<div v-else class="media-empty">HFB</div>
|
<div v-else class="media-empty">HFB</div>
|
||||||
|
|
||||||
@@ -299,7 +297,6 @@ function formatStatNumber(value: number) {
|
|||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-media :deep(img),
|
|
||||||
.card-media img {
|
.card-media img {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
@@ -308,25 +305,10 @@ function formatStatNumber(value: number) {
|
|||||||
transition: transform 0.25s ease;
|
transition: transform 0.25s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.hfb-listing-card:hover .card-media :deep(img),
|
|
||||||
.hfb-listing-card:hover .card-media img {
|
.hfb-listing-card:hover .card-media img {
|
||||||
transform: scale(1.03);
|
transform: scale(1.03);
|
||||||
}
|
}
|
||||||
|
|
||||||
.card-media :deep(.auth-image-fallback) {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
background: #f1f5f9;
|
|
||||||
color: #94a3b8;
|
|
||||||
font-size: 12px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-empty {
|
.media-empty {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import {
|
|||||||
useListingCollections,
|
useListingCollections,
|
||||||
type ListingCollectionItem,
|
type ListingCollectionItem,
|
||||||
} from '@/features/listings/composables/useListingCollections'
|
} from '@/features/listings/composables/useListingCollections'
|
||||||
import AuthImage from '@/shared/components/business/AuthImage.vue'
|
|
||||||
import { formatCent, formatMoney } from '@/shared/utils/money'
|
import { formatCent, formatMoney } from '@/shared/utils/money'
|
||||||
import {
|
import {
|
||||||
formatHafCoinM,
|
formatHafCoinM,
|
||||||
@@ -17,7 +16,6 @@ import {
|
|||||||
getCoinWan,
|
getCoinWan,
|
||||||
getListingDisplayPrice,
|
getListingDisplayPrice,
|
||||||
getListingTitle,
|
getListingTitle,
|
||||||
getCoinScreenshotURL,
|
|
||||||
} from '@/shared/utils/listingDisplay'
|
} from '@/shared/utils/listingDisplay'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -138,11 +136,12 @@ function timeText(item: ListingCollectionItem) {
|
|||||||
<section v-else class="mobile-list">
|
<section v-else class="mobile-list">
|
||||||
<article v-for="item in activeItems" :key="item.listing.id" class="mobile-card">
|
<article v-for="item in activeItems" :key="item.listing.id" class="mobile-card">
|
||||||
<RouterLink :to="detailPath(item)" class="mobile-card-main">
|
<RouterLink :to="detailPath(item)" class="mobile-card-main">
|
||||||
<AuthImage
|
<img
|
||||||
v-if="getCoinScreenshotURL(item.listing)"
|
v-if="item.listing.cover_url || item.listing.screenshot_urls?.[0]"
|
||||||
:source="getCoinScreenshotURL(item.listing)"
|
:src="item.listing.cover_url || item.listing.screenshot_urls?.[0]"
|
||||||
:alt="getListingTitle(item.listing)"
|
:alt="getListingTitle(item.listing)"
|
||||||
fit="cover"
|
loading="lazy"
|
||||||
|
decoding="async"
|
||||||
/>
|
/>
|
||||||
<div v-else class="mobile-cover-empty">HFB</div>
|
<div v-else class="mobile-cover-empty">HFB</div>
|
||||||
<div class="mobile-info">
|
<div class="mobile-info">
|
||||||
|
|||||||
@@ -728,20 +728,6 @@
|
|||||||
display: block;
|
display: block;
|
||||||
}
|
}
|
||||||
|
|
||||||
.mobile-card-media .auth-image-fallback {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
min-height: 100%;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0;
|
|
||||||
background: #f1f5f9;
|
|
||||||
color: #94a3b8;
|
|
||||||
font-size: 11px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.media-empty {
|
.media-empty {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -53,7 +53,6 @@ import {
|
|||||||
hasAcceleratedSaleRatio,
|
hasAcceleratedSaleRatio,
|
||||||
hasGiftResources,
|
hasGiftResources,
|
||||||
isTopTierInsurance,
|
isTopTierInsurance,
|
||||||
getCoinScreenshotURL,
|
|
||||||
getCoinWan,
|
getCoinWan,
|
||||||
formatHafCoinM,
|
formatHafCoinM,
|
||||||
readAssetString,
|
readAssetString,
|
||||||
@@ -1046,13 +1045,12 @@ syncMobileHomeQuery()
|
|||||||
<div class="mobile-card-body">
|
<div class="mobile-card-body">
|
||||||
<!-- 左侧封面 -->
|
<!-- 左侧封面 -->
|
||||||
<div class="mobile-card-media">
|
<div class="mobile-card-media">
|
||||||
<AuthImage
|
<img
|
||||||
v-if="getCoinScreenshotURL(item)"
|
v-if="item.cover_url || item.screenshot_urls?.[0]"
|
||||||
:source="getCoinScreenshotURL(item)"
|
:src="item.cover_url || item.screenshot_urls?.[0]"
|
||||||
:alt="formatListingCode(item)"
|
:alt="formatListingCode(item)"
|
||||||
image-class="mobile-media-img"
|
loading="lazy"
|
||||||
fallback-class="mobile-media-fallback"
|
decoding="async"
|
||||||
fit="cover"
|
|
||||||
/>
|
/>
|
||||||
<div v-else class="media-empty">HFB</div>
|
<div v-else class="media-empty">HFB</div>
|
||||||
|
|
||||||
|
|||||||
@@ -411,28 +411,8 @@ export function isTopTierInsurance(value: string | undefined | null): boolean {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取账号封面/纯币截图(仅展示一张,优先纯币截图,其次封面图,其次首张截图)
|
* 获取账号封面图(优先公开缩略图接口,其次首张截图)
|
||||||
*/
|
*/
|
||||||
export function getCoinScreenshotURL(item: Listing): string {
|
export function getCoinScreenshotURL(item: Listing): string {
|
||||||
const groups = item.asset_summary?.screenshot_groups
|
|
||||||
if (typeof groups === 'object' && groups !== null) {
|
|
||||||
const groupRecord = groups as Record<string, unknown>
|
|
||||||
const orderedKeys = ['coin', 'tencentSecurity', 'skin', 'gameId', 'totalAsset']
|
|
||||||
for (const key of orderedKeys) {
|
|
||||||
const urls = groupRecord[key]
|
|
||||||
if (Array.isArray(urls)) {
|
|
||||||
const found = urls.find(url => typeof url === 'string' && Boolean(url))
|
|
||||||
if (found) return found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const key of Object.keys(groupRecord)) {
|
|
||||||
if (orderedKeys.includes(key)) continue
|
|
||||||
const urls = groupRecord[key]
|
|
||||||
if (Array.isArray(urls)) {
|
|
||||||
const found = urls.find(url => typeof url === 'string' && Boolean(url))
|
|
||||||
if (found) return found
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return item.cover_url || item.screenshot_urls?.[0] || ''
|
return item.cover_url || item.screenshot_urls?.[0] || ''
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user