feat: 表格分页支持统一配置并记忆各表格每页条数

- 后端新增聚合用户偏好 paginationPrefs(每页条数候选列表与默认每页条数),提供 GET/PUT /api/setting/pagination-prefs,默认候选 [10,20,50,100]、默认每页 20,非法值自动过滤并回退
- 后端新增 /category/page、/tag/page、/channel/page、/email-bill/configs/page、/email-bill/sender-rules/page 分页接口,原全量接口保持不变以免影响下拉、筛选、AI 与导出
- 前端新增共享 composables/usePageSizeOptions.ts 与 useTablePageSize.ts,所有表格统一取用候选列表并记住各自上次选择的每页条数,优先级为表格记忆 > 全局默认 > 20
- 各表格上次选择以聚合偏好 finance.pagination.tablePageSizes 存于本机 localStorage,收支记录页旧的 filterPrefs.pageSize 自动迁移
- 设置页外观设置新增「表格每页条数选项」分组,可增删候选值并配置默认每页条数,保存后全局即时生效
- 路由守卫在已登录时预加载分页偏好,保证表格页首次渲染即使用配置值
- 前端分类、标签、渠道、邮箱配置、发件人规则表格接入后端分页
- 补充中文、英文、日文三语文案
This commit is contained in:
2026-09-23 11:14:54 +08:00
parent 7172f6aa44
commit e0fa1abec5
39 changed files with 1042 additions and 62 deletions
+13 -6
View File
@@ -17,6 +17,9 @@ import { getChannelLabel } from '../utils/channel'
import { renderCategoryLabel } from '../utils/render'
import { useKeywordHistory } from '../composables/useKeywordHistory'
import { useStringPreference } from '../composables/usePreference'
import { usePageSizeOptions } from '../composables/usePageSizeOptions'
import { useTablePageSize } from '../composables/useTablePageSize'
import { readTablePageSize, writeTablePageSize } from '../utils/tablePageSizePrefs'
import { openSse } from '../utils/sse'
import { STORAGE_KEYS } from '../utils/storageKeys'
import { loadTransactionFilterPrefs, saveTransactionFilterPrefs } from '../utils/transactionFilterPrefs'
@@ -94,7 +97,7 @@ interface AiRecordEditRow {
const transactions = ref<TransactionItem[]>([]) // 交易记录列表
const total = ref(0) // 总记录数
const page = ref(1) // 当前页码
const pageSize = ref(20) // 每页条数
const pageSize = useTablePageSize('transaction') // 每页条数
const sortValue = ref('transactionTime-desc') // 排序方式:排序字段-方向
const sortOptions = computed(() => [
{ label: t('transaction.sort.timeDesc'), value: 'transactionTime-desc' },
@@ -104,7 +107,7 @@ const sortOptions = computed(() => [
])
const sortField = computed(() => sortValue.value.split('-')[0]) // 服务端排序字段
const sortOrder = computed(() => sortValue.value.split('-')[1]) // 服务端排序方向:asc/desc
const pageSizes = [10, 20, 50, 100, 200, 500] // 可选的每页条数
const { pageSizeOptions } = usePageSizeOptions() // 所有表格共用的每页条数候选列表
const totalPages = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value))) // 总页数
// 当前页金额汇总:支出、收入与结余(含本页所有展示记录)
const pageSummary = computed(() => {
@@ -345,7 +348,12 @@ function loadFilterPrefs() {
if (typeof p.filterRemark === 'string') filterRemark.value = p.filterRemark
if (p.filterMinAmount !== undefined) filterMinAmount.value = p.filterMinAmount
if (p.filterMaxAmount !== undefined) filterMaxAmount.value = p.filterMaxAmount
if (typeof p.pageSize === 'number' && pageSizes.includes(p.pageSize)) pageSize.value = p.pageSize
// 迁移旧筛选偏好中的每页条数到统一的表格分页偏好(新机制尚无值时)
if (typeof p.pageSize === 'number' && Number.isInteger(p.pageSize) && p.pageSize > 0
&& readTablePageSize('transaction') === null) {
writeTablePageSize('transaction', p.pageSize)
pageSize.value = p.pageSize
}
if (typeof p.sortValue === 'string' && sortOptions.value.some((o) => o.value === p.sortValue)) sortValue.value = p.sortValue
if (p.filterTag) filterTag.value = p.filterTag
}
@@ -361,7 +369,6 @@ function saveFilterPrefs() {
filterRemark: filterRemark.value,
filterMinAmount: filterMinAmount.value,
filterMaxAmount: filterMaxAmount.value,
pageSize: pageSize.value,
sortValue: sortValue.value,
filterTag: filterTag.value,
})
@@ -369,7 +376,7 @@ function saveFilterPrefs() {
// 筛选条件变化时自动保存用户偏好
watch(
[filterType, filterCategory, filterChannel, filterDateRange, filterIncludeInStats, filterRemark, filterMinAmount, filterMaxAmount, pageSize, sortValue, filterTag],
[filterType, filterCategory, filterChannel, filterDateRange, filterIncludeInStats, filterRemark, filterMinAmount, filterMaxAmount, sortValue, filterTag],
saveFilterPrefs,
{ deep: true },
)
@@ -2224,7 +2231,7 @@ onBeforeUnmount(() => {
v-model:page="page"
v-model:page-size="pageSize"
:page-count="Math.ceil(total / pageSize)"
:page-sizes="pageSizes"
:page-sizes="pageSizeOptions"
show-size-picker
show-quick-jumper
@update:page="handlePageChange"