iter(g88): 数据表支持导出 CSV
This commit is contained in:
@@ -1060,6 +1060,14 @@
|
|||||||
"acceptance": "焦点进入(focusin)与移出(focusout)同样暂停自动播放,两者都不在才前进;区域补 aria-roledescription=轮播,每张补 aria-roledescription=幻灯片 且标签为「第 x 张,共 n 张」;演示描述同步;npm run check 全绿",
|
"acceptance": "焦点进入(focusin)与移出(focusout)同样暂停自动播放,两者都不在才前进;区域补 aria-roledescription=轮播,每张补 aria-roledescription=幻灯片 且标签为「第 x 张,共 n 张」;演示描述同步;npm run check 全绿",
|
||||||
"notes": "自补:无障碍;hover 与 focus 分开计数,避免一个 pointerleave 就误解除暂停",
|
"notes": "自补:无障碍;hover 与 focus 分开计数,避免一个 pointerleave 就误解除暂停",
|
||||||
"status": "done"
|
"status": "done"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "g88",
|
||||||
|
"title": "数据表支持导出 CSV",
|
||||||
|
"motivation": "表格能筛能排却导不出来,用户只能截图或手抄;导出还必须导「筛选+排序后的全量行」而不是当前页,但内核只有 getVisibleRows()(已分页裁剪),UI 层拿不到全量派生行",
|
||||||
|
"acceptance": "内核 getVisibleRows(paged=false) 跳过 pagination 阶段并新增 getAllRows(),派生上下文带 paged 标记(缺省分页),分页快照不受影响;dataTable 新增 exportable / exportName,工具栏按钮导出筛选+排序后的全量行、单元格走 format;CSV 带 BOM + CRLF、逗号引号换行正确转义;冒烟补 4 条不变量;npm run check 全绿",
|
||||||
|
"notes": "自补:功能缺口;核心只加可选参数与一个实例方法,不动既有签名",
|
||||||
|
"status": "open"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
+13
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"$schema": "ui-arch-registry-index/0.1",
|
"$schema": "ui-arch-registry-index/0.1",
|
||||||
"generatedAt": "2026-10-26T05:03:25.043Z",
|
"generatedAt": "2026-10-26T05:08:07.129Z",
|
||||||
"repoRoot": ".",
|
"repoRoot": ".",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
@@ -1767,6 +1767,16 @@
|
|||||||
"n": "selected",
|
"n": "selected",
|
||||||
"t": "Signal<RowKey[]>",
|
"t": "Signal<RowKey[]>",
|
||||||
"d": "受控选中行信号:与内核选择双向同步(内容比较,不来回派发),供批量操作读取/改写"
|
"d": "受控选中行信号:与内核选择双向同步(内容比较,不来回派发),供批量操作读取/改写"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "exportable",
|
||||||
|
"t": "boolean",
|
||||||
|
"d": "工具栏显示「导出 CSV」:导出筛选 + 排序后的全量行(不分页);缺省 false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "exportName",
|
||||||
|
"t": "string",
|
||||||
|
"d": "导出文件名;缺省 table.csv"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
||||||
@@ -1782,7 +1792,8 @@
|
|||||||
"数据在途时传 loading(布尔或信号):表格体换成骨架行、筛选与翻页禁用,返回后置回 false 即恢复",
|
"数据在途时传 loading(布尔或信号):表格体换成骨架行、筛选与翻页禁用,返回后置回 false 即恢复",
|
||||||
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用",
|
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用",
|
||||||
"需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)",
|
"需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)",
|
||||||
"可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充"
|
"可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充",
|
||||||
|
"需要导出时开 exportable:按钮导的是筛选+排序后的全量行,与当前页无关,单元格按 format 输出"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
+2
-1
@@ -1077,10 +1077,11 @@ const content = h(
|
|||||||
section(
|
section(
|
||||||
'sec-table',
|
'sec-table',
|
||||||
'数据表格',
|
'数据表格',
|
||||||
'粘性表头、斑马纹、行选择(全选三态 + 已选计数,选中行低视觉比重)、点表头或 Tab 聚焦后按 Enter/空格 排序、定宽列表头右缘可拖拽或 ←→ 调列宽(双击复位)、工具栏筛选、底部分页。',
|
'粘性表头、斑马纹、行选择(全选三态 + 已选计数,选中行低视觉比重)、点表头或 Tab 聚焦后按 Enter/空格 排序、定宽列表头右缘可拖拽或 ←→ 调列宽(双击复位)、工具栏筛选、底部分页;工具栏「导出 CSV」导的是筛选 + 排序后的全量行(不含勾选,与当前页无关)。',
|
||||||
card({ title: '用户表' }, dataTable({
|
card({ title: '用户表' }, dataTable({
|
||||||
data: users,
|
data: users,
|
||||||
selectable: true,
|
selectable: true,
|
||||||
|
exportable: true,
|
||||||
loading: tableLoading,
|
loading: tableLoading,
|
||||||
columns: [
|
columns: [
|
||||||
{ key: 'name', header: '姓名', sortable: true, width: 160 },
|
{ key: 'name', header: '姓名', sortable: true, width: 160 },
|
||||||
|
|||||||
+4
-1
@@ -1,6 +1,6 @@
|
|||||||
# @ui-arch 组件库 Agent 文档(无图形版)
|
# @ui-arch 组件库 Agent 文档(无图形版)
|
||||||
|
|
||||||
> 自动生成于 2026-10-26T05:03:25.043Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
> 自动生成于 2026-10-26T05:08:07.129Z,数据源:各包 ua.manifest.json,勿手改。人类版文档站:apps/docs/index.html。
|
||||||
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
> 组件 = 普通函数,传 props 返回 DOM;状态用 signal 双向绑定:读 signal() 写 signal(v)。
|
||||||
|
|
||||||
## 快速上手
|
## 快速上手
|
||||||
@@ -936,6 +936,8 @@ const name = signal(''); const f = form({ onSubmit: submit }, [formItem({ label:
|
|||||||
- page:Signal<number> —— 受控页码信号:与内核分页双向同步,传同一个信号给独立 pagination 组件即可联动
|
- page:Signal<number> —— 受控页码信号:与内核分页双向同步,传同一个信号给独立 pagination 组件即可联动
|
||||||
- loading:boolean | Signal<boolean> —— 加载态:为真时数据行换成骨架行(行数随 pageSize),筛选输入与翻页按钮临时禁用,容器带 aria-busy
|
- loading:boolean | Signal<boolean> —— 加载态:为真时数据行换成骨架行(行数随 pageSize),筛选输入与翻页按钮临时禁用,容器带 aria-busy
|
||||||
- selected:Signal<RowKey[]> —— 受控选中行信号:与内核选择双向同步(内容比较,不来回派发),供批量操作读取/改写
|
- selected:Signal<RowKey[]> —— 受控选中行信号:与内核选择双向同步(内容比较,不来回派发),供批量操作读取/改写
|
||||||
|
- exportable:boolean —— 工具栏显示「导出 CSV」:导出筛选 + 排序后的全量行(不分页);缺省 false
|
||||||
|
- exportName:string —— 导出文件名;缺省 table.csv
|
||||||
|
|
||||||
示例:
|
示例:
|
||||||
|
|
||||||
@@ -957,6 +959,7 @@ dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓
|
|||||||
10. 需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用
|
10. 需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用
|
||||||
11. 需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)
|
11. 需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)
|
||||||
12. 可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充
|
12. 可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充
|
||||||
|
13. 需要导出时开 exportable:按钮导的是筛选+排序后的全量行,与当前页无关,单元格按 format 输出
|
||||||
|
|
||||||
现场演示:apps/docs/demo.html#sec-table
|
现场演示:apps/docs/demo.html#sec-table
|
||||||
|
|
||||||
|
|||||||
@@ -154,4 +154,22 @@ assert(
|
|||||||
'reset 回到默认态(排序/筛选清空,分页回 1/20)',
|
'reset 回到默认态(排序/筛选清空,分页回 1/20)',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// getAllRows:过筛选 + 排序但跳过分页,供导出用
|
||||||
|
const all = createTable({
|
||||||
|
data: users,
|
||||||
|
columns: [{ key: 'name', header: 'Name' }, { key: 'city', header: 'City' }],
|
||||||
|
rowKey: 'id',
|
||||||
|
plugins: [filterPlugin(), sortPlugin(), paginationPlugin()],
|
||||||
|
initialState: { pagination: { page: 1, pageSize: 2 } },
|
||||||
|
});
|
||||||
|
assert(all.getVisibleRows().rows.length === 2, 'getVisibleRows 受分页约束只给当页');
|
||||||
|
all.dispatch({ type: 'setFilter', key: 'city', value: 'Beijing' });
|
||||||
|
assert(all.getAllRows().rows.length === 2 && all.getVisibleRows().rows.length === 2, 'getAllRows 跳过分页但与筛选一致(Beijing 2 行)');
|
||||||
|
all.dispatch({ type: 'toggleSort', key: 'age' });
|
||||||
|
assert(
|
||||||
|
JSON.stringify(all.getAllRows().rows.map((r) => r.id)) === JSON.stringify([4, 2]),
|
||||||
|
'getAllRows 仍按排序规则排(age 升序 4→2)',
|
||||||
|
);
|
||||||
|
assert(all.getPageInfo().totalPages === 1, 'getAllRows 不影响分页快照(2 行 / 每页 2 = 1 页)');
|
||||||
|
|
||||||
console.log('SMOKE OK');
|
console.log('SMOKE OK');
|
||||||
|
|||||||
@@ -80,12 +80,14 @@ export function createTable<T>(
|
|||||||
return cachedBase;
|
return cachedBase;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** 可见行模型:依序过插件 derive(filter → sort → paginate) ***/
|
/*** 可见行模型:依序过插件 derive(filter → sort → paginate);paged=false 时跳过分页阶段取全量派生行 ***/
|
||||||
function getVisibleRows(): RowModel<T> {
|
function getVisibleRows(paged = true): RowModel<T> {
|
||||||
|
ctx.paged = paged; // 由插件自行判断是否裁剪,核心不认插件细节
|
||||||
let model = baseModel();
|
let model = baseModel();
|
||||||
for (const p of plugins) {
|
for (const p of plugins) {
|
||||||
if (p.derive) model = p.derive(model, state, ctx);
|
if (p.derive) model = p.derive(model, state, ctx);
|
||||||
}
|
}
|
||||||
|
ctx.paged = true; // 还原缺省,避免影响后续调用
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -94,6 +96,7 @@ export function createTable<T>(
|
|||||||
dispatch,
|
dispatch,
|
||||||
subscribe,
|
subscribe,
|
||||||
getVisibleRows,
|
getVisibleRows,
|
||||||
|
getAllRows: () => getVisibleRows(false), // 筛选 + 排序后、未分页的全量行(导出等场景)
|
||||||
getColumns: () => columns,
|
getColumns: () => columns,
|
||||||
getCellValue,
|
getCellValue,
|
||||||
isRowSelected: (key) => state.selection.includes(key),
|
isRowSelected: (key) => state.selection.includes(key),
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type {
|
import type {
|
||||||
Action,
|
Action,
|
||||||
|
DeriveContext,
|
||||||
RowModel,
|
RowModel,
|
||||||
TableInstance,
|
TableInstance,
|
||||||
TablePlugin,
|
TablePlugin,
|
||||||
@@ -32,7 +33,8 @@ export function paginationPlugin<T = unknown>(): TablePlugin<T> {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
derive(model: RowModel<T>, state: TableState): RowModel<T> {
|
derive(model: RowModel<T>, state: TableState, ctx: DeriveContext<T>): RowModel<T> {
|
||||||
|
if (ctx.paged === false) return model; // 不分页:原样放行上游模型(导出全量行)
|
||||||
if (model === lastInput && state.pagination === lastPage && cached) return cached;
|
if (model === lastInput && state.pagination === lastPage && cached) return cached;
|
||||||
const { page, pageSize } = state.pagination;
|
const { page, pageSize } = state.pagination;
|
||||||
const totalPages = Math.max(1, Math.ceil(model.total / pageSize));
|
const totalPages = Math.max(1, Math.ceil(model.total / pageSize));
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ export interface RowModel<T = unknown> {
|
|||||||
|
|
||||||
/*** 派生上下文:核心注入给插件的取值能力 ***/
|
/*** 派生上下文:核心注入给插件的取值能力 ***/
|
||||||
export interface DeriveContext<T = unknown> {
|
export interface DeriveContext<T = unknown> {
|
||||||
|
paged?: boolean; // 是否分页裁剪;false 时 pagination 插件原样放行(导出全量行用)
|
||||||
getCellValue(row: T, key: string): unknown; // 单元格取值(走列 accessor)
|
getCellValue(row: T, key: string): unknown; // 单元格取值(走列 accessor)
|
||||||
getKey(row: T): RowKey; // 行标识取值
|
getKey(row: T): RowKey; // 行标识取值
|
||||||
}
|
}
|
||||||
@@ -89,6 +90,7 @@ export interface TableInstance<T = unknown> {
|
|||||||
dispatch(action: Action): void; // 派发动作,唯一变更入口
|
dispatch(action: Action): void; // 派发动作,唯一变更入口
|
||||||
subscribe(fn: () => void): () => void; // 订阅变更,返回取消函数
|
subscribe(fn: () => void): () => void; // 订阅变更,返回取消函数
|
||||||
getVisibleRows(): RowModel<T>; // 可见行模型(过完整管线)
|
getVisibleRows(): RowModel<T>; // 可见行模型(过完整管线)
|
||||||
|
getAllRows(): RowModel<T>; // 筛选 + 排序后、未分页的全量行(导出用)
|
||||||
getColumns(): ColumnDef<T>[]; // 列定义
|
getColumns(): ColumnDef<T>[]; // 列定义
|
||||||
getCellValue(row: T, key: string): unknown; // 单元格取值
|
getCellValue(row: T, key: string): unknown; // 单元格取值
|
||||||
isRowSelected(key: RowKey): boolean; // 行是否已选
|
isRowSelected(key: RowKey): boolean; // 行是否已选
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import { h } from '@ui-arch/frame';
|
||||||
|
import type { RowModel } from '@ui-arch/table-core';
|
||||||
|
import { button } from './button.js';
|
||||||
|
import type { TableColumn } from './types.js';
|
||||||
|
|
||||||
|
/*** 单元格转义:含逗号 / 引号 / 换行时整体加引号,内部引号翻倍 ***/
|
||||||
|
function cell(v: unknown): string {
|
||||||
|
const s = v == null ? '' : String(v);
|
||||||
|
return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** 导出 CSV:前置 BOM + CRLF 换行,Excel 打开中文不乱码 ***/
|
||||||
|
export function downloadCsv(name: string, header: string[], rows: unknown[][]): void {
|
||||||
|
const text = '\ufeff' + [header, ...rows].map((r) => r.map(cell).join(',')).join('\r\n') + '\r\n';
|
||||||
|
const url = URL.createObjectURL(new Blob([text], { type: 'text/csv;charset=utf-8' }));
|
||||||
|
const a = h('a', { href: url, download: name }) as HTMLAnchorElement;
|
||||||
|
document.body.appendChild(a); // 挂进文档再点击:Safari 对游离锚点不触发下载
|
||||||
|
a.click();
|
||||||
|
a.remove(); // 立即摘除,不留残留节点
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** 导出按钮:取筛选 + 排序后的全量行(不分页),列序与表头一致、单元格走 format ***/
|
||||||
|
export function exportButton<T>(
|
||||||
|
table: { getAllRows(): RowModel<T>; getCellValue(row: T, key: string): unknown },
|
||||||
|
columns: TableColumn<T>[],
|
||||||
|
name: string,
|
||||||
|
): HTMLElement {
|
||||||
|
const run = (): void => {
|
||||||
|
const rows = table.getAllRows().rows;
|
||||||
|
const header = columns.map((c) => String(c.header ?? c.key));
|
||||||
|
const body = rows.map((row) => columns.map((c) => {
|
||||||
|
const raw = table.getCellValue(row, c.key);
|
||||||
|
return c.format ? c.format(raw, row) : raw; // 与单元格显示同源,导出内容与看到的一致
|
||||||
|
}));
|
||||||
|
downloadCsv(name, header, body);
|
||||||
|
};
|
||||||
|
return button({ label: '导出 CSV', variant: 'ghost', onClick: run });
|
||||||
|
}
|
||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
import type { PageInfo, RowKey, TableInstance } from '@ui-arch/table-core';
|
import type { PageInfo, RowKey, TableInstance } from '@ui-arch/table-core';
|
||||||
import { computed, effect, h, renderList, signal } from '@ui-arch/frame';
|
import { computed, effect, h, renderList, signal } from '@ui-arch/frame';
|
||||||
import { attachResize } from './columnResize.js';
|
import { attachResize } from './columnResize.js';
|
||||||
|
import { exportButton } from './csv.js';
|
||||||
import { button } from './button.js';
|
import { button } from './button.js';
|
||||||
import { skeleton } from './skeleton.js';
|
import { skeleton } from './skeleton.js';
|
||||||
import { textField } from './textField.js';
|
import { textField } from './textField.js';
|
||||||
@@ -184,6 +185,7 @@ export function dataTable<T extends { id: RowKey }>(props: DataTableProps<T>): H
|
|||||||
{ class: 'ua-table-toolbar' },
|
{ class: 'ua-table-toolbar' },
|
||||||
filterInput,
|
filterInput,
|
||||||
selectable ? h('span', { class: 'ua-sel-count' }, () => (selectedCount() ? `已选 ${selectedCount()} 行` : '')) : null,
|
selectable ? h('span', { class: 'ua-sel-count' }, () => (selectedCount() ? `已选 ${selectedCount()} 行` : '')) : null,
|
||||||
|
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-table-scroll' }, h('table', null, colgroup, h('thead', null, h('tr', null, selectable ? headCheck : null, ...props.columns.map(th))), tbody)),
|
||||||
h(
|
h(
|
||||||
|
|||||||
@@ -63,6 +63,8 @@ export interface DataTableProps<T> {
|
|||||||
page?: Signal<number>; // 受控页码信号:与内核分页双向同步,可接独立 pagination 组件联动
|
page?: Signal<number>; // 受控页码信号:与内核分页双向同步,可接独立 pagination 组件联动
|
||||||
loading?: boolean | Signal<boolean>; // 加载态:为真时数据行换成骨架行,筛选与翻页禁用
|
loading?: boolean | Signal<boolean>; // 加载态:为真时数据行换成骨架行,筛选与翻页禁用
|
||||||
selected?: Signal<RowKey[]>; // 受控选中行信号:与内核选择双向同步,供批量操作读取
|
selected?: Signal<RowKey[]>; // 受控选中行信号:与内核选择双向同步,供批量操作读取
|
||||||
|
exportable?: boolean; // 工具栏显示导出按钮:导出筛选 + 排序后的全量行(不分页);缺省 false
|
||||||
|
exportName?: string; // 导出文件名;缺省 table.csv
|
||||||
}
|
}
|
||||||
|
|
||||||
/*** 选项条目:select / radioGroup / tabs / segmented 共用的选项形状 ***/
|
/*** 选项条目:select / radioGroup / tabs / segmented 共用的选项形状 ***/
|
||||||
|
|||||||
@@ -1638,6 +1638,16 @@
|
|||||||
"n": "selected",
|
"n": "selected",
|
||||||
"t": "Signal<RowKey[]>",
|
"t": "Signal<RowKey[]>",
|
||||||
"d": "受控选中行信号:与内核选择双向同步(内容比较,不来回派发),供批量操作读取/改写"
|
"d": "受控选中行信号:与内核选择双向同步(内容比较,不来回派发),供批量操作读取/改写"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "exportable",
|
||||||
|
"t": "boolean",
|
||||||
|
"d": "工具栏显示「导出 CSV」:导出筛选 + 排序后的全量行(不分页);缺省 false"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"n": "exportName",
|
||||||
|
"t": "string",
|
||||||
|
"d": "导出文件名;缺省 table.csv"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
"example": "dataTable({ data: users, selectable: true, columns: [{ key: 'name', header: '姓名', sortable: true }], pageSize: 5 })",
|
||||||
@@ -1653,7 +1663,8 @@
|
|||||||
"数据在途时传 loading(布尔或信号):表格体换成骨架行、筛选与翻页禁用,返回后置回 false 即恢复",
|
"数据在途时传 loading(布尔或信号):表格体换成骨架行、筛选与翻页禁用,返回后置回 false 即恢复",
|
||||||
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用",
|
"需要独立分页器联动时传 page 信号:const page = signal(1),dataTable({ page }) 与 pagination({ page }) 共用",
|
||||||
"需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)",
|
"需要批量操作时传 selected 信号:内部勾选实时写回,外部置空或赋值即同步整表(跨页保留,按 rowKey 存)",
|
||||||
"可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充"
|
"可排序表头的 aria-sort 跟随排序态同步(ascending / descending / none),读屏能直接播报当前列序,箭头只是视觉补充",
|
||||||
|
"需要导出时开 exportable:按钮导的是筛选+排序后的全量行,与当前页无关,单元格按 format 输出"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user