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 HomeFilters from '../components/HomeFilters.vue'
|
||||||
import HomeZones from '../components/HomeZones.vue'
|
import HomeZones from '../components/HomeZones.vue'
|
||||||
import ListingCard from '../components/ListingCard.vue'
|
import ListingCard from '../components/ListingCard.vue'
|
||||||
import { useDistributionSiteStore } from '@/stores/distributionSite'
|
import { mapDistributionHomeBanners, useDistributionSiteStore } from '@/stores/distributionSite'
|
||||||
|
|
||||||
const siteStore = useDistributionSiteStore()
|
const siteStore = useDistributionSiteStore()
|
||||||
|
|
||||||
@@ -59,28 +59,8 @@ const displayAnnouncements = computed(() => {
|
|||||||
})
|
})
|
||||||
const banners = ref<HomeBannerSlide[]>(defaultHomeBanners)
|
const banners = ref<HomeBannerSlide[]>(defaultHomeBanners)
|
||||||
const displayBanners = computed<HomeBannerSlide[]>(() => {
|
const displayBanners = computed<HomeBannerSlide[]>(() => {
|
||||||
if (siteStore.isDistribution && siteStore.banners && siteStore.banners.length > 0) {
|
if (siteStore.isDistribution && siteStore.banners.length > 0) {
|
||||||
return siteStore.banners.map(b => {
|
return mapDistributionHomeBanners(siteStore.banners, siteStore.siteName)
|
||||||
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',
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
return banners.value
|
return banners.value
|
||||||
})
|
})
|
||||||
@@ -461,7 +441,7 @@ if (!restored) {
|
|||||||
<span class="dist-pulse"></span>
|
<span class="dist-pulse"></span>
|
||||||
{{ siteStore.storefrontText.hero_badge }}
|
{{ siteStore.storefrontText.hero_badge }}
|
||||||
</div>
|
</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>
|
<p class="dist-portal-desc">{{ siteStore.storefrontText.hero_desc }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="dist-portal-badges">
|
<div class="dist-portal-badges">
|
||||||
|
|||||||
@@ -328,6 +328,15 @@
|
|||||||
object-fit: cover;
|
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 {
|
.mobile-banner.has-image.has-copy::after {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
inset: 0;
|
inset: 0;
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ import {
|
|||||||
getListingResources,
|
getListingResources,
|
||||||
} from '@/shared/utils/listingDisplay'
|
} from '@/shared/utils/listingDisplay'
|
||||||
import { buildMobileHomeListSignature, useMobileHomeCacheStore } from '@/stores/mobileHomeCache'
|
import { buildMobileHomeListSignature, useMobileHomeCacheStore } from '@/stores/mobileHomeCache'
|
||||||
import { useDistributionSiteStore } from '@/stores/distributionSite'
|
import { mapDistributionHomeBanners, useDistributionSiteStore } from '@/stores/distributionSite'
|
||||||
import { useSessionStore } from '@/stores/session'
|
import { useSessionStore } from '@/stores/session'
|
||||||
|
|
||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
@@ -97,6 +97,12 @@ const displayAnnouncements = computed(() => {
|
|||||||
return announcements.value
|
return announcements.value
|
||||||
})
|
})
|
||||||
const bannerSlides = ref<HomeBannerSlide[]>(defaultHomeBanners)
|
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 awRecycle = ref<AWRecycleConfig>({ ...defaultAWRecycleConfig })
|
||||||
const awRecyclePreviewVisible = ref(false)
|
const awRecyclePreviewVisible = ref(false)
|
||||||
const cooperationFeedback = ref<CooperationFeedbackConfig>({ ...defaultCooperationFeedbackConfig })
|
const cooperationFeedback = ref<CooperationFeedbackConfig>({ ...defaultCooperationFeedbackConfig })
|
||||||
@@ -507,7 +513,7 @@ function openCooperationFeedback() {
|
|||||||
function openBannerPreview(slide: HomeBannerSlide) {
|
function openBannerPreview(slide: HomeBannerSlide) {
|
||||||
const url = slide.image_url?.trim()
|
const url = slide.image_url?.trim()
|
||||||
if (!url) return
|
if (!url) return
|
||||||
const images = bannerSlides.value
|
const images = displayBanners.value
|
||||||
.map(item => item.image_url?.trim() || '')
|
.map(item => item.image_url?.trim() || '')
|
||||||
.filter((item): item is string => Boolean(item))
|
.filter((item): item is string => Boolean(item))
|
||||||
const startPosition = images.indexOf(url)
|
const startPosition = images.indexOf(url)
|
||||||
@@ -903,14 +909,14 @@ if (!restoreHomeConfigFromCache()) {
|
|||||||
<div v-if="siteStore.isDistribution" class="mobile-dist-card">
|
<div v-if="siteStore.isDistribution" class="mobile-dist-card">
|
||||||
<div class="dist-card-top">
|
<div class="dist-card-top">
|
||||||
<span class="dist-card-badge">{{ siteStore.storefrontText.hero_badge }}</span>
|
<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>
|
</div>
|
||||||
<p class="dist-card-sub">{{ siteStore.storefrontText.card_sub }}</p>
|
<p class="dist-card-sub">{{ siteStore.storefrontText.card_sub }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Banner 轮播 -->
|
<!-- Banner 轮播 -->
|
||||||
<van-swipe class="banner-swipe" :autoplay="3600" lazy-render>
|
<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
|
<div
|
||||||
class="mobile-banner"
|
class="mobile-banner"
|
||||||
:class="[
|
: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
|
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 {
|
export interface DistributionFooterInfo {
|
||||||
icp?: string
|
icp?: string
|
||||||
copyright?: string
|
copyright?: string
|
||||||
@@ -92,6 +136,10 @@ export const useDistributionSiteStore = defineStore('distributionSite', {
|
|||||||
brandMark(state): string {
|
brandMark(state): string {
|
||||||
return state.isDistribution && state.siteName ? state.siteName.slice(0, 1) : '锤'
|
return state.isDistribution && state.siteName ? state.siteName.slice(0, 1) : '锤'
|
||||||
},
|
},
|
||||||
|
/** 分站首页主标题:站名与主站联名。 */
|
||||||
|
coBrandName(state): string {
|
||||||
|
return formatDistributionCoBrandName(state.siteName, state.isDistribution)
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
actions: {
|
actions: {
|
||||||
|
|||||||
Reference in New Issue
Block a user