feat(distribution): 分站首页改为联名标题并同步手机轮播
首页主标题显示为站名×大锤商行,手机端使用与电脑端相同的分站自定义海报。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -40,7 +40,7 @@ import HomeBanner from '../components/HomeBanner.vue'
|
||||
import HomeFilters from '../components/HomeFilters.vue'
|
||||
import HomeZones from '../components/HomeZones.vue'
|
||||
import ListingCard from '../components/ListingCard.vue'
|
||||
import { useDistributionSiteStore } from '@/stores/distributionSite'
|
||||
import { mapDistributionHomeBanners, useDistributionSiteStore } from '@/stores/distributionSite'
|
||||
|
||||
const siteStore = useDistributionSiteStore()
|
||||
|
||||
@@ -59,28 +59,8 @@ const displayAnnouncements = computed(() => {
|
||||
})
|
||||
const banners = ref<HomeBannerSlide[]>(defaultHomeBanners)
|
||||
const displayBanners = computed<HomeBannerSlide[]>(() => {
|
||||
if (siteStore.isDistribution && siteStore.banners && siteStore.banners.length > 0) {
|
||||
return siteStore.banners.map(b => {
|
||||
const imageUrl = b.image_url?.trim() || ''
|
||||
// 整张海报已含文案。再叠标题会触发整幅压暗遮罩,左右两侧颜色会被压暗。
|
||||
if (imageUrl) {
|
||||
return {
|
||||
eyebrow: '',
|
||||
title: '',
|
||||
badge: '本店精选',
|
||||
pill: '',
|
||||
tone: 'gold',
|
||||
image_url: imageUrl,
|
||||
}
|
||||
}
|
||||
return {
|
||||
eyebrow: siteStore.siteName,
|
||||
title: b.title || '精选游戏账号 · 随租随玩',
|
||||
badge: '本店精选',
|
||||
pill: '官方保障',
|
||||
tone: 'gold',
|
||||
}
|
||||
})
|
||||
if (siteStore.isDistribution && siteStore.banners.length > 0) {
|
||||
return mapDistributionHomeBanners(siteStore.banners, siteStore.siteName)
|
||||
}
|
||||
return banners.value
|
||||
})
|
||||
@@ -461,7 +441,7 @@ if (!restored) {
|
||||
<span class="dist-pulse"></span>
|
||||
{{ siteStore.storefrontText.hero_badge }}
|
||||
</div>
|
||||
<h2 class="dist-portal-title">{{ siteStore.siteName }}</h2>
|
||||
<h2 class="dist-portal-title">{{ siteStore.coBrandName }}</h2>
|
||||
<p class="dist-portal-desc">{{ siteStore.storefrontText.hero_desc }}</p>
|
||||
</div>
|
||||
<div class="dist-portal-badges">
|
||||
|
||||
@@ -328,6 +328,15 @@
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.mobile-banner.has-image:not(.has-copy) {
|
||||
background: #0f172a;
|
||||
}
|
||||
|
||||
.mobile-banner.has-image:not(.has-copy) .banner-image {
|
||||
object-fit: contain;
|
||||
background: #0f172a;
|
||||
}
|
||||
|
||||
.mobile-banner.has-image.has-copy::after {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
||||
@@ -60,7 +60,7 @@ import {
|
||||
getListingResources,
|
||||
} from '@/shared/utils/listingDisplay'
|
||||
import { buildMobileHomeListSignature, useMobileHomeCacheStore } from '@/stores/mobileHomeCache'
|
||||
import { useDistributionSiteStore } from '@/stores/distributionSite'
|
||||
import { mapDistributionHomeBanners, useDistributionSiteStore } from '@/stores/distributionSite'
|
||||
import { useSessionStore } from '@/stores/session'
|
||||
|
||||
const router = useRouter()
|
||||
@@ -97,6 +97,12 @@ const displayAnnouncements = computed(() => {
|
||||
return announcements.value
|
||||
})
|
||||
const bannerSlides = ref<HomeBannerSlide[]>(defaultHomeBanners)
|
||||
const displayBanners = computed<HomeBannerSlide[]>(() => {
|
||||
if (siteStore.isDistribution && siteStore.banners.length > 0) {
|
||||
return mapDistributionHomeBanners(siteStore.banners, siteStore.siteName)
|
||||
}
|
||||
return bannerSlides.value
|
||||
})
|
||||
const awRecycle = ref<AWRecycleConfig>({ ...defaultAWRecycleConfig })
|
||||
const awRecyclePreviewVisible = ref(false)
|
||||
const cooperationFeedback = ref<CooperationFeedbackConfig>({ ...defaultCooperationFeedbackConfig })
|
||||
@@ -507,7 +513,7 @@ function openCooperationFeedback() {
|
||||
function openBannerPreview(slide: HomeBannerSlide) {
|
||||
const url = slide.image_url?.trim()
|
||||
if (!url) return
|
||||
const images = bannerSlides.value
|
||||
const images = displayBanners.value
|
||||
.map(item => item.image_url?.trim() || '')
|
||||
.filter((item): item is string => Boolean(item))
|
||||
const startPosition = images.indexOf(url)
|
||||
@@ -903,14 +909,14 @@ if (!restoreHomeConfigFromCache()) {
|
||||
<div v-if="siteStore.isDistribution" class="mobile-dist-card">
|
||||
<div class="dist-card-top">
|
||||
<span class="dist-card-badge">{{ siteStore.storefrontText.hero_badge }}</span>
|
||||
<strong class="dist-card-name">{{ siteStore.siteName }}</strong>
|
||||
<strong class="dist-card-name">{{ siteStore.coBrandName }}</strong>
|
||||
</div>
|
||||
<p class="dist-card-sub">{{ siteStore.storefrontText.card_sub }}</p>
|
||||
</div>
|
||||
|
||||
<!-- Banner 轮播 -->
|
||||
<van-swipe class="banner-swipe" :autoplay="3600" lazy-render>
|
||||
<van-swipe-item v-for="slide in bannerSlides" :key="slide.title || slide.image_url">
|
||||
<van-swipe-item v-for="slide in displayBanners" :key="slide.image_url || slide.title">
|
||||
<div
|
||||
class="mobile-banner"
|
||||
:class="[
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { formatDistributionCoBrandName, mapDistributionHomeBanners } from './distributionSite'
|
||||
|
||||
describe('formatDistributionCoBrandName', () => {
|
||||
it('joins a distribution site with the main brand', () => {
|
||||
expect(formatDistributionCoBrandName('天域商行', true)).toBe('天域商行×大锤商行')
|
||||
})
|
||||
|
||||
it('keeps the main site name', () => {
|
||||
expect(formatDistributionCoBrandName('大锤商行', false)).toBe('大锤商行')
|
||||
})
|
||||
})
|
||||
|
||||
describe('mapDistributionHomeBanners', () => {
|
||||
it('uses the custom image without overlay copy', () => {
|
||||
const [slide] = mapDistributionHomeBanners(
|
||||
[{ image_url: 'https://cdn.example/poster.webp', title: '海报标题' }],
|
||||
'天域商行'
|
||||
)
|
||||
expect(slide?.image_url).toBe('https://cdn.example/poster.webp')
|
||||
expect(slide?.title).toBe('')
|
||||
expect(slide?.eyebrow).toBe('')
|
||||
expect(slide?.pill).toBe('')
|
||||
})
|
||||
})
|
||||
@@ -14,6 +14,50 @@ export interface DistributionBanner {
|
||||
title?: string
|
||||
}
|
||||
|
||||
export interface DistributionHomeBanner {
|
||||
eyebrow: string
|
||||
title: string
|
||||
badge: string
|
||||
pill: string
|
||||
tone: string
|
||||
image_url?: string
|
||||
}
|
||||
|
||||
/** 分站首页主标题:站名与主站联名。主站或站名已含主站名时保持原样。 */
|
||||
export function formatDistributionCoBrandName(siteName: string, isDistribution: boolean): string {
|
||||
const name = siteName.trim()
|
||||
if (!isDistribution) return name || '大锤商行'
|
||||
if (!name || name.includes('大锤商行')) return name || '大锤商行'
|
||||
return `${name}×大锤商行`
|
||||
}
|
||||
|
||||
/** 分站首页轮播:有图时不再叠标题,避免整幅压暗。 */
|
||||
export function mapDistributionHomeBanners(
|
||||
banners: DistributionBanner[],
|
||||
siteName: string
|
||||
): DistributionHomeBanner[] {
|
||||
return banners.map(banner => {
|
||||
const imageUrl = banner.image_url?.trim() || ''
|
||||
if (imageUrl) {
|
||||
return {
|
||||
eyebrow: '',
|
||||
title: '',
|
||||
badge: '本店精选',
|
||||
pill: '',
|
||||
tone: 'gold',
|
||||
image_url: imageUrl,
|
||||
}
|
||||
}
|
||||
return {
|
||||
eyebrow: siteName,
|
||||
title: banner.title?.trim() || '精选游戏账号 · 随租随玩',
|
||||
badge: '本店精选',
|
||||
pill: '官方保障',
|
||||
tone: 'gold',
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export interface DistributionFooterInfo {
|
||||
icp?: string
|
||||
copyright?: string
|
||||
@@ -92,6 +136,10 @@ export const useDistributionSiteStore = defineStore('distributionSite', {
|
||||
brandMark(state): string {
|
||||
return state.isDistribution && state.siteName ? state.siteName.slice(0, 1) : '锤'
|
||||
},
|
||||
/** 分站首页主标题:站名与主站联名。 */
|
||||
coBrandName(state): string {
|
||||
return formatDistributionCoBrandName(state.siteName, state.isDistribution)
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
Reference in New Issue
Block a user