import { createTable, filterPlugin, paginationPlugin, selectionPlugin, sortPlugin, } from '../dist/index.js'; const users = [ { id: 1, name: 'Charlie', age: 35, city: 'Shenzhen' }, { id: 2, name: 'Alice', age: 28, city: 'Beijing' }, { id: 3, name: 'Bob', age: 31, city: 'Shanghai' }, { id: 4, name: 'Dave', age: 22, city: 'Beijing' }, { id: 5, name: 'Eve', age: 29, city: 'Shanghai' }, ]; const table = createTable({ data: users, columns: [ { key: 'name', header: 'Name', sortable: true }, { key: 'age', header: 'Age', sortable: true }, { key: 'city', header: 'City' }, ], rowKey: 'id', plugins: [filterPlugin(), sortPlugin(), paginationPlugin(), selectionPlugin()], initialState: { pagination: { page: 1, pageSize: 2 } }, }); /*** 断言工具:失败即退出 ***/ const assert = (cond, msg) => { if (!cond) { console.error('FAIL:', msg); process.exit(1); } console.log('ok -', msg); }; table.dispatch({ type: 'toggleSort', key: 'name' }); // 按名称升序 assert( JSON.stringify(table.getVisibleRows().rows.map((r) => r.name)) === JSON.stringify(['Alice', 'Bob']), 'sort by name asc + page 1 of size 2', ); table.dispatch({ type: 'setFilter', key: 'city', value: 'shang' }); // 筛选上海 const filtered = table.getVisibleRows(); assert( JSON.stringify(filtered.rows.map((r) => r.name)) === JSON.stringify(['Bob', 'Eve']) && filtered.total === 2, 'filter city~sh keeps Shanghai rows, total=2', ); assert(table.getPageInfo().totalPages === 1, 'totalPages computed from filtered total'); table.dispatch({ type: 'clearFilters' }); // 清筛选回到全量 table.dispatch({ type: 'setPage', page: 2 }); assert( JSON.stringify(table.getVisibleRows().rows.map((r) => r.name)) === JSON.stringify(['Charlie', 'Dave']), 'page 2 after clearing filter', ); assert(table.getPageInfo().page === 2 && table.getPageInfo().totalPages === 3, 'page info total=3 pages'); table.dispatch({ type: 'toggleSelect', key: 3 }); // 勾选行 3 assert(JSON.stringify(table.getState().selection) === JSON.stringify([3]), 'toggleSelect adds rowKey'); table.selectAllVisible(); // 全选当前页(替换语义) assert( JSON.stringify(table.getState().selection) === JSON.stringify([1, 4]), 'selectAllVisible replaces selection with visible rowKeys', ); table.dispatch({ type: 'setPage', page: 99 }); // 越界页码 const clamped = table.getVisibleRows(); assert( table.getPageInfo().page === 3 && clamped.rows.length === 1, 'page clamped to totalPages', ); const before = table.getVisibleRows(); // memo 基线 table.dispatch({ type: 'clearSelection' }); // 无关状态变更 assert(table.getVisibleRows() === before, 'derive stages memoized across unrelated state changes'); // —— 补充不变量 —— const fresh = createTable({ data: users, columns: [{ key: 'name', header: 'Name', sortable: true }], rowKey: 'id', plugins: [paginationPlugin()], initialState: { pagination: { page: 2, pageSize: 2 } }, }); assert( fresh.getPageInfo().page === 2 && fresh.getPageInfo().pageSize === 2, 'getPageInfo 无需先取可见行即返回 initialState 生效值', ); assert(fresh.getPageInfo().totalPages === 3, 'getPageInfo 的 totalPages 由上游 total 推导'); const marks = createTable({ data: users, columns: [{ key: 'name', header: 'Name' }, { key: 'city', header: 'City' }], rowKey: 'id', plugins: [filterPlugin(), paginationPlugin(), selectionPlugin()], initialState: { pagination: { page: 1, pageSize: 2 } }, }); marks.selectAllVisible(); // 选中第 1 页的 id 1 / 2 marks.dispatch({ type: 'setFilter', key: 'city', value: 'Beijing' }); // 可见行整体换掉 assert( JSON.stringify(marks.getState().selection) === JSON.stringify([1, 2]), 'selection 以 rowKey 记账,筛选换掉可见行后仍保留', ); const toggle = createTable({ data: users, columns: [{ key: 'age', header: 'Age', sortable: true }], rowKey: 'id', plugins: [sortPlugin()], }); toggle.dispatch({ type: 'toggleSort', key: 'age' }); toggle.dispatch({ type: 'toggleSort', key: 'age' }); toggle.dispatch({ type: 'toggleSort', key: 'age' }); assert(toggle.getState().sorting.length === 0, '同一列连点三次回到无序(asc → desc → 取消)'); const multi = createTable({ data: users, columns: [{ key: 'city', header: 'City' }, { key: 'age', header: 'Age', sortable: true }], rowKey: 'id', plugins: [sortPlugin({ multi: true })], }); multi.dispatch({ type: 'toggleSort', key: 'city', multi: true }); multi.dispatch({ type: 'toggleSort', key: 'age', multi: true }); assert(multi.getState().sorting.length === 2, 'multi 排序保留两列规则'); assert( JSON.stringify(multi.getVisibleRows().rows.map((r) => r.id)) === JSON.stringify([4, 2, 5, 3, 1]), '多列排序按规则顺序生效(city 升序,同城内 age 升序:Beijing 4→2、Shanghai 5→3)', ); const sizing = createTable({ data: users, columns: [{ key: 'name', header: 'Name' }], rowKey: 'id', plugins: [paginationPlugin()], initialState: { pagination: { page: 1, pageSize: 2 } }, }); sizing.dispatch({ type: 'setPage', page: 2 }); sizing.dispatch({ type: 'setPageSize', pageSize: 3 }); assert(sizing.getPageInfo().page === 1 && sizing.getPageInfo().pageSize === 3, '改页大小回到第 1 页'); sizing.dispatch({ type: 'toggleSort', key: 'name' }); sizing.dispatch({ type: 'reset' }); assert( sizing.getState().sorting.length === 0 && sizing.getState().filters.length === 0 && sizing.getState().pagination.page === 1 && sizing.getState().pagination.pageSize === 20, 'reset 回到默认态(排序/筛选清空,分页回 1/20)', ); console.log('SMOKE OK');