import { ref, watch, onUnmounted } from 'vue' import { getActivityContributionsLatestApi } from '@/utils/api.js' /** * 贡献轮询逻辑 composable * @param {Ref} activityId - 活动ID * @param {boolean} isPageActive - 页面是否可见 * @returns {Object} records, loading, error, start, stop, refresh */ export function useContributionPolling(activityId, isPageActive) { const MAX_RECORDS = 5 const POLL_INTERVAL = 1000 // 每秒拉取 const RECORD_TTL = 5000 // 每条记录 5 秒后消失 const records = ref([]) const loading = ref(false) const error = ref(null) let latestId = 0 let pollingTimer = null let isPolling = false const recordTimers = new Map() // 重置记录计时器 function resetRecordTimer(record) { if (recordTimers.has(record.id)) { clearTimeout(recordTimers.get(record.id)) } const timer = setTimeout(() => { records.value = records.value.filter(r => r.id !== record.id) recordTimers.delete(record.id) }, RECORD_TTL) recordTimers.set(record.id, timer) } // 获取最新贡献记录 async function fetchLatest() { if (!activityId.value) return try { const res = await getActivityContributionsLatestApi(activityId.value, latestId, 1) // 处理 code 不为 200 的情况(静默忽略) if (res.code !== 200) return const newRecords = res.data?.records || [] if (newRecords.length === 0) return const newRecord = newRecords[0] // 检测到新记录(id > latestId) if (newRecord.id > latestId) { // 重置所有现有记录的计时器 records.value.forEach(resetRecordTimer) // 新记录插入到列表头部 records.value = [newRecord, ...records.value].slice(0, MAX_RECORDS) // 为新记录启动消失计时器 resetRecordTimer(newRecord) // 更新 latestId latestId = newRecord.id } } catch (e) { // 网络错误等,静默忽略,继续等待下一次轮询 console.error('[useContributionPolling] fetchLatest error:', e) } } // 全量拉取(首次或重新开始) async function fetchAll() { if (!activityId.value) return loading.value = true error.value = null try { const res = await getActivityContributionsLatestApi(activityId.value, 0, MAX_RECORDS) if (res.code !== 200) { throw new Error(res.message || '获取贡献记录失败') } const newRecords = res.data?.records || [] // 清除所有计时器 recordTimers.forEach(timer => clearTimeout(timer)) recordTimers.clear() // 重置列表 records.value = newRecords // 为每条记录启动计时器 newRecords.forEach(record => resetRecordTimer(record)) // 更新 latestId latestId = newRecords.length > 0 ? newRecords[newRecords.length - 1].id : 0 } catch (e) { error.value = e.message || '获取贡献记录失败' console.error('[useContributionPolling] fetchAll error:', e) } finally { loading.value = false } } // 开始轮询 function start() { if (pollingTimer) return if (!activityId.value) return isPolling = true latestId = 0 // 全量拉取首次 fetchAll() // 启动定时轮询 pollingTimer = setInterval(fetchLatest, POLL_INTERVAL) } // 停止轮询 function stop() { if (pollingTimer) { clearInterval(pollingTimer) pollingTimer = null } // 清除所有计时器 recordTimers.forEach(timer => clearTimeout(timer)) recordTimers.clear() isPolling = false records.value = [] latestId = 0 } // 强制刷新(全量拉取) function refresh() { stop() start() } // 监听页面可见性 watch(isPageActive, (active) => { if (active) { start() } else { stop() } }, { immediate: true }) // 监听 activityId 变化 watch(activityId, (newId, oldId) => { if (newId !== oldId) { stop() if (isPageActive.value && newId) { start() } } }) // 组件卸载时清理 onUnmounted(() => { stop() }) return { records, loading, error, start, stop, refresh } }