Appearance
Vue 分页(客户端)
客户端分页(默认模式):把全量数据交给表格,翻页 / 切换每页条数 / 排序 / 筛选全部由表格自己处理,不用写 onPageChange,也不用管 total。
total由筛选后的行数派生,筛选变化时页码自动回到第 1 页- 排序作用于全量数据而不是当前页
- 与服务端模式(见分页(服务端))的差别就是
manualPagination那一个开关
使用的 API
ts
type Options = {
list: T[]; // 全量数据
itemKey: string; // 行唯一标识字段名
estimatedSize: number; // 行预估高度(px)
// 传了 pagination 就有分页器;不开 manualPagination 即客户端分页
pagination: { pageSize: number; pageSizes?: number[] };
}
// 实例方法(ref 上调用)
tableRef.value?.setPage(7);
tableRef.value?.setPageSize(50);
tableRef.value?.getPagination(); // { page, pageSize, total }
tableRef.value?.getState(); // 列宽/列序/排序/筛选/分页快照
tableRef.value?.setState(saved); // 还原快照完整语义(total 派生规则、与排序筛选的先后、状态快照字段)见 Vanilla · 分页(客户端)。
示例
微应用尚未挂载。
源码
点击查看源码
vue
<template>
<div class="demo-wrapper">
<h3 class="demo-title">Vue 分页(客户端)</h3>
<div class="demo-hint">
客户端分页(默认):全量 {{ ROW_COUNT }} 条一次性交给表格,翻页只换切片。 total
由筛选后的行数派生 —— 试试筛选部门,总数会跟着变,页码自动回到第 1 页。
排序作用于全量数据而不是当前页。
</div>
<div class="virt-table-controls">
<button type="button" @click="goPage7">跳到第 7 页</button>
<button type="button" @click="filterDept">筛「工程部」</button>
<button type="button" @click="clearFilter">清空筛选</button>
<button type="button" @click="saveState">存状态</button>
<button type="button" @click="loadState">还原状态</button>
</div>
<div class="status-text">{{ status }}</div>
<div style="width: 100%; height: 520px" class="demo-container">
<VirtTableVue ref="tableRef" :columns="columns" :options="options" />
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import { VirtTableVue, type VueTableColumn } from '@virt-table/vue';
import { faker } from '@faker-js/faker';
import type { VirtTableVueInstance } from '../../virt-table-ref';
/**
* 客户端分页(默认模式):把全量数据交给表格,翻页 / 切换每页条数 / 排序 / 筛选
* 全部由表格自己处理,**不用写 onPageChange,也不用管 total**。
*
* 与服务端模式(`manualPagination: true`,见 feature/pagination)的差别就是那一个开关。
*/
const ROW_COUNT = 2000;
const DEPTS = ['工程部', '设计部', '市场部'];
interface Row extends Record<string, unknown> {
id: number;
name: string;
dept: string;
score: number;
}
const list: Row[] = Array.from({ length: ROW_COUNT }, (_, i) => ({
id: i + 1,
name: faker.person.fullName(),
dept: DEPTS[i % DEPTS.length]!,
score: 40 + ((i * 17) % 60),
}));
const columns: VueTableColumn<Row>[] = [
{ key: 'id', title: 'ID', width: 90, align: 'center', sortable: true },
{ key: 'name', title: '姓名', width: 200 },
{
key: 'dept',
title: '部门',
width: 160,
align: 'center',
filters: DEPTS.map((d) => ({ label: d, value: d })),
},
{ key: 'score', title: '分数', width: 120, align: 'right', sortable: true },
];
const options = {
list,
itemKey: 'id',
estimatedSize: 40,
buffer: 6,
border: true,
stripe: true,
// 传了 pagination 就有分页器;不开 manualPagination 即客户端分页
pagination: { pageSize: 20, pageSizes: [20, 50, 100] },
};
const tableRef = ref<VirtTableVueInstance | null>(null);
const status = ref('');
let saved: string | null = null;
const syncStatus = () => {
const p = tableRef.value?.getPagination();
if (!p) return;
status.value = `第 ${p.page} 页 · 每页 ${p.pageSize} 条 · 共 ${p.total} 条(筛选后)`;
};
onMounted(syncStatus);
const goPage7 = () => {
tableRef.value?.setPage(7);
syncStatus();
};
const filterDept = () => {
tableRef.value?.setColumnFilter('dept', ['工程部']);
syncStatus();
};
const clearFilter = () => {
tableRef.value?.clearAllFilters();
syncStatus();
};
const saveState = () => {
saved = JSON.stringify(tableRef.value?.getState());
status.value = '已存下当前列宽 / 列序 / 排序 / 筛选 / 分页';
};
const loadState = () => {
if (!saved) return;
tableRef.value?.setState(JSON.parse(saved));
syncStatus();
};
</script>