feat(distribution): 独立分销前台商城多租户体系、货源与订单数据严格隔离

This commit is contained in:
yml2213
2026-09-07 17:18:14 +08:00
parent 550b42eb22
commit ecaf15417b
34 changed files with 2397 additions and 119 deletions
@@ -106,6 +106,7 @@ export interface PublicListingQuery {
max_daily_loss?: number
min_secret_kd?: number
max_secret_kd?: number
distribution_site_code?: string
[key: `resource_${string}_min`]: number | undefined
[key: `resource_${string}_max`]: number | undefined
}
@@ -124,8 +125,13 @@ export async function fetchListings(query: PublicListingQuery = {}) {
}
export async function fetchListingsPage(query: PublicListingQuery = {}) {
const siteCode =
query.distribution_site_code ||
(typeof window !== 'undefined' ? localStorage.getItem('hfb_site_code') : '') ||
undefined
const effectiveQuery = siteCode ? { distribution_site_code: siteCode, ...query } : query
const params = Object.fromEntries(
Object.entries(query).filter(
Object.entries(effectiveQuery).filter(
([, value]) => value !== '' && value !== undefined && value !== null
)
)
@@ -40,8 +40,20 @@ import HomeStats from '../components/HomeStats.vue'
import HomeFilters from '../components/HomeFilters.vue'
import HomeZones from '../components/HomeZones.vue'
import ListingCard from '../components/ListingCard.vue'
import { useDistributionSiteStore } from '@/stores/distributionSite'
const siteStore = useDistributionSiteStore()
const announcements = ref<string[]>(defaultHomeAnnouncements)
const displayAnnouncements = computed(() => {
if (siteStore.isDistribution) {
return [
`【${siteStore.siteName}】官方认证独立分销站:账号实时同步主站,官方质检,安全秒交接!`,
...announcements.value,
]
}
return announcements.value
})
const banners = ref<HomeBannerSlide[]>(defaultHomeBanners)
const awRecycle = ref<AWRecycleConfig>({ ...defaultAWRecycleConfig })
const awRecycleDialogVisible = ref(false)
@@ -354,9 +366,46 @@ loadHome()
<template>
<section class="pc-home-redesign">
<HomeAnnouncement :announcements="announcements" />
<HomeAnnouncement :announcements="displayAnnouncements" />
<main class="home-content">
<!-- 分销站专属标识 Banner -->
<div v-if="siteStore.isDistribution" class="dist-portal-banner">
<div class="dist-portal-content">
<div class="dist-portal-tag">
<span class="dist-pulse"></span>
官方分销认证
</div>
<h2 class="dist-portal-title">{{ siteStore.siteName }} 专区</h2>
<p class="dist-portal-desc">
全站上架账号均由平台统一质检与自动交付,订单数据独立隔离,为您提供安全无忧的租号体验。
</p>
</div>
<div class="dist-portal-badges">
<div class="dist-badge-item">
<span class="badge-icon">🛡️</span>
<div class="badge-text">
<strong>官方质检</strong>
<small>账号状态实时同步</small>
</div>
</div>
<div class="dist-badge-item">
<span class="badge-icon">⚡</span>
<div class="badge-text">
<strong>极速交付</strong>
<small>自动交接 快速上号</small>
</div>
</div>
<div class="dist-badge-item">
<span class="badge-icon">📑</span>
<div class="badge-text">
<strong>独立账单</strong>
<small>专属订单 隐私隔离</small>
</div>
</div>
</div>
</div>
<div class="hero-section">
<HomeBanner :banners="banners" />
<HomeStats :stats="statCards" />
@@ -367,7 +416,7 @@ loadHome()
<strong>租号大厅</strong>
<small>高哈夫币 · 安全交接 · 随租随玩</small>
</RouterLink>
<RouterLink class="biz-entry crash" to="/crash">
<RouterLink v-if="!siteStore.isDistribution" class="biz-entry crash" to="/crash">
<strong>撞车 <em>NEW</em></strong>
<small>选购商品 · 支付后进群联系客服</small>
</RouterLink>
@@ -697,4 +746,112 @@ loadHome()
font-weight: 800;
cursor: pointer;
}
.dist-portal-banner {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
padding: 24px 32px;
border-radius: 20px;
background: linear-gradient(135deg, #0f172a 0%, #1e293b 50%, #0f172a 100%);
border: 1px solid rgba(255, 255, 255, 0.08);
box-shadow: 0 10px 30px rgba(15, 23, 42, 0.12);
color: #ffffff;
}
.dist-portal-content {
display: flex;
flex-direction: column;
gap: 8px;
max-width: 600px;
}
.dist-portal-tag {
display: inline-flex;
align-items: center;
gap: 6px;
width: fit-content;
padding: 3px 10px;
border-radius: 999px;
background: rgba(20, 119, 255, 0.2);
border: 1px solid rgba(20, 119, 255, 0.4);
color: #60a5fa;
font-size: 12px;
font-weight: 700;
letter-spacing: 0.5px;
}
.dist-pulse {
width: 6px;
height: 6px;
border-radius: 50%;
background: #38bdf8;
box-shadow: 0 0 8px #38bdf8;
}
.dist-portal-title {
margin: 0;
font-size: 24px;
font-weight: 800;
color: #f8fafc;
letter-spacing: -0.5px;
}
.dist-portal-desc {
margin: 0;
font-size: 14px;
line-height: 1.5;
color: #94a3b8;
}
.dist-portal-badges {
display: flex;
align-items: center;
gap: 16px;
}
.dist-badge-item {
display: flex;
align-items: center;
gap: 10px;
padding: 12px 18px;
border-radius: 14px;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.08);
backdrop-filter: blur(8px);
}
.badge-icon {
font-size: 22px;
}
.badge-text {
display: flex;
flex-direction: column;
gap: 2px;
}
.badge-text strong {
font-size: 14px;
font-weight: 700;
color: #f1f5f9;
}
.badge-text small {
font-size: 11px;
color: #64748b;
}
@media (max-width: 960px) {
.dist-portal-banner {
flex-direction: column;
align-items: flex-start;
}
.dist-portal-badges {
width: 100%;
flex-direction: column;
align-items: stretch;
}
}
</style>
@@ -60,11 +60,30 @@
white-space: nowrap;
}
.mobile-brand-title-row {
display: flex;
align-items: center;
gap: 6px;
}
.mobile-brand strong {
color: #0f172a;
font-size: 17px;
}
.mobile-brand-badge {
display: inline-flex;
align-items: center;
padding: 1px 5px;
font-size: 10px;
font-weight: 700;
color: #ff6a00;
background: rgba(255, 106, 0, 0.1);
border: 1px solid rgba(255, 106, 0, 0.25);
border-radius: 999px;
line-height: 1.3;
}
.mobile-brand small {
color: #64748b;
font-size: 13px;
@@ -212,6 +231,46 @@
padding: 12px;
}
.mobile-dist-card {
margin-bottom: 12px;
padding: 12px 14px;
border-radius: 12px;
background: linear-gradient(135deg, #1e293b 0%, #0f172a 100%);
color: #fff;
box-shadow: 0 4px 12px rgba(15, 23, 42, 0.12);
}
.dist-card-top {
display: flex;
align-items: center;
gap: 8px;
}
.dist-card-badge {
display: inline-block;
padding: 2px 8px;
border-radius: 999px;
background: rgba(20, 119, 255, 0.25);
border: 1px solid rgba(20, 119, 255, 0.4);
color: #60a5fa;
font-size: 10px;
font-weight: 700;
letter-spacing: 0.2px;
}
.dist-card-name {
font-size: 15px;
font-weight: 800;
color: #f8fafc;
}
.dist-card-sub {
margin: 6px 0 0;
font-size: 12px;
color: #94a3b8;
line-height: 1.4;
}
.banner-swipe {
margin-bottom: 12px;
border-radius: 8px;
@@ -55,11 +55,13 @@ import {
hasGiftResources,
} from '@/shared/utils/listingDisplay'
import { buildMobileHomeListSignature, useMobileHomeCacheStore } from '@/stores/mobileHomeCache'
import { useDistributionSiteStore } from '@/stores/distributionSite'
import { useSessionStore } from '@/stores/session'
const router = useRouter()
const route = useRoute()
const session = useSessionStore()
const siteStore = useDistributionSiteStore()
const homeCache = useMobileHomeCacheStore()
const scrollEl = ref<HTMLElement | null>(null)
const loading = ref(false)
@@ -80,6 +82,15 @@ const rangeFilters = ref<Record<string, { min: string; max: string }>>({})
const refreshing = ref(false)
const searchValue = ref('')
const announcements = ref<string[]>(defaultHomeAnnouncements)
const displayAnnouncements = computed(() => {
if (siteStore.isDistribution) {
return [
`【${siteStore.siteName}】官方认证分销站,账号实时同步主站,售后有保障!`,
...announcements.value,
]
}
return announcements.value
})
const bannerSlides = ref<HomeBannerSlide[]>(defaultHomeBanners)
const awRecycle = ref<AWRecycleConfig>({ ...defaultAWRecycleConfig })
const awRecyclePreviewVisible = ref(false)
@@ -797,10 +808,15 @@ syncMobileHomeQuery()
<section class="mobile-hero">
<div class="mobile-topbar">
<div class="mobile-brand">
<span class="mobile-logo">锤</span>
<span class="mobile-logo">{{
siteStore.isDistribution ? siteStore.siteName.slice(0, 1) : '锤'
}}</span>
<div>
<strong>大锤商行</strong>
<small>哈夫币租号</small>
<div class="mobile-brand-title-row">
<strong>{{ siteStore.siteName }}</strong>
<span v-if="siteStore.isDistribution" class="mobile-brand-badge">官方分销</span>
</div>
<small>{{ siteStore.isDistribution ? '官方分销·平台保障' : '哈夫币租号' }}</small>
</div>
</div>
<button
@@ -844,7 +860,7 @@ syncMobileHomeQuery()
:show-indicators="false"
touchable
>
<van-swipe-item v-for="item in announcements" :key="item">
<van-swipe-item v-for="item in displayAnnouncements" :key="item">
<span>{{ item }}</span>
</van-swipe-item>
</van-swipe>
@@ -855,6 +871,15 @@ syncMobileHomeQuery()
<!-- ========== Content 区域 ========== -->
<van-pull-refresh v-model="refreshing" @refresh="onRefresh">
<section class="mobile-content">
<!-- 分销站移动端专属认证卡片 -->
<div v-if="siteStore.isDistribution" class="mobile-dist-card">
<div class="dist-card-top">
<span class="dist-card-badge">官方分销站</span>
<strong class="dist-card-name">{{ siteStore.siteName }}</strong>
</div>
<p class="dist-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">
@@ -891,7 +916,7 @@ syncMobileHomeQuery()
<strong>租号大厅</strong>
<small>高哈夫币 · 随租随玩</small>
</RouterLink>
<RouterLink class="biz-entry" to="/crash">
<RouterLink v-if="!siteStore.isDistribution" class="biz-entry" to="/crash">
<strong>撞车</strong>
<small>选购商品 · 一键找客服</small>
<em class="biz-entry-badge">NEW</em>
@@ -1082,7 +1107,7 @@ syncMobileHomeQuery()
>湘ICP备2025146668号</a
>
<span class="home-footer-divider">|</span>
<span class="home-footer-copyright">版权所有 ©2026 大锤网络游戏有限公司</span>
<span class="home-footer-copyright">版权所有 ©2026 {{ siteStore.siteName }}</span>
</footer>
</div>