首页主标题显示为站名×大锤商行,手机端使用与电脑端相同的分站自定义海报。 Co-authored-by: Cursor <cursoragent@cursor.com>
307 lines
10 KiB
TypeScript
307 lines
10 KiB
TypeScript
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 = '大锤商行 - 官方租号平台'
|
||
},
|
||
},
|
||
})
|