import { ref, type Ref } from 'vue' import type { PageData } from '@/api/types' import { handledError } from '@/utils/error' /** * 分页列表状态管理。 * * 四个列表页(用户/角色/操作日志/登录日志)共用同一套分页交互,要点: * * - 「删除当前页最后一条后页码回退」是高频踩坑点:不回退就会停在空页 * - **请求序号防竞态**:快速翻页/改条件时并发请求乱序返回,只有最新 * 请求的响应允许写入状态,否则旧数据会覆盖新数据、与分页器错位 * - 请求失败不外抛(提示由拦截器统一完成),loading 仅由最新请求复位 */ export function usePagedList( fetcher: (params: F & { pageNum: number; pageSize: number }) => Promise>, getFilters: () => F, ) { const records = ref([]) as Ref const total = ref(0) const pageNum = ref(1) const pageSize = ref(10) const loading = ref(false) /** 自增请求序号:仅最新请求的响应可写入状态 */ let requestSeq = 0 /** 按当前页码与筛选条件加载 */ async function load(): Promise { const seq = ++requestSeq loading.value = true try { const data = await fetcher({ ...getFilters(), pageNum: pageNum.value, pageSize: pageSize.value, }) if (seq !== requestSeq) return // 已有更新的请求发出,丢弃过期响应 records.value = data.list total.value = data.total } catch (error) { handledError(error) // 失败提示由请求拦截器统一完成 } finally { if (seq === requestSeq) loading.value = false } } /** 条件查询:回到第一页再加载(停留旧页码会查到空页) */ function search(): void { pageNum.value = 1 void load() } function onPageChange(page: number): void { pageNum.value = page void load() } function onSizeChange(size: number): void { pageSize.value = size pageNum.value = 1 void load() } /** 删除后刷新:当前页已被删空且不在第一页时,先回退页码再加载 */ async function reloadAfterDelete(removedCount = 1): Promise { if (records.value.length - removedCount <= 0 && pageNum.value > 1) { pageNum.value -= 1 } await load() } return { records, total, pageNum, pageSize, loading, load, search, onPageChange, onSizeChange, reloadAfterDelete, } }