iter(g256): 抽出分页条并加每页条数切换

This commit is contained in:
HIsheR
2026-10-26 22:19:14 +08:00
parent 8e97e99cf4
commit 036e044070
6 changed files with 64 additions and 15 deletions
+8 -8
View File
@@ -9,10 +9,10 @@ import {
import { iconSearch } from './icons.js'; import { computed, effect, h, renderList, signal } from '@ui-arch/frame';
import { attachResize } from './columnResize.js';
import { exportButton } from './csv.js';
import { button } from './button.js';
import { skeleton } from './skeleton.js';
import { textField } from './textField.js';
import { emptyRow } from './tableEmpty.js';
import { tablePager } from './tablePager.js';
import { attachDataSource, readRows } from './tableDataSource.js';
import type { DataTableProps, TableApi } from './types.js';
@@ -188,12 +188,12 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
props.exportable ? exportButton(table, props.columns, props.exportName ?? 'table.csv') : null, // 导出筛选+排序后的全量行
),
h('div', { class: 'ua-table-scroll' }, h('table', null, colgroup, h('thead', null, h('tr', null, selectable ? headCheck : null, ...props.columns.map(th))), tbody)),
h(
'div',
{ class: 'ua-pager', role: 'navigation', 'aria-label': '分页' },
button({ label: '上一页', variant: 'ghost', disabled: props.loading, onClick: () => table.dispatch({ type: 'setPage', page: info().page - 1 }) }),
h('span', { class: 'ua-page-info', role: 'status', 'aria-live': 'polite' }, () => `第 ${info().page} / ${info().totalPages} 页 · 共 ${visible().total} 行`), // 翻页/筛选后面向读屏播报
button({ label: '下一页', variant: 'ghost', disabled: props.loading, onClick: () => table.dispatch({ type: 'setPage', page: info().page + 1 }) }),
),
tablePager({
info,
total: () => visible().total,
loading: busy,
onPage: (page) => table.dispatch({ type: 'setPage', page }),
onPageSize: (pageSize) => table.dispatch({ type: 'setPageSize', pageSize }),
}),
);
}
+36
View File
@@ -0,0 +1,36 @@
import { effect, h, signal } from '@ui-arch/frame';
import { button } from './button.js';
import { select } from './select.js';
import type { TablePagerProps } from './types.js';
/*** 表格分页条:上一页 / 页码播报 / 下一页 + 每页条数切换;页码与总页数都是内核派生快照,这里只读不持有 ***/
export function tablePager(props: TablePagerProps): HTMLElement {
const sizes = props.pageSizes ?? [5, 10, 20]; // 每页条数候选:默认三档,够覆盖表格常见密度
const initial = props.info().pageSize; // 调用方在建表时可能传了候选外的页大小,补进候选避免下拉显示空白
const list = sizes.includes(initial) ? sizes : [...sizes, initial].sort((a, b) => a - b);
const sizeSig = signal(String(initial)); // 下拉受控值:外部(内核)改页大小时由下方 effect 同步
const atFirst = () => props.info().page <= 1; // 首/末页边界:按钮置灰,避免派发无效页码
const atLast = () => props.info().page >= props.info().totalPages;
effect(() => {
const cur = String(props.info().pageSize);
if (cur !== sizeSig()) sizeSig(cur); // 内核页大小变化(含列表恢复默认)→ 回写下拉
});
const sizeSelect = select({
size: 'sm',
options: list.map((n) => ({ value: String(n), label: `${n} 条/页` })),
value: sizeSig,
disabled: () => props.loading(), // 加载中不改页大小:避免与在途结果竞争
onChange: (v) => props.onPageSize(Number(v)),
});
return h(
'div',
{ class: 'ua-pager', role: 'navigation', 'aria-label': '分页' },
button({ label: '上一页', variant: 'ghost', disabled: () => props.loading() || atFirst(), onClick: () => props.onPage(Math.max(1, props.info().page - 1)) }),
h('span', { class: 'ua-page-info', role: 'status', 'aria-live': 'polite' }, () => `第 ${props.info().page} / ${props.info().totalPages} 页 · 共 ${props.total()} 行`), // 翻页 / 筛选后面向读屏播报
button({ label: '下一页', variant: 'ghost', disabled: () => props.loading() || atLast(), onClick: () => props.onPage(Math.min(props.info().totalPages, props.info().page + 1)) }),
sizeSelect,
);
}
+10
View File
@@ -813,6 +813,16 @@ export interface SpaceProps {
/*** 表格扩展接口:table-core 实例 + 皮肤层用到的两个取数方法 ***/
export type TableApi<T> = TableInstance<T> & { getPageInfo(): PageInfo; selectAllVisible(): void };
/*** 分页条入参:取值一律走读取器,保持响应式依赖收集 ***/
export interface TablePagerProps {
info: () => PageInfo; // 分页快照读取器(page / pageSize / totalPages)
total: () => number; // 筛选后总行数,只用于播报文案
loading: () => boolean; // 加载态:翻页与改页大小一并禁用
pageSizes?: number[]; // 每页条数候选;缺省 [5, 10, 20]
onPage: (page: number) => void; // 翻页回调(入参已按边界钳制)
onPageSize: (pageSize: number) => void; // 改页大小回调
}
/*** 锚点导航项 ***/
export interface AnchorItem {
href: string; // 目标区块地址(#id);对应元素须存在于页面上
+1
View File
@@ -1958,6 +1958,7 @@
"工具栏输入框即筛选,输入即时过滤并回第 1 页;无匹配时显示空状态行",
"表头粘性固定(滚动容器 max-height 420px),斑马纹 + 悬停高亮由 CSS 提供",
"数据在途时传 loading(布尔或信号):表格体换成骨架行、筛选与翻页禁用,返回后置回 false 即恢复",
"分页条自带每页条数下拉(5 / 10 / 20 条,调用方传的 pageSize 若不在候选里会自动补进去):切换即改页大小并回第 1 页,首末页时上/下一页置灰不再派发无效页码",
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用",
"需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)",
"可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充",