PayrollManagementSystem/frontend/src/composables/usePagedList.ts
liuwenxin 8355e0cc7d feat(system): 完成前端系统管理(布局框架、三个管理页面、e2e)
- BasicLayout:侧边栏/顶栏/面包屑/移动端抽屉;菜单从路由表派生;
  路由守卫新增 requiresAdmin 层(菜单隐藏 + 直连 URL 弹回)
- 用户/角色/日志三个页面:分页请求序号防竞态、确认动作流抽 useConfirmAction、
  时间范围配 default-time、日志详情按需懒加载并缓存
- 空 catch 统一改 handledError 显式声明;词表/选项常量集中(与后端对齐),
  「停用」文案全站统一为「禁用」
- 品牌色提升为全局 token,Element Plus 主色指向赭金
- e2e:公共工具抽 common.py;系统管理 13 项(含详情懒加载脱敏断言),
  管理员密码改 E2E_ADMIN_PASSWORD 环境变量;login_flow 8 项回归通过

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-10 16:28:53 +08:00

87 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { ref, type Ref } from 'vue'
import type { PageData } from '@/api/types'
import { handledError } from '@/utils/error'
/**
* 分页列表状态管理。
*
* 四个列表页(用户/角色/操作日志/登录日志)共用同一套分页交互,要点:
*
* - 「删除当前页最后一条后页码回退」是高频踩坑点:不回退就会停在空页
* - **请求序号防竞态**:快速翻页/改条件时并发请求乱序返回,只有最新
* 请求的响应允许写入状态,否则旧数据会覆盖新数据、与分页器错位
* - 请求失败不外抛(提示由拦截器统一完成),loading 仅由最新请求复位
*/
export function usePagedList<T, F extends object>(
fetcher: (params: F & { pageNum: number; pageSize: number }) => Promise<PageData<T>>,
getFilters: () => F,
) {
const records = ref([]) as Ref<T[]>
const total = ref(0)
const pageNum = ref(1)
const pageSize = ref(10)
const loading = ref(false)
/** 自增请求序号:仅最新请求的响应可写入状态 */
let requestSeq = 0
/** 按当前页码与筛选条件加载 */
async function load(): Promise<void> {
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<void> {
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,
}
}