feat: P2阶段完成 - listings和auth模块迁移

## P2.1: 商品浏览模块(listings)

### 完整迁移(23个文件)
- API: listings.ts, listingOptions.ts, homeConfig.ts
- Views: 5个页面(HomeView, ListingsView, ListingDetailView + 移动端2个)
- Composables: 3个(useHomeFilters, useFilterOptions, useListingQuery)
- Components: 9个(ListingCard, 各种过滤器组件)
- Tests: 2个测试文件

**功能:**
- 首页浏览、商品列表、详情查看
- 高级筛选(服务器、等级、哈夫币、皮肤等)
- 排序和分区功能
- 横幅、公告、统计展示

**技术改进:**
- 更新所有导入路径到 @/shared/
- 建立清晰的模块导出

---

## P2.2: 用户认证模块(auth)

### 完整迁移(12个文件)
- API: auth.ts, realname.ts, notifications.ts
- Views: 8个页面(登录、注册、个人资料、实名认证、通知 + 移动端)
- 模块导出

**功能:**
- 用户注册、登录、Token管理
- 实名认证、风险检查
- 个人资料编辑、头像上传
- 消息/通知中心

**技术改进:**
- 统一 API 导入路径
- 类型安全增强

---

## 里程碑

🎉 **P2 阶段完成!**

**累计完成:** 6个模块
-  P0: shared(基础设施)- 22个文件
-  P1: wallet, chats, orders - 24个文件
-  P2: listings, auth - 35个文件

**总计:** 81个文件已迁移

**剩余:** P3阶段(seller, disputes, admin)+ 清理工作

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
yml2213
2026-06-04 08:56:42 +08:00
co-authored by Claude Opus 4.7
parent f415832c19
commit 405abfa4f7
35 changed files with 9932 additions and 0 deletions
@@ -0,0 +1,262 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { ElEmpty } from 'element-plus'
import {
defaultHomeAnnouncements,
defaultHomeBanners,
fetchMobileHomeConfig,
type HomeBannerSlide,
} from '@/api/homeConfig'
import {
emptyListingPublishOptions,
type ListingPublishOptions,
} from '@/api/listingOptions'
import { useHomeFilters } from '@/composables/home/useHomeFilters'
import { useListingQuery } from '@/composables/home/useListingQuery'
import HomeAnnouncement from './components/HomeAnnouncement.vue'
import HomeBanner from './components/HomeBanner.vue'
import HomeStats from './components/HomeStats.vue'
import HomeFilters from './components/HomeFilters.vue'
import HomeZonesAndSort from './components/HomeZonesAndSort.vue'
import ListingCard from './components/ListingCard.vue'
const announcements = ref<string[]>(defaultHomeAnnouncements)
const banners = ref<HomeBannerSlide[]>(defaultHomeBanners)
const publishOptions = ref<ListingPublishOptions>(emptyListingPublishOptions)
const sortBy = ref('recommended')
const activeZone = ref('all')
const {
filters,
activeFilterPopover,
regionOptions,
loginMethodOptions,
skinFilterGroups,
skinChipLabel,
resetFilters,
setFilterPopover,
closeFilterPopover,
} = useHomeFilters(publishOptions, computed(() => listings.value))
const {
loading,
loadingMore,
listings,
totalListings,
zoneCounts,
hasMoreListings,
loadListingsPage,
zoneCount,
} = useListingQuery(filters, sortBy, activeZone)
const statCards = computed(() => [
{ label: '可租账号', value: `${totalListings.value}`, hint: '当前筛选结果' },
{
label: '高哈夫币',
value: `${zoneCount('highCoin')}`,
hint: '100M 以上',
},
{
label: '账密登录',
value: `${zoneCount('password')}`,
hint: '交接更快',
},
])
const zoneOptions = computed(() => [
{
key: 'all',
label: '全部专区',
hint: '当前可租账号',
count: zoneCount('all'),
},
{
key: 'sale',
label: '特惠专区',
hint: '价格更划算',
count: zoneCount('sale'),
},
{
key: 'gift',
label: '赠送专区',
hint: '含赠送物品',
count: zoneCount('gift'),
},
{
key: 'night',
label: '夜间专区',
hint: '夜间也好上号',
count: zoneCount('night'),
},
{
key: 'password',
label: '账密专区',
hint: '交接更快',
count: zoneCount('password'),
},
{
key: 'highCoin',
label: '高币专区',
hint: '100M 以上',
count: zoneCount('highCoin'),
},
])
async function loadHome() {
loading.value = true
try {
const [, config] = await Promise.all([
loadListingsPage(true),
fetchMobileHomeConfig(),
])
announcements.value = config.announcements
banners.value = config.banners
publishOptions.value = config.publish_options
} catch {
announcements.value = defaultHomeAnnouncements
banners.value = defaultHomeBanners
publishOptions.value = emptyListingPublishOptions
} finally {
loading.value = false
}
}
function updateFilters(partial: Partial<typeof filters>) {
Object.assign(filters, partial)
}
function handleResetFilters() {
resetFilters()
activeZone.value = 'all'
sortBy.value = 'recommended'
}
loadHome()
</script>
<template>
<section class="pc-home-redesign">
<HomeAnnouncement :announcements="announcements" />
<main class="home-content">
<div class="hero-section">
<HomeBanner :banners="banners" />
<HomeStats :stats="statCards" />
</div>
<HomeFilters
:filters="filters"
:total-listings="totalListings"
:publish-options="publishOptions"
:region-options="regionOptions"
:login-method-options="loginMethodOptions"
:skin-filter-groups="skinFilterGroups"
:skin-chip-label="skinChipLabel"
:active-filter-popover="activeFilterPopover"
@update:filters="updateFilters"
@reset="handleResetFilters"
@set-filter-popover="setFilterPopover"
@close-filter-popover="closeFilterPopover"
/>
<div class="list-head">
<div class="zone-head">
<p class="eyebrow">Account Zone</p>
<h2>账号专区</h2>
</div>
</div>
<HomeZonesAndSort
:zones="zoneOptions"
:active-zone="activeZone"
:sort-by="sortBy"
@update:active-zone="activeZone = $event"
@update:sort-by="sortBy = $event"
/>
<el-empty
v-if="!loading && listings.length === 0"
description="没有符合条件的账号"
/>
<div v-else v-loading="loading" class="enhanced-desktop-list">
<ListingCard
v-for="item in listings"
:key="item.id"
:listing="item"
/>
</div>
<div v-if="!loading && listings.length" class="infinite-load-state">
<span v-if="loadingMore">正在加载更多账号...</span>
<span v-else-if="!hasMoreListings">已经到底了</span>
</div>
</main>
</section>
</template>
<style scoped>
.pc-home-redesign {
display: grid;
gap: 20px;
width: 100%;
max-width: 1720px;
min-width: 0;
margin: 0 auto;
overflow-x: clip;
}
.home-content {
display: grid;
gap: 20px;
min-width: 0;
}
.infinite-load-state {
padding: 6px 0 18px;
color: #94a3b8;
font-size: 13px;
font-weight: 700;
text-align: center;
}
.hero-section {
display: grid;
grid-template-columns: minmax(0, 1fr) 320px;
gap: 20px;
min-width: 0;
}
.list-head {
display: flex;
justify-content: space-between;
align-items: flex-end;
}
.zone-head {
display: flex;
flex-direction: column;
gap: 4px;
}
.eyebrow {
margin: 0;
font-size: 12px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #ff6a00;
}
.zone-head h2 {
margin: 0;
font-size: 28px;
font-weight: 900;
color: #17233d;
}
.enhanced-desktop-list {
display: grid;
gap: 16px;
min-width: 0;
}
</style>