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, activeZone: Ref ) { const loading = ref(false) const loadingMore = ref(false) const listings = ref([]) const totalListings = ref(0) const zoneCounts = ref>({}) 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 | null = null watch(() => listingQuerySignature(), () => { if (debounceTimer) clearTimeout(debounceTimer) debounceTimer = setTimeout(() => { loadListingsPage(true) }, 300) }) return { loading, loadingMore, listings, totalListings, zoneCounts, hasMoreListings, loadListingsPage, zoneCount, } }