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>(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 = '大锤商行 - 官方租号平台' }, }, })