iter: d04 dataTable 接加载态(骨架行 + 交互禁用)
This commit is contained in:
@@ -8,6 +8,7 @@ import {
|
||||
import type { PageInfo, RowKey, TableInstance } from '@ui-arch/table-core';
|
||||
import { computed, effect, h, renderList, signal } from '@ui-arch/frame';
|
||||
import { button } from './button.js';
|
||||
import { skeleton } from './skeleton.js';
|
||||
import { textField } from './textField.js';
|
||||
import type { DataTableProps } from './types.js';
|
||||
|
||||
@@ -43,6 +44,8 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
|
||||
return api.getPageInfo();
|
||||
});
|
||||
const selectedCount = computed(() => state().selection.length);
|
||||
// 加载态读取器:布尔直接读,信号则保持响应性(供 renderList 依赖收集)
|
||||
const busy = (): boolean => (typeof props.loading === 'function' ? props.loading() : props.loading === true);
|
||||
|
||||
// —— 受控页码桥接:外部信号 ↔ 内核分页;外部越界先钳制,signal 等值写回不触发,双向同步收敛安全 ——
|
||||
if (props.page) {
|
||||
@@ -96,8 +99,16 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
|
||||
col.sortable ? sortMark(col.key) : null,
|
||||
);
|
||||
|
||||
/*** 骨架行:列宽与表头对齐,行数取当前每页条数(上限 6 行防过长) ***/
|
||||
const skeletonRows = (): Node[] => {
|
||||
const count = Math.min(props.pageSize ?? 5, 6);
|
||||
const cell = () => h('td', null, skeleton({ variant: 'text', rows: 1 }));
|
||||
return Array.from({ length: count }, () => h('tr', { class: 'ua-loading-row' }, ...Array.from({ length: colspan }, cell)));
|
||||
};
|
||||
|
||||
const tbody = h('tbody');
|
||||
renderList(tbody, () => {
|
||||
if (busy()) return skeletonRows(); // 加载态优先:不看数据,避免闪出旧行
|
||||
const sel = state().selection; // 读入依赖:选择变化即整表重绘(行数 = pageSize,代价可控)
|
||||
const rows = visible().rows;
|
||||
if (!rows.length) {
|
||||
@@ -119,6 +130,7 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
|
||||
|
||||
const filterInput = textField({
|
||||
placeholder: `筛选 ${filterKey}…`,
|
||||
disabled: props.loading, // 加载中禁止筛选,避免与在途结果竞争
|
||||
onInput: (value) => {
|
||||
table.dispatch({ type: 'setFilter', key: filterKey, value });
|
||||
table.dispatch({ type: 'setPage', page: 1 }); // 筛选变化回第 1 页
|
||||
@@ -127,7 +139,7 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
|
||||
|
||||
return h(
|
||||
'section',
|
||||
{ class: 'ua-table' },
|
||||
{ class: 'ua-table', 'aria-busy': () => String(busy()) },
|
||||
h(
|
||||
'div',
|
||||
{ class: 'ua-table-toolbar' },
|
||||
@@ -138,9 +150,9 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
|
||||
h(
|
||||
'div',
|
||||
{ class: 'ua-pager' },
|
||||
button({ label: '上一页', variant: 'ghost', onClick: () => table.dispatch({ type: 'setPage', page: info().page - 1 }) }),
|
||||
button({ label: '上一页', variant: 'ghost', disabled: props.loading, onClick: () => table.dispatch({ type: 'setPage', page: info().page - 1 }) }),
|
||||
h('span', { class: 'ua-page-info' }, () => `第 ${info().page} / ${info().totalPages} 页 · 共 ${visible().total} 行`),
|
||||
button({ label: '下一页', variant: 'ghost', onClick: () => table.dispatch({ type: 'setPage', page: info().page + 1 }) }),
|
||||
button({ label: '下一页', variant: 'ghost', disabled: props.loading, onClick: () => table.dispatch({ type: 'setPage', page: info().page + 1 }) }),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ export interface DataTableProps<T> {
|
||||
pageSize?: number; // 每页行数;缺省 5
|
||||
selectable?: boolean; // 是否启用行选择列(含全选三态与已选计数);缺省 false
|
||||
page?: Signal<number>; // 受控页码信号:与内核分页双向同步,可接独立 pagination 组件联动
|
||||
loading?: boolean | Signal<boolean>; // 加载态:为真时数据行换成骨架行,筛选与翻页禁用
|
||||
}
|
||||
|
||||
/*** 选项条目:select / radioGroup / tabs 共用的选项形状 ***/
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
"modal(props, ...children):弹窗,open 信号驱动显隐,Esc / 遮罩 / × 关闭",
|
||||
"form(props, items):表单容器,布局 + 聚合校验,全部通过才触发 onSubmit",
|
||||
"formItem(props, control):表单字段,label + 必填红星 + 规则校验 + 错误区,返回 { el, validate } 句柄",
|
||||
"dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态,内核为 table-core"
|
||||
"dataTable(props):数据表格,粘性表头 / 行选择(全选三态)/ 排序 / 筛选 / 分页 / 空状态;loading 为真时数据行换骨架行且筛选/翻页禁用,内核为 table-core"
|
||||
],
|
||||
"keyTypes": [
|
||||
"ButtonProps",
|
||||
@@ -846,6 +846,11 @@
|
||||
"n": "page",
|
||||
"t": "Signal<number>",
|
||||
"d": "受控页码信号:与内核分页双向同步,传同一个信号给独立 pagination 组件即可联动"
|
||||
},
|
||||
{
|
||||
"n": "loading",
|
||||
"t": "boolean | Signal<boolean>",
|
||||
"d": "加载态:为真时数据行换成骨架行(行数随 pageSize),筛选输入与翻页按钮临时禁用,容器带 aria-busy"
|
||||
}
|
||||
],
|
||||
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
||||
@@ -856,6 +861,7 @@
|
||||
"选中行用低视觉比重样式(淡色底 + 弱化文字),选择状态存 rowKey,跨页不丢失",
|
||||
"工具栏输入框即筛选,输入即时过滤并回第 1 页;无匹配时显示空状态行",
|
||||
"表头粘性固定(滚动容器 max-height 420px),斑马纹 + 悬停高亮由 CSS 提供",
|
||||
"数据在途时传 loading(布尔或信号):表格体换成骨架行、筛选与翻页禁用,返回后置回 false 即恢复",
|
||||
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user