Files
ojnext/docs/PAGINATION_REFACTOR_SUMMARY.md
yuetsh ad7ea92769
Some checks failed
Deploy / deploy (push) Has been cancelled
fix UI
2025-09-29 21:19:57 +08:00

4.4 KiB
Raw Blame History

分页组件重构总结

🎯 重构目标

通过创建 usePagination composable 来减少项目中分页相关的重复代码,统一分页逻辑。

📋 已完成的更新

1. 核心 Composable

  • 创建 usePagination composable (/shared/composables/pagination.ts)
    • 自动处理分页状态管理(页码、每页条数)
    • 自动同步 URL 查询参数
    • 支持自定义查询条件
    • 提供便捷的清空、重置方法
    • 完整的 TypeScript 类型支持

2. 前台用户页面

  • 问题列表页 (/oj/problem/list.vue)

    • 移除 25+ 行重复代码
    • 简化查询逻辑
    • 保留防抖搜索功能
  • 提交记录页 (/oj/submission/list.vue)

    • 简化复杂的查询条件处理
    • 统一 URL 同步逻辑
    • 保留所有筛选功能
  • 比赛列表页 (/oj/contest/list.vue)

    • 减少路由处理代码
    • 统一分页行为

3. 管理员页面

  • 用户管理页 (/admin/user/list.vue)

    • 简化用户查询和筛选逻辑
    • 统一分页处理
  • 题目管理页 (/admin/problem/list.vue)

    • 减少重复的路由同步代码
    • 保留题目搜索功能

📊 重构效果

代码减少统计

  • 每个页面减少代码量: 20-30 行
  • 总计减少代码量: 约 150+ 行
  • 重复逻辑消除: 100%

重构前后对比

重构前(每个分页页面都需要)

<script setup>
const route = useRoute()
const router = useRouter()

const query = reactive({
  page: parseInt(route.query.page) || 1,
  limit: parseInt(route.query.limit) || 10,
  keyword: route.query.keyword ?? "",
  // ... 其他查询条件
})

function routerPush() {
  router.push({
    path: route.path,
    query: filterEmptyValue(query),
  })
}

async function loadData() {
  // 从 URL 同步状态
  query.keyword = route.query.keyword ?? ""
  query.page = parseInt(route.query.page) || 1
  query.limit = parseInt(route.query.limit) || 10
  // ... 其他同步逻辑
  
  // API 调用
  const res = await api.getData(...)
}

// 大量的 watch 逻辑
watch(() => query.page, routerPush)
watch(() => [query.limit, /* 其他条件 */], () => {
  query.page = 1
  routerPush()
})
watchDebounced(() => query.keyword, () => {
  query.page = 1
  routerPush()
}, { debounce: 500 })
</script>

重构后(简洁高效)

<script setup>
// 一行代码完成所有分页逻辑
const { query, clearQuery } = usePagination({
  keyword: "",
  // ... 其他查询条件
})

async function loadData() {
  // 直接使用 query无需同步逻辑
  const res = await api.getData({
    offset: (query.page - 1) * query.limit,
    limit: query.limit,
    keyword: query.keyword,
  })
}

// 简化的监听逻辑
watchDebounced(() => query.keyword, loadData, { debounce: 500 })
watch(() => [query.page, query.limit], loadData)
</script>

🚀 主要优势

1. 代码复用性

  • 所有分页逻辑集中管理
  • 统一的行为模式
  • 减少维护成本

2. 类型安全

  • 完整的 TypeScript 支持
  • 自定义查询条件的类型推导
  • 编译时错误检查

3. 自动化处理

  • 自动 URL 同步
  • 自动页码重置
  • 自动路由监听
  • 浏览器前进后退支持

4. 灵活配置

  • 可自定义默认值
  • 可选择重置行为
  • 支持复杂查询条件

🔧 使用方法

基本用法

const { query } = usePagination()
// query.page, query.limit 自动可用

带查询条件

const { query, clearQuery } = usePagination({
  keyword: "",
  status: "",
  category: "",
})

配置选项

const { query } = usePagination(
  { keyword: "" },
  {
    defaultLimit: 20,
    defaultPage: 1,
    resetPageOnChange: true,
  }
)

📝 迁移指南

  1. 导入 usePagination
  2. 替换原有的 query 定义
  3. 移除 routerPush 函数
  4. 简化 watch 逻辑
  5. 更新 clear 函数使用 clearQuery

🎉 总结

这次重构成功地:

  • 大幅减少了重复代码(每个页面减少 20-30 行)
  • 统一了分页行为(所有页面行为一致)
  • 提升了开发效率(新页面只需 1 行代码即可获得完整分页功能)
  • 增强了类型安全(完整的 TypeScript 支持)
  • 保持了组件纯净性Pagination.vue 组件职责单一)

通过这个 composable未来添加新的分页页面将变得非常简单只需要一行代码就能获得完整的分页功能。