Files
hfb_sys/frontend/src/features/listings/composables/useListingQuery.ts
T
yml2213andClaude Opus 4.7 405abfa4f7 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>
2026-06-04 08:56:42 +08:00

123 lines
3.5 KiB
TypeScript

import { ref, onMounted, onBeforeUnmount, watch, type Ref } from 'vue'
import { fetchListingsPage, type Listing, type PublicListingQuery } from '../api/listings'
import type { HomeFilters } from './useHomeFilters'
const homePageSize = 12
export function useListingQuery(
filters: HomeFilters,
sortBy: Ref<string>,
activeZone: Ref<string>
) {
const loading = ref(false)
const loadingMore = ref(false)
const listings = ref<Listing[]>([])
const totalListings = ref(0)
const zoneCounts = ref<Record<string, number>>({})
const currentPage = ref(1)
const hasMoreListings = ref(true)
let listingRequestSeq = 0
function buildListingQuery(page: number): PublicListingQuery {
return {
page,
page_size: homePageSize,
keyword: filters.keyword.trim(),
sort: sortBy.value,
zone: activeZone.value,
server: filters.server,
region: filters.region,
login_method: filters.loginMethod,
rank: filters.rank,
insurance: filters.insurance,
stamina: filters.stamina,
load: filters.load,
skin_group: filters.skinGroup,
skin_name: filters.skinName,
min_coin: filters.minCoin,
max_coin: filters.maxCoin,
min_price: filters.minPrice,
max_price: filters.maxPrice,
min_deposit: filters.minDeposit,
max_deposit: filters.maxDeposit,
min_total: filters.minTotal,
max_total: filters.maxTotal,
min_fire_level: filters.minFireLevel,
max_fire_level: filters.maxFireLevel,
}
}
function listingQuerySignature() {
return JSON.stringify(buildListingQuery(1))
}
async function loadListingsPage(reset = false) {
if ((loadingMore.value && !reset) || (!hasMoreListings.value && !reset)) return
const requestSeq = ++listingRequestSeq
if (reset) {
currentPage.value = 1
hasMoreListings.value = true
}
loadingMore.value = true
try {
const page = await fetchListingsPage(buildListingQuery(currentPage.value))
if (requestSeq !== listingRequestSeq) return
listings.value = reset ? page.items : [...listings.value, ...page.items]
totalListings.value = page.total
zoneCounts.value = page.zone_counts
hasMoreListings.value = listings.value.length < page.total
currentPage.value = page.page + 1
requestAnimationFrame(handleWindowScroll)
} catch {
if (reset) {
listings.value = []
totalListings.value = 0
zoneCounts.value = {}
hasMoreListings.value = false
}
} finally {
if (requestSeq === listingRequestSeq) {
loadingMore.value = false
}
}
}
function handleWindowScroll() {
if (window.innerHeight + window.scrollY < document.documentElement.scrollHeight - 480) return
loadListingsPage(false)
}
function zoneCount(key: string) {
if (key === 'all') return zoneCounts.value.all ?? totalListings.value
return zoneCounts.value[key] ?? 0
}
onMounted(() => {
window.addEventListener('scroll', handleWindowScroll, { passive: true })
})
onBeforeUnmount(() => {
window.removeEventListener('scroll', handleWindowScroll)
})
// 使用防抖优化搜索
let debounceTimer: ReturnType<typeof setTimeout> | null = null
watch(() => listingQuerySignature(), () => {
if (debounceTimer) clearTimeout(debounceTimer)
debounceTimer = setTimeout(() => {
loadListingsPage(true)
}, 300)
})
return {
loading,
loadingMore,
listings,
totalListings,
zoneCounts,
hasMoreListings,
loadListingsPage,
zoneCount,
}
}