列表页是后台项目最常见的页面之一。表格、分页、筛选、加载状态一起出现后,问题往往不是“表格能不能显示”,而是换页后序号不连续、重复请求、页码没有重置,或者前端排序和服务端分页混在一起。
跨页连续序号怎么算
el-table-column 的 type="index" 默认会从 1 开始重新编号。要显示跨页连续序号,可以给它传一个函数:
1
| <el-table-column type="index" label="序号" :index="getIndex" width="72" />
|
1
2
3
| const getIndex = (index: number) => {
return (currentPage.value - 1) * pageSize.value + index + 1
}
|
例如当前是第 3 页、每页 20 条,当前页第一行的 index 是 0,最终序号就是 41。
一个可复用的分页列表
下面的例子把页码、每页数量、总数和加载状态放在一起管理。筛选条件变化后回到第 1 页,再重新请求数据。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
| <script setup lang="ts">
import { onMounted, ref } from 'vue'
interface User {
id: number
name: string
status: 'enabled' | 'disabled'
}
const rows = ref<User[]>([])
const total = ref(0)
const currentPage = ref(1)
const pageSize = ref(20)
const keyword = ref('')
const loading = ref(false)
const getIndex = (index: number) => {
return (currentPage.value - 1) * pageSize.value + index + 1
}
const loadUsers = async () => {
loading.value = true
try {
const params = new URLSearchParams({
page: String(currentPage.value),
pageSize: String(pageSize.value),
keyword: keyword.value,
})
const response = await fetch(`/api/users?${params}`)
const data = await response.json()
rows.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
const search = () => {
currentPage.value = 1
loadUsers()
}
const changePageSize = () => {
currentPage.value = 1
loadUsers()
}
onMounted(loadUsers)
</script>
<template>
<el-input
v-model="keyword"
clearable
placeholder="按用户名搜索"
@keyup.enter="search"
/>
<el-table v-loading="loading" :data="rows" row-key="id">
<el-table-column type="index" label="序号" :index="getIndex" width="72" />
<el-table-column prop="name" label="用户名" min-width="160" />
<el-table-column prop="status" label="状态" width="120" />
</el-table>
<el-pagination
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="total"
background
layout="total, sizes, prev, pager, next"
@current-change="loadUsers"
@size-change="changePageSize"
/>
</template>
|
loading 要在 finally 中关闭。无论请求成功还是失败,页面都不会一直停在加载状态。
筛选后为什么要重置页码
用户本来在第 8 页,搜索条件一变,新的结果可能只有 1 页。如果继续带着第 8 页请求,列表就会空掉。因此所有会改变结果集的筛选项,都应该先执行:
1
2
| currentPage.value = 1
loadUsers()
|
服务端分页时,排序也交给服务端
如果表格数据来自分页接口,前端只拿到当前页的 20 条数据。此时直接在前端对这 20 条排序,得到的并不是全量排序结果。
可以监听 sort-change,把字段名和顺序传回接口:
1
2
3
| <el-table @sort-change="handleSortChange">
<el-table-column prop="createdAt" label="创建时间" sortable="custom" />
</el-table>
|
1
2
3
4
5
6
7
8
9
10
11
12
| const sortBy = ref('createdAt')
const sortOrder = ref<'ascending' | 'descending' | ''>('descending')
const handleSortChange = ({ prop, order }: {
prop: string
order: 'ascending' | 'descending' | null
}) => {
sortBy.value = prop
sortOrder.value = order ?? ''
currentPage.value = 1
loadUsers()
}
|
小结
表格页真正需要统一管理的是请求参数和页面状态:页码、每页数量、筛选条件、排序条件、总数与加载状态。把这些状态放在同一处后,分页和表格才不会各自运行、互相打架。