Files
hfb_sys/frontend/src/stores/distributionSite.ts
T
yml2213andCursor 4f69657017 feat(distribution): 分站首页改为联名标题并同步手机轮播
首页主标题显示为站名×大锤商行,手机端使用与电脑端相同的分站自定义海报。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-05 14:46:28 +08:00

307 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { defineStore } from 'pinia'
import { apiClient } from '@/shared/api/client'
import type { ApiResponse } from '@/shared/types/types'
import { normalizeAnnouncements } from '@/shared/utils/announcements'
import {
defaultStorefrontText,
mergeStorefrontText,
type StorefrontText,
} from '@/shared/utils/storefrontText'
export interface DistributionBanner {
image_url: string
link_url?: 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 {
icp?: string
copyright?: string
contact?: string
disclaimer?: string
}
export interface DistributionSiteInfo {
site_code: string
site_name: string
domain: string
status: string
display_account_enabled: boolean
default_support_group_id?: number | null
logo_url?: string
favicon_url?: string
banners_json?: string
announcements_json?: string
footer_info_json?: string
storefront_text_json?: string
receive_ratio_delta?: number
receive_cost_delta?: number
}
/**
* 获取当前上下文生效的分销站点编码
* 优先级:
* 1. 环境变量 VITE_DISTRIBUTION_SITE_CODE(针对独立分销前台开发端口,如 5191)
* 2. 当前标签页 sessionStorage 中的 hfb_site_code(避免跨 Tab 污染主站)
* 3. 本地二级域名(如 test_1.localhost:5189)
*
* 注意:独立域名下的站点编码只来自 sessionStorage,由 initSite() 通过
* /distribution/site-info?domain= 探测后写入。历史上曾读取 localStorage 缓存,
* 会让同一浏览器访问主站时串到分站站点码(把主站发布记到分站/反之),因此已移除。
*/
export function getEffectiveSiteCode(): string {
if (typeof window === 'undefined') return ''
const envCode = ((import.meta.env.VITE_DISTRIBUTION_SITE_CODE as string) || '').trim()
if (envCode) return envCode
const sessionCode = sessionStorage.getItem('hfb_site_code') || ''
if (sessionCode) return sessionCode
const hostname = window.location.hostname
if (hostname && hostname.endsWith('.localhost')) {
return hostname.replace(/\.localhost$/, '')
}
return ''
}
export const useDistributionSiteStore = defineStore('distributionSite', {
state: () => {
const code = getEffectiveSiteCode()
return {
siteCode: code,
siteName: sessionStorage.getItem('hfb_site_name') || '大锤商行',
domain: '',
isDistribution: Boolean(code),
displayAccountEnabled: true,
defaultSupportGroupId: null as number | null,
logoUrl: '',
faviconUrl: '',
banners: [] as DistributionBanner[],
announcements: [] as string[],
footerInfo: null as DistributionFooterInfo | null,
storefrontText: { ...defaultStorefrontText } as StorefrontText,
receiveRatioDelta: 0,
receiveCostDelta: 0,
initialized: false,
}
},
getters: {
/** 品牌标识字符:分站取站名首字,主站沿用「锤」。 */
brandMark(state): string {
return state.isDistribution && state.siteName ? state.siteName.slice(0, 1) : '锤'
},
/** 分站首页主标题:站名与主站联名。 */
coBrandName(state): string {
return formatDistributionCoBrandName(state.siteName, state.isDistribution)
},
},
actions: {
async initSite() {
const envCode = ((import.meta.env.VITE_DISTRIBUTION_SITE_CODE as string) || '').trim()
const hostname = window.location.hostname
const urlParams = new URLSearchParams(window.location.search)
const querySite = (
urlParams.get('site') ||
urlParams.get('site_code') ||
urlParams.get('distribution_site_code') ||
''
).trim()
// 1. 显式清除或重置为主站
if (querySite === 'main' || querySite === 'clear' || querySite === '0') {
this.resetToMainSite()
this.initialized = true
return
}
// 2. 识别目标分销站点编码
let targetCode: string
if (querySite) {
targetCode = querySite
sessionStorage.setItem('hfb_site_code', querySite)
} else if (envCode) {
targetCode = envCode
sessionStorage.setItem('hfb_site_code', envCode)
} else if (hostname && hostname.endsWith('.localhost')) {
targetCode = hostname.replace(/\.localhost$/, '')
sessionStorage.setItem('hfb_site_code', targetCode)
} else {
targetCode = sessionStorage.getItem('hfb_site_code') || ''
// 若在普通 localhost 且无 envCode、无 query、无 sessionStorage,则视为纯主站,清理旧的 localStorage 串扰
if (!targetCode && (hostname === 'localhost' || hostname === '127.0.0.1')) {
this.resetToMainSite()
this.initialized = true
return
}
}
let queryUrl = ''
if (targetCode) {
queryUrl = `/distribution/site-info?code=${encodeURIComponent(targetCode)}`
} else if (hostname && hostname !== 'localhost' && hostname !== '127.0.0.1') {
queryUrl = `/distribution/site-info?domain=${encodeURIComponent(hostname)}`
}
if (!queryUrl) {
this.resetToMainSite()
this.initialized = true
return
}
try {
const { data } = await apiClient.get<ApiResponse<DistributionSiteInfo>>(queryUrl, {
silent: true,
skipErrorHandler: true,
})
const info = data.data
if (info && info.site_code && info.status === 'active') {
this.siteCode = info.site_code
this.siteName = info.site_name || info.site_code
this.domain = info.domain || ''
this.isDistribution = true
this.displayAccountEnabled = info.display_account_enabled ?? true
this.defaultSupportGroupId = info.default_support_group_id ?? null
this.logoUrl = info.logo_url || ''
this.faviconUrl = info.favicon_url || ''
if (info.banners_json) {
try {
this.banners = JSON.parse(info.banners_json)
} catch {
this.banners = []
}
} else {
this.banners = []
}
if (info.announcements_json) {
try {
// 站点公告存的是 { title, content } 对象数组,跑马灯只认文本,必须先归一化,
// 否则 Vue 会把对象 stringify 成 {"title":"…","content":""} 原文展示。
this.announcements = normalizeAnnouncements(JSON.parse(info.announcements_json))
} catch {
this.announcements = []
}
} else {
this.announcements = []
}
if (info.footer_info_json) {
try {
this.footerInfo = JSON.parse(info.footer_info_json)
} catch {
this.footerInfo = null
}
} else {
this.footerInfo = null
}
if (info.storefront_text_json) {
try {
this.storefrontText = mergeStorefrontText(JSON.parse(info.storefront_text_json))
} catch {
this.storefrontText = { ...defaultStorefrontText }
}
} else {
this.storefrontText = { ...defaultStorefrontText }
}
this.receiveRatioDelta = Math.max(0, Number(info.receive_ratio_delta || 0))
this.receiveCostDelta = Math.max(0, Number(info.receive_cost_delta || 0))
sessionStorage.setItem('hfb_site_code', this.siteCode)
sessionStorage.setItem('hfb_site_name', this.siteName)
// 更新页面标题和 Favicon
document.title = `【${this.siteName}】租号商城 - 精选游戏账号平台`
if (this.faviconUrl && typeof document !== 'undefined') {
let link = document.querySelector("link[rel*='icon']") as HTMLLinkElement | null
if (!link) {
link = document.createElement('link')
link.rel = 'icon'
document.head.appendChild(link)
}
link.href = this.faviconUrl
}
} else {
this.resetToMainSite()
}
} catch {
// 若站点编码无效或未找到,自动回退为主站
this.resetToMainSite()
} finally {
this.initialized = true
}
},
resetToMainSite() {
this.siteCode = ''
this.siteName = '大锤商行'
this.domain = ''
this.isDistribution = false
this.displayAccountEnabled = true
this.defaultSupportGroupId = null
this.logoUrl = ''
this.faviconUrl = ''
this.banners = []
this.announcements = []
this.footerInfo = null
this.storefrontText = { ...defaultStorefrontText }
this.receiveRatioDelta = 0
this.receiveCostDelta = 0
sessionStorage.removeItem('hfb_site_code')
sessionStorage.removeItem('hfb_site_name')
localStorage.removeItem('hfb_site_code')
localStorage.removeItem('hfb_site_name')
document.title = '大锤商行 - 官方租号平台'
},
},
})