数据表格 ​
管理请求、分页和跨页选择,并复用表格展示能力。
基础用法 ​
示例使用本地异步函数、显式分页参数构造器和 Row 响应转换。分页改变会请求对应切片,列和工具栏与当前数据使用同一份源码。
本地分页与跨页选择
<template>
<div class="demo-root-layout">
<AsamDataTable
ref="table"
:api="api"
:query="query"
:build-page-params="buildPageParams"
:transform="transform"
:row-key="rowKey"
:page-size="2"
:page-sizes="[2, 5]"
selectable
size="default"
@selection-change="selection = $event"
>
<AsamTableColumn prop="id" label="编号" />
<AsamTableColumn prop="name" label="名称" />
<template #toolbar="{ selection: selected, loading, stale }">
<span>已选 {{ selected.keys.length }} 项</span>
<AsamButton
native-type="submit"
type="default"
:disabled="loading || stale"
@click="table?.clearSelection()"
>清空选择</AsamButton
>
</template>
</AsamDataTable>
<pre>{{ selection }}</pre>
</div>
</template>
<script lang="ts" setup>
import { ref } from 'vue';
import { AsamButton, AsamDataTable, AsamTableColumn } from '@asam-ui/ui';
import type {
AsamApiRequest,
AsamPage,
AsamPageRequest,
AsamDataTableExpose,
AsamTableSelection
} from '@asam-ui/ui';
defineOptions({ name: 'AsamDataTableBasicDemoExample' });
interface Row {
id: number;
name: string;
}
interface Query {
text: string;
}
interface Params extends Query {
currentPage: number;
pageSize: number;
}
const query = { text: '' };
const table = ref<AsamDataTableExpose<Row>>();
const selection = ref<AsamTableSelection<Row>>({ keys: [], rows: [] });
const rows: Row[] = Array.from({ length: 6 }, (_, index) => ({
id: index + 1,
name: '记录 ' + (index + 1)
}));
const rowKey = (row: Row) => row.id;
const buildPageParams = (input: Query, page: AsamPageRequest) => ({ ...input, ...page });
const api: AsamApiRequest<Params, AsamPage<Row>> = async (params, context) => {
context.signal.throwIfAborted();
const matched = rows.filter((row) => row.name.includes(params.text));
const start = (params.currentPage - 1) * params.pageSize;
return { list: matched.slice(start, start + params.pageSize), total: matched.length };
};
const transform = (response: AsamPage<Row>) => response;
</script>
<style scoped>
.demo-root-layout {
display: grid;
gap: var(--asam-space-md);
justify-items: start;
}
.demo-root-layout > :is(p, pre, output) {
margin-block: 0;
}
</style>
<template>
<div class="demo-root-layout">
<AsamDataTable
ref="table"
:api="api"
:query="query"
:build-page-params="buildPageParams"
:transform="transform"
:row-key="rowKey"
:page-size="2"
:page-sizes="[2, 5]"
selectable
size="default"
@selection-change="selection = $event"
>
<AsamTableColumn prop="id" label="编号" />
<AsamTableColumn prop="name" label="名称" />
<template #toolbar="{ selection: selected, loading, stale }">
<span>已选 {{ selected.keys.length }} 项</span>
<AsamButton
native-type="submit"
type="default"
:disabled="loading || stale"
@click="table?.clearSelection()"
>清空选择</AsamButton
>
</template>
</AsamDataTable>
<pre>{{ selection }}</pre>
</div>
</template>
<script setup>
import { ref } from 'vue';
import { AsamButton, AsamDataTable, AsamTableColumn } from '@asam-ui/ui';
defineOptions({ name: 'AsamDataTableBasicDemoExample' });
const query = { text: '' };
const table = ref();
const selection = ref({ keys: [], rows: [] });
const rows = Array.from({ length: 6 }, (_, index) => ({
id: index + 1,
name: '记录 ' + (index + 1)
}));
const rowKey = (row) => row.id;
const buildPageParams = (input, page) => ({ ...input, ...page });
const api = async (params, context) => {
context.signal.throwIfAborted();
const matched = rows.filter((row) => row.name.includes(params.text));
const start = (params.currentPage - 1) * params.pageSize;
return { list: matched.slice(start, start + params.pageSize), total: matched.length };
};
const transform = (response) => response;
</script>
<style scoped>
.demo-root-layout {
display: grid;
gap: var(--asam-space-md);
justify-items: start;
}
.demo-root-layout > :is(p, pre, output) {
margin-block: 0;
}
</style>
请求与分页覆盖 ​
api 与 request 互斥;API 分支必须提供 buildPageParams,URL 分支使用 buildPageRequest 或 pageTarget。 完整说明。
选择、事件与实例 ​
selection-change 返回 { keys, rows } 的跨页快照,覆盖基础表格的行数组签名。 完整说明。
完整表格实例与树半选 ​
组件转发当前基础表格的完整公开实例,包括 getHalfSelectionRows、columns、tableId、ns、layout 和 store;方法保持真实实例接收者,观察值随挂载和当前表格变化更新。ns、layout、store 等对象只用于只读观察,不承诺其内部命令为稳定的公共接口。
getHalfSelectionRows 返回当前原生树选择列中的半选行。 完整说明。
原生实例与半选
<template>
<div class="demo-root-layout">
<AsamDataTable
ref="table"
:api="loadTree"
:query="{}"
:build-page-params="buildPageParams"
:transform="transform"
:page-size="10"
:page-sizes="[10]"
default-expand-all
>
<AsamTableColumn type="selection" width="54" />
<AsamTableColumn prop="name" label="树节点" />
</AsamDataTable>
<p class="example-paragraph">使用上表原生选择列勾选一个子节点,再读取当前树的半选父节点。</p>
<AsamButton type="default" native-type="button" @click="readInstance"
>读取表格实例</AsamButton
>
<pre role="status">{{ observation }}</pre>
</div>
</template>
<script lang="ts" setup>
defineOptions({ name: 'AsamDataTableNativeInstanceDemo' });
import { ref } from 'vue';
import { AsamButton, AsamDataTable, AsamTableColumn } from '@asam-ui/ui';
import type { AsamDataTableExpose, AsamPage, AsamPageRequest } from '@asam-ui/ui';
interface Row {
id: number;
name: string;
children?: Row[];
}
const table = ref<AsamDataTableExpose<Row>>();
const observation = ref({ halfSelected: [] as string[], tableId: '', columns: 0, selectedKeys: 0 });
const rows: Row[] = [
{
id: 1,
name: '研发组',
children: [
{ id: 11, name: '组件实现' },
{ id: 12, name: '独立验证' }
]
}
];
const buildPageParams = (_query: object, page: AsamPageRequest) => page;
const loadTree = async (): Promise<AsamPage<Row>> => ({ list: rows, total: rows.length });
const transform = (response: AsamPage<Row>) => response;
function readInstance(): void {
if (!table.value) return;
observation.value = {
halfSelected: table.value.getHalfSelectionRows().map((row) => row.name),
tableId: table.value.tableId,
columns: table.value.columns.length,
selectedKeys: table.value.getSelection().keys.length
};
}
</script>
<style scoped>
.demo-root-layout {
display: grid;
gap: var(--asam-space-md);
justify-items: start;
}
.demo-root-layout > :is(p, pre, output) {
margin-block: 0;
}
</style>
<style scoped>
p:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
<template>
<div class="demo-root-layout">
<AsamDataTable
ref="table"
:api="loadTree"
:query="{}"
:build-page-params="buildPageParams"
:transform="transform"
:page-size="10"
:page-sizes="[10]"
default-expand-all
>
<AsamTableColumn type="selection" width="54" />
<AsamTableColumn prop="name" label="树节点" />
</AsamDataTable>
<p class="example-paragraph">使用上表原生选择列勾选一个子节点,再读取当前树的半选父节点。</p>
<AsamButton type="default" native-type="button" @click="readInstance"
>读取表格实例</AsamButton
>
<pre role="status">{{ observation }}</pre>
</div>
</template>
<script setup>
defineOptions({ name: 'AsamDataTableNativeInstanceDemo' });
import { ref } from 'vue';
import { AsamButton, AsamDataTable, AsamTableColumn } from '@asam-ui/ui';
const table = ref();
const observation = ref({ halfSelected: [], tableId: '', columns: 0, selectedKeys: 0 });
const rows = [
{
id: 1,
name: '研发组',
children: [
{ id: 11, name: '组件实现' },
{ id: 12, name: '独立验证' }
]
}
];
const buildPageParams = (_query, page) => page;
const loadTree = async () => ({ list: rows, total: rows.length });
const transform = (response) => response;
function readInstance() {
if (!table.value) return;
observation.value = {
halfSelected: table.value.getHalfSelectionRows().map((row) => row.name),
tableId: table.value.tableId,
columns: table.value.columns.length,
selectedKeys: table.value.getSelection().keys.length
};
}
</script>
<style scoped>
.demo-root-layout {
display: grid;
gap: var(--asam-space-md);
justify-items: start;
}
.demo-root-layout > :is(p, pre, output) {
margin-block: 0;
}
</style>
<style scoped>
p:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
失败和重试 ​
error 返回 AsamDataError,success 返回 AsamPage
本站继承关系 ​
- AsamTable:适用的基础展示、交互与公开 API。
- AsamPagination:适用的基础展示、交互与公开 API。
本页数据来源、模型、事件和实例的覆盖规则优先;本站链接不扩大本组件公开契约。尺寸使用基础组件的 ASAM 尺寸规则,示例使用 default。
跨页选择、陈旧恢复与取消 ​
完整示例返回严格 list/total 分页结构,buildPageParams 明确合并查询和分页输入,rowKey 从行生成稳定标识。 完整说明。
完整示例同时记录七个数据入口事件:update:currentPage/page-change 和 update:pageSize/page-size-change 保持数字;分页每页数量入口可在2/4之间切换。 完整说明。
跨页选择、陈旧恢复与取消
<template><StandaloneDemo /></template>
<script lang="ts" setup>
import * as __external0 from "vue";
import * as __external1 from "@asam-ui/ui";
const __external: any = {"vue": { ...__external0, __esModule: true },
"@asam-ui/ui": { ...__external1, __esModule: true }};
const __modules: any = {
"Demo.vue": (module: any, exports: any, require: any) => {
"use strict";
var __importDefault: any = function (mod?: any) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1: any = require("vue");
const vue_2: any = require("vue");
const _hoisted_1: any = { class: "demo-layout-1" };
const _hoisted_2: any = { class: "demo-slot-layout" };
const _hoisted_3: any = { "data-testid": "paged-state-contract-result" };
const _hoisted_4: any = ["data-row-id"];
const _hoisted_5: any = ["data-slot-loading", "data-slot-stale", "data-slot-keys", "data-slot-rows"];
const _hoisted_6: any = {
class: "example-paragraph",
role: "status"
};
const _hoisted_7: any = { "data-testid": "table-selection" };
const _hoisted_8: any = { "data-testid": "table-observation" };
const _hoisted_9: any = {
class: "lifecycle-events",
"data-testid": "table-lifecycle-events"
};
const _hoisted_10: any = { "data-testid": "table-events" };
const NativeSlotsContract_vue_1: any = __importDefault(require("./NativeSlotsContract.vue"));
const RowKeyContract_vue_1: any = __importDefault(require("./RowKeyContract.vue"));
const vue_3: any = require("vue");
const ui_1: any = require("@asam-ui/ui");
exports.default = (0, vue_1.defineComponent)({
...{ name: 'AsamDataTableLifecycleDemo' },
__name: 'Demo',
setup(__props?: any) {
const table: any = (0, vue_3.ref)();
const query: any = { text: '' };
const failure: any = (0, vue_3.ref)(false);
const slow: any = (0, vue_3.ref)(false);
const held: any = (0, vue_3.ref)(false);
const pendingReleases: any = new Set();
function holdReload() {
held.value = true;
void reload();
}
function releaseHeld() {
held.value = false;
for (const finish of ([...pendingReleases] as any))
finish();
}
async function waitHeld(signal?: any) {
signal.throwIfAborted();
if (!held.value)
return;
await new Promise((done?: any, reject?: any) => {
const finish: any = () => {
pendingReleases.delete(finish);
signal.removeEventListener('abort', abort);
done();
};
const abort: any = () => {
pendingReleases.delete(finish);
signal.removeEventListener('abort', abort);
reject(signal.reason);
};
pendingReleases.add(finish);
signal.addEventListener('abort', abort, { once: true });
if (signal.aborted)
abort();
});
signal.throwIfAborted();
}
const result: any = (0, vue_3.ref)('');
const error: any = (0, vue_3.ref)('');
const events: any = (0, vue_3.ref)([]);
function recordPage(name?: any, value?: any) {
events.value.push({ name, payload: value });
}
function recordSelection(value?: any) {
selection.value = value;
events.value.push({
name: 'selection-change',
payload: { keys: [...value.keys], rows: value.rows.map((row?: any) => ({ ...row })) }
});
}
function recordSuccess(value?: any) {
pageRows.value = value.list;
successes.value++;
events.value.push({
name: 'success',
payload: { list: value.list.map((row?: any) => ({ ...row })), total: value.total }
});
}
function recordError(value?: any) {
error.value = value.message;
events.value.push({ name: 'error', payload: { kind: value.kind, message: value.message } });
}
const successes: any = (0, vue_3.ref)(0);
const sortEvents: any = (0, vue_3.ref)(0);
const rowEvents: any = (0, vue_3.ref)([]);
const pageRows: any = (0, vue_3.ref)([]);
const selection: any = (0, vue_3.ref)({ keys: [], rows: [] });
const observation: any = (0, vue_3.ref)({
keys: [],
rows: []
});
const rows: any = Array.from({ length: 6 }, (_?: any, index?: any) => ({
id: index + 1,
name: '成员 ' + (index + 1)
}));
const rowKey: any = (row?: any) => row.id;
const buildPageParams: any = (input?: any, page?: any) => ({ ...input, ...page });
const api: any = async (params?: any, context?: any) => {
await waitHeld(context.signal);
await new Promise((done?: any) => setTimeout(done, slow.value ? 500 : 100));
context.signal.throwIfAborted();
if (failure.value)
throw new Error('演示分页接口失败');
const matched: any = rows.filter((row?: any) => row.name.includes(params.text));
const start: any = (params.currentPage - 1) * params.pageSize;
return { list: matched.slice(start, start + params.pageSize), total: matched.length };
};
const transform: any = (page?: any) => page;
async function reload() {
const response: any = await table.value?.reload({ force: true });
result.value = response?.status ?? 'cancelled';
if (response?.status === 'success')
error.value = '';
}
async function cancelSlow() {
slow.value = true;
const pending: any = table.value?.reload({ force: true });
setTimeout(() => table.value?.cancel(), 30);
result.value = (await pending)?.status ?? 'cancelled';
slow.value = false;
}
function inspect() {
const snapshot: any = table.value?.getSelection();
observation.value = { keys: snapshot?.keys ?? [], rows: table.value?.getSelectionRows() ?? [] };
}
function toggleFirst(selected?: any) {
const first: any = pageRows.value[0];
if (first)
table.value?.toggleRowSelection(first, selected);
}
function toggleLast() {
const last: any = pageRows.value.at(-1);
if (last)
table.value?.toggleRowSelection(last, true);
}
const pagedStateContractResult: any = (0, vue_3.ref)([]);
const pagedStateContractBusy: any = (0, vue_3.ref)(false);
const pagedStateInjectionTarget: any = (0, vue_3.ref)();
async function runPagedStateContract() {
pagedStateContractBusy.value = true;
pagedStateContractResult.value = [];
const observations: any = [], scopes: any = [];
const record: any = (phase?: any, state?: any) => observations.push({ phase, state });
const attach: any = (config?: any) => {
const scope: any = (0, vue_3.effectScope)();
scopes.push(scope);
const state: any = scope.run(() => (0, ui_1.useAsamPagedData)(config));
if (!state)
throw Error('Missing paged state');
return { state, scope };
};
const snapshot: any = (state?: any) => ({
ids: state.data.value.list.map((row?: any) => row.id),
total: state.data.value.total,
loading: state.loading.value,
error: state.error.value?.kind ?? null,
errorMessage: state.error.value?.message ?? null,
stale: state.stale.value,
hasSucceeded: state.hasSucceeded.value,
empty: state.empty.value,
currentPage: state.currentPage.value,
pageSize: state.pageSize.value,
dataKeySet: state.dataKey.value !== null,
requestKeySet: state.requestKey.value !== null,
keysMatch: state.dataKey.value !== null && state.dataKey.value === state.requestKey.value,
resolved: state.resolvedPage.value === null
? null
: {
currentPage: state.resolvedPage.value.currentPage,
pageSize: state.resolvedPage.value.pageSize,
queryKeyNonempty: state.resolvedPage.value.queryKey.length > 0
},
selection: {
keys: state.selection.value.keys,
ids: state.selection.value.rows.map((row?: any) => row.id)
}
});
const build: any = (query?: any, page?: any) => ({
...query,
p: page.currentPage,
s: page.pageSize
});
const gate: any = () => {
let release: any = () => {
throw Error('Missing gate');
}, reject: any = () => {
throw Error('Missing gate');
};
const promise: any = new Promise((done?: any, fail?: any) => {
release = done;
reject = fail;
});
return { promise, release, reject };
};
const settle: any = async (check?: any) => {
for (let i: any = 0; i < 100 && !check(); i++)
await Promise.resolve();
if (!check())
throw Error('Paged state did not settle within microtasks');
};
try {
const firstGate: any = gate(), nextGate: any = gate(), failedGate: any = gate(), emptyGate: any = gate(), jobs: any = [firstGate, nextGate, failedGate, emptyGate];
let calls: any = 0, successes: any = 0, localErrors: any = 0, notifications: any = 0, successSame: any = false;
const pageEvents: any = [], sizeEvents: any = [], selectionEvents: any = [], parameters: any = [], contexts: any = [];
const runtime: any = (0, ui_1.createAsamDataRuntime)({
scope: 'paged-contract',
provider: {
onError: () => {
notifications++;
}
}
}), owner: any = attach({
source: {
api: (params?: any, context?: any) => {
parameters.push(params);
contexts.push({
scope: context.scope,
epoch: context.epoch,
signalAborted: context.signal.aborted
});
return jobs[calls++].promise;
},
query: { term: 'initial' },
buildPageParams: build,
currentPage: 2,
pageSize: 2,
defaultSelectedRowKeys: [1, '1', 'future'],
immediate: false
},
runtime,
onSuccess: (data?: any) => {
successes++;
successSame = data === owner.state.data.value;
},
onError: () => {
localErrors++;
},
onPageChange: (page?: any) => pageEvents.push(page),
onPageSizeChange: (size?: any) => sizeEvents.push(size),
onSelectionChange: (selection?: any) => selectionEvents.push(selection.keys)
}), state: any = owner.state;
record('paged-state-complete-public-members-initial-default-keys-and-pagination', {
members: Object.keys(state),
snapshot: snapshot(state),
calls,
selectionEvents: [...selectionEvents]
});
const first: any = state.reload();
record('paged-state-held-api-build-params-and-original-context', {
snapshot: snapshot(state),
calls,
parameters: [...parameters],
contexts: [...contexts]
});
const host: any = {
list: [
{ id: 1, name: 'Numeric', meta: { count: 1 } },
{ id: '1', name: 'String', meta: { count: 1 } }
],
total: 6
};
firstGate.release(host);
const firstResult: any = await first;
host.list[0].meta.count = 9;
record('paged-state-success-resolved-page-frozen-host-snapshot-and-callback', {
snapshot: snapshot(state),
status: firstResult.status,
fromCache: firstResult.status === 'success' ? firstResult.fromCache : null,
successes,
successSame,
frozen: Object.isFrozen(state.data.value) &&
Object.isFrozen(state.data.value.list) &&
Object.isFrozen(state.data.value.list[0]) &&
Object.isFrozen(state.data.value.list[0]?.meta),
hostCount: host.list[0].meta.count,
snapshotCount: Reflect.get(Object(state.data.value.list[0].meta), 'count'),
selectionEvents: [...selectionEvents]
});
state.clearSelection();
state.toggleRowSelection(state.data.value.list[0]);
state.toggleRowSelection({ id: 'outsider' }, true);
state.toggleRowSelection(state.data.value.list[1], true);
state.selectPage([state.data.value.list[1]]);
const selection: any = state.getSelection();
record('paged-state-selection-typed-keys-toggle-page-filter-and-frozen-snapshot', {
snapshot: snapshot(state),
samePublicSelection: selection === state.selection.value,
frozen: Object.isFrozen(selection) &&
Object.isFrozen(selection.keys) &&
Object.isFrozen(selection.rows),
selectionEvents: [...selectionEvents]
});
state.setCurrentPage(3);
const pageChanged: any = snapshot(state), next: any = state.reload();
nextGate.release({
list: [
{ id: 3, name: 'Third' },
{ id: 4, name: 'Fourth' }
],
total: 6
});
await next;
state.toggleRowSelection(state.data.value.list[0], true);
state.selectPage([state.data.value.list[1]]);
record('paged-state-page-navigation-retains-cross-page-rows-and-selected-keys', {
beforeReload: pageChanged,
after: snapshot(state),
parameters: [...parameters],
pageEvents: [...pageEvents],
sizeEvents: [...sizeEvents]
});
const original: any = Error('owned paged request failure'), failed: any = state.reload();
failedGate.reject(original);
const failure: any = await failed;
const staleSelection: any = state.getSelection();
state.toggleRowSelection(state.data.value.list[0], true);
state.selectPage([]);
const prevented: any = state.getSelection() === staleSelection;
state.toggleRowSelection(state.data.value.list[1], false);
record('paged-state-error-retains-data-and-blocks-stale-selection-additions', {
snapshot: snapshot(state),
status: failure.status,
originalCause: state.error.value?.cause === original,
notified: state.error.value?.notified,
prevented,
successes,
localErrors,
notifications
});
const empty: any = state.reload({ force: true });
emptyGate.release({ list: [], total: 0 });
await empty;
const beforeClear: any = state.getSelection();
state.clearSelection();
const clearEvents: any = selectionEvents.length;
state.clearSelection();
record('paged-state-zero-total-resets-page-success-empty-and-idempotent-clear', {
snapshot: snapshot(state),
retainedKeyBeforeClear: beforeClear.keys,
clearEventsUnchanged: selectionEvents.length === clearEvents,
successes,
localErrors,
notifications,
pageEvents: [...pageEvents]
});
owner.scope.stop();
record('paged-state-disposal-stops-reload-and-scope-reissue', {
status: (await state.reload()).status,
calls
});
let liveCalls: any = 0;
const liveApi: any = async (params?: any) => {
liveCalls++;
return { list: [{ id: params.term, name: 'Live' }], total: 100 };
}, liveQuery: any = (0, vue_3.ref)({ term: 'one' }), liveSource: any = (0, vue_3.computed)(() => ({
api: liveApi,
query: liveQuery.value,
buildPageParams: build,
currentPage: 2,
pageSize: 2
})), live: any = attach({ source: liveSource, runtime: (0, ui_1.createAsamDataRuntime)() }).state;
await settle(() => live.hasSucceeded.value);
live.toggleRowSelection(live.data.value.list[0], true);
const initialLive: any = snapshot(live);
liveQuery.value.term = 'two';
await settle(() => live.data.value.list[0]?.id === 'two');
const changedLive: any = snapshot(live);
const beforeEcho: any = liveCalls;
record('paged-state-immediate-reactive-query-resets-page-and-clears-selection', {
initial: initialLive,
changed: changedLive,
calls: liveCalls,
noChangeObservation: liveCalls === beforeEcho
});
const obsolete: any = gate(), latest: any = gate();
let raceCalls: any = 0, raceCallbacks: any = 0;
const raceQuery: any = (0, vue_3.ref)({ term: 'old' }), raceApi: any = (params?: any) => {
raceCalls++;
return params.term === 'old' ? obsolete.promise : latest.promise;
}, race: any = attach({
source: (0, vue_3.computed)(() => ({
api: raceApi,
query: raceQuery.value,
buildPageParams: build,
immediate: false
})),
runtime: (0, ui_1.createAsamDataRuntime)(),
onSuccess: () => {
raceCallbacks++;
}
}).state;
const oldPending: any = race.reload();
raceQuery.value.term = 'new';
const oldCancelled: any = await oldPending;
await Promise.resolve();
const newPending: any = race.reload();
latest.release({ list: [{ id: 'new' }], total: 1 });
await newPending;
obsolete.release({ list: [{ id: 'old' }], total: 1 });
await settle(() => !race.loading.value);
record('paged-state-query-race-cancels-old-and-ignores-late-result', {
old: oldCancelled.status,
calls: raceCalls,
callbacks: raceCallbacks,
snapshot: snapshot(race)
});
let sizeCalls: any = 0;
const sizeState: any = attach({
source: {
api: async () => {
sizeCalls++;
return { list: [{ id: 1 }], total: 100 };
},
buildPageParams: build,
currentPage: 3,
pageSize: 2,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)(),
onPageChange: (page?: any) => pageEvents.push(page),
onPageSizeChange: (size?: any) => sizeEvents.push(size)
}).state;
await sizeState.reload();
sizeState.setPageSize(4);
const sizedBefore: any = snapshot(sizeState);
await sizeState.reload();
sizeState.setPageSize(4);
sizeState.setCurrentPage(2);
const pagedBefore: any = snapshot(sizeState);
await sizeState.reload();
record('paged-state-size-reset-and-page-setters-prepare-manual-requests', {
sizeBefore: sizedBefore,
pageBefore: pagedBefore,
final: snapshot(sizeState),
calls: sizeCalls,
sizeEvents: [...sizeEvents],
pageEvents: [...pageEvents]
});
const correctionCalls: any = [];
let correctionSuccess: any = 0;
const correction: any = attach({
source: {
api: async (params?: any) => {
correctionCalls.push(Number(params.p));
return { list: [], total: 3 };
},
buildPageParams: build,
currentPage: 9,
pageSize: 2,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)(),
onSuccess: () => {
correctionSuccess++;
}
}).state;
const corrected: any = await correction.reload();
record('paged-state-out-of-range-corrects-once-and-preserves-reload-promise', {
status: corrected.status,
calls: correctionCalls,
successes: correctionSuccess,
snapshot: snapshot(correction)
});
const repeatedCalls: any = [];
const repeated: any = attach({
source: {
api: async (params?: any) => {
repeatedCalls.push(Number(params.p));
return { list: [], total: repeatedCalls.length === 1 ? 3 : 1 };
},
buildPageParams: build,
currentPage: 9,
pageSize: 2,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
const uncorrectable: any = await repeated.reload();
record('paged-state-repeated-out-of-range-is-response-error-after-one-correction', {
status: uncorrectable.status,
calls: repeatedCalls,
snapshot: snapshot(repeated)
});
const requests: any = [], urlRuntime: any = (0, ui_1.createAsamDataRuntime)({
provider: {
pagination: { page: 'p', pageSize: 's' },
request: async (request?: any, context?: any) => {
requests.push({
request,
context: {
scope: context.scope,
epoch: context.epoch,
signalAborted: context.signal.aborted
}
});
return { list: [{ id: 1 }], total: 20 };
}
}
});
const urlDefault: any = attach({
source: {
request: { url: '/page-default', params: { host: 'base' } },
query: { term: 'filter' },
currentPage: 2,
pageSize: 4,
immediate: false
},
runtime: urlRuntime
}).state;
await urlDefault.reload();
const urlData: any = attach({
source: {
request: {
url: '/page-data',
method: 'post',
data: { host: 'body' },
params: { untouched: true }
},
query: { term: 'body-query' },
pageTarget: 'data',
currentPage: 3,
pageSize: 2,
immediate: false
},
runtime: urlRuntime
}).state;
await urlData.reload();
let customBuildCalls: any = 0;
const urlCustom: any = attach({
source: {
request: { url: '/unused-base' },
query: { term: 'custom' },
buildPageRequest: (query?: any, page?: any) => {
customBuildCalls++;
return {
url: '/custom-built',
method: 'put',
data: { term: query.term, page: page.currentPage, size: page.pageSize }
};
},
currentPage: 2,
pageSize: 2,
immediate: false
},
runtime: urlRuntime
}).state;
await urlCustom.reload();
record('paged-state-url-default-target-data-and-custom-builder', {
requests,
customBuildCalls,
states: [snapshot(urlDefault), snapshot(urlData), snapshot(urlCustom)]
});
let adapterCalls: any = 0, transformCalls: any = 0;
const adaptedRuntime: any = (0, ui_1.createAsamDataRuntime)({
provider: {
adaptPage: () => {
adapterCalls++;
return { list: [{ id: 'adapter' }], total: 1 };
}
}
});
const transformed: any = attach({
source: {
api: async () => ({ list: [], total: 0 }),
buildPageParams: build,
transform: () => {
transformCalls++;
return { list: [{ id: 'transform' }], total: 1 };
},
immediate: false
},
runtime: adaptedRuntime
}).state;
await transformed.reload();
const adapted: any = attach({
source: {
api: async () => ({ list: [], total: 0 }),
buildPageParams: build,
immediate: false
},
runtime: adaptedRuntime
}).state;
await adapted.reload();
record('paged-state-transform-precedes-provider-adapter-and-default-response', {
transform: snapshot(transformed),
adapter: snapshot(adapted),
transformCalls,
adapterCalls
});
let sharedCalls: any = 0;
const sharedApi: any = async () => {
sharedCalls++;
return { list: [{ id: sharedCalls }], total: 1 };
}, sharedInput: any = {
api: sharedApi,
buildPageParams: build,
cache: { ttlMs: 10000 },
immediate: false
}, sharedRuntime: any = (0, ui_1.createAsamDataRuntime)(), sharedA: any = attach({ source: sharedInput, runtime: sharedRuntime }).state, sharedB: any = attach({ source: sharedInput, runtime: sharedRuntime }).state;
await Promise.all([sharedA.reload(), sharedB.reload()]);
const cached: any = await sharedA.reload();
await Promise.all([sharedA.reload({ force: true }), sharedB.reload({ force: true })]);
record('paged-state-shared-cache-flight-hit-and-deduplicated-force', {
calls: sharedCalls,
cached: cached.status === 'success' ? cached.fromCache : null,
sameSnapshot: sharedA.data.value === sharedB.data.value,
a: snapshot(sharedA),
b: snapshot(sharedB)
});
const cancelGate: any = gate();
let cancelSuccess: any = 0;
const cancelOwner: any = attach({
source: { api: () => cancelGate.promise, buildPageParams: build, immediate: false },
runtime: (0, ui_1.createAsamDataRuntime)(),
onSuccess: () => {
cancelSuccess++;
}
}), cancelJob: any = cancelOwner.state.reload();
cancelOwner.state.cancel();
const cancelled: any = await cancelJob;
cancelGate.release({ list: [{ id: 9 }], total: 1 });
await settle(() => !cancelOwner.state.loading.value);
record('paged-state-explicit-cancel-settles-job-and-keeps-late-success-out', {
status: cancelled.status,
successes: cancelSuccess,
snapshot: snapshot(cancelOwner.state)
});
const disposedGate: any = gate(), disposedOwner: any = attach({
source: {
api: () => disposedGate.promise,
buildPageParams: build,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}), disposedJob: any = disposedOwner.state.reload();
disposedOwner.scope.stop();
const disposedResult: any = await disposedJob;
disposedGate.release({ list: [{ id: 9 }], total: 1 });
await settle(() => !disposedOwner.state.loading.value);
record('paged-state-dispose-held-job-and-block-followup-reload', {
status: disposedResult.status,
reloadAfterDispose: (await disposedOwner.state.reload()).status,
snapshot: snapshot(disposedOwner.state)
});
const invalidRows: any = [];
for (const [name, rows] of ([
['missing-id', [{ name: 'Missing' }]],
['numeric-string-id', [{ id: '1' }, { id: 1 }]],
['duplicate-id', [{ id: 1 }, { id: 1 }]],
['infinite-id', [{ id: Infinity }]],
['boolean-id', [{ id: true }]],
['inherited-id', [Object.create({ id: 1 })]]
] as any)) {
const checked: any = attach({
source: {
api: async () => ({ list: rows, total: rows.length }),
buildPageParams: build,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
const result: any = await checked.reload();
invalidRows.push({ name, status: result.status, snapshot: snapshot(checked) });
}
record('paged-state-own-finite-typed-row-keys-and-duplicate-validation', {
cases: invalidRows
});
const invalidConfig: any = [];
const baseInput: any = {
api: async () => ({ list: [], total: 0 }),
buildPageParams: build,
immediate: false
};
for (const [name, extra] of ([
['no-source', { api: undefined }],
['both-sources', { request: { url: '/x' } }],
['legacy-data', { data: [] }],
['api-no-builder', { buildPageParams: undefined }],
['api-url-builder', { buildPageRequest: () => ({ url: '/x' }) }],
['api-page-target', { pageTarget: 'params' }],
['page-zero', { currentPage: 0 }],
['size-fraction', { pageSize: 1.5 }],
['row-key-invalid', { rowKey: 1 }],
['cache-zero', { cache: { ttlMs: 0 } }],
['cache-key-nonfunction', { cache: { ttlMs: 1, key: 1 } }],
['empty-query-key', { queryKey: () => '' }],
['builder-async', { buildPageParams: async () => ({}) }],
['unknown-default-key', { defaultSelectedRowKeys: [true] }]
] as any)) {
const invalid: any = (Reflect.apply(attach, undefined, [
{ source: { ...baseInput, ...extra }, runtime: (0, ui_1.createAsamDataRuntime)() }
]) as any).state;
invalidConfig.push({
name,
status: (await invalid.reload()).status,
snapshot: snapshot(invalid)
});
}
record('paged-state-api-config-pagination-builder-cache-query-selection-guards', {
cases: invalidConfig
});
const urlInvalid: any = [];
for (const [name, input] of ([
['missing-provider', { request: { url: '/x' } }],
['get-data-target', { request: { url: '/x' }, pageTarget: 'data' }],
['bad-target', { request: { url: '/x', method: 'post' }, pageTarget: 'body' }],
['query-collision', { request: { url: '/x', params: { q: 1 } }, query: { q: 2 } }],
['pagination-collision', { request: { url: '/x', params: { page: 1 } } }],
['array-query', { request: { url: '/x' }, query: [] }],
[
'array-target',
{ request: { url: '/x', method: 'post', data: [] }, pageTarget: 'data' }
],
['url-api-builder', { request: { url: '/x' }, buildPageParams: build }],
[
'custom-plus-target',
{
request: { url: '/x' },
buildPageRequest: () => ({ url: '/custom' }),
pageTarget: 'params'
}
],
[
'custom-async',
{ request: { url: '/x' }, buildPageRequest: async () => ({ url: '/custom' }) }
],
[
'custom-invalid-url',
{ request: { url: '/x' }, buildPageRequest: () => ({ url: '' }) }
]
] as any)) {
const invalid: any = (Reflect.apply(attach, undefined, [
{
source: { ...input, immediate: false },
runtime: (0, ui_1.createAsamDataRuntime)({
provider: name === 'missing-provider'
? {}
: { request: async () => ({ list: [], total: 0 }) }
})
}
]) as any).state;
urlInvalid.push({
name,
status: (await invalid.reload()).status,
snapshot: snapshot(invalid)
});
}
record('paged-state-url-provider-target-collision-plain-input-and-custom-builder-guards', {
cases: urlInvalid
});
const stages: any = [];
for (const [name, api, transform] of ([
[
'transport-throw',
() => {
throw Error('page transport throw');
},
undefined
],
['transport-reject', () => Promise.reject(Error('page transport reject')), undefined],
['transport-not-promise', () => ({ list: [], total: 0 }), undefined],
['bad-response', async () => ({ data: [] }), undefined],
[
'transform-throw',
async () => ({ list: [], total: 0 }),
() => {
throw Error('page transform throw');
}
],
[
'transform-async',
async () => ({ list: [], total: 0 }),
async () => ({ list: [], total: 0 })
]
] as any)) {
const checked: any = (Reflect.apply(attach, undefined, [
{
source: { api, buildPageParams: build, transform, immediate: false },
runtime: (0, ui_1.createAsamDataRuntime)()
}
]) as any).state;
stages.push({
name,
status: (await checked.reload()).status,
snapshot: snapshot(checked)
});
}
record('paged-state-transport-response-transform-error-stages', { cases: stages });
const controlledPage: any = (0, vue_3.ref)(2), controlledSize: any = (0, vue_3.ref)(2), wrapperVersion: any = (0, vue_3.ref)(0);
let controlledCalls: any = 0;
const controlledApi: any = async () => {
controlledCalls++;
return { list: [{ id: controlledCalls }], total: 50 };
};
const controlled: any = attach({
source: (0, vue_3.computed)(() => {
void wrapperVersion.value;
return {
api: controlledApi,
query: { stable: 'same' },
buildPageParams: build,
currentPage: controlledPage.value,
pageSize: controlledSize.value
};
}),
runtime: (0, ui_1.createAsamDataRuntime)(),
onPageChange: (page?: any) => {
controlledPage.value = page;
},
onPageSizeChange: (size?: any) => {
controlledSize.value = size;
}
}).state;
await settle(() => controlled.hasSucceeded.value);
controlled.toggleRowSelection(controlled.data.value.list[0], true);
const wrapperBefore: any = snapshot(controlled);
wrapperVersion.value++;
await Promise.resolve();
await Promise.resolve();
record('paged-state-equivalent-wrapper-keeps-data-selection-and-request', {
before: wrapperBefore,
after: snapshot(controlled),
calls: controlledCalls
});
controlled.setCurrentPage(3);
await settle(() => controlled.data.value.list[0]?.id === 2);
controlled.setPageSize(4);
await settle(() => controlled.data.value.list[0]?.id === 3);
record('paged-state-controlled-page-and-size-echo-do-not-duplicate-requests', {
calls: controlledCalls,
pageProp: controlledPage.value,
sizeProp: controlledSize.value,
snapshot: snapshot(controlled)
});
const sourceApiA: any = async () => ({ list: [{ id: 'source-a' }], total: 100 }), sourceApiB: any = async () => ({ list: [{ id: 'source-b' }], total: 100 }), chosenApi: any = (0, vue_3.ref)(sourceApiA), sourceSwitch: any = attach({
source: (0, vue_3.computed)(() => ({
api: chosenApi.value,
buildPageParams: build,
currentPage: 4,
pageSize: 2
})),
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await settle(() => sourceSwitch.hasSucceeded.value);
sourceSwitch.toggleRowSelection(sourceSwitch.data.value.list[0], true);
chosenApi.value = sourceApiB;
await settle(() => sourceSwitch.data.value.list[0]?.id === 'source-b');
record('paged-state-source-identity-change-resets-page-selection-and-reloads', {
snapshot: snapshot(sourceSwitch)
});
let manualCalls: any = 0;
const manualQuery: any = (0, vue_3.ref)({ term: 'first' }), manualApi: any = async (params?: any) => {
manualCalls++;
return { list: [{ id: params.term }], total: 100 };
}, manual: any = attach({
source: (0, vue_3.computed)(() => ({
api: manualApi,
query: manualQuery.value,
buildPageParams: build,
currentPage: 3,
pageSize: 2,
immediate: false
})),
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await manual.reload();
manual.toggleRowSelection(manual.data.value.list[0], true);
manualQuery.value.term = 'second';
await Promise.resolve();
const manualPrepared: any = snapshot(manual), manualBefore: any = manualCalls;
await manual.reload();
record('paged-state-manual-query-change-prepares-stale-reset-without-transport', {
prepared: manualPrepared,
callsBeforeReload: manualBefore,
callsAfterReload: manualCalls,
final: snapshot(manual)
});
const epochGate: any = gate(), epochRuntime: any = (0, ui_1.createAsamDataRuntime)({ scope: 'scope-before' }), epochContexts: any = [];
let epochCalls: any = 0, epochSuccess: any = 0;
const epochApi: any = async (_params?: any, context?: any) => {
epochContexts.push({ scope: context.scope, epoch: context.epoch });
epochCalls++;
return epochCalls === 2
? epochGate.promise
: { list: [{ id: epochCalls }], total: 1 };
}, epochState: any = attach({
source: {
api: epochApi,
buildPageParams: build,
cache: { ttlMs: 10000 },
immediate: false
},
runtime: epochRuntime,
onSuccess: () => {
epochSuccess++;
}
}).state;
await epochState.reload();
epochState.toggleRowSelection(epochState.data.value.list[0], true);
const epochPending: any = epochState.reload({ force: true });
epochRuntime.setScope('scope-after');
const epochCancelled: any = await epochPending;
await Promise.resolve();
const epochReset: any = snapshot(epochState);
epochGate.release({ list: [{ id: 'obsolete' }], total: 1 });
await new Promise((done?: any) => setTimeout(done, 0));
const epochReload: any = await epochState.reload();
record('paged-state-scope-epoch-invalidates-held-job-data-selection-and-cache', {
status: epochCancelled.status,
reset: epochReset,
reloadStatus: epochReload.status,
fromCache: epochReload.status === 'success' ? epochReload.fromCache : null,
calls: epochCalls,
successes: epochSuccess,
contexts: epochContexts,
final: snapshot(epochState)
});
let isolatedCalls: any = 0;
const isolatedApi: any = async () => {
isolatedCalls++;
return { list: [{ id: isolatedCalls }], total: 1 };
}, isolatedRuntime: any = (0, ui_1.createAsamDataRuntime)(), isolatedInput: any = {
api: isolatedApi,
buildPageParams: build,
immediate: false
}, isolatedA: any = attach({ source: isolatedInput, runtime: isolatedRuntime }).state, isolatedB: any = attach({ source: isolatedInput, runtime: isolatedRuntime }).state;
await Promise.all([isolatedA.reload(), isolatedB.reload()]);
record('paged-state-uncached-consumers-have-separate-flights-and-request-keys', {
calls: isolatedCalls,
distinctKeys: isolatedA.requestKey.value !== isolatedB.requestKey.value,
a: snapshot(isolatedA),
b: snapshot(isolatedB)
});
const sharedCancelGate: any = gate();
let sharedCancelCalls: any = 0;
const sharedCancelInput: any = {
api: () => {
sharedCancelCalls++;
return sharedCancelGate.promise;
},
buildPageParams: build,
cache: { ttlMs: 10000 },
immediate: false
}, sharedCancelRuntime: any = (0, ui_1.createAsamDataRuntime)(), sharedCancelA: any = attach({
source: sharedCancelInput,
runtime: sharedCancelRuntime
}).state, sharedCancelB: any = attach({
source: sharedCancelInput,
runtime: sharedCancelRuntime
}).state;
const ca: any = sharedCancelA.reload(), cb: any = sharedCancelB.reload();
sharedCancelA.cancel();
const cancelledShared: any = await ca;
sharedCancelGate.release({ list: [{ id: 'shared' }], total: 1 });
const cbResult: any = await cb, rejoined: any = await sharedCancelA.reload();
record('paged-state-one-shared-consumer-cancel-preserves-peer-and-rejoins-cache', {
cancelled: cancelledShared.status,
peer: cbResult.status,
rejoined: rejoined.status,
rejoinedFromCache: rejoined.status === 'success' ? rejoined.fromCache : null,
calls: sharedCancelCalls,
a: snapshot(sharedCancelA),
b: snapshot(sharedCancelB)
});
let ttlCalls: any = 0;
const ttlState: any = attach({
source: {
api: async () => {
ttlCalls++;
return { list: [{ id: ttlCalls }], total: 1 };
},
buildPageParams: build,
cache: { ttlMs: 25 },
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await ttlState.reload();
const ttlHit: any = await ttlState.reload();
await new Promise((done?: any) => setTimeout(done, 40));
const ttlExpired: any = await ttlState.reload();
record('paged-state-real-cache-ttl-hit-and-expiry', {
calls: ttlCalls,
hit: ttlHit.status === 'success' ? ttlHit.fromCache : null,
expired: ttlExpired.status === 'success' ? ttlExpired.fromCache : null,
snapshot: snapshot(ttlState)
});
let dateCalls: any = 0;
const dateApi: any = async () => {
dateCalls++;
return { list: [{ id: 'date' }], total: 1 };
}, dateBuilder: any = () => ({ stamp: new Date('2026-01-01T00:00:00Z') }), dateUncached: any = attach({
source: { api: dateApi, buildPageParams: dateBuilder, immediate: false },
runtime: (0, ui_1.createAsamDataRuntime)()
}).state, dateCached: any = attach({
source: {
api: dateApi,
buildPageParams: dateBuilder,
cache: { ttlMs: 10000 },
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state, dateCustom: any = attach({
source: {
api: dateApi,
buildPageParams: dateBuilder,
cache: { ttlMs: 10000, key: () => 'known-date' },
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await dateUncached.reload();
await dateCached.reload();
await dateCustom.reload();
record('paged-state-nonserializable-params-fallback-cache-error-and-custom-key', {
calls: dateCalls,
uncached: snapshot(dateUncached),
cached: snapshot(dateCached),
custom: snapshot(dateCustom)
});
const cacheGuards: any = [];
for (const [name, key] of ([
['empty', () => ''],
['number', () => 1],
['async', async () => 'key'],
[
'throw',
() => {
throw Error('paged key throw');
}
]
] as any)) {
let io: any = 0;
const checked: any = (Reflect.apply(attach, undefined, [
{
source: {
api: async () => {
io++;
return { list: [], total: 0 };
},
buildPageParams: build,
cache: { ttlMs: 100, key },
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}
]) as any).state;
const result: any = await checked.reload();
cacheGuards.push({ name, status: result.status, io, snapshot: snapshot(checked) });
}
record('paged-state-custom-cache-key-synchronous-nonempty-string-guards', {
cases: cacheGuards
});
const rowFunction: any = attach({
source: {
api: async () => ({ list: [{ key: 'row-function' }], total: 1 }),
buildPageParams: build,
rowKey: (row?: any) => String(row.key),
defaultSelectedRowKeys: ['row-function'],
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await rowFunction.reload();
record('paged-state-custom-row-key-function-resolves-default-selection', {
keys: rowFunction.selection.value.keys,
rows: rowFunction.selection.value.rows,
snapshot: snapshot(rowFunction)
});
let getterBad: any = false, getterCalls: any = 0;
const getterTick: any = (0, vue_3.ref)(0), getterApi: any = async () => {
getterCalls++;
return { list: [{ id: 'getter' }], total: 1 };
}, getter: any = attach({
source: () => {
void getterTick.value;
if (getterBad)
throw Error('paged getter throw');
return { api: getterApi, buildPageParams: build, immediate: false };
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await getter.reload();
getterBad = true;
getterTick.value++;
await Promise.resolve();
const getterFault: any = snapshot(getter);
getterBad = false;
getterTick.value++;
await Promise.resolve();
const getterRecovery: any = await getter.reload();
record('paged-state-throwing-source-getter-retains-stale-data-and-recovers', {
fault: getterFault,
status: getterRecovery.status,
calls: getterCalls,
final: snapshot(getter)
});
let missingRuntime: any;
try {
(0, ui_1.useAsamPagedData)({
source: {
api: async () => ({ list: [], total: 0 }),
buildPageParams: build,
immediate: false
}
});
missingRuntime = { threw: false };
}
catch (cause: any) {
missingRuntime = {
threw: true,
kind: cause instanceof ui_1.AsamDataFailure ? cause.kind : null,
message: cause instanceof Error ? cause.message : String(cause)
};
}
record('paged-state-missing-runtime-is-config-error', missingRuntime);
if (typeof document !== 'undefined' && pagedStateInjectionTarget.value) {
let injected: any, injectedCalls: any = 0, mounted: any = false;
const host: any = document.createElement('div'), app: any = (0, vue_3.createApp)((0, vue_3.defineComponent)({
setup() {
injected = (0, ui_1.useAsamPagedData)({
source: {
api: async () => {
injectedCalls++;
return { list: [{ id: 8 }], total: 1 };
},
buildPageParams: build,
immediate: false
}
});
const state: any = injected;
return () => (0, vue_3.h)('output', { 'data-testid': 'paged-state-injected-output' }, JSON.stringify(snapshot(state)));
}
}));
pagedStateInjectionTarget.value.append(host);
(0, ui_1.provideAsamData)(app, {});
try {
app.mount(host);
mounted = true;
if (!injected)
throw Error('Missing injected paged state');
const noInitialRequest: any = injectedCalls === 0, before: any = snapshot(injected);
await injected.reload();
await (0, vue_3.nextTick)();
const after: any = snapshot(injected), rendered: any = host.querySelector('output') !== null, renderedSnapshot: any = JSON.parse(host.querySelector('output')?.textContent ?? '{}');
app.unmount();
mounted = false;
record('paged-state-mounted-app-injection-and-unmount-cleanup', {
noInitialRequest,
before,
after,
rendered,
renderedSnapshot,
calls: injectedCalls,
cancelledReload: (await injected.reload()).status
});
}
finally {
if (mounted)
app.unmount();
host.remove();
}
}
pagedStateContractResult.value = observations;
}
catch (cause: any) {
pagedStateContractResult.value = [
...observations,
{
phase: 'paged-state-contract-unexpected-failure',
state: { message: cause instanceof Error ? cause.message : String(cause) }
}
];
}
finally {
for (const scope of (scopes as any))
scope.stop();
pagedStateContractBusy.value = false;
}
}
return (_ctx?: any, _cache?: any) => {
return ((0, vue_2.openBlock)(), (0, vue_2.createElementBlock)("section", _hoisted_1, [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamCollapse), { "data-testid": "paged-state-contract-case" }, {
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamCollapseItem), { name: "auxiliary-content" }, {
title: (0, vue_2.withCtx)(() => [...(_cache[14] || (_cache[14] = [
(0, vue_2.createElementVNode)("span", null, "完整分页状态与生命周期", -1 /* CACHED */)
]))]),
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createElementVNode)("div", _hoisted_2, [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
disabled: pagedStateContractBusy.value,
onClick: runPagedStateContract
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[15] || (_cache[15] = [
(0, vue_2.createTextVNode)(" 验证分页状态与生命周期 ", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["disabled"]),
(0, vue_2.createElementVNode)("div", {
ref_key: "pagedStateInjectionTarget",
ref: pagedStateInjectionTarget
}, null, 512 /* NEED_PATCH */),
(0, vue_2.createElementVNode)("pre", _hoisted_3, (0, vue_2.toDisplayString)(JSON.stringify(pagedStateContractResult.value)), 1 /* TEXT */)
])
]),
_: 1 /* STABLE */
})
]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamCheckboxItem), {
modelValue: failure.value,
"onUpdate:modelValue": _cache[0] || (_cache[0] = ($event?: any) => ((failure).value = $event))
}, {
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createCommentVNode)(" prettier-ignore "),
_cache[16] || (_cache[16] = (0, vue_2.createTextVNode)(" 模拟接口失败 ", -1 /* CACHED */))
]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["modelValue"]),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: holdReload
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[17] || (_cache[17] = [
(0, vue_2.createTextVNode)("开始持有分页请求", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: releaseHeld
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[18] || (_cache[18] = [
(0, vue_2.createTextVNode)("完成持有分页请求", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: reload
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[19] || (_cache[19] = [
(0, vue_2.createTextVNode)("强制重载", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: cancelSlow
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[20] || (_cache[20] = [
(0, vue_2.createTextVNode)("取消慢请求", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[1] || (_cache[1] = ($event?: any) => (table.value?.clearSelection()))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[21] || (_cache[21] = [
(0, vue_2.createTextVNode)("清空全部选择", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[2] || (_cache[2] = ($event?: any) => (table.value?.sort('id', 'descending')))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[22] || (_cache[22] = [
(0, vue_2.createTextVNode)("按编号倒序", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: inspect
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[23] || (_cache[23] = [
(0, vue_2.createTextVNode)("读取跨页选择", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[3] || (_cache[3] = ($event?: any) => (toggleFirst(true)))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[24] || (_cache[24] = [
(0, vue_2.createTextVNode)("选择当前页首行", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[4] || (_cache[4] = ($event?: any) => (toggleFirst(false)))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[25] || (_cache[25] = [
(0, vue_2.createTextVNode)("取消当前页首行", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[5] || (_cache[5] = ($event?: any) => (toggleFirst()))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[26] || (_cache[26] = [
(0, vue_2.createTextVNode)("切换当前页首行", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[6] || (_cache[6] = ($event?: any) => (toggleLast()))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[27] || (_cache[27] = [
(0, vue_2.createTextVNode)("选择当前页末行", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[7] || (_cache[7] = ($event?: any) => (table.value?.toggleAllSelection()))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[28] || (_cache[28] = [
(0, vue_2.createTextVNode)("切换当前页全选", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamDataTable), {
ref_key: "table",
ref: table,
api: api,
query: query,
"build-page-params": buildPageParams,
transform: transform,
"row-key": rowKey,
"page-size": 2,
"page-sizes": [2, 4],
pagination: { layout: 'sizes, prev, pager, next, total' },
selectable: "",
border: "",
stripe: "",
size: "default",
"onUpdate:currentPage": _cache[8] || (_cache[8] = ($event?: any) => (recordPage('update:currentPage', $event))),
"onUpdate:pageSize": _cache[9] || (_cache[9] = ($event?: any) => (recordPage('update:pageSize', $event))),
onPageChange: _cache[10] || (_cache[10] = ($event?: any) => (recordPage('page-change', $event))),
onPageSizeChange: _cache[11] || (_cache[11] = ($event?: any) => (recordPage('page-size-change', $event))),
onSelectionChange: recordSelection,
onSuccess: recordSuccess,
onSortChange: _cache[12] || (_cache[12] = ($event?: any) => (sortEvents.value++)),
onRowClick: _cache[13] || (_cache[13] = ($event?: any) => (rowEvents.value.push($event.id))),
onError: recordError
}, {
toolbar: (0, vue_2.withCtx)(({ loading, stale, selection: snapshot }: any) => [
(0, vue_2.createElementVNode)("p", {
class: "example-paragraph",
"data-testid": "table-toolbar",
"data-slot-loading": String(loading),
"data-slot-stale": String(stale),
"data-slot-keys": JSON.stringify(snapshot.keys),
"data-slot-rows": JSON.stringify(snapshot.rows)
}, " 已选 " + (0, vue_2.toDisplayString)(snapshot.keys.length) + ";loading=" + (0, vue_2.toDisplayString)(loading) + ";stale=" + (0, vue_2.toDisplayString)(stale), 9 /* TEXT, PROPS */, _hoisted_5)
]),
append: (0, vue_2.withCtx)(() => [...(_cache[29] || (_cache[29] = [
(0, vue_2.createElementVNode)("p", {
class: "example-paragraph",
"data-testid": "table-append"
}, "成员只在本地生成", -1 /* CACHED */)
]))]),
empty: (0, vue_2.withCtx)(() => [...(_cache[30] || (_cache[30] = [
(0, vue_2.createElementVNode)("p", { class: "example-paragraph" }, "当前没有成员", -1 /* CACHED */)
]))]),
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamTableColumn), {
prop: "id",
label: "编号",
sortable: ""
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamTableColumn), {
prop: "name",
label: "成员"
}, {
default: (0, vue_2.withCtx)(({ row }: any) => [
(0, vue_2.createElementVNode)("span", {
"data-row-id": row.id
}, (0, vue_2.toDisplayString)(row.name), 9 /* TEXT, PROPS */, _hoisted_4)
]),
_: 1 /* STABLE */
})
]),
_: 1 /* STABLE */
}, 512 /* NEED_PATCH */),
(0, vue_2.createElementVNode)("p", _hoisted_6, "结果:" + (0, vue_2.toDisplayString)(result.value) + ";成功次数:" + (0, vue_2.toDisplayString)(successes.value) + ";" + (0, vue_2.toDisplayString)(error.value), 1 /* TEXT */),
(0, vue_2.createElementVNode)("pre", _hoisted_7, (0, vue_2.toDisplayString)(JSON.stringify(selection.value)), 1 /* TEXT */),
(0, vue_2.createElementVNode)("pre", _hoisted_8, (0, vue_2.toDisplayString)(JSON.stringify(observation.value)), 1 /* TEXT */),
(0, vue_2.createElementVNode)("pre", _hoisted_9, (0, vue_2.toDisplayString)(JSON.stringify(events.value)), 1 /* TEXT */),
(0, vue_2.createElementVNode)("pre", _hoisted_10, (0, vue_2.toDisplayString)(JSON.stringify({ sortEvents: sortEvents.value, rowEvents: rowEvents.value })), 1 /* TEXT */),
(0, vue_2.createVNode)(RowKeyContract_vue_1.default),
(0, vue_2.createVNode)(NativeSlotsContract_vue_1.default)
]));
};
}
});
exports.default.__scopeId = "data-v-24425916";
},
"RowKeyContract.vue": (module: any, exports: any, require: any) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1: any = require("vue");
const vue_2: any = require("vue");
const _hoisted_1: any = { class: "demo-slot-layout" };
const _hoisted_2: any = { "data-testid": "row-key-boundary-result" };
const vue_3: any = require("vue");
const ui_1: any = require("@asam-ui/ui");
exports.default = (0, vue_1.defineComponent)({
__name: 'RowKeyContract',
setup(__props?: any) {
const busy: any = (0, vue_3.ref)(false);
const observations: any = (0, vue_3.ref)([]);
async function runRowKeyBoundary() {
busy.value = true;
observations.value = [];
const results: any = [];
try {
const cases: any = [
{ name: 'own-empty-string', key: 'id', rows: [{ id: '' }], valid: true },
{ name: 'own-zero', key: 'id', rows: [{ id: 0 }], valid: true },
{ name: 'own-negative', key: 'id', rows: [{ id: -1 }], valid: true },
{ name: 'own-fraction', key: 'id', rows: [{ id: 1.5 }], valid: true },
{ name: 'sync-string-callback', key: () => 'callback', rows: [{}], valid: true },
{ name: 'sync-number-callback', key: () => 7, rows: [{}], valid: true },
{ name: 'own-NaN', key: 'id', rows: [{ id: NaN }], valid: false },
{ name: 'own-Infinity', key: 'id', rows: [{ id: Infinity }], valid: false },
{ name: 'own-boolean', key: 'id', rows: [{ id: true }], valid: false },
{ name: 'own-null', key: 'id', rows: [{ id: null }], valid: false },
{ name: 'own-object', key: 'id', rows: [{ id: {} }], valid: false },
{ name: 'missing-own-key', key: 'id', rows: [{}], valid: false },
{ name: 'inherited-key', key: 'id', rows: [Object.create({ id: 8 })], valid: false },
{ name: 'duplicate-key', key: 'id', rows: [{ id: 1 }, { id: 1 }], valid: false },
{ name: 'async-resolved', key: async () => 1, rows: [{}], valid: false },
{
name: 'async-rejected',
key: () => Promise.reject(Error('row-key-rejected')),
rows: [{}],
valid: false
},
{
name: 'thenable-resolved',
key: () => ({ then: (done?: any) => done(1) }),
rows: [{}],
valid: false
},
{
name: 'thenable-rejected',
key: () => ({
then: (_done?: any, reject?: any) => reject(Error('row-key-thenable-rejected'))
}),
rows: [{}],
valid: false
},
{
name: 'callback-throws',
key: () => {
throw Error('row-key-thrown');
},
rows: [{}],
valid: false
}
];
for (const item of (cases as any)) {
const scope: any = (0, vue_3.effectScope)();
let calls: any = 0;
const state: any = scope.run(() => (Reflect.apply(ui_1.useAsamPagedData, undefined, [
{
source: {
api: async () => {
calls++;
return { list: item.rows, total: item.rows.length };
},
buildPageParams: () => ({}),
rowKey: item.key,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}
]) as any));
try {
const result: any = await state.reload();
const row: any = {
phase: item.name,
valid: item.valid,
status: result.status,
calls,
errorKind: state.error.value?.kind ?? null,
errorMessage: state.error.value?.message ?? null,
dataLength: state.data.value.list.length,
hasSucceeded: state.hasSucceeded.value,
selection: state.getSelection()
};
if (item.valid) {
state.toggleRowSelection(item.rows[0], true);
row.selection = state.getSelection();
}
results.push(row);
}
finally {
scope.stop();
}
}
const scope: any = (0, vue_3.effectScope)();
let invalid: any = false;
const state: any = scope.run(() => (Reflect.apply(ui_1.useAsamPagedData, undefined, [
{
source: {
api: async () => ({ list: [{ id: 9 }], total: 1 }),
buildPageParams: () => ({}),
rowKey: () => invalid ? Promise.reject(Error('row-key-selection-rejected')) : 9,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}
]) as any));
try {
await state.reload();
invalid = true;
const errors: any = [];
for (const operation of (['toggleRowSelection', 'selectPage'] as any)) {
try {
if (operation === 'toggleRowSelection')
state.toggleRowSelection({ id: 9 }, true);
else
state.selectPage([{ id: 9 }]);
}
catch (cause: any) {
errors.push({
operation,
kind: cause instanceof ui_1.AsamDataFailure ? cause.kind : null,
message: cause instanceof Error ? cause.message : String(cause)
});
}
}
const afterInvalid: any = state.getSelection();
invalid = false;
state.selectPage([{ id: 9 }]);
results.push({
phase: 'selection-synchronous-key-guard-and-recovery',
errors,
afterInvalid,
recovered: state.getSelection()
});
}
finally {
scope.stop();
}
await new Promise((done?: any) => setTimeout(done, 20));
observations.value = results;
}
catch (cause: any) {
observations.value = [
...results,
{
phase: 'unexpected-failure',
message: cause instanceof Error ? cause.message : String(cause)
}
];
}
finally {
busy.value = false;
}
}
return (_ctx?: any, _cache?: any) => {
return ((0, vue_2.openBlock)(), (0, vue_2.createBlock)((0, vue_2.unref)(ui_1.AsamCollapse), { "data-testid": "row-key-boundary-case" }, {
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamCollapseItem), { name: "auxiliary-content" }, {
title: (0, vue_2.withCtx)(() => [...(_cache[0] || (_cache[0] = [
(0, vue_2.createElementVNode)("span", null, "公开分页行键边界", -1 /* CACHED */)
]))]),
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createElementVNode)("div", _hoisted_1, [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
disabled: busy.value,
onClick: runRowKeyBoundary
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[1] || (_cache[1] = [
(0, vue_2.createTextVNode)("验证行键边界", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["disabled"]),
(0, vue_2.createElementVNode)("pre", _hoisted_2, (0, vue_2.toDisplayString)(JSON.stringify(observations.value)), 1 /* TEXT */)
])
]),
_: 1 /* STABLE */
})
]),
_: 1 /* STABLE */
}));
};
}
});
exports.default.__scopeId = "data-v-b5da04ac";
},
"NativeSlotsContract.vue": (module: any, exports: any, require: any) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1: any = require("vue");
const vue_2: any = require("vue");
const _hoisted_1: any = { class: "demo-slot-layout" };
const _hoisted_2: any = { class: "demo-slot-actions" };
const _hoisted_3: any = { "data-testid": "native-slot-row" };
const _hoisted_4: any = { "data-testid": "native-slot-result" };
const vue_3: any = require("vue");
const ui_1: any = require("@asam-ui/ui");
exports.default = (0, vue_1.defineComponent)({
__name: 'NativeSlotsContract',
setup(__props?: any) {
const table: any = (0, vue_3.ref)();
const activeNames: any = (0, vue_3.ref)([]);
const query: any = {};
const mode: any = (0, vue_3.ref)('empty');
const customSlots: any = (0, vue_3.ref)(true);
const layout: any = (0, vue_3.ref)('fixed');
const calls: any = (0, vue_3.ref)(0);
const result: any = (0, vue_3.ref)('unloaded');
let complete: any;
const buildPageParams: any = (_query?: any, page?: any) => ({ ...page });
const api: any = (_params?: any, context?: any) => new Promise((resolve?: any, reject?: any) => {
calls.value++;
const requested: any = mode.value;
const clear: any = () => {
context.signal.removeEventListener('abort', abort);
complete = undefined;
};
const abort: any = () => {
clear();
reject(new Error('插槽请求已取消'));
};
context.signal.addEventListener('abort', abort, { once: true });
complete = () => {
clear();
if (requested === 'failure')
reject(new Error('插槽示例失败'));
else
resolve({
list: requested === 'empty' ? [] : [{ id: 1, name: '插槽成员 1' }],
total: requested === 'empty' ? 0 : 1
});
};
});
async function load(next?: any) {
mode.value = next;
result.value = 'pending';
const outcome: any = await table.value?.reload({ force: true });
result.value = outcome?.status ?? 'missing';
}
function release() {
complete?.();
}
(0, vue_3.onBeforeUnmount)(() => table.value?.cancel());
return (_ctx?: any, _cache?: any) => {
return ((0, vue_2.openBlock)(), (0, vue_2.createBlock)((0, vue_2.unref)(ui_1.AsamCollapse), {
modelValue: activeNames.value,
"onUpdate:modelValue": _cache[5] || (_cache[5] = ($event?: any) => ((activeNames).value = $event)),
"data-testid": "table-native-slots-case"
}, {
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamCollapseItem), { name: "auxiliary-content" }, {
title: (0, vue_2.withCtx)(() => [...(_cache[6] || (_cache[6] = [
(0, vue_2.createElementVNode)("span", null, "默认列与 append/empty 插槽", -1 /* CACHED */)
]))]),
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createElementVNode)("div", _hoisted_1, [
(0, vue_2.createElementVNode)("div", _hoisted_2, [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[0] || (_cache[0] = ($event?: any) => (load('empty')))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[7] || (_cache[7] = [
(0, vue_2.createTextVNode)("加载空页", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[1] || (_cache[1] = ($event?: any) => (load('row')))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[8] || (_cache[8] = [
(0, vue_2.createTextVNode)("加载成员", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[2] || (_cache[2] = ($event?: any) => (load('failure')))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[9] || (_cache[9] = [
(0, vue_2.createTextVNode)("使插槽分页失败", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: release
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[10] || (_cache[10] = [
(0, vue_2.createTextVNode)("完成插槽分页请求", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[3] || (_cache[3] = ($event?: any) => (customSlots.value = !customSlots.value))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[11] || (_cache[11] = [
(0, vue_2.createTextVNode)("切换自定义插槽", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[4] || (_cache[4] = ($event?: any) => (layout.value = layout.value === 'fixed' ? 'auto' : 'fixed'))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[12] || (_cache[12] = [
(0, vue_2.createTextVNode)(" 切换汇总布局 ", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
})
]),
(activeNames.value.includes('auxiliary-content'))
? ((0, vue_2.openBlock)(), (0, vue_2.createBlock)((0, vue_2.unref)(ui_1.AsamDataTable), {
key: 0,
ref_key: "table",
ref: table,
api: api,
query: query,
"build-page-params": buildPageParams,
immediate: false,
"page-size": 2,
"show-summary": "",
"table-layout": layout.value
}, (0, vue_2.createSlots)({
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamTableColumn), {
prop: "name",
label: "插槽成员"
}, {
default: (0, vue_2.withCtx)(({ row }: any) => [
(0, vue_2.createElementVNode)("span", _hoisted_3, (0, vue_2.toDisplayString)(row.name), 1 /* TEXT */)
]),
_: 1 /* STABLE */
})
]),
_: 2 /* DYNAMIC */
}, [
(customSlots.value)
? {
name: "append",
fn: (0, vue_2.withCtx)(() => [
_cache[13] || (_cache[13] = (0, vue_2.createElementVNode)("p", {
class: "example-paragraph",
"data-testid": "native-slot-append"
}, "分页之外的附加说明", -1 /* CACHED */))
]),
key: "0"
}
: undefined,
(customSlots.value)
? {
name: "empty",
fn: (0, vue_2.withCtx)(() => [
_cache[14] || (_cache[14] = (0, vue_2.createElementVNode)("p", {
class: "example-paragraph",
"data-testid": "native-slot-empty"
}, "自定义空页说明", -1 /* CACHED */))
]),
key: "1"
}
: undefined
]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["table-layout"]))
: (0, vue_2.createCommentVNode)("v-if", true),
(0, vue_2.createElementVNode)("pre", _hoisted_4, (0, vue_2.toDisplayString)(JSON.stringify({ result: result.value, calls: calls.value })), 1 /* TEXT */)
])
]),
_: 1 /* STABLE */
})
]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["modelValue"]));
};
}
});
exports.default.__scopeId = "data-v-7692d3ad";
},
};
const __dependencies: any = {"Demo.vue":{"./NativeSlotsContract.vue":"NativeSlotsContract.vue","./RowKeyContract.vue":"RowKeyContract.vue"},"RowKeyContract.vue":{},"NativeSlotsContract.vue":{}};
const __cache: any = Object.create(null);
function __require(id: any): any {
if (__external[id]) return __external[id];
if (__cache[id]) return __cache[id].exports;
const module: any = { exports: {} };
__cache[id] = module;
__modules[id](module, module.exports, (dependency: any) => __require(__dependencies[id][dependency] || dependency));
return module.exports;
}
const StandaloneDemo = __require('Demo.vue').default;
</script>
<style>
.lifecycle-events[data-v-24425916] {
max-height: 32rem;
overflow: auto;
}
.demo-layout-1[data-v-24425916] {
display: grid;
gap: var(--asam-space-md);
}
.demo-slot-layout[data-v-24425916] {
display: grid;
gap: var(--asam-space-md);
width: 100%;
min-width: 0;
}
.demo-slot-layout > .el-button[data-v-24425916] {
justify-self: start;
}
.demo-slot-layout[data-v-24425916] > :is(p, pre, output) {
margin-block: 0;
}
.demo-slot-actions[data-v-24425916] {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm);
}
.demo-slot-actions > .el-button + .el-button[data-v-24425916] {
margin-left: 0;
}
p[data-v-24425916]:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
.demo-slot-layout[data-v-b5da04ac] {
display: grid;
gap: var(--asam-space-md);
width: 100%;
min-width: 0;
}
.demo-slot-layout > .el-button[data-v-b5da04ac] {
justify-self: start;
}
.demo-slot-layout[data-v-b5da04ac] > :is(p, pre, output) {
margin-block: 0;
}
.demo-slot-actions[data-v-b5da04ac] {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm);
}
.demo-slot-actions > .el-button + .el-button[data-v-b5da04ac] {
margin-left: 0;
}
.demo-slot-layout[data-v-7692d3ad] {
display: grid;
gap: var(--asam-space-md);
width: 100%;
min-width: 0;
}
.demo-slot-layout > .el-button[data-v-7692d3ad] {
justify-self: start;
}
.demo-slot-layout[data-v-7692d3ad] > :is(p, pre, output) {
margin-block: 0;
}
.demo-slot-actions[data-v-7692d3ad] {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm);
}
.demo-slot-actions > .el-button + .el-button[data-v-7692d3ad] {
margin-left: 0;
}
p[data-v-7692d3ad]:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
<template><StandaloneDemo /></template>
<script setup>
import * as __external0 from "vue";
import * as __external1 from "@asam-ui/ui";
const __external = {"vue": { ...__external0, __esModule: true },
"@asam-ui/ui": { ...__external1, __esModule: true }};
const __modules = {
"Demo.vue": (module, exports, require) => {
"use strict";
var __importDefault = function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1 = require("vue");
const _hoisted_1 = { class: "demo-layout-1" };
const _hoisted_2 = { class: "demo-slot-layout" };
const _hoisted_3 = { "data-testid": "paged-state-contract-result" };
const _hoisted_4 = ["data-row-id"];
const _hoisted_5 = ["data-slot-loading", "data-slot-stale", "data-slot-keys", "data-slot-rows"];
const _hoisted_6 = {
class: "example-paragraph",
role: "status"
};
const _hoisted_7 = { "data-testid": "table-selection" };
const _hoisted_8 = { "data-testid": "table-observation" };
const _hoisted_9 = {
class: "lifecycle-events",
"data-testid": "table-lifecycle-events"
};
const _hoisted_10 = { "data-testid": "table-events" };
const NativeSlotsContract_vue_1 = __importDefault(require("./NativeSlotsContract.vue"));
const RowKeyContract_vue_1 = __importDefault(require("./RowKeyContract.vue"));
const vue_2 = require("vue");
const ui_1 = require("@asam-ui/ui");
exports.default = Object.assign({ name: 'AsamDataTableLifecycleDemo' }, {
__name: 'Demo',
setup(__props) {
const table = (0, vue_2.ref)();
const query = { text: '' };
const failure = (0, vue_2.ref)(false);
const slow = (0, vue_2.ref)(false);
const held = (0, vue_2.ref)(false);
const pendingReleases = new Set();
function holdReload() {
held.value = true;
void reload();
}
function releaseHeld() {
held.value = false;
for (const finish of [...pendingReleases])
finish();
}
async function waitHeld(signal) {
signal.throwIfAborted();
if (!held.value)
return;
await new Promise((done, reject) => {
const finish = () => {
pendingReleases.delete(finish);
signal.removeEventListener('abort', abort);
done();
};
const abort = () => {
pendingReleases.delete(finish);
signal.removeEventListener('abort', abort);
reject(signal.reason);
};
pendingReleases.add(finish);
signal.addEventListener('abort', abort, { once: true });
if (signal.aborted)
abort();
});
signal.throwIfAborted();
}
const result = (0, vue_2.ref)('');
const error = (0, vue_2.ref)('');
const events = (0, vue_2.ref)([]);
function recordPage(name, value) {
events.value.push({ name, payload: value });
}
function recordSelection(value) {
selection.value = value;
events.value.push({
name: 'selection-change',
payload: { keys: [...value.keys], rows: value.rows.map((row) => ({ ...row })) }
});
}
function recordSuccess(value) {
pageRows.value = value.list;
successes.value++;
events.value.push({
name: 'success',
payload: { list: value.list.map((row) => ({ ...row })), total: value.total }
});
}
function recordError(value) {
error.value = value.message;
events.value.push({ name: 'error', payload: { kind: value.kind, message: value.message } });
}
const successes = (0, vue_2.ref)(0);
const sortEvents = (0, vue_2.ref)(0);
const rowEvents = (0, vue_2.ref)([]);
const pageRows = (0, vue_2.ref)([]);
const selection = (0, vue_2.ref)({ keys: [], rows: [] });
const observation = (0, vue_2.ref)({
keys: [],
rows: []
});
const rows = Array.from({ length: 6 }, (_, index) => ({
id: index + 1,
name: '成员 ' + (index + 1)
}));
const rowKey = (row) => row.id;
const buildPageParams = (input, page) => ({ ...input, ...page });
const api = async (params, context) => {
await waitHeld(context.signal);
await new Promise((done) => setTimeout(done, slow.value ? 500 : 100));
context.signal.throwIfAborted();
if (failure.value)
throw new Error('演示分页接口失败');
const matched = rows.filter((row) => row.name.includes(params.text));
const start = (params.currentPage - 1) * params.pageSize;
return { list: matched.slice(start, start + params.pageSize), total: matched.length };
};
const transform = (page) => page;
async function reload() {
const response = await table.value?.reload({ force: true });
result.value = response?.status ?? 'cancelled';
if (response?.status === 'success')
error.value = '';
}
async function cancelSlow() {
slow.value = true;
const pending = table.value?.reload({ force: true });
setTimeout(() => table.value?.cancel(), 30);
result.value = (await pending)?.status ?? 'cancelled';
slow.value = false;
}
function inspect() {
const snapshot = table.value?.getSelection();
observation.value = { keys: snapshot?.keys ?? [], rows: table.value?.getSelectionRows() ?? [] };
}
function toggleFirst(selected) {
const first = pageRows.value[0];
if (first)
table.value?.toggleRowSelection(first, selected);
}
function toggleLast() {
const last = pageRows.value.at(-1);
if (last)
table.value?.toggleRowSelection(last, true);
}
const pagedStateContractResult = (0, vue_2.ref)([]);
const pagedStateContractBusy = (0, vue_2.ref)(false);
const pagedStateInjectionTarget = (0, vue_2.ref)();
async function runPagedStateContract() {
pagedStateContractBusy.value = true;
pagedStateContractResult.value = [];
const observations = [], scopes = [];
const record = (phase, state) => observations.push({ phase, state });
const attach = (config) => {
const scope = (0, vue_2.effectScope)();
scopes.push(scope);
const state = scope.run(() => (0, ui_1.useAsamPagedData)(config));
if (!state)
throw Error('Missing paged state');
return { state, scope };
};
const snapshot = (state) => ({
ids: state.data.value.list.map((row) => row.id),
total: state.data.value.total,
loading: state.loading.value,
error: state.error.value?.kind ?? null,
errorMessage: state.error.value?.message ?? null,
stale: state.stale.value,
hasSucceeded: state.hasSucceeded.value,
empty: state.empty.value,
currentPage: state.currentPage.value,
pageSize: state.pageSize.value,
dataKeySet: state.dataKey.value !== null,
requestKeySet: state.requestKey.value !== null,
keysMatch: state.dataKey.value !== null && state.dataKey.value === state.requestKey.value,
resolved: state.resolvedPage.value === null
? null
: {
currentPage: state.resolvedPage.value.currentPage,
pageSize: state.resolvedPage.value.pageSize,
queryKeyNonempty: state.resolvedPage.value.queryKey.length > 0
},
selection: {
keys: state.selection.value.keys,
ids: state.selection.value.rows.map((row) => row.id)
}
});
const build = (query, page) => ({
...query,
p: page.currentPage,
s: page.pageSize
});
const gate = () => {
let release = () => {
throw Error('Missing gate');
}, reject = () => {
throw Error('Missing gate');
};
const promise = new Promise((done, fail) => {
release = done;
reject = fail;
});
return { promise, release, reject };
};
const settle = async (check) => {
for (let i = 0; i < 100 && !check(); i++)
await Promise.resolve();
if (!check())
throw Error('Paged state did not settle within microtasks');
};
try {
const firstGate = gate(), nextGate = gate(), failedGate = gate(), emptyGate = gate(), jobs = [firstGate, nextGate, failedGate, emptyGate];
let calls = 0, successes = 0, localErrors = 0, notifications = 0, successSame = false;
const pageEvents = [], sizeEvents = [], selectionEvents = [], parameters = [], contexts = [];
const runtime = (0, ui_1.createAsamDataRuntime)({
scope: 'paged-contract',
provider: {
onError: () => {
notifications++;
}
}
}), owner = attach({
source: {
api: (params, context) => {
parameters.push(params);
contexts.push({
scope: context.scope,
epoch: context.epoch,
signalAborted: context.signal.aborted
});
return jobs[calls++].promise;
},
query: { term: 'initial' },
buildPageParams: build,
currentPage: 2,
pageSize: 2,
defaultSelectedRowKeys: [1, '1', 'future'],
immediate: false
},
runtime,
onSuccess: (data) => {
successes++;
successSame = data === owner.state.data.value;
},
onError: () => {
localErrors++;
},
onPageChange: (page) => pageEvents.push(page),
onPageSizeChange: (size) => sizeEvents.push(size),
onSelectionChange: (selection) => selectionEvents.push(selection.keys)
}), state = owner.state;
record('paged-state-complete-public-members-initial-default-keys-and-pagination', {
members: Object.keys(state),
snapshot: snapshot(state),
calls,
selectionEvents: [...selectionEvents]
});
const first = state.reload();
record('paged-state-held-api-build-params-and-original-context', {
snapshot: snapshot(state),
calls,
parameters: [...parameters],
contexts: [...contexts]
});
const host = {
list: [
{ id: 1, name: 'Numeric', meta: { count: 1 } },
{ id: '1', name: 'String', meta: { count: 1 } }
],
total: 6
};
firstGate.release(host);
const firstResult = await first;
host.list[0].meta.count = 9;
record('paged-state-success-resolved-page-frozen-host-snapshot-and-callback', {
snapshot: snapshot(state),
status: firstResult.status,
fromCache: firstResult.status === 'success' ? firstResult.fromCache : null,
successes,
successSame,
frozen: Object.isFrozen(state.data.value) &&
Object.isFrozen(state.data.value.list) &&
Object.isFrozen(state.data.value.list[0]) &&
Object.isFrozen(state.data.value.list[0]?.meta),
hostCount: host.list[0].meta.count,
snapshotCount: Reflect.get(Object(state.data.value.list[0].meta), 'count'),
selectionEvents: [...selectionEvents]
});
state.clearSelection();
state.toggleRowSelection(state.data.value.list[0]);
state.toggleRowSelection({ id: 'outsider' }, true);
state.toggleRowSelection(state.data.value.list[1], true);
state.selectPage([state.data.value.list[1]]);
const selection = state.getSelection();
record('paged-state-selection-typed-keys-toggle-page-filter-and-frozen-snapshot', {
snapshot: snapshot(state),
samePublicSelection: selection === state.selection.value,
frozen: Object.isFrozen(selection) &&
Object.isFrozen(selection.keys) &&
Object.isFrozen(selection.rows),
selectionEvents: [...selectionEvents]
});
state.setCurrentPage(3);
const pageChanged = snapshot(state), next = state.reload();
nextGate.release({
list: [
{ id: 3, name: 'Third' },
{ id: 4, name: 'Fourth' }
],
total: 6
});
await next;
state.toggleRowSelection(state.data.value.list[0], true);
state.selectPage([state.data.value.list[1]]);
record('paged-state-page-navigation-retains-cross-page-rows-and-selected-keys', {
beforeReload: pageChanged,
after: snapshot(state),
parameters: [...parameters],
pageEvents: [...pageEvents],
sizeEvents: [...sizeEvents]
});
const original = Error('owned paged request failure'), failed = state.reload();
failedGate.reject(original);
const failure = await failed;
const staleSelection = state.getSelection();
state.toggleRowSelection(state.data.value.list[0], true);
state.selectPage([]);
const prevented = state.getSelection() === staleSelection;
state.toggleRowSelection(state.data.value.list[1], false);
record('paged-state-error-retains-data-and-blocks-stale-selection-additions', {
snapshot: snapshot(state),
status: failure.status,
originalCause: state.error.value?.cause === original,
notified: state.error.value?.notified,
prevented,
successes,
localErrors,
notifications
});
const empty = state.reload({ force: true });
emptyGate.release({ list: [], total: 0 });
await empty;
const beforeClear = state.getSelection();
state.clearSelection();
const clearEvents = selectionEvents.length;
state.clearSelection();
record('paged-state-zero-total-resets-page-success-empty-and-idempotent-clear', {
snapshot: snapshot(state),
retainedKeyBeforeClear: beforeClear.keys,
clearEventsUnchanged: selectionEvents.length === clearEvents,
successes,
localErrors,
notifications,
pageEvents: [...pageEvents]
});
owner.scope.stop();
record('paged-state-disposal-stops-reload-and-scope-reissue', {
status: (await state.reload()).status,
calls
});
let liveCalls = 0;
const liveApi = async (params) => {
liveCalls++;
return { list: [{ id: params.term, name: 'Live' }], total: 100 };
}, liveQuery = (0, vue_2.ref)({ term: 'one' }), liveSource = (0, vue_2.computed)(() => ({
api: liveApi,
query: liveQuery.value,
buildPageParams: build,
currentPage: 2,
pageSize: 2
})), live = attach({ source: liveSource, runtime: (0, ui_1.createAsamDataRuntime)() }).state;
await settle(() => live.hasSucceeded.value);
live.toggleRowSelection(live.data.value.list[0], true);
const initialLive = snapshot(live);
liveQuery.value.term = 'two';
await settle(() => live.data.value.list[0]?.id === 'two');
const changedLive = snapshot(live);
const beforeEcho = liveCalls;
record('paged-state-immediate-reactive-query-resets-page-and-clears-selection', {
initial: initialLive,
changed: changedLive,
calls: liveCalls,
noChangeObservation: liveCalls === beforeEcho
});
const obsolete = gate(), latest = gate();
let raceCalls = 0, raceCallbacks = 0;
const raceQuery = (0, vue_2.ref)({ term: 'old' }), raceApi = (params) => {
raceCalls++;
return params.term === 'old' ? obsolete.promise : latest.promise;
}, race = attach({
source: (0, vue_2.computed)(() => ({
api: raceApi,
query: raceQuery.value,
buildPageParams: build,
immediate: false
})),
runtime: (0, ui_1.createAsamDataRuntime)(),
onSuccess: () => {
raceCallbacks++;
}
}).state;
const oldPending = race.reload();
raceQuery.value.term = 'new';
const oldCancelled = await oldPending;
await Promise.resolve();
const newPending = race.reload();
latest.release({ list: [{ id: 'new' }], total: 1 });
await newPending;
obsolete.release({ list: [{ id: 'old' }], total: 1 });
await settle(() => !race.loading.value);
record('paged-state-query-race-cancels-old-and-ignores-late-result', {
old: oldCancelled.status,
calls: raceCalls,
callbacks: raceCallbacks,
snapshot: snapshot(race)
});
let sizeCalls = 0;
const sizeState = attach({
source: {
api: async () => {
sizeCalls++;
return { list: [{ id: 1 }], total: 100 };
},
buildPageParams: build,
currentPage: 3,
pageSize: 2,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)(),
onPageChange: (page) => pageEvents.push(page),
onPageSizeChange: (size) => sizeEvents.push(size)
}).state;
await sizeState.reload();
sizeState.setPageSize(4);
const sizedBefore = snapshot(sizeState);
await sizeState.reload();
sizeState.setPageSize(4);
sizeState.setCurrentPage(2);
const pagedBefore = snapshot(sizeState);
await sizeState.reload();
record('paged-state-size-reset-and-page-setters-prepare-manual-requests', {
sizeBefore: sizedBefore,
pageBefore: pagedBefore,
final: snapshot(sizeState),
calls: sizeCalls,
sizeEvents: [...sizeEvents],
pageEvents: [...pageEvents]
});
const correctionCalls = [];
let correctionSuccess = 0;
const correction = attach({
source: {
api: async (params) => {
correctionCalls.push(Number(params.p));
return { list: [], total: 3 };
},
buildPageParams: build,
currentPage: 9,
pageSize: 2,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)(),
onSuccess: () => {
correctionSuccess++;
}
}).state;
const corrected = await correction.reload();
record('paged-state-out-of-range-corrects-once-and-preserves-reload-promise', {
status: corrected.status,
calls: correctionCalls,
successes: correctionSuccess,
snapshot: snapshot(correction)
});
const repeatedCalls = [];
const repeated = attach({
source: {
api: async (params) => {
repeatedCalls.push(Number(params.p));
return { list: [], total: repeatedCalls.length === 1 ? 3 : 1 };
},
buildPageParams: build,
currentPage: 9,
pageSize: 2,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
const uncorrectable = await repeated.reload();
record('paged-state-repeated-out-of-range-is-response-error-after-one-correction', {
status: uncorrectable.status,
calls: repeatedCalls,
snapshot: snapshot(repeated)
});
const requests = [], urlRuntime = (0, ui_1.createAsamDataRuntime)({
provider: {
pagination: { page: 'p', pageSize: 's' },
request: async (request, context) => {
requests.push({
request,
context: {
scope: context.scope,
epoch: context.epoch,
signalAborted: context.signal.aborted
}
});
return { list: [{ id: 1 }], total: 20 };
}
}
});
const urlDefault = attach({
source: {
request: { url: '/page-default', params: { host: 'base' } },
query: { term: 'filter' },
currentPage: 2,
pageSize: 4,
immediate: false
},
runtime: urlRuntime
}).state;
await urlDefault.reload();
const urlData = attach({
source: {
request: {
url: '/page-data',
method: 'post',
data: { host: 'body' },
params: { untouched: true }
},
query: { term: 'body-query' },
pageTarget: 'data',
currentPage: 3,
pageSize: 2,
immediate: false
},
runtime: urlRuntime
}).state;
await urlData.reload();
let customBuildCalls = 0;
const urlCustom = attach({
source: {
request: { url: '/unused-base' },
query: { term: 'custom' },
buildPageRequest: (query, page) => {
customBuildCalls++;
return {
url: '/custom-built',
method: 'put',
data: { term: query.term, page: page.currentPage, size: page.pageSize }
};
},
currentPage: 2,
pageSize: 2,
immediate: false
},
runtime: urlRuntime
}).state;
await urlCustom.reload();
record('paged-state-url-default-target-data-and-custom-builder', {
requests,
customBuildCalls,
states: [snapshot(urlDefault), snapshot(urlData), snapshot(urlCustom)]
});
let adapterCalls = 0, transformCalls = 0;
const adaptedRuntime = (0, ui_1.createAsamDataRuntime)({
provider: {
adaptPage: () => {
adapterCalls++;
return { list: [{ id: 'adapter' }], total: 1 };
}
}
});
const transformed = attach({
source: {
api: async () => ({ list: [], total: 0 }),
buildPageParams: build,
transform: () => {
transformCalls++;
return { list: [{ id: 'transform' }], total: 1 };
},
immediate: false
},
runtime: adaptedRuntime
}).state;
await transformed.reload();
const adapted = attach({
source: {
api: async () => ({ list: [], total: 0 }),
buildPageParams: build,
immediate: false
},
runtime: adaptedRuntime
}).state;
await adapted.reload();
record('paged-state-transform-precedes-provider-adapter-and-default-response', {
transform: snapshot(transformed),
adapter: snapshot(adapted),
transformCalls,
adapterCalls
});
let sharedCalls = 0;
const sharedApi = async () => {
sharedCalls++;
return { list: [{ id: sharedCalls }], total: 1 };
}, sharedInput = {
api: sharedApi,
buildPageParams: build,
cache: { ttlMs: 10000 },
immediate: false
}, sharedRuntime = (0, ui_1.createAsamDataRuntime)(), sharedA = attach({ source: sharedInput, runtime: sharedRuntime }).state, sharedB = attach({ source: sharedInput, runtime: sharedRuntime }).state;
await Promise.all([sharedA.reload(), sharedB.reload()]);
const cached = await sharedA.reload();
await Promise.all([sharedA.reload({ force: true }), sharedB.reload({ force: true })]);
record('paged-state-shared-cache-flight-hit-and-deduplicated-force', {
calls: sharedCalls,
cached: cached.status === 'success' ? cached.fromCache : null,
sameSnapshot: sharedA.data.value === sharedB.data.value,
a: snapshot(sharedA),
b: snapshot(sharedB)
});
const cancelGate = gate();
let cancelSuccess = 0;
const cancelOwner = attach({
source: { api: () => cancelGate.promise, buildPageParams: build, immediate: false },
runtime: (0, ui_1.createAsamDataRuntime)(),
onSuccess: () => {
cancelSuccess++;
}
}), cancelJob = cancelOwner.state.reload();
cancelOwner.state.cancel();
const cancelled = await cancelJob;
cancelGate.release({ list: [{ id: 9 }], total: 1 });
await settle(() => !cancelOwner.state.loading.value);
record('paged-state-explicit-cancel-settles-job-and-keeps-late-success-out', {
status: cancelled.status,
successes: cancelSuccess,
snapshot: snapshot(cancelOwner.state)
});
const disposedGate = gate(), disposedOwner = attach({
source: {
api: () => disposedGate.promise,
buildPageParams: build,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}), disposedJob = disposedOwner.state.reload();
disposedOwner.scope.stop();
const disposedResult = await disposedJob;
disposedGate.release({ list: [{ id: 9 }], total: 1 });
await settle(() => !disposedOwner.state.loading.value);
record('paged-state-dispose-held-job-and-block-followup-reload', {
status: disposedResult.status,
reloadAfterDispose: (await disposedOwner.state.reload()).status,
snapshot: snapshot(disposedOwner.state)
});
const invalidRows = [];
for (const [name, rows] of [
['missing-id', [{ name: 'Missing' }]],
['numeric-string-id', [{ id: '1' }, { id: 1 }]],
['duplicate-id', [{ id: 1 }, { id: 1 }]],
['infinite-id', [{ id: Infinity }]],
['boolean-id', [{ id: true }]],
['inherited-id', [Object.create({ id: 1 })]]
]) {
const checked = attach({
source: {
api: async () => ({ list: rows, total: rows.length }),
buildPageParams: build,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
const result = await checked.reload();
invalidRows.push({ name, status: result.status, snapshot: snapshot(checked) });
}
record('paged-state-own-finite-typed-row-keys-and-duplicate-validation', {
cases: invalidRows
});
const invalidConfig = [];
const baseInput = {
api: async () => ({ list: [], total: 0 }),
buildPageParams: build,
immediate: false
};
for (const [name, extra] of [
['no-source', { api: undefined }],
['both-sources', { request: { url: '/x' } }],
['legacy-data', { data: [] }],
['api-no-builder', { buildPageParams: undefined }],
['api-url-builder', { buildPageRequest: () => ({ url: '/x' }) }],
['api-page-target', { pageTarget: 'params' }],
['page-zero', { currentPage: 0 }],
['size-fraction', { pageSize: 1.5 }],
['row-key-invalid', { rowKey: 1 }],
['cache-zero', { cache: { ttlMs: 0 } }],
['cache-key-nonfunction', { cache: { ttlMs: 1, key: 1 } }],
['empty-query-key', { queryKey: () => '' }],
['builder-async', { buildPageParams: async () => ({}) }],
['unknown-default-key', { defaultSelectedRowKeys: [true] }]
]) {
const invalid = Reflect.apply(attach, undefined, [
{ source: { ...baseInput, ...extra }, runtime: (0, ui_1.createAsamDataRuntime)() }
]).state;
invalidConfig.push({
name,
status: (await invalid.reload()).status,
snapshot: snapshot(invalid)
});
}
record('paged-state-api-config-pagination-builder-cache-query-selection-guards', {
cases: invalidConfig
});
const urlInvalid = [];
for (const [name, input] of [
['missing-provider', { request: { url: '/x' } }],
['get-data-target', { request: { url: '/x' }, pageTarget: 'data' }],
['bad-target', { request: { url: '/x', method: 'post' }, pageTarget: 'body' }],
['query-collision', { request: { url: '/x', params: { q: 1 } }, query: { q: 2 } }],
['pagination-collision', { request: { url: '/x', params: { page: 1 } } }],
['array-query', { request: { url: '/x' }, query: [] }],
[
'array-target',
{ request: { url: '/x', method: 'post', data: [] }, pageTarget: 'data' }
],
['url-api-builder', { request: { url: '/x' }, buildPageParams: build }],
[
'custom-plus-target',
{
request: { url: '/x' },
buildPageRequest: () => ({ url: '/custom' }),
pageTarget: 'params'
}
],
[
'custom-async',
{ request: { url: '/x' }, buildPageRequest: async () => ({ url: '/custom' }) }
],
[
'custom-invalid-url',
{ request: { url: '/x' }, buildPageRequest: () => ({ url: '' }) }
]
]) {
const invalid = Reflect.apply(attach, undefined, [
{
source: { ...input, immediate: false },
runtime: (0, ui_1.createAsamDataRuntime)({
provider: name === 'missing-provider'
? {}
: { request: async () => ({ list: [], total: 0 }) }
})
}
]).state;
urlInvalid.push({
name,
status: (await invalid.reload()).status,
snapshot: snapshot(invalid)
});
}
record('paged-state-url-provider-target-collision-plain-input-and-custom-builder-guards', {
cases: urlInvalid
});
const stages = [];
for (const [name, api, transform] of [
[
'transport-throw',
() => {
throw Error('page transport throw');
},
undefined
],
['transport-reject', () => Promise.reject(Error('page transport reject')), undefined],
['transport-not-promise', () => ({ list: [], total: 0 }), undefined],
['bad-response', async () => ({ data: [] }), undefined],
[
'transform-throw',
async () => ({ list: [], total: 0 }),
() => {
throw Error('page transform throw');
}
],
[
'transform-async',
async () => ({ list: [], total: 0 }),
async () => ({ list: [], total: 0 })
]
]) {
const checked = Reflect.apply(attach, undefined, [
{
source: { api, buildPageParams: build, transform, immediate: false },
runtime: (0, ui_1.createAsamDataRuntime)()
}
]).state;
stages.push({
name,
status: (await checked.reload()).status,
snapshot: snapshot(checked)
});
}
record('paged-state-transport-response-transform-error-stages', { cases: stages });
const controlledPage = (0, vue_2.ref)(2), controlledSize = (0, vue_2.ref)(2), wrapperVersion = (0, vue_2.ref)(0);
let controlledCalls = 0;
const controlledApi = async () => {
controlledCalls++;
return { list: [{ id: controlledCalls }], total: 50 };
};
const controlled = attach({
source: (0, vue_2.computed)(() => {
void wrapperVersion.value;
return {
api: controlledApi,
query: { stable: 'same' },
buildPageParams: build,
currentPage: controlledPage.value,
pageSize: controlledSize.value
};
}),
runtime: (0, ui_1.createAsamDataRuntime)(),
onPageChange: (page) => {
controlledPage.value = page;
},
onPageSizeChange: (size) => {
controlledSize.value = size;
}
}).state;
await settle(() => controlled.hasSucceeded.value);
controlled.toggleRowSelection(controlled.data.value.list[0], true);
const wrapperBefore = snapshot(controlled);
wrapperVersion.value++;
await Promise.resolve();
await Promise.resolve();
record('paged-state-equivalent-wrapper-keeps-data-selection-and-request', {
before: wrapperBefore,
after: snapshot(controlled),
calls: controlledCalls
});
controlled.setCurrentPage(3);
await settle(() => controlled.data.value.list[0]?.id === 2);
controlled.setPageSize(4);
await settle(() => controlled.data.value.list[0]?.id === 3);
record('paged-state-controlled-page-and-size-echo-do-not-duplicate-requests', {
calls: controlledCalls,
pageProp: controlledPage.value,
sizeProp: controlledSize.value,
snapshot: snapshot(controlled)
});
const sourceApiA = async () => ({ list: [{ id: 'source-a' }], total: 100 }), sourceApiB = async () => ({ list: [{ id: 'source-b' }], total: 100 }), chosenApi = (0, vue_2.ref)(sourceApiA), sourceSwitch = attach({
source: (0, vue_2.computed)(() => ({
api: chosenApi.value,
buildPageParams: build,
currentPage: 4,
pageSize: 2
})),
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await settle(() => sourceSwitch.hasSucceeded.value);
sourceSwitch.toggleRowSelection(sourceSwitch.data.value.list[0], true);
chosenApi.value = sourceApiB;
await settle(() => sourceSwitch.data.value.list[0]?.id === 'source-b');
record('paged-state-source-identity-change-resets-page-selection-and-reloads', {
snapshot: snapshot(sourceSwitch)
});
let manualCalls = 0;
const manualQuery = (0, vue_2.ref)({ term: 'first' }), manualApi = async (params) => {
manualCalls++;
return { list: [{ id: params.term }], total: 100 };
}, manual = attach({
source: (0, vue_2.computed)(() => ({
api: manualApi,
query: manualQuery.value,
buildPageParams: build,
currentPage: 3,
pageSize: 2,
immediate: false
})),
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await manual.reload();
manual.toggleRowSelection(manual.data.value.list[0], true);
manualQuery.value.term = 'second';
await Promise.resolve();
const manualPrepared = snapshot(manual), manualBefore = manualCalls;
await manual.reload();
record('paged-state-manual-query-change-prepares-stale-reset-without-transport', {
prepared: manualPrepared,
callsBeforeReload: manualBefore,
callsAfterReload: manualCalls,
final: snapshot(manual)
});
const epochGate = gate(), epochRuntime = (0, ui_1.createAsamDataRuntime)({ scope: 'scope-before' }), epochContexts = [];
let epochCalls = 0, epochSuccess = 0;
const epochApi = async (_params, context) => {
epochContexts.push({ scope: context.scope, epoch: context.epoch });
epochCalls++;
return epochCalls === 2
? epochGate.promise
: { list: [{ id: epochCalls }], total: 1 };
}, epochState = attach({
source: {
api: epochApi,
buildPageParams: build,
cache: { ttlMs: 10000 },
immediate: false
},
runtime: epochRuntime,
onSuccess: () => {
epochSuccess++;
}
}).state;
await epochState.reload();
epochState.toggleRowSelection(epochState.data.value.list[0], true);
const epochPending = epochState.reload({ force: true });
epochRuntime.setScope('scope-after');
const epochCancelled = await epochPending;
await Promise.resolve();
const epochReset = snapshot(epochState);
epochGate.release({ list: [{ id: 'obsolete' }], total: 1 });
await new Promise((done) => setTimeout(done, 0));
const epochReload = await epochState.reload();
record('paged-state-scope-epoch-invalidates-held-job-data-selection-and-cache', {
status: epochCancelled.status,
reset: epochReset,
reloadStatus: epochReload.status,
fromCache: epochReload.status === 'success' ? epochReload.fromCache : null,
calls: epochCalls,
successes: epochSuccess,
contexts: epochContexts,
final: snapshot(epochState)
});
let isolatedCalls = 0;
const isolatedApi = async () => {
isolatedCalls++;
return { list: [{ id: isolatedCalls }], total: 1 };
}, isolatedRuntime = (0, ui_1.createAsamDataRuntime)(), isolatedInput = {
api: isolatedApi,
buildPageParams: build,
immediate: false
}, isolatedA = attach({ source: isolatedInput, runtime: isolatedRuntime }).state, isolatedB = attach({ source: isolatedInput, runtime: isolatedRuntime }).state;
await Promise.all([isolatedA.reload(), isolatedB.reload()]);
record('paged-state-uncached-consumers-have-separate-flights-and-request-keys', {
calls: isolatedCalls,
distinctKeys: isolatedA.requestKey.value !== isolatedB.requestKey.value,
a: snapshot(isolatedA),
b: snapshot(isolatedB)
});
const sharedCancelGate = gate();
let sharedCancelCalls = 0;
const sharedCancelInput = {
api: () => {
sharedCancelCalls++;
return sharedCancelGate.promise;
},
buildPageParams: build,
cache: { ttlMs: 10000 },
immediate: false
}, sharedCancelRuntime = (0, ui_1.createAsamDataRuntime)(), sharedCancelA = attach({
source: sharedCancelInput,
runtime: sharedCancelRuntime
}).state, sharedCancelB = attach({
source: sharedCancelInput,
runtime: sharedCancelRuntime
}).state;
const ca = sharedCancelA.reload(), cb = sharedCancelB.reload();
sharedCancelA.cancel();
const cancelledShared = await ca;
sharedCancelGate.release({ list: [{ id: 'shared' }], total: 1 });
const cbResult = await cb, rejoined = await sharedCancelA.reload();
record('paged-state-one-shared-consumer-cancel-preserves-peer-and-rejoins-cache', {
cancelled: cancelledShared.status,
peer: cbResult.status,
rejoined: rejoined.status,
rejoinedFromCache: rejoined.status === 'success' ? rejoined.fromCache : null,
calls: sharedCancelCalls,
a: snapshot(sharedCancelA),
b: snapshot(sharedCancelB)
});
let ttlCalls = 0;
const ttlState = attach({
source: {
api: async () => {
ttlCalls++;
return { list: [{ id: ttlCalls }], total: 1 };
},
buildPageParams: build,
cache: { ttlMs: 25 },
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await ttlState.reload();
const ttlHit = await ttlState.reload();
await new Promise((done) => setTimeout(done, 40));
const ttlExpired = await ttlState.reload();
record('paged-state-real-cache-ttl-hit-and-expiry', {
calls: ttlCalls,
hit: ttlHit.status === 'success' ? ttlHit.fromCache : null,
expired: ttlExpired.status === 'success' ? ttlExpired.fromCache : null,
snapshot: snapshot(ttlState)
});
let dateCalls = 0;
const dateApi = async () => {
dateCalls++;
return { list: [{ id: 'date' }], total: 1 };
}, dateBuilder = () => ({ stamp: new Date('2026-01-01T00:00:00Z') }), dateUncached = attach({
source: { api: dateApi, buildPageParams: dateBuilder, immediate: false },
runtime: (0, ui_1.createAsamDataRuntime)()
}).state, dateCached = attach({
source: {
api: dateApi,
buildPageParams: dateBuilder,
cache: { ttlMs: 10000 },
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state, dateCustom = attach({
source: {
api: dateApi,
buildPageParams: dateBuilder,
cache: { ttlMs: 10000, key: () => 'known-date' },
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await dateUncached.reload();
await dateCached.reload();
await dateCustom.reload();
record('paged-state-nonserializable-params-fallback-cache-error-and-custom-key', {
calls: dateCalls,
uncached: snapshot(dateUncached),
cached: snapshot(dateCached),
custom: snapshot(dateCustom)
});
const cacheGuards = [];
for (const [name, key] of [
['empty', () => ''],
['number', () => 1],
['async', async () => 'key'],
[
'throw',
() => {
throw Error('paged key throw');
}
]
]) {
let io = 0;
const checked = Reflect.apply(attach, undefined, [
{
source: {
api: async () => {
io++;
return { list: [], total: 0 };
},
buildPageParams: build,
cache: { ttlMs: 100, key },
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}
]).state;
const result = await checked.reload();
cacheGuards.push({ name, status: result.status, io, snapshot: snapshot(checked) });
}
record('paged-state-custom-cache-key-synchronous-nonempty-string-guards', {
cases: cacheGuards
});
const rowFunction = attach({
source: {
api: async () => ({ list: [{ key: 'row-function' }], total: 1 }),
buildPageParams: build,
rowKey: (row) => String(row.key),
defaultSelectedRowKeys: ['row-function'],
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await rowFunction.reload();
record('paged-state-custom-row-key-function-resolves-default-selection', {
keys: rowFunction.selection.value.keys,
rows: rowFunction.selection.value.rows,
snapshot: snapshot(rowFunction)
});
let getterBad = false, getterCalls = 0;
const getterTick = (0, vue_2.ref)(0), getterApi = async () => {
getterCalls++;
return { list: [{ id: 'getter' }], total: 1 };
}, getter = attach({
source: () => {
void getterTick.value;
if (getterBad)
throw Error('paged getter throw');
return { api: getterApi, buildPageParams: build, immediate: false };
},
runtime: (0, ui_1.createAsamDataRuntime)()
}).state;
await getter.reload();
getterBad = true;
getterTick.value++;
await Promise.resolve();
const getterFault = snapshot(getter);
getterBad = false;
getterTick.value++;
await Promise.resolve();
const getterRecovery = await getter.reload();
record('paged-state-throwing-source-getter-retains-stale-data-and-recovers', {
fault: getterFault,
status: getterRecovery.status,
calls: getterCalls,
final: snapshot(getter)
});
let missingRuntime;
try {
(0, ui_1.useAsamPagedData)({
source: {
api: async () => ({ list: [], total: 0 }),
buildPageParams: build,
immediate: false
}
});
missingRuntime = { threw: false };
}
catch (cause) {
missingRuntime = {
threw: true,
kind: cause instanceof ui_1.AsamDataFailure ? cause.kind : null,
message: cause instanceof Error ? cause.message : String(cause)
};
}
record('paged-state-missing-runtime-is-config-error', missingRuntime);
if (typeof document !== 'undefined' && pagedStateInjectionTarget.value) {
let injected, injectedCalls = 0, mounted = false;
const host = document.createElement('div'), app = (0, vue_2.createApp)((0, vue_2.defineComponent)({
setup() {
injected = (0, ui_1.useAsamPagedData)({
source: {
api: async () => {
injectedCalls++;
return { list: [{ id: 8 }], total: 1 };
},
buildPageParams: build,
immediate: false
}
});
const state = injected;
return () => (0, vue_2.h)('output', { 'data-testid': 'paged-state-injected-output' }, JSON.stringify(snapshot(state)));
}
}));
pagedStateInjectionTarget.value.append(host);
(0, ui_1.provideAsamData)(app, {});
try {
app.mount(host);
mounted = true;
if (!injected)
throw Error('Missing injected paged state');
const noInitialRequest = injectedCalls === 0, before = snapshot(injected);
await injected.reload();
await (0, vue_2.nextTick)();
const after = snapshot(injected), rendered = host.querySelector('output') !== null, renderedSnapshot = JSON.parse(host.querySelector('output')?.textContent ?? '{}');
app.unmount();
mounted = false;
record('paged-state-mounted-app-injection-and-unmount-cleanup', {
noInitialRequest,
before,
after,
rendered,
renderedSnapshot,
calls: injectedCalls,
cancelledReload: (await injected.reload()).status
});
}
finally {
if (mounted)
app.unmount();
host.remove();
}
}
pagedStateContractResult.value = observations;
}
catch (cause) {
pagedStateContractResult.value = [
...observations,
{
phase: 'paged-state-contract-unexpected-failure',
state: { message: cause instanceof Error ? cause.message : String(cause) }
}
];
}
finally {
for (const scope of scopes)
scope.stop();
pagedStateContractBusy.value = false;
}
}
return (_ctx, _cache) => {
return ((0, vue_1.openBlock)(), (0, vue_1.createElementBlock)("section", _hoisted_1, [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamCollapse), { "data-testid": "paged-state-contract-case" }, {
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamCollapseItem), { name: "auxiliary-content" }, {
title: (0, vue_1.withCtx)(() => [...(_cache[14] || (_cache[14] = [
(0, vue_1.createElementVNode)("span", null, "完整分页状态与生命周期", -1 /* CACHED */)
]))]),
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createElementVNode)("div", _hoisted_2, [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
disabled: pagedStateContractBusy.value,
onClick: runPagedStateContract
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[15] || (_cache[15] = [
(0, vue_1.createTextVNode)(" 验证分页状态与生命周期 ", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["disabled"]),
(0, vue_1.createElementVNode)("div", {
ref_key: "pagedStateInjectionTarget",
ref: pagedStateInjectionTarget
}, null, 512 /* NEED_PATCH */),
(0, vue_1.createElementVNode)("pre", _hoisted_3, (0, vue_1.toDisplayString)(JSON.stringify(pagedStateContractResult.value)), 1 /* TEXT */)
])
]),
_: 1 /* STABLE */
})
]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamCheckboxItem), {
modelValue: failure.value,
"onUpdate:modelValue": _cache[0] || (_cache[0] = $event => ((failure).value = $event))
}, {
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createCommentVNode)(" prettier-ignore "),
_cache[16] || (_cache[16] = (0, vue_1.createTextVNode)(" 模拟接口失败 ", -1 /* CACHED */))
]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["modelValue"]),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: holdReload
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[17] || (_cache[17] = [
(0, vue_1.createTextVNode)("开始持有分页请求", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: releaseHeld
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[18] || (_cache[18] = [
(0, vue_1.createTextVNode)("完成持有分页请求", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: reload
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[19] || (_cache[19] = [
(0, vue_1.createTextVNode)("强制重载", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: cancelSlow
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[20] || (_cache[20] = [
(0, vue_1.createTextVNode)("取消慢请求", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[1] || (_cache[1] = $event => (table.value?.clearSelection()))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[21] || (_cache[21] = [
(0, vue_1.createTextVNode)("清空全部选择", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[2] || (_cache[2] = $event => (table.value?.sort('id', 'descending')))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[22] || (_cache[22] = [
(0, vue_1.createTextVNode)("按编号倒序", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: inspect
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[23] || (_cache[23] = [
(0, vue_1.createTextVNode)("读取跨页选择", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[3] || (_cache[3] = $event => (toggleFirst(true)))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[24] || (_cache[24] = [
(0, vue_1.createTextVNode)("选择当前页首行", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[4] || (_cache[4] = $event => (toggleFirst(false)))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[25] || (_cache[25] = [
(0, vue_1.createTextVNode)("取消当前页首行", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[5] || (_cache[5] = $event => (toggleFirst()))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[26] || (_cache[26] = [
(0, vue_1.createTextVNode)("切换当前页首行", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[6] || (_cache[6] = $event => (toggleLast()))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[27] || (_cache[27] = [
(0, vue_1.createTextVNode)("选择当前页末行", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[7] || (_cache[7] = $event => (table.value?.toggleAllSelection()))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[28] || (_cache[28] = [
(0, vue_1.createTextVNode)("切换当前页全选", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamDataTable), {
ref_key: "table",
ref: table,
api: api,
query: query,
"build-page-params": buildPageParams,
transform: transform,
"row-key": rowKey,
"page-size": 2,
"page-sizes": [2, 4],
pagination: { layout: 'sizes, prev, pager, next, total' },
selectable: "",
border: "",
stripe: "",
size: "default",
"onUpdate:currentPage": _cache[8] || (_cache[8] = $event => (recordPage('update:currentPage', $event))),
"onUpdate:pageSize": _cache[9] || (_cache[9] = $event => (recordPage('update:pageSize', $event))),
onPageChange: _cache[10] || (_cache[10] = $event => (recordPage('page-change', $event))),
onPageSizeChange: _cache[11] || (_cache[11] = $event => (recordPage('page-size-change', $event))),
onSelectionChange: recordSelection,
onSuccess: recordSuccess,
onSortChange: _cache[12] || (_cache[12] = $event => (sortEvents.value++)),
onRowClick: _cache[13] || (_cache[13] = $event => (rowEvents.value.push($event.id))),
onError: recordError
}, {
toolbar: (0, vue_1.withCtx)(({ loading, stale, selection: snapshot }) => [
(0, vue_1.createElementVNode)("p", {
class: "example-paragraph",
"data-testid": "table-toolbar",
"data-slot-loading": String(loading),
"data-slot-stale": String(stale),
"data-slot-keys": JSON.stringify(snapshot.keys),
"data-slot-rows": JSON.stringify(snapshot.rows)
}, " 已选 " + (0, vue_1.toDisplayString)(snapshot.keys.length) + ";loading=" + (0, vue_1.toDisplayString)(loading) + ";stale=" + (0, vue_1.toDisplayString)(stale), 9 /* TEXT, PROPS */, _hoisted_5)
]),
append: (0, vue_1.withCtx)(() => [...(_cache[29] || (_cache[29] = [
(0, vue_1.createElementVNode)("p", {
class: "example-paragraph",
"data-testid": "table-append"
}, "成员只在本地生成", -1 /* CACHED */)
]))]),
empty: (0, vue_1.withCtx)(() => [...(_cache[30] || (_cache[30] = [
(0, vue_1.createElementVNode)("p", { class: "example-paragraph" }, "当前没有成员", -1 /* CACHED */)
]))]),
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamTableColumn), {
prop: "id",
label: "编号",
sortable: ""
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamTableColumn), {
prop: "name",
label: "成员"
}, {
default: (0, vue_1.withCtx)(({ row }) => [
(0, vue_1.createElementVNode)("span", {
"data-row-id": row.id
}, (0, vue_1.toDisplayString)(row.name), 9 /* TEXT, PROPS */, _hoisted_4)
]),
_: 1 /* STABLE */
})
]),
_: 1 /* STABLE */
}, 512 /* NEED_PATCH */),
(0, vue_1.createElementVNode)("p", _hoisted_6, "结果:" + (0, vue_1.toDisplayString)(result.value) + ";成功次数:" + (0, vue_1.toDisplayString)(successes.value) + ";" + (0, vue_1.toDisplayString)(error.value), 1 /* TEXT */),
(0, vue_1.createElementVNode)("pre", _hoisted_7, (0, vue_1.toDisplayString)(JSON.stringify(selection.value)), 1 /* TEXT */),
(0, vue_1.createElementVNode)("pre", _hoisted_8, (0, vue_1.toDisplayString)(JSON.stringify(observation.value)), 1 /* TEXT */),
(0, vue_1.createElementVNode)("pre", _hoisted_9, (0, vue_1.toDisplayString)(JSON.stringify(events.value)), 1 /* TEXT */),
(0, vue_1.createElementVNode)("pre", _hoisted_10, (0, vue_1.toDisplayString)(JSON.stringify({ sortEvents: sortEvents.value, rowEvents: rowEvents.value })), 1 /* TEXT */),
(0, vue_1.createVNode)(RowKeyContract_vue_1.default),
(0, vue_1.createVNode)(NativeSlotsContract_vue_1.default)
]));
};
}
});
exports.default.__scopeId = "data-v-e103b593";
},
"RowKeyContract.vue": (module, exports, require) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1 = require("vue");
const _hoisted_1 = { class: "demo-slot-layout" };
const _hoisted_2 = { "data-testid": "row-key-boundary-result" };
const vue_2 = require("vue");
const ui_1 = require("@asam-ui/ui");
exports.default = {
__name: 'RowKeyContract',
setup(__props) {
const busy = (0, vue_2.ref)(false);
const observations = (0, vue_2.ref)([]);
async function runRowKeyBoundary() {
busy.value = true;
observations.value = [];
const results = [];
try {
const cases = [
{ name: 'own-empty-string', key: 'id', rows: [{ id: '' }], valid: true },
{ name: 'own-zero', key: 'id', rows: [{ id: 0 }], valid: true },
{ name: 'own-negative', key: 'id', rows: [{ id: -1 }], valid: true },
{ name: 'own-fraction', key: 'id', rows: [{ id: 1.5 }], valid: true },
{ name: 'sync-string-callback', key: () => 'callback', rows: [{}], valid: true },
{ name: 'sync-number-callback', key: () => 7, rows: [{}], valid: true },
{ name: 'own-NaN', key: 'id', rows: [{ id: NaN }], valid: false },
{ name: 'own-Infinity', key: 'id', rows: [{ id: Infinity }], valid: false },
{ name: 'own-boolean', key: 'id', rows: [{ id: true }], valid: false },
{ name: 'own-null', key: 'id', rows: [{ id: null }], valid: false },
{ name: 'own-object', key: 'id', rows: [{ id: {} }], valid: false },
{ name: 'missing-own-key', key: 'id', rows: [{}], valid: false },
{ name: 'inherited-key', key: 'id', rows: [Object.create({ id: 8 })], valid: false },
{ name: 'duplicate-key', key: 'id', rows: [{ id: 1 }, { id: 1 }], valid: false },
{ name: 'async-resolved', key: async () => 1, rows: [{}], valid: false },
{
name: 'async-rejected',
key: () => Promise.reject(Error('row-key-rejected')),
rows: [{}],
valid: false
},
{
name: 'thenable-resolved',
key: () => ({ then: (done) => done(1) }),
rows: [{}],
valid: false
},
{
name: 'thenable-rejected',
key: () => ({
then: (_done, reject) => reject(Error('row-key-thenable-rejected'))
}),
rows: [{}],
valid: false
},
{
name: 'callback-throws',
key: () => {
throw Error('row-key-thrown');
},
rows: [{}],
valid: false
}
];
for (const item of cases) {
const scope = (0, vue_2.effectScope)();
let calls = 0;
const state = scope.run(() => Reflect.apply(ui_1.useAsamPagedData, undefined, [
{
source: {
api: async () => {
calls++;
return { list: item.rows, total: item.rows.length };
},
buildPageParams: () => ({}),
rowKey: item.key,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}
]));
try {
const result = await state.reload();
const row = {
phase: item.name,
valid: item.valid,
status: result.status,
calls,
errorKind: state.error.value?.kind ?? null,
errorMessage: state.error.value?.message ?? null,
dataLength: state.data.value.list.length,
hasSucceeded: state.hasSucceeded.value,
selection: state.getSelection()
};
if (item.valid) {
state.toggleRowSelection(item.rows[0], true);
row.selection = state.getSelection();
}
results.push(row);
}
finally {
scope.stop();
}
}
const scope = (0, vue_2.effectScope)();
let invalid = false;
const state = scope.run(() => Reflect.apply(ui_1.useAsamPagedData, undefined, [
{
source: {
api: async () => ({ list: [{ id: 9 }], total: 1 }),
buildPageParams: () => ({}),
rowKey: () => invalid ? Promise.reject(Error('row-key-selection-rejected')) : 9,
immediate: false
},
runtime: (0, ui_1.createAsamDataRuntime)()
}
]));
try {
await state.reload();
invalid = true;
const errors = [];
for (const operation of ['toggleRowSelection', 'selectPage']) {
try {
if (operation === 'toggleRowSelection')
state.toggleRowSelection({ id: 9 }, true);
else
state.selectPage([{ id: 9 }]);
}
catch (cause) {
errors.push({
operation,
kind: cause instanceof ui_1.AsamDataFailure ? cause.kind : null,
message: cause instanceof Error ? cause.message : String(cause)
});
}
}
const afterInvalid = state.getSelection();
invalid = false;
state.selectPage([{ id: 9 }]);
results.push({
phase: 'selection-synchronous-key-guard-and-recovery',
errors,
afterInvalid,
recovered: state.getSelection()
});
}
finally {
scope.stop();
}
await new Promise((done) => setTimeout(done, 20));
observations.value = results;
}
catch (cause) {
observations.value = [
...results,
{
phase: 'unexpected-failure',
message: cause instanceof Error ? cause.message : String(cause)
}
];
}
finally {
busy.value = false;
}
}
return (_ctx, _cache) => {
return ((0, vue_1.openBlock)(), (0, vue_1.createBlock)((0, vue_1.unref)(ui_1.AsamCollapse), { "data-testid": "row-key-boundary-case" }, {
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamCollapseItem), { name: "auxiliary-content" }, {
title: (0, vue_1.withCtx)(() => [...(_cache[0] || (_cache[0] = [
(0, vue_1.createElementVNode)("span", null, "公开分页行键边界", -1 /* CACHED */)
]))]),
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createElementVNode)("div", _hoisted_1, [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
disabled: busy.value,
onClick: runRowKeyBoundary
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[1] || (_cache[1] = [
(0, vue_1.createTextVNode)("验证行键边界", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["disabled"]),
(0, vue_1.createElementVNode)("pre", _hoisted_2, (0, vue_1.toDisplayString)(JSON.stringify(observations.value)), 1 /* TEXT */)
])
]),
_: 1 /* STABLE */
})
]),
_: 1 /* STABLE */
}));
};
}
};
exports.default.__scopeId = "data-v-7850150c";
},
"NativeSlotsContract.vue": (module, exports, require) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1 = require("vue");
const _hoisted_1 = { class: "demo-slot-layout" };
const _hoisted_2 = { class: "demo-slot-actions" };
const _hoisted_3 = { "data-testid": "native-slot-row" };
const _hoisted_4 = { "data-testid": "native-slot-result" };
const vue_2 = require("vue");
const ui_1 = require("@asam-ui/ui");
exports.default = {
__name: 'NativeSlotsContract',
setup(__props) {
const table = (0, vue_2.ref)();
const activeNames = (0, vue_2.ref)([]);
const query = {};
const mode = (0, vue_2.ref)('empty');
const customSlots = (0, vue_2.ref)(true);
const layout = (0, vue_2.ref)('fixed');
const calls = (0, vue_2.ref)(0);
const result = (0, vue_2.ref)('unloaded');
let complete;
const buildPageParams = (_query, page) => ({ ...page });
const api = (_params, context) => new Promise((resolve, reject) => {
calls.value++;
const requested = mode.value;
const clear = () => {
context.signal.removeEventListener('abort', abort);
complete = undefined;
};
const abort = () => {
clear();
reject(new Error('插槽请求已取消'));
};
context.signal.addEventListener('abort', abort, { once: true });
complete = () => {
clear();
if (requested === 'failure')
reject(new Error('插槽示例失败'));
else
resolve({
list: requested === 'empty' ? [] : [{ id: 1, name: '插槽成员 1' }],
total: requested === 'empty' ? 0 : 1
});
};
});
async function load(next) {
mode.value = next;
result.value = 'pending';
const outcome = await table.value?.reload({ force: true });
result.value = outcome?.status ?? 'missing';
}
function release() {
complete?.();
}
(0, vue_2.onBeforeUnmount)(() => table.value?.cancel());
return (_ctx, _cache) => {
return ((0, vue_1.openBlock)(), (0, vue_1.createBlock)((0, vue_1.unref)(ui_1.AsamCollapse), {
modelValue: activeNames.value,
"onUpdate:modelValue": _cache[5] || (_cache[5] = $event => ((activeNames).value = $event)),
"data-testid": "table-native-slots-case"
}, {
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamCollapseItem), { name: "auxiliary-content" }, {
title: (0, vue_1.withCtx)(() => [...(_cache[6] || (_cache[6] = [
(0, vue_1.createElementVNode)("span", null, "默认列与 append/empty 插槽", -1 /* CACHED */)
]))]),
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createElementVNode)("div", _hoisted_1, [
(0, vue_1.createElementVNode)("div", _hoisted_2, [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[0] || (_cache[0] = $event => (load('empty')))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[7] || (_cache[7] = [
(0, vue_1.createTextVNode)("加载空页", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[1] || (_cache[1] = $event => (load('row')))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[8] || (_cache[8] = [
(0, vue_1.createTextVNode)("加载成员", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[2] || (_cache[2] = $event => (load('failure')))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[9] || (_cache[9] = [
(0, vue_1.createTextVNode)("使插槽分页失败", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: release
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[10] || (_cache[10] = [
(0, vue_1.createTextVNode)("完成插槽分页请求", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[3] || (_cache[3] = $event => (customSlots.value = !customSlots.value))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[11] || (_cache[11] = [
(0, vue_1.createTextVNode)("切换自定义插槽", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
onClick: _cache[4] || (_cache[4] = $event => (layout.value = layout.value === 'fixed' ? 'auto' : 'fixed'))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[12] || (_cache[12] = [
(0, vue_1.createTextVNode)(" 切换汇总布局 ", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
})
]),
(activeNames.value.includes('auxiliary-content'))
? ((0, vue_1.openBlock)(), (0, vue_1.createBlock)((0, vue_1.unref)(ui_1.AsamDataTable), {
key: 0,
ref_key: "table",
ref: table,
api: api,
query: query,
"build-page-params": buildPageParams,
immediate: false,
"page-size": 2,
"show-summary": "",
"table-layout": layout.value
}, (0, vue_1.createSlots)({
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamTableColumn), {
prop: "name",
label: "插槽成员"
}, {
default: (0, vue_1.withCtx)(({ row }) => [
(0, vue_1.createElementVNode)("span", _hoisted_3, (0, vue_1.toDisplayString)(row.name), 1 /* TEXT */)
]),
_: 1 /* STABLE */
})
]),
_: 2 /* DYNAMIC */
}, [
(customSlots.value)
? {
name: "append",
fn: (0, vue_1.withCtx)(() => [
_cache[13] || (_cache[13] = (0, vue_1.createElementVNode)("p", {
class: "example-paragraph",
"data-testid": "native-slot-append"
}, "分页之外的附加说明", -1 /* CACHED */))
]),
key: "0"
}
: undefined,
(customSlots.value)
? {
name: "empty",
fn: (0, vue_1.withCtx)(() => [
_cache[14] || (_cache[14] = (0, vue_1.createElementVNode)("p", {
class: "example-paragraph",
"data-testid": "native-slot-empty"
}, "自定义空页说明", -1 /* CACHED */))
]),
key: "1"
}
: undefined
]), 1032 /* PROPS, DYNAMIC_SLOTS */, ["table-layout"]))
: (0, vue_1.createCommentVNode)("v-if", true),
(0, vue_1.createElementVNode)("pre", _hoisted_4, (0, vue_1.toDisplayString)(JSON.stringify({ result: result.value, calls: calls.value })), 1 /* TEXT */)
])
]),
_: 1 /* STABLE */
})
]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["modelValue"]));
};
}
};
exports.default.__scopeId = "data-v-bcf49ef4";
},
};
const __dependencies = {"Demo.vue":{"./NativeSlotsContract.vue":"NativeSlotsContract.vue","./RowKeyContract.vue":"RowKeyContract.vue"},"RowKeyContract.vue":{},"NativeSlotsContract.vue":{}};
const __cache = Object.create(null);
function __require(id) {
if (__external[id]) return __external[id];
if (__cache[id]) return __cache[id].exports;
const module = { exports: {} };
__cache[id] = module;
__modules[id](module, module.exports, (dependency) => __require(__dependencies[id][dependency] || dependency));
return module.exports;
}
const StandaloneDemo = __require('Demo.vue').default;
</script>
<style>
.lifecycle-events[data-v-e103b593] {
max-height: 32rem;
overflow: auto;
}
.demo-layout-1[data-v-e103b593] {
display: grid;
gap: var(--asam-space-md);
}
.demo-slot-layout[data-v-e103b593] {
display: grid;
gap: var(--asam-space-md);
width: 100%;
min-width: 0;
}
.demo-slot-layout > .el-button[data-v-e103b593] {
justify-self: start;
}
.demo-slot-layout[data-v-e103b593] > :is(p, pre, output) {
margin-block: 0;
}
.demo-slot-actions[data-v-e103b593] {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm);
}
.demo-slot-actions > .el-button + .el-button[data-v-e103b593] {
margin-left: 0;
}
p[data-v-e103b593]:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
.demo-slot-layout[data-v-7850150c] {
display: grid;
gap: var(--asam-space-md);
width: 100%;
min-width: 0;
}
.demo-slot-layout > .el-button[data-v-7850150c] {
justify-self: start;
}
.demo-slot-layout[data-v-7850150c] > :is(p, pre, output) {
margin-block: 0;
}
.demo-slot-actions[data-v-7850150c] {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm);
}
.demo-slot-actions > .el-button + .el-button[data-v-7850150c] {
margin-left: 0;
}
.demo-slot-layout[data-v-bcf49ef4] {
display: grid;
gap: var(--asam-space-md);
width: 100%;
min-width: 0;
}
.demo-slot-layout > .el-button[data-v-bcf49ef4] {
justify-self: start;
}
.demo-slot-layout[data-v-bcf49ef4] > :is(p, pre, output) {
margin-block: 0;
}
.demo-slot-actions[data-v-bcf49ef4] {
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--asam-space-sm);
}
.demo-slot-actions > .el-button + .el-button[data-v-bcf49ef4] {
margin-left: 0;
}
p[data-v-bcf49ef4]:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
独立分页状态与完整生命周期 ​
useAsamPagedData(config) 可供自定义表格消费同一分页协议。 完整说明。
| 配置 | 完整约束 |
|---|---|
api + buildPageParams |
builder 同步接收 query 与 { currentPage, pageSize };api 返回 Promise,并接收原始请求 context |
request |
默认将 query 与分页参数合并到 params;provider 的 pagination 可指定参数名 |
pageTarget: 'data' |
适用于非 GET 请求;基础 target、query 和分页参数不得重名 |
buildPageRequest |
同步构造完整 URL 请求,与 pageTarget、buildPageParams 互斥 |
transform |
同步返回 { list, total },优先于 provider 的 adaptPage;自定义行类型必须提供 transform |
queryKey |
同步返回非空字符串,用于不能默认稳定序列化的 query;变化会重置页码并清空选择 |
cache |
false 或 { ttlMs, key? };TTL 为正安全整数,key 同步返回非空字符串 |
rowKey |
默认 'id',也可使用字段名或同步函数;结果为字符串或有限数字,同页不得重复 |
defaultSelectedRowKeys |
字符串/数字键保持类型区分,初次加载前可保留未知行键,后续遇到该行时补齐行快照 |
currentPage / pageSize |
正安全整数;修改 pageSize 重置到第一页,受控值回显不重复请求 |
AsamApiPageSource<Q, P, R, T> 明确声明 api、query、分页参数 builder 与 transform。 完整说明。
| 状态 / 方法 | 行为 |
|---|---|
data, loading, error, stale, hasSucceeded, empty |
只读 refs;失败保留最后成功数据并标记 stale;empty 仅在当前成功、非加载、无错误且非 stale 的空页成立 |
dataKey, requestKey, resolvedPage |
分别标识成功数据与待执行参数;resolvedPage 保存实际成功的页码、页大小及 queryKey |
currentPage, pageSize, selection |
只读 refs;通过方法修改分页与选择 |
reload({ force? }) |
返回 success / error / cancelled 闭合结果;force 跳过缓存并共享同一个强制请求 |
cancel() |
结算当前等待为 cancelled;其他共享消费者可继续,晚到结果不得回写取消者 |
setCurrentPage(page), setPageSize(size) |
自动模式发起请求,手动模式准备新参数并标记旧数据 stale |
getSelection(), clearSelection() |
返回冻结快照;清空重复调用不再次发出选择变更 |
toggleRowSelection(row, selected?), selectPage(rows) |
当前页增选仅采用当前成功数据,保留其他页选择;stale 期间阻止增选和整页选择,仍可取消已选行 |
响应总数为零时页码归一到 1;页码超出末页时最多纠正并请求一次,第二次仍越界返回 response 错误。 完整说明。
onSuccess 接收冻结的标准化页,onError 接收结构化错误,onPageChange、
onPageSizeChange 接收数字,onSelectionChange 接收完整只读选择快照。回调不代替宿主
自己的受控数据更新。
通过实例方法管理当前页选择,并读取跨页选择键与已解析行。 完整说明。
当前页的原生展示用途 ​
完整示例在同一数据入口组合固定表头/列、两层分组表头、列模板/自定义表头、序号、当前行高亮、原生排序与列筛选、展开内容、汇总、状态样式、状态单元格合并及溢出提示。 完整说明。
高度按钮演示 maxHeight=150 的滚动区和 height=310/maxHeight=310 的固定高度;布局按钮切换 tableLayout 的 fixed/auto,请求计数用于确认这些展示操作不重新请求。 完整说明。
展示排序、筛选、展开与汇总
<template>
<section class="demo-layout-1">
<AsamButton type="default" native-type="button" @click="table?.sort('amount', 'descending')"
>金额倒序</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.sort('amount', 'ascending')"
>金额正序</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.clearSort()"
>清除排序</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.clearFilter()"
>清除筛选</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.clearFilter('status')"
>清除状态筛选</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.clearFilter(['status'])"
>按键数组清除筛选</AsamButton
>
<AsamButton
type="default"
native-type="button"
@click="table?.toggleRowExpansion(loadedRows[0]!, true)"
>
展开第一行
</AsamButton>
<AsamButton
type="default"
native-type="button"
@click="table?.toggleRowExpansion(loadedRows[0]!, false)"
>
收起第一行
</AsamButton>
<AsamButton
type="default"
native-type="button"
@click="table?.setCurrentRow(loadedRows[1]!)"
>高亮第二行</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.setCurrentRow()"
>清除当前行</AsamButton
>
<AsamButton
type="default"
native-type="button"
@click="table?.toggleRowExpansion(loadedRows[0]!)"
>
切换第一行展开
</AsamButton>
<AsamButton type="default" native-type="button" @click="currentKey = 1"
>按键高亮第一行</AsamButton
>
<AsamButton type="default" native-type="button" @click="expandedKeys = [1]"
>按键展开第一行</AsamButton
>
<AsamButton type="default" native-type="button" @click="expandedKeys = []"
>按键收起全部行</AsamButton
>
<AsamButton type="default" native-type="button" @click="merged = !merged"
>切换状态合并</AsamButton
>
<AsamButton type="default" native-type="button" @click="objectSpan = !objectSpan"
>切换对象跨度</AsamButton
>
<AsamButton type="default" native-type="button" @click="defaultSpan = true"
>使用默认跨度</AsamButton
>
<AsamButton type="default" native-type="button" @click="defaultSpan = false"
>恢复合并跨度</AsamButton
>
<AsamButton type="default" native-type="button" @click="constantClass = !constantClass"
>切换字符串行样式</AsamButton
>
<AsamButton type="default" native-type="button" @click="renderVNode = !renderVNode"
>切换 VNode 回调</AsamButton
>
<AsamButton type="default" native-type="button" @click="showSummary = !showSummary"
>切换汇总显示</AsamButton
>
<AsamButton
type="default"
native-type="button"
@click="
height = '310px';
maxHeight = '310px';
"
>
使用字符串高度
</AsamButton>
<AsamButton
type="default"
native-type="button"
@click="
height = undefined;
maxHeight = '150px';
"
>
使用字符串最大高度
</AsamButton>
<AsamButton
type="default"
native-type="button"
@click="
height = undefined;
maxHeight = 150;
"
>
使用最大高度
</AsamButton>
<AsamButton
type="default"
native-type="button"
@click="
height = 310;
maxHeight = 310;
"
>
恢复固定高度
</AsamButton>
<AsamButton
type="default"
native-type="button"
@click="
layout = layout === 'fixed' ? 'auto' : 'fixed';
maxHeight = layout === 'auto' ? undefined : 310;
"
>
切换表格布局
</AsamButton>
<AsamDataTable
ref="table"
:api="api"
:query="{}"
:build-page-params="buildPageParams"
:transform="transform"
:current-row-key="currentKey"
:expand-row-keys="expandedKeys"
:page-size="10"
:page-sizes="[10]"
:pagination="{ layout: 'total' }"
:summary-method="summary"
:span-method="span"
:row-class-name="constantClass ? 'native-constant-row' : rowClass"
:tooltip-formatter="tooltip"
:height="height"
:max-height="maxHeight"
:table-layout="layout"
:show-summary="showSummary"
show-overflow-tooltip
highlight-current-row
preserve-expanded-content
border
stripe
@sort-change="sortEvents++"
@success="loadedRows = $event.list"
@filter-change="filters = $event"
@expand-change="expanded = $event.id"
@current-change="current = $event?.id ?? null"
>
<!-- @vue-generic {Row} -->
<AsamTableColumn type="expand" width="48">
<!-- prettier-ignore -->
<template #default="{ row }">
<p class="example-paragraph" :data-expanded-id="row.id">详情:{{ row.note }}</p>
</template>
</AsamTableColumn>
<!-- prettier-ignore -->
<AsamTableColumn
type="index"
label="序号"
:index="index"
width="70"
fixed
/>
<AsamTableColumn label="成员资料">
<!-- @vue-generic {Row} -->
<AsamTableColumn prop="name" width="140" fixed>
<!-- prettier-ignore -->
<template #header>
<span data-testid="native-custom-header">成员姓名</span>
</template>
<!-- prettier-ignore -->
<template #default="{ row }">
<strong :data-member-id="row.id">{{ row.name }}</strong>
</template>
</AsamTableColumn>
<!-- prettier-ignore -->
<AsamTableColumn
prop="amount"
label="金额"
sortable
width="120"
/>
</AsamTableColumn>
<AsamTableColumn
prop="status"
label="状态"
column-key="status"
:filters="statusFilters"
:filter-method="filterStatus"
width="120"
/>
<AsamTableColumn prop="note" label="备注" width="200" />
</AsamDataTable>
<pre data-testid="native-table-events">{{
JSON.stringify({ sortEvents, filters, expanded, current, merged, pageCalls, layout })
}}</pre>
</section>
</template>
<script lang="ts" setup>
defineOptions({ name: 'AsamDataTableNativeFeaturesDemo' });
import { h, ref } from 'vue';
import { AsamButton, AsamDataTable, AsamTableColumn } from '@asam-ui/ui';
import type { AsamDataTableExpose, AsamPage, AsamPageRequest, AsamTableProps } from '@asam-ui/ui';
interface Row {
id: number;
name: string;
amount: number;
status: string;
note: string;
}
const rows: Row[] = [
{
id: 1,
name: '林一',
amount: 10,
status: '准备',
note: '本地演示备注用于显示完整的原生溢出提示内容。'
},
{ id: 2, name: '陈二', amount: 20, status: '准备', note: '第二行备注' },
{ id: 3, name: '许三', amount: 30, status: '进行', note: '第三行备注' }
];
const table = ref<AsamDataTableExpose<Row>>();
const loadedRows = ref<readonly Row[]>([]);
const currentKey = ref<string | number>();
const expandedKeys = ref<(string | number)[]>([]);
const sortEvents = ref(0);
const pageCalls = ref(0);
const filters = ref<Record<string, string[]>>({});
const expanded = ref<number | null>(null);
const current = ref<number | null>(null);
const merged = ref(false);
const height = ref<string | number | undefined>(310);
const maxHeight = ref<string | number>();
const layout = ref<'fixed' | 'auto'>('fixed');
const objectSpan = ref(false);
const defaultSpan = ref(false);
const constantClass = ref(false);
const renderVNode = ref(false);
const showSummary = ref(true);
const statusFilters = [
{ text: '准备', value: '准备' },
{ text: '进行', value: '进行' }
];
const buildPageParams = (_query: object, page: AsamPageRequest) => page;
const api = async (): Promise<AsamPage<Row>> => {
pageCalls.value++;
return { list: rows, total: rows.length };
};
const transform = (page: AsamPage<Row>) => page;
const index = (at: number) => at + 101;
const filterStatus = (value: string, row: Row) => row.status === value;
const summary: NonNullable<AsamTableProps<Row>['summaryMethod']> = ({ columns, data }) =>
columns.map((column, at) =>
at === 0
? renderVNode.value
? h('strong', { 'data-testid': 'summary-vnode-label' }, '合计')
: '合计'
: column.property === 'amount'
? renderVNode.value
? h(
'span',
{ 'data-testid': 'summary-vnode-total' },
String(data.reduce((total, row) => total + row.amount, 0))
)
: String(data.reduce((total, row) => total + row.amount, 0))
: ''
);
const span: NonNullable<AsamTableProps<Row>['spanMethod']> = ({ row, column }) => {
if (defaultSpan.value) return undefined;
const rowspan =
!merged.value || column.property !== 'status' ? 1 : row.id === 1 ? 2 : row.id === 2 ? 0 : 1;
const colspan = rowspan === 0 ? 0 : 1;
return objectSpan.value ? { rowspan, colspan } : [rowspan, colspan];
};
const rowClass: NonNullable<AsamTableProps<Row>['rowClassName']> = ({ row }) =>
row.id === 3 ? 'native-status-row' : '';
const tooltip: NonNullable<AsamTableProps<Row>['tooltipFormatter']> = ({ row }) =>
renderVNode.value
? h('strong', { 'data-testid': 'tooltip-vnode-content' }, `${row.name}:${row.note}`)
: `${row.name}:${row.note}`;
</script>
<style scoped>
.demo-layout-1 {
display: grid;
gap: var(--asam-space-md);
}
</style>
<style scoped>
p:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
<template>
<section class="demo-layout-1">
<AsamButton type="default" native-type="button" @click="table?.sort('amount', 'descending')"
>金额倒序</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.sort('amount', 'ascending')"
>金额正序</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.clearSort()"
>清除排序</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.clearFilter()"
>清除筛选</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.clearFilter('status')"
>清除状态筛选</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.clearFilter(['status'])"
>按键数组清除筛选</AsamButton
>
<AsamButton
type="default"
native-type="button"
@click="table?.toggleRowExpansion(loadedRows[0], true)"
>
展开第一行
</AsamButton>
<AsamButton
type="default"
native-type="button"
@click="table?.toggleRowExpansion(loadedRows[0], false)"
>
收起第一行
</AsamButton>
<AsamButton type="default" native-type="button" @click="table?.setCurrentRow(loadedRows[1])"
>高亮第二行</AsamButton
>
<AsamButton type="default" native-type="button" @click="table?.setCurrentRow()"
>清除当前行</AsamButton
>
<AsamButton
type="default"
native-type="button"
@click="table?.toggleRowExpansion(loadedRows[0])"
>
切换第一行展开
</AsamButton>
<AsamButton type="default" native-type="button" @click="currentKey = 1"
>按键高亮第一行</AsamButton
>
<AsamButton type="default" native-type="button" @click="expandedKeys = [1]"
>按键展开第一行</AsamButton
>
<AsamButton type="default" native-type="button" @click="expandedKeys = []"
>按键收起全部行</AsamButton
>
<AsamButton type="default" native-type="button" @click="merged = !merged"
>切换状态合并</AsamButton
>
<AsamButton type="default" native-type="button" @click="objectSpan = !objectSpan"
>切换对象跨度</AsamButton
>
<AsamButton type="default" native-type="button" @click="defaultSpan = true"
>使用默认跨度</AsamButton
>
<AsamButton type="default" native-type="button" @click="defaultSpan = false"
>恢复合并跨度</AsamButton
>
<AsamButton type="default" native-type="button" @click="constantClass = !constantClass"
>切换字符串行样式</AsamButton
>
<AsamButton type="default" native-type="button" @click="renderVNode = !renderVNode"
>切换 VNode 回调</AsamButton
>
<AsamButton type="default" native-type="button" @click="showSummary = !showSummary"
>切换汇总显示</AsamButton
>
<AsamButton
type="default"
native-type="button"
@click="
height = '310px';
maxHeight = '310px';
"
>
使用字符串高度
</AsamButton>
<AsamButton
type="default"
native-type="button"
@click="
height = undefined;
maxHeight = '150px';
"
>
使用字符串最大高度
</AsamButton>
<AsamButton
type="default"
native-type="button"
@click="
height = undefined;
maxHeight = 150;
"
>
使用最大高度
</AsamButton>
<AsamButton
type="default"
native-type="button"
@click="
height = 310;
maxHeight = 310;
"
>
恢复固定高度
</AsamButton>
<AsamButton
type="default"
native-type="button"
@click="
layout = layout === 'fixed' ? 'auto' : 'fixed';
maxHeight = layout === 'auto' ? undefined : 310;
"
>
切换表格布局
</AsamButton>
<AsamDataTable
ref="table"
:api="api"
:query="{}"
:build-page-params="buildPageParams"
:transform="transform"
:current-row-key="currentKey"
:expand-row-keys="expandedKeys"
:page-size="10"
:page-sizes="[10]"
:pagination="{ layout: 'total' }"
:summary-method="summary"
:span-method="span"
:row-class-name="constantClass ? 'native-constant-row' : rowClass"
:tooltip-formatter="tooltip"
:height="height"
:max-height="maxHeight"
:table-layout="layout"
:show-summary="showSummary"
show-overflow-tooltip
highlight-current-row
preserve-expanded-content
border
stripe
@sort-change="sortEvents++"
@success="loadedRows = $event.list"
@filter-change="filters = $event"
@expand-change="expanded = $event.id"
@current-change="current = $event?.id ?? null"
>
<!-- @vue-generic {Row} -->
<AsamTableColumn type="expand" width="48">
<!-- prettier-ignore -->
<template #default="{ row }">
<p class="example-paragraph" :data-expanded-id="row.id">详情:{{ row.note }}</p>
</template>
</AsamTableColumn>
<!-- prettier-ignore -->
<AsamTableColumn
type="index"
label="序号"
:index="index"
width="70"
fixed
/>
<AsamTableColumn label="成员资料">
<!-- @vue-generic {Row} -->
<AsamTableColumn prop="name" width="140" fixed>
<!-- prettier-ignore -->
<template #header>
<span data-testid="native-custom-header">成员姓名</span>
</template>
<!-- prettier-ignore -->
<template #default="{ row }">
<strong :data-member-id="row.id">{{ row.name }}</strong>
</template>
</AsamTableColumn>
<!-- prettier-ignore -->
<AsamTableColumn
prop="amount"
label="金额"
sortable
width="120"
/>
</AsamTableColumn>
<AsamTableColumn
prop="status"
label="状态"
column-key="status"
:filters="statusFilters"
:filter-method="filterStatus"
width="120"
/>
<AsamTableColumn prop="note" label="备注" width="200" />
</AsamDataTable>
<pre data-testid="native-table-events">{{
JSON.stringify({ sortEvents, filters, expanded, current, merged, pageCalls, layout })
}}</pre>
</section>
</template>
<script setup>
defineOptions({ name: 'AsamDataTableNativeFeaturesDemo' });
import { h, ref } from 'vue';
import { AsamButton, AsamDataTable, AsamTableColumn } from '@asam-ui/ui';
const rows = [
{
id: 1,
name: '林一',
amount: 10,
status: '准备',
note: '本地演示备注用于显示完整的原生溢出提示内容。'
},
{ id: 2, name: '陈二', amount: 20, status: '准备', note: '第二行备注' },
{ id: 3, name: '许三', amount: 30, status: '进行', note: '第三行备注' }
];
const table = ref();
const loadedRows = ref([]);
const currentKey = ref();
const expandedKeys = ref([]);
const sortEvents = ref(0);
const pageCalls = ref(0);
const filters = ref({});
const expanded = ref(null);
const current = ref(null);
const merged = ref(false);
const height = ref(310);
const maxHeight = ref();
const layout = ref('fixed');
const objectSpan = ref(false);
const defaultSpan = ref(false);
const constantClass = ref(false);
const renderVNode = ref(false);
const showSummary = ref(true);
const statusFilters = [
{ text: '准备', value: '准备' },
{ text: '进行', value: '进行' }
];
const buildPageParams = (_query, page) => page;
const api = async () => {
pageCalls.value++;
return { list: rows, total: rows.length };
};
const transform = (page) => page;
const index = (at) => at + 101;
const filterStatus = (value, row) => row.status === value;
const summary = ({ columns, data }) =>
columns.map((column, at) =>
at === 0
? renderVNode.value
? h('strong', { 'data-testid': 'summary-vnode-label' }, '合计')
: '合计'
: column.property === 'amount'
? renderVNode.value
? h(
'span',
{ 'data-testid': 'summary-vnode-total' },
String(data.reduce((total, row) => total + row.amount, 0))
)
: String(data.reduce((total, row) => total + row.amount, 0))
: ''
);
const span = ({ row, column }) => {
if (defaultSpan.value) return undefined;
const rowspan =
!merged.value || column.property !== 'status' ? 1 : row.id === 1 ? 2 : row.id === 2 ? 0 : 1;
const colspan = rowspan === 0 ? 0 : 1;
return objectSpan.value ? { rowspan, colspan } : [rowspan, colspan];
};
const rowClass = ({ row }) => (row.id === 3 ? 'native-status-row' : '');
const tooltip = ({ row }) =>
renderVNode.value
? h('strong', { 'data-testid': 'tooltip-vnode-content' }, `${row.name}:${row.note}`)
: `${row.name}:${row.note}`;
</script>
<style scoped>
.demo-layout-1 {
display: grid;
gap: var(--asam-space-md);
}
</style>
<style scoped>
p:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
回调按钮分别演示汇总与 tooltip 的文本/VNode 返回值、spanMethod 的数组/对象跨度,以及 rowClassName 的字符串/泛型函数。 完整说明。
原生树加载与行键边界 ​
currentRowKey、expandRowKeys 和 updateKeyChildren 使用 rowKey 生成的原始 ASAM 键;数字 1 与字符串 "1" 分别定位,不要求调用方填写内部类型前缀。 完整说明。
原生懒加载与数值/字符串行键
<template>
<section class="demo-layout-1">
<AsamButton type="default" native-type="button" @click="currentKey = 1"
>高亮数字键</AsamButton
>
<AsamButton type="default" native-type="button" @click="currentKey = '1'"
>高亮字符串键</AsamButton
>
<AsamButton type="default" native-type="button" @click="replace(1)"
>替换数字子节点</AsamButton
>
<AsamButton type="default" native-type="button" @click="replace('1')"
>替换字符串子节点</AsamButton
>
<AsamDataTable
ref="table"
:api="api"
:query="{}"
:build-page-params="buildPageParams"
:transform="transform"
v-bind="currentKey === undefined ? {} : { currentRowKey: currentKey }"
:load="load"
:page-size="10"
:page-sizes="[10]"
highlight-current-row
lazy
>
<!-- @vue-generic {Row} -->
<AsamTableColumn prop="name" label="父子节点">
<!-- prettier-ignore -->
<template #default="{ row }">
<span :data-tree-key="`${typeof row.id}:${row.id}`">{{
row.name
}}</span>
</template>
</AsamTableColumn>
</AsamDataTable>
<pre data-testid="lazy-key-observation">{{
JSON.stringify({ pageCalls, loads, currentKey })
}}</pre>
</section>
</template>
<script lang="ts" setup>
defineOptions({ name: 'AsamDataTableLazyTreeKeysDemo' });
import { ref } from 'vue';
import { AsamButton, AsamDataTable, AsamTableColumn } from '@asam-ui/ui';
import type { AsamDataTableExpose, AsamPage, AsamPageRequest, AsamTableProps } from '@asam-ui/ui';
interface Row {
id: string | number;
name: string;
hasChildren?: boolean;
}
const table = ref<AsamDataTableExpose<Row>>();
const currentKey = ref<string | number>();
const pageCalls = ref(0);
const loads = ref<(string | number)[]>([]);
const api = async (): Promise<AsamPage<Row>> => {
pageCalls.value++;
return {
list: [
{ id: 1, name: '数字父节点', hasChildren: true },
{ id: '1', name: '字符串父节点', hasChildren: true }
],
total: 2
};
};
const buildPageParams = (_query: object, page: AsamPageRequest) => page;
const transform = (page: AsamPage<Row>) => page;
const load: NonNullable<AsamTableProps<Row>['load']> = (row, _treeNode, resolve) => {
loads.value.push(row.id);
resolve([
{
id: typeof row.id === 'number' ? 11 : '11',
name: typeof row.id === 'number' ? '数字原子节点' : '字符串原子节点'
}
]);
};
function replace(key: string | number): void {
table.value?.updateKeyChildren(key, [
{
id: typeof key === 'number' ? 12 : '12',
name: typeof key === 'number' ? '数字新子节点' : '字符串新子节点'
}
]);
}
</script>
<style scoped>
.demo-layout-1 {
display: grid;
gap: var(--asam-space-md);
}
</style>
<template>
<section class="demo-layout-1">
<AsamButton type="default" native-type="button" @click="currentKey = 1"
>高亮数字键</AsamButton
>
<AsamButton type="default" native-type="button" @click="currentKey = '1'"
>高亮字符串键</AsamButton
>
<AsamButton type="default" native-type="button" @click="replace(1)"
>替换数字子节点</AsamButton
>
<AsamButton type="default" native-type="button" @click="replace('1')"
>替换字符串子节点</AsamButton
>
<AsamDataTable
ref="table"
:api="api"
:query="{}"
:build-page-params="buildPageParams"
:transform="transform"
v-bind="currentKey === undefined ? {} : { currentRowKey: currentKey }"
:load="load"
:page-size="10"
:page-sizes="[10]"
highlight-current-row
lazy
>
<!-- @vue-generic {Row} -->
<AsamTableColumn prop="name" label="父子节点">
<!-- prettier-ignore -->
<template #default="{ row }">
<span :data-tree-key="`${typeof row.id}:${row.id}`">{{
row.name
}}</span>
</template>
</AsamTableColumn>
</AsamDataTable>
<pre data-testid="lazy-key-observation">{{
JSON.stringify({ pageCalls, loads, currentKey })
}}</pre>
</section>
</template>
<script setup>
defineOptions({ name: 'AsamDataTableLazyTreeKeysDemo' });
import { ref } from 'vue';
import { AsamButton, AsamDataTable, AsamTableColumn } from '@asam-ui/ui';
const table = ref();
const currentKey = ref();
const pageCalls = ref(0);
const loads = ref([]);
const api = async () => {
pageCalls.value++;
return {
list: [
{ id: 1, name: '数字父节点', hasChildren: true },
{ id: '1', name: '字符串父节点', hasChildren: true }
],
total: 2
};
};
const buildPageParams = (_query, page) => page;
const transform = (page) => page;
const load = (row, _treeNode, resolve) => {
loads.value.push(row.id);
resolve([
{
id: typeof row.id === 'number' ? 11 : '11',
name: typeof row.id === 'number' ? '数字原子节点' : '字符串原子节点'
}
]);
};
function replace(key) {
table.value?.updateKeyChildren(key, [
{
id: typeof key === 'number' ? 12 : '12',
name: typeof key === 'number' ? '数字新子节点' : '字符串新子节点'
}
]);
}
</script>
<style scoped>
.demo-layout-1 {
display: grid;
gap: var(--asam-space-md);
}
</style>
工具栏插槽的 selection 保留完整原始键和业务行快照,loading/stale 为布尔值;分页示例提供明确持有/完成请求操作,可观察加载中、失败过期和恢复的状态。默认插槽没有载荷参数,直接放置 AsamTableColumn;列内的 row 作用域属于列组件。
行键读取的内部边界 ​
应用通过公开 rowKey 字段名或同步回调配置行键,由分页数据与表格完成读取和校验。 完整说明。
生命周期完整示例还可展开“公开分页行键边界”。 完整说明。
append 与 empty 都是零参数插槽,经 DataTable → AsamTable → 原生 Table 转交,不接收行、loading 或跨页 selection。 完整说明。
成功空页未提供 empty 插槽时,公开 emptyText 使用调用方文案;省略或传入
undefined 时保留默认“暂无数据”。未加载、加载中和错误状态保留数据表自有文案,
empty 插槽仍优先于空文本。
生命周期示例的“默认列与 append/empty 插槽”可持有和完成空页、成员页及失败请求。未加载和成功空页显示自定义空内容;加载成员后隐藏空内容,附加说明继续显示。成功页随后失败时保留旧行,空内容不覆盖陈旧快照;恢复空页后重新显示。关闭自定义插槽可查看原生“暂无数据”文本,再打开可恢复自定义内容。
固定原生 2.14.7 的 append 顺序随布局变化:fixed 布局位于汇总之前,auto 布局位于表体内的汇总之后。 完整说明。
局部请求错误插槽 ​
可选 error 插槽仅在当前请求错误存在时调用,接收只读 { error, stale, retry }。 完整说明。
retry() 不接收参数,直接返回现有 reload({ force: true }) 的 Promise,结果为
AsamReloadResult<AsamPage<Row>> 的 success、error 或 cancelled 分支。
重试保留当前查询、分页与选择,不自动重置这些值。
提供该插槽时,自有内容替换整个默认局部错误区;返回空内容也表示主动替换。 未提供时继续显示原错误文字、过期提示和默认重试按钮。无 error 的单独 stale 状态 不调用此插槽;empty、loading 和其他基础插槽继续使用原作用域。基础表格的 empty 内容仍属于独立的空数据展示。
以下完整 TS/JS 示例先制造失败,再等待插槽 retry 的返回结果;成功后可再次制造 更新失败,观察旧结果与模型保留。每次挂载使用独立子 app 和本地 provider。
局部错误定制、等待重试与过期恢复
<template><StandaloneDemo /></template>
<script lang="ts" setup>
import * as __external0 from "vue";
import * as __external1 from "@asam-ui/ui";
const __external: any = {"vue": { ...__external0, __esModule: true },
"@asam-ui/ui": { ...__external1, __esModule: true }};
const __modules: any = {
"Demo.vue": (module: any, exports: any, require: any) => {
"use strict";
var __importDefault: any = function (mod?: any) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1: any = require("vue");
const vue_2: any = require("vue");
const vue_3: any = require("vue");
const ui_1: any = require("@asam-ui/ui");
const Child_vue_1: any = __importDefault(require("./Child.vue"));
const transport_js_1: any = require("./transport.js");
exports.default = (0, vue_1.defineComponent)({
...{ name: 'AsamDataTableDemoLocalErrorExample' },
__name: 'Demo',
setup(__props?: any) {
const host: any = (0, vue_3.ref)();
let app: any;
(0, vue_3.onMounted)(() => {
if (!host.value)
return;
const transport: any = (0, transport_js_1.createTransport)();
app = (0, vue_3.createApp)(Child_vue_1.default, { transport });
(0, ui_1.provideAsamData)(app, transport.provider, { scope: 'AsamDataTable-local-error' });
app.mount(host.value);
});
(0, vue_3.onBeforeUnmount)(() => app?.unmount());
return (_ctx?: any, _cache?: any) => {
return ((0, vue_2.openBlock)(), (0, vue_2.createElementBlock)("div", {
ref_key: "host",
ref: host
}, null, 512 /* NEED_PATCH */));
};
}
});
},
"Child.vue": (module: any, exports: any, require: any) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1: any = require("vue");
const vue_2: any = require("vue");
const _hoisted_1: any = { class: "demo-spacing-1" };
const _hoisted_2: any = {
role: "alert",
"data-testid": "local-error"
};
const _hoisted_3: any = { class: "example-paragraph" };
const _hoisted_4: any = {
key: 0,
class: "example-paragraph"
};
const _hoisted_5: any = {
class: "example-paragraph",
role: "status",
"data-testid": "retry-result"
};
const _hoisted_6: any = {
class: "example-paragraph",
"data-testid": "transport-count"
};
const vue_3: any = require("vue");
const ui_1: any = require("@asam-ui/ui");
exports.default = (0, vue_1.defineComponent)({
...{ name: 'AsamDataTableChildLocalErrorExample' },
__name: 'Child',
props: {
transport: { type: null, required: true }
},
setup(__props?: any) {
const props: any = __props;
const transport: any = props.transport;
const control: any = (0, vue_3.ref)();
const busy: any = (0, vue_3.ref)(false), result: any = (0, vue_3.ref)('');
const buildPageParams: any = () => ({});
const transform: any = (rows?: any) => ({ list: rows, total: rows.length });
(0, vue_3.onMounted)(async () => {
const response: any = await control.value?.reload();
result.value = response?.status ?? 'cancelled';
});
async function recover(retry?: any) {
busy.value = true;
transport.failing.value = false;
try {
const response: any = await retry();
result.value = response.status;
}
finally {
busy.value = false;
}
}
async function failAgain() {
busy.value = true;
transport.failing.value = true;
try {
const response: any = await control.value?.reload({ force: true });
result.value = response?.status ?? 'cancelled';
}
finally {
busy.value = false;
}
}
return (_ctx?: any, _cache?: any) => {
return ((0, vue_2.openBlock)(), (0, vue_2.createElementBlock)("section", _hoisted_1, [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamDataTable), {
ref_key: "control",
ref: control,
immediate: false,
api: (0, vue_2.unref)(transport).api,
"build-page-params": buildPageParams,
transform: transform
}, {
error: (0, vue_2.withCtx)(({ error, stale, retry }: any) => [
(0, vue_2.createElementVNode)("div", _hoisted_2, [
(0, vue_2.createElementVNode)("p", _hoisted_3, (0, vue_2.toDisplayString)(error.message) + "(" + (0, vue_2.toDisplayString)(error.kind) + ")", 1 /* TEXT */),
stale
? ((0, vue_2.openBlock)(), (0, vue_2.createElementBlock)("p", _hoisted_4, "已保留上次成功结果。"))
: (0, vue_2.createCommentVNode)("v-if", true),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
disabled: busy.value,
onClick: ($event?: any) => (recover(retry))
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[0] || (_cache[0] = [
(0, vue_2.createTextVNode)(" 恢复并重试 ", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["disabled", "onClick"])
])
]),
default: (0, vue_2.withCtx)(() => [
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamTableColumn), {
prop: "label",
label: "状态"
})
]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["api"]),
(0, vue_2.createVNode)((0, vue_2.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
disabled: busy.value,
onClick: failAgain
}, {
default: (0, vue_2.withCtx)(() => [...(_cache[1] || (_cache[1] = [
(0, vue_2.createTextVNode)("模拟更新失败", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["disabled"]),
(0, vue_2.createElementVNode)("p", _hoisted_5, (0, vue_2.toDisplayString)(result.value), 1 /* TEXT */),
(0, vue_2.createElementVNode)("p", _hoisted_6, " 请求 " + (0, vue_2.toDisplayString)((0, vue_2.unref)(transport).calls.value) + " 次,字典刷新 " + (0, vue_2.toDisplayString)((0, vue_2.unref)(transport).refreshes.value) + " 次 ", 1 /* TEXT */)
]));
};
}
});
exports.default.__scopeId = "data-v-10b4c655";
},
"transport.ts": (module: any, exports: any, require: any) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTransport = createTransport;
const vue_1: any = require("vue");
function createTransport() {
const failing: any = (0, vue_1.ref)(true), calls: any = (0, vue_1.ref)(0), refreshes: any = (0, vue_1.ref)(0);
const api: any = async () => {
calls.value++;
if (failing.value)
throw Error('演示请求暂不可用');
return [
{ id: 1, value: 'ready', label: '准备' },
{ id: 2, value: 'running', label: '进行' }
];
};
const provider: any = {
dict: {
get: async (_type?: any, context?: any) => {
context.signal.throwIfAborted();
return api();
},
refresh: async (context?: any) => {
context.signal.throwIfAborted();
refreshes.value++;
}
}
};
return { failing, calls, refreshes, api, provider };
}
},
};
const __dependencies: any = {"Demo.vue":{"./Child.vue":"Child.vue","./transport.js":"transport.ts"},"Child.vue":{"./transport.js":"transport.ts"},"transport.ts":{}};
const __cache: any = Object.create(null);
function __require(id: any): any {
if (__external[id]) return __external[id];
if (__cache[id]) return __cache[id].exports;
const module: any = { exports: {} };
__cache[id] = module;
__modules[id](module, module.exports, (dependency: any) => __require(__dependencies[id][dependency] || dependency));
return module.exports;
}
const StandaloneDemo = __require('Demo.vue').default;
</script>
<style>
.demo-spacing-1[data-v-10b4c655] {
display: grid;
gap: var(--asam-space-md);
}
p[data-v-10b4c655]:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
<template><StandaloneDemo /></template>
<script setup>
import * as __external0 from "vue";
import * as __external1 from "@asam-ui/ui";
const __external = {"vue": { ...__external0, __esModule: true },
"@asam-ui/ui": { ...__external1, __esModule: true }};
const __modules = {
"Demo.vue": (module, exports, require) => {
"use strict";
var __importDefault = function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1 = require("vue");
const vue_2 = require("vue");
const ui_1 = require("@asam-ui/ui");
const Child_vue_1 = __importDefault(require("./Child.vue"));
const transport_js_1 = require("./transport.js");
exports.default = Object.assign({ name: 'AsamDataTableDemoLocalErrorExample' }, {
__name: 'Demo',
setup(__props) {
const host = (0, vue_2.ref)();
let app;
(0, vue_2.onMounted)(() => {
if (!host.value)
return;
const transport = (0, transport_js_1.createTransport)();
app = (0, vue_2.createApp)(Child_vue_1.default, { transport });
(0, ui_1.provideAsamData)(app, transport.provider, { scope: 'AsamDataTable-local-error' });
app.mount(host.value);
});
(0, vue_2.onBeforeUnmount)(() => app?.unmount());
return (_ctx, _cache) => {
return ((0, vue_1.openBlock)(), (0, vue_1.createElementBlock)("div", {
ref_key: "host",
ref: host
}, null, 512 /* NEED_PATCH */));
};
}
});
},
"Child.vue": (module, exports, require) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const vue_1 = require("vue");
const _hoisted_1 = { class: "demo-spacing-1" };
const _hoisted_2 = {
role: "alert",
"data-testid": "local-error"
};
const _hoisted_3 = { class: "example-paragraph" };
const _hoisted_4 = {
key: 0,
class: "example-paragraph"
};
const _hoisted_5 = {
class: "example-paragraph",
role: "status",
"data-testid": "retry-result"
};
const _hoisted_6 = {
class: "example-paragraph",
"data-testid": "transport-count"
};
const vue_2 = require("vue");
const ui_1 = require("@asam-ui/ui");
exports.default = Object.assign({ name: 'AsamDataTableChildLocalErrorExample' }, {
__name: 'Child',
props: { transport: { type: Object, required: true } },
setup(__props) {
const props = __props;
const transport = props.transport;
const control = (0, vue_2.ref)();
const busy = (0, vue_2.ref)(false), result = (0, vue_2.ref)('');
const buildPageParams = () => ({});
const transform = (rows) => ({ list: rows, total: rows.length });
(0, vue_2.onMounted)(async () => {
const response = await control.value?.reload();
result.value = response?.status ?? 'cancelled';
});
async function recover(retry) {
busy.value = true;
transport.failing.value = false;
try {
const response = await retry();
result.value = response.status;
}
finally {
busy.value = false;
}
}
async function failAgain() {
busy.value = true;
transport.failing.value = true;
try {
const response = await control.value?.reload({ force: true });
result.value = response?.status ?? 'cancelled';
}
finally {
busy.value = false;
}
}
return (_ctx, _cache) => {
return ((0, vue_1.openBlock)(), (0, vue_1.createElementBlock)("section", _hoisted_1, [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamDataTable), {
ref_key: "control",
ref: control,
immediate: false,
api: (0, vue_1.unref)(transport).api,
"build-page-params": buildPageParams,
transform: transform
}, {
error: (0, vue_1.withCtx)(({ error, stale, retry }) => [
(0, vue_1.createElementVNode)("div", _hoisted_2, [
(0, vue_1.createElementVNode)("p", _hoisted_3, (0, vue_1.toDisplayString)(error.message) + "(" + (0, vue_1.toDisplayString)(error.kind) + ")", 1 /* TEXT */),
stale
? ((0, vue_1.openBlock)(), (0, vue_1.createElementBlock)("p", _hoisted_4, "已保留上次成功结果。"))
: (0, vue_1.createCommentVNode)("v-if", true),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
disabled: busy.value,
onClick: $event => (recover(retry))
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[0] || (_cache[0] = [
(0, vue_1.createTextVNode)(" 恢复并重试 ", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["disabled", "onClick"])
])
]),
default: (0, vue_1.withCtx)(() => [
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamTableColumn), {
prop: "label",
label: "状态"
})
]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["api"]),
(0, vue_1.createVNode)((0, vue_1.unref)(ui_1.AsamButton), {
type: "default",
"native-type": "button",
disabled: busy.value,
onClick: failAgain
}, {
default: (0, vue_1.withCtx)(() => [...(_cache[1] || (_cache[1] = [
(0, vue_1.createTextVNode)("模拟更新失败", -1 /* CACHED */)
]))]),
_: 1 /* STABLE */
}, 8 /* PROPS */, ["disabled"]),
(0, vue_1.createElementVNode)("p", _hoisted_5, (0, vue_1.toDisplayString)(result.value), 1 /* TEXT */),
(0, vue_1.createElementVNode)("p", _hoisted_6, " 请求 " + (0, vue_1.toDisplayString)((0, vue_1.unref)(transport).calls.value) + " 次,字典刷新 " + (0, vue_1.toDisplayString)((0, vue_1.unref)(transport).refreshes.value) + " 次 ", 1 /* TEXT */)
]));
};
}
});
exports.default.__scopeId = "data-v-a80df784";
},
"transport.js": (module, exports, require) => {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createTransport = createTransport;
const vue_1 = require("vue");
function createTransport() {
const failing = (0, vue_1.ref)(true), calls = (0, vue_1.ref)(0), refreshes = (0, vue_1.ref)(0);
const api = async () => {
calls.value++;
if (failing.value)
throw Error('演示请求暂不可用');
return [
{ id: 1, value: 'ready', label: '准备' },
{ id: 2, value: 'running', label: '进行' }
];
};
const provider = {
dict: {
get: async (_type, context) => {
context.signal.throwIfAborted();
return api();
},
refresh: async (context) => {
context.signal.throwIfAborted();
refreshes.value++;
}
}
};
return { failing, calls, refreshes, api, provider };
}
},
};
const __dependencies = {"Demo.vue":{"./Child.vue":"Child.vue","./transport.js":"transport.js"},"Child.vue":{},"transport.js":{}};
const __cache = Object.create(null);
function __require(id) {
if (__external[id]) return __external[id];
if (__cache[id]) return __cache[id].exports;
const module = { exports: {} };
__cache[id] = module;
__modules[id](module, module.exports, (dependency) => __require(__dependencies[id][dependency] || dependency));
return module.exports;
}
const StandaloneDemo = __require('Demo.vue').default;
</script>
<style>
.demo-spacing-1[data-v-a80df784] {
display: grid;
gap: var(--asam-space-md);
}
p[data-v-a80df784]:where(.example-paragraph) {
margin-block: 16px;
line-height: 1.8;
overflow-wrap: anywhere;
}
</style>
API
属性 AsamDataTableProps
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
data | — | undefined | — |
rowKey | — | string | ((row: T) => AsamRowKey) | undefined | — |
cache | — | false | AsamOptionsCache | undefined | false |
query | — | Q | undefined | — |
queryKey | — | ((query: Q) => string) | undefined | — |
immediate | — | boolean | undefined | true |
currentPage | — | number | undefined | — |
pageSize | — | number | undefined | — |
defaultSelectedRowKeys | — | readonly AsamRowKey[] | undefined | — |
request | — | AsamRequestConfig | undefined | — |
api | — | undefined | AsamApiRequest<P, R> | — |
buildPageParams | — | undefined | (query: Q, page: AsamPageRequest) => P | — |
buildPageRequest | — | undefined | (query: Q, page: AsamPageRequest) => AsamRequestConfig | — |
pageTarget | — | enum"data" | "params"完整类型:"data" | "params" | undefined | undefined | — |
transform | — | ((response: R) => AsamPage<T>) | ((response: R) => AsamPage<T>) | undefined | — |
currentRowKey | 以原始 ASAM 键高亮当前页行,不要求调用方使用内部编码键。 | AsamRowKey | undefined | — |
expandRowKeys | 以原始键展开当前页行;数字 1 与字符串 '1' 保持区分。 | readonly AsamRowKey[] | undefined | — |
selectable | — | boolean | undefined | false |
pageSizes | — | number[] | undefined | — |
minWidth | — | string | undefined | — |
stripe | 是否为斑马纹 table | boolean | undefined | false |
border | 是否带有纵向边框 | boolean | undefined | false |
pagination | 分页外观继承 AsamPagination;分页值和禁用状态由数据层维护。 | Omit<AsamPaginationProps, "disabled" | "total" | "currentPage" | "pageSize"> | undefined | — |
style | — | AsamDataTableProps<Q, P, R, T>["style"] | false |
className | — | string | undefined | — |
width | — | string | number | undefined | — |
size | — | enum"large" | "big" | "default" | "small"完整类型:AsamControlSize | undefined | — |
height | table 的高度。默认为自动高度。如果 height 为 number 类型,单位 px;如果 height 为 string 类型,则这个高度会设置为 Table 的 style.height 的值,Table 的高度会受控于外部样式。 | string | number | undefined | — |
maxHeight | table 的最大高度。合法的值为数字或者单位为 px 的高度。 | string | number | undefined | — |
fit | 列的宽度是否自撑开 | boolean | undefined | false |
showHeader | 是否显示表头 | boolean | undefined | false |
showSummary | 是否在表尾显示合计行 | boolean | undefined | false |
sumText | 显示摘要行第一列的文本 | string | undefined | — |
summaryMethod | 自定义的合计计算方法 | AsamTableSummaryMethod<T> | undefined | — |
rowClassName | 行的 className 的回调方法,也可以使用字符串为所有行设置一个固定的 className。 | AsamTableColumnCls<T> | undefined | — |
rowStyle | 行的 style 的回调方法,也可以使用一个固定的 Object 为所有行设置一样的 Style。 | AsamTableColumnStyle<T> | undefined | — |
cellClassName | 单元格的 className 的回调方法,也可以使用字符串为所有单元格设置一个固定的 className。 | AsamTableCellCls<T> | undefined | — |
cellStyle | 单元格的 style 的回调方法,也可以使用一个固定的 Object 为所有单元格设置一样的 Style。 | AsamTableCellStyle<T> | undefined | — |
headerRowClassName | 表头行的 className 的回调方法,也可以使用字符串为所有表头行设置一个固定的 className。 | AsamTableColumnCls<T> | undefined | — |
headerRowStyle | 表头行的 style 的回调方法,也可以使用一个固定的 Object 为所有表头行设置一样的 Style。 | AsamTableColumnStyle<T> | undefined | — |
headerCellClassName | 表头单元格的 className 的回调方法,也可以使用字符串为所有表头单元格设置一个固定的 className。 | AsamTableCellCls<T> | undefined | — |
headerCellStyle | 表头单元格的 style 的回调方法,也可以使用一个固定的 Object 为所有表头单元格设置一样的 Style。 | AsamTableCellStyle<T> | undefined | — |
highlightCurrentRow | 是否要高亮当前行 | boolean | undefined | false |
emptyText | 空数据时显示的文本内容,也可以通过 #empty 设置 | string | undefined | — |
defaultExpandAll | 是否默认展开所有行,当 Table 包含展开行存在或者为树形表格时有效 | boolean | undefined | false |
rowExpandable | 启用可展开行,当表格具有 type="expand" 列时有效 | ((row: T, index: number) => boolean) | undefined | — |
defaultSort | 默认的排序列的 prop 和顺序。它的 prop 属性指定默认的排序的列,order 指定默认排序的顺序 | AsamTableSort | undefined | — |
tooltipEffect | 溢出的 tooltip 的 effect | string | undefined | — |
tooltipOptions | 溢出提示配置,参阅提示组件参数。 | (AsamDataTableProps<Q, P, R, T>)["tooltipOptions"] | — |
spanMethod | 合并行或列的计算方法 | 查看完整类型((data: { row: T; rowIndex: number; column: AsamTableColumnCtx<T>; columnIndex: number; }) => number[] | { rowspan: number; colspan: number; } | undefined) | undefined | — |
selectOnIndeterminate | 在多选表格中,当仅有部分行被选中时,点击表头的多选框时的行为。若为 true,则选中所有行;若为 false,则取消选择所有行 | boolean | undefined | false |
indent | 展示树形数据时,树节点的缩进 | number | undefined | — |
treeProps | 渲染嵌套数据的配置选项 | (AsamDataTableProps<Q, P, R, T>)["treeProps"] | — |
lazy | 是否懒加载子节点数据 | boolean | undefined | false |
load | 加载子节点数据的函数,lazy 为 true 时生效 | ((row: T, treeNode: AsamTableTreeNode, resolve: (data: T[]) => void) => void) | undefined | — |
tableLayout | 原生自动或固定布局;追加内容和合计行顺序遵循原生表格。 | enum"auto" | "fixed"完整类型:("auto" | "fixed") | undefined | — |
scrollbarAlwaysOn | 总是显示滚动条 | boolean | undefined | false |
flexible | 确保主轴的最小尺寸,以便不超过内容 | boolean | undefined | false |
showOverflowTooltip | 是否隐藏溢出内容并在悬停单元格时显示提示,作用于全部列。 | (AsamDataTableProps<Q, P, R, T>)["showOverflowTooltip"] | false |
tooltipFormatter | 格式化单元格提示内容的函数,show-overflow-tooltip 为 true 时生效。 | AsamTableOverflowTooltipFormatter<T> | undefined | — |
appendFilterPanelTo | — | string | undefined | — |
scrollbarTabindex | — | string | number | undefined | — |
allowDragLastColumn | 是否允许拖动最后一列 | boolean | undefined | false |
preserveExpandedContent | 在折叠后是否在DOM中保留展开行内容 | boolean | undefined | false |
nativeScrollbar | 是否使用原生滚动条样式 | boolean | undefined | false |
reserveSelection | — | boolean | undefined | false |
事件 AsamDataTableEmits
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
update:currentPage | — | [value: number] | — |
update:pageSize | — | [value: number] | — |
page-change | — | [value: number] | — |
page-size-change | — | [value: number] | — |
selection-change | 以现有跨页键和行快照覆盖基础行数组。 | [selection: AsamTableSelection<T>] | — |
success | — | [data: AsamPage<T>] | — |
error | — | [error: AsamDataError] | — |
select | — | (AsamDataTableEmits<T>)["select"] | — |
select-all | — | (AsamDataTableEmits<T>)["select-all"] | — |
cell-mouse-enter | — | (AsamDataTableEmits<T>)["cell-mouse-enter"] | — |
cell-mouse-leave | — | (AsamDataTableEmits<T>)["cell-mouse-leave"] | — |
cell-dblclick | — | (AsamDataTableEmits<T>)["cell-dblclick"] | — |
cell-contextmenu | — | (AsamDataTableEmits<T>)["cell-contextmenu"] | — |
cell-click | — | (AsamDataTableEmits<T>)["cell-click"] | — |
row-dblclick | — | (AsamDataTableEmits<T>)["row-dblclick"] | — |
row-click | — | (AsamDataTableEmits<T>)["row-click"] | — |
row-contextmenu | — | (AsamDataTableEmits<T>)["row-contextmenu"] | — |
header-click | — | (AsamDataTableEmits<T>)["header-click"] | — |
header-contextmenu | — | (AsamDataTableEmits<T>)["header-contextmenu"] | — |
sort-change | — | (AsamDataTableEmits<T>)["sort-change"] | — |
filter-change | — | [filters: Record<string, string[]>] | — |
current-change | — | (AsamDataTableEmits<T>)["current-change"] | — |
header-dragend | — | (AsamDataTableEmits<T>)["header-dragend"] | — |
expand-change | — | (AsamDataTableEmits<T>)["expand-change"] | — |
scroll | — | [position: { scrollLeft: number; scrollTop: number; }] | — |
插槽 AsamDataTableSlots
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
default | — | (() => unknown) | undefined | — |
toolbar | — | ((props: { selection: AsamTableSelection<T>; loading: boolean; stale: boolean; }) => unknown) | undefined | — |
empty | — | (() => unknown) | undefined | — |
append | 原生追加插槽;布局及合计行顺序遵循固定上游版本。 | (() => unknown) | undefined | — |
error | 替换本地错误区域;重试返回现有强制重新加载的 Promise。 | (AsamDataTableSlots<T>)["error"] | — |
实例方法与状态 AsamDataTableExpose
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
clearSelection | 清空全部页面的 ASAM 选择快照,而非仅当前可见行。 | () => void | — |
getSelection | 返回跨页 ASAM 键及已知行快照。 | () => AsamTableSelection<T> | — |
toggleRowSelection | 选择当前已加载的行;过期页面拒绝新增选择。 | (row: T, selected?: boolean) => void | — |
getSelectionRows | 返回 ASAM 跨页快照中已知的行。 | () => T[] | — |
toggleAllSelection | 切换当前已加载页面的选择状态。 | () => void | — |
updateKeyChildren | 原生懒加载子节点替换接收原始键,而非内部编码键。 | (key: AsamRowKey, children: T[]) => void | — |
reload | — | (options?: { readonly force?: boolean; }) => Promise<AsamReloadResult<AsamPage<T>>> | — |
cancel | — | () => void | — |
sort | 手动排序表格。参数 prop 属性指定排序列,order 指定排序顺序。 | (prop: string, order: string) => void | — |
context | — | AsamTableContext<T> | — |
tableLayout | — | enum"auto" | "fixed"完整类型:"auto" | "fixed" | — |
allowDragLastColumn | 是否允许拖动最后一列 | boolean | — |
ns | — | (AsamDataTableExpose<T>)["ns"] | — |
layout | — | (AsamDataTableExpose<T>)["layout"] | — |
store | — | (AsamDataTableExpose<T>)["store"] | — |
columns | — | AsamTableColumnCtx<T>[] | — |
handleHeaderFooterMousewheel | — | (event: WheelEvent, delta: { pixelX: number; pixelY: number; }) => void | — |
handleMouseLeave | — | () => void | — |
tableId | — | string | — |
tableSize | — | enum"" | "large" | "default" | "small"完整类型:"" | "large" | "default" | "small" | — |
isHidden | — | boolean | — |
isEmpty | — | boolean | — |
renderExpanded | — | (AsamDataTableExpose<T>)["renderExpanded"] | — |
resizeProxyVisible | — | boolean | — |
resizeState | — | { width: null | number; height: null | number; headerHeight: null | number; } | — |
isGroup | — | boolean | — |
bodyWidth | — | string | — |
tableBodyStyles | — | { width: string; } | — |
emptyBlockStyle | — | { width: string; height: string; } | undefined | — |
debouncedUpdateLayout | — | (AsamDataTableExpose<T>)["debouncedUpdateLayout"] | — |
setCurrentRow | 用于单选表格,设定某一行为选中行,如果调用时不加参数,则会取消目前高亮行的选中状态。 | (row?: T | undefined) => void | — |
getHalfSelectionRows | 返回当前半选中的行 | () => T[] | — |
clearFilter | 传入由columnKey 组成的数组以清除指定列的过滤条件。如果没有参数,清除所有过滤器 | (columnKeys?: string[] | string) => void | — |
toggleRowExpansion | 用于可扩展的表格或树表格,如果某行被扩展,则切换。使用第二个参数,您可以直接设置该行应该被扩展或折叠。 | (row: T, expanded?: boolean) => void | — |
clearSort | 用于清空排序条件,数据会恢复成未排序的状态 | () => void | — |
doLayout | 对 Table 进行重新布局。当表格可见性变化时,您可能需要调用此方法以获得正确的布局 | () => void | — |
t | — | AsamDataTableExpose<T>["t"] | — |
setDragVisible | — | (visible: boolean) => void | — |
computedSumText | — | string | — |
computedEmptyText | — | string | — |
computedTooltipEffect | — | string | undefined | — |
computedTooltipOptions | — | (AsamDataTableExpose<T>)["computedTooltipOptions"] | — |
scrollbarViewStyle | — | { display: string; verticalAlign: string; } | — |
scrollbarStyle | — | { height: string; maxHeight?: undefined; } | { maxHeight: string; height?: undefined; } | { height?: undefined; maxHeight?: undefined; } | — |
scrollBarRef | — | (AsamDataTableExpose<T>)["scrollBarRef"] | — |
scrollTo | 滚动到一组特定坐标 | (options: ScrollToOptions | number, yCoord?: number) => void | — |
setScrollLeft | 设置水平滚动位置 | (left?: number) => void | — |
setScrollTop | 设置垂直滚动位置 | (top?: number) => void | — |
类型 AsamPagedInput
| 属性名 | 说明 | 类型 | 默认值 |
|---|---|---|---|
data | — | undefined | — |
rowKey | — | string | ((row: T) => AsamRowKey) | undefined | — |
cache | — | false | AsamOptionsCache | undefined | — |
query | — | Q | undefined | — |
queryKey | — | ((query: Q) => string) | undefined | — |
immediate | — | boolean | undefined | — |
currentPage | — | number | undefined | — |
pageSize | — | number | undefined | — |
defaultSelectedRowKeys | — | readonly AsamRowKey[] | undefined | — |
api | — | AsamApiRequest<P, R> | undefined | — |
buildPageParams | — | (query: Q, page: AsamPageRequest) => P | undefined | — |
request | — | undefined | AsamRequestConfig | — |
buildPageRequest | — | undefined | (query: Q, page: AsamPageRequest) => AsamRequestConfig | — |
pageTarget | — | enum"data" | "params"完整类型:undefined | "data" | "params" | undefined | — |
transform | — | ((response: R) => AsamPage<T>) | ((response: R) => AsamPage<T>) | undefined | — |
用法与契约补充说明 ​
请求与分页覆盖 ​
api 与 request 互斥;API 分支必须提供 buildPageParams,URL 分支使用 buildPageRequest 或 pageTarget。具体 Row 类型必须提供 transform,将响应转换为 AsamPage
选择、事件与实例 ​
selection-change 返回 { keys, rows } 的跨页快照,覆盖基础表格的行数组签名。getSelection 返回同样的快照,getSelectionRows 返回已解析行,clearSelection 清空跨页选择。toolbar 插槽接收 selection、loading、stale;使用 selection.keys 统计选择,不能把它当作行数组。基础表格的适用事件、插槽与实例仍以本站基础 API 为准。
完整表格实例与树半选 ​
getHalfSelectionRows 返回当前原生树选择列中的半选行。下例使用 AsamTableColumn 的 selection 类型勾选一个子节点,读取父节点的半选状态。该原生选择列与 selectable 提供的 ASAM 跨页选择分别维护:getSelection、getSelectionRows、clearSelection、toggleRowSelection 和 toggleAllSelection 仍遵守本页跨页快照覆盖,不把原生半选数组伪装成跨页选中键。
失败和重试 ​
error 返回 AsamDataError,success 返回 AsamPage
跨页选择、陈旧恢复与取消 ​
完整示例返回严格 list/total 分页结构,buildPageParams 明确合并查询和分页输入,rowKey 从行生成稳定标识。selection-change 和 getSelection/getSelectionRows 返回跨页快照;原生 row-click、sort-change 和列插槽保持展示语义,sort() 仅排序当前原生表格展示。请求失败保留旧成功页、显示原页数和 total,陈旧时禁止新选择并隐藏分页;恢复后重新启用。cancel 返回 cancelled,不能当作成功。toolbar 提供 loading/stale 与快照,append/empty 交给基础表格。示例接口在本地运行。
跨页选择、陈旧恢复与取消 ​
完整示例同时记录七个数据入口事件:update:currentPage/page-change 和 update:pageSize/page-size-change 保持数字;分页每页数量入口可在2/4之间切换。selection-change 记录跨页 keys/rows 快照,success 保留严格 list/total,error 保留结构化错误种类与消息。原生 row/sort 事件仍单独记录;不能将 ASAM 选择快照当作原生行数组。
独立分页状态与完整生命周期 ​
useAsamPagedData(config) 可供自定义表格消费同一分页协议。config.source 接受普通配置、
ref、computed 或同步 getter;显式 runtime 与 app 的 provideAsamData(app, provider)
均可提供请求环境。immediate: false 仅准备参数与键,调用 reload() 后才执行请求。
生命周期示例中的“完整分页状态与生命周期”包含 TS/JS 两种同源实现,验证成功、空结果、
失败、取消、参数变化、跨页选择、缓存和实际 app 注入与卸载。
独立分页状态与完整生命周期 ​
AsamApiPageSource<Q, P, R, T> 明确声明 api、query、分页参数 builder 与 transform。
AsamPagedInput<Q, P, R, T> 进一步区分 API、默认 URL 合并和自定义 URL builder 三种合法
组合。AsamPagedInputBase 描述共有字段,消费完整配置时应使用 AsamPagedInput 约束组合。
AsamPage<T>.list、total 与 AsamTableSelection<T>.keys、rows 为只读数据;数字 1
与字符串 '1' 是两个独立行键。
独立分页状态与完整生命周期 ​
响应总数为零时页码归一到 1;页码超出末页时最多纠正并请求一次,第二次仍越界返回 response 错误。query 或源身份变化会取消旧请求、清空选择并从第一页开始;等价 wrapper 与受控回显 保持当前请求。runtime 的 scope/epoch 失效会取消请求,清空数据、成功页与选择并淘汰缓存。 卸载所属作用域后 reload 返回 cancelled。
当前页的原生展示用途 ​
完整示例在同一数据入口组合固定表头/列、两层分组表头、列模板/自定义表头、序号、当前行高亮、原生排序与列筛选、展开内容、汇总、状态样式、状态单元格合并及溢出提示。sort/clearSort、clearFilter、toggleRowExpansion、setCurrentRow 操作当前原生表格,事件保留 Row;它们不会替调用方改变服务器 query 或发起新的分页请求。此例只有三个本地行,合并规则针对相邻的前两行;动态分组、排序和筛选组合时应由业务按实际可见行计算 spanMethod,不能照搬固定行号。汇总使用当前展示行,不表示服务器全量总计。
当前页的原生展示用途 ​
高度按钮演示 maxHeight=150 的滚动区和 height=310/maxHeight=310 的固定高度;布局按钮切换 tableLayout 的 fixed/auto,请求计数用于确认这些展示操作不重新请求。固定原生 2.14.7 将已设置的 maxHeight 改为 undefined 时仍保留原内联上限,因此本例恢复时显式设置相同的高度和上限。原生组件在 maxHeight 有值时固定使用 fixed 布局,因此 auto 分支同时不提供 maxHeight。
当前页的原生展示用途 ​
回调按钮分别演示汇总与 tooltip 的文本/VNode 返回值、spanMethod 的数组/对象跨度,以及 rowClassName 的字符串/泛型函数。spanMethod 返回 undefined 时使用原生默认跨度,可恢复全部默认单元格,再恢复合并。showSummary 可动态关闭并恢复;高度可使用数字或 CSS 字符串,未设置 height 时由 maxHeight 约束实际滚动区。clearFilter 接受省略参数、单个键或键数组,固定原生源码的签名比原文档表格仅列出的数组更完整。setCurrentRow 省略行时清除高亮,toggleRowExpansion 省略第二参数时切换展开;传入具体 Row 保留行类型。这些本地展示操作保持同一次分页请求,不代表服务器筛选或排序。
原生树加载与行键边界 ​
currentRowKey、expandRowKeys 和 updateKeyChildren 使用 rowKey 生成的原始 ASAM 键;数字 1 与字符串 "1" 分别定位,不要求调用方填写内部类型前缀。updateKeyChildren 遵守原生条件:lazy=true、已加载的父节点及新子行;它只更新当前原生树展示,不重写分页来源或跨页选择快照。Table 的原生 lazy/load 行加载与 Remote/Dict Cascader、TreeSelect 的完整树来源不同;后两者仍排除逐节点数据加载。setCurrentRow、toggleRowExpansion 使用当前实例的 Row;示例从 success.list/列插槽取得已规范化行,不能假定原 API 对象与实例行同一引用。
行键读取的内部边界 ​
应用通过公开 rowKey 字段名或同步回调配置行键,由分页数据与表格完成读取和校验。asamRowKey 的函数声明只通过根入口的 export type * 提供类型查询,不是根入口可调用的函数;import type 配合 typeof 可以描述其签名,不能据此发出运行时导入。内部读取要求自有字符串键或有限数字,拒绝异步/thenable 回调结果;这项内部校验不能代替应用正确配置 rowKey。包未开放内部 paging 模块的深路径导入。
行键读取的内部边界 ​
生命周期完整示例还可展开“公开分页行键边界”。通过真实 useAsamPagedData 验证自有空字符串、零、负数、有限小数和同步字符串/数字回调;布尔值、null、对象、缺失或继承键、NaN、Infinity 及重复键产生 response 错误,不发布成功页。async Promise、返回拒绝 Promise 及成功/拒绝 thenable 都必须被同步回调守卫拒绝,拒绝 Promise 不额外泄漏未处理异常。当前页选择的 toggleRowSelection/selectPage 使用相同守卫;非法异步结果不改变选择,恢复同步回调后可以重新选中。运行期非法输入仅用于展示拒绝边界,正常 TS 应按公开同步签名提供 rowKey。
行键读取的内部边界 ​
append 与 empty 都是零参数插槽,经 DataTable → AsamTable → 原生 Table 转交,不接收行、loading 或跨页 selection。append 位于表体行之后;empty 在没有当前可展示行时显示,覆盖原生空文本。默认插槽直接放置列,行作用域由 AsamTableColumn 提供。
行键读取的内部边界 ​
固定原生 2.14.7 的 append 顺序随布局变化:fixed 布局位于汇总之前,auto 布局位于表体内的汇总之后。DataTable 与基础表格都通过原生公开插槽委托,不统一重排这一原生差异。生命周期示例保留布局切换,并覆盖空页、成员页、失败保留旧行、恢复空页及移除/恢复自定义插槽。旧候选曾通过的统一顺序依赖私有派生补丁,其原件仅作旧契约历史,不证明当前原生透传契约合格。
局部请求错误插槽 ​
可选 error 插槽仅在当前请求错误存在时调用,接收只读 { error, stale, retry }。
error 与本实例 error 事件中的 AsamDataError 为同一对象,保留 cause、kind、
message 与 notified;渲染插槽不会再次通知 provider。stale 表示保留的数据已过期。
更多参数与事件说明 ​
独立分页状态与完整生命周期 ​
实例控制按钮通过 success.list 中的当前行调用 toggleRowSelection(row, true/false),省略第二参数时切换这一行;toggleAllSelection() 只切换当前已加载页,保留其他页的选择。读取按钮分别调用 getSelection() 和 getSelectionRows(),比较跨页键与已解析行。陈旧页拒绝新增选择和当前页全选切换,仍允许取消已有选择及 clearSelection() 全局清空;恢复后重新启用新增选择。cancel() 只取消请求,保留调用方可见的原成功页和选择,reload 的 cancelled 返回值单独处理。
继承能力
本入口的独有 API 见上表,基础能力见本站对应页面。
关联组件
参数与事件补充说明
组合契约按分支生效;请同时查看公开类型。
参数与事件说明
data
必填:否;声明来源:数据 / provider 协议。
rowKey
必填:否;声明来源:数据 / provider 协议。
rowKey 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(row: T): AsamRowKey
cache
必填:否;声明来源:数据 / provider 协议。
cache 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false
cache.ttlMs:number— 数据 / provider 协议cache.key:((requestInput: unknown) => string) | undefined— 数据 / provider 协议事件参数:
(requestInput: unknown): string
默认值与来源
false(隐式 Boolean 默认;已安装组件)query
必填:否;声明来源:数据 / provider 协议。
queryKey
必填:否;声明来源:数据 / provider 协议。
queryKey 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(query: Q): string
immediate
必填:否;声明来源:数据 / provider 协议。
immediate 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
true(源码表达式;ASAM 声明)currentPage
必填:否;声明来源:数据 / provider 协议。
pageSize
必填:否;声明来源:数据 / provider 协议。
defaultSelectedRowKeys
必填:否;声明来源:数据 / provider 协议。
request
必填:否;声明来源:数据 / provider 协议。
request 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
request.url:string— 数据 / provider 协议request.method:"delete" | "get" | "post" | "put" | undefined— 数据 / provider 协议枚举:
"delete"/"get"/"post"/"put"request.params:Readonly<Record<string, unknown>> | undefined— 数据 / provider 协议request.data:unknown— 数据 / provider 协议
api
必填:否;声明来源:数据 / provider 协议。
api 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(params: P, context: AsamRequestContext): Promise<R>
buildPageParams
必填:否;声明来源:数据 / provider 协议。
buildPageParams 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(query: Q, page: AsamPageRequest): P
buildPageRequest
必填:否;声明来源:数据 / provider 协议。
buildPageRequest 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(query: Q, page: AsamPageRequest): AsamRequestConfig
pageTarget
必填:否;声明来源:数据 / provider 协议。
pageTarget 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"data" / "params"
transform
必填:否;声明来源:数据 / provider 协议。
transform 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(response: R): AsamPage<T>
事件参数:(response: R): AsamPage<T>
currentRowKey
必填:否;声明来源:ASAM 声明。
expandRowKeys
必填:否;声明来源:ASAM 声明。
selectable
必填:否;声明来源:ASAM 声明。
selectable 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)pageSizes
必填:否;声明来源:ASAM 声明。
minWidth
必填:否;声明来源:ASAM 声明。
stripe
必填:否;声明来源:ASAM 声明。
stripe 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)border
必填:否;声明来源:ASAM 声明。
border 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)pagination
必填:否;声明来源:ASAM 声明。
pagination 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
pagination.small:boolean | undefined— 上游声明枚举:
false/truepagination.popperClass:PublicMember<NonNullable<Omit<AsamPaginationProps, "disabled" | "total" | "currentPage" | "pageSize"> | undefined>, "popperClass">— 上游声明枚举:
falsepagination.popperStyle:AsamDataTableProps<Q, P, R, T>["style"]— 上游声明枚举:
false嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
pagination.teleported:PublicMember<NonNullable<Omit<AsamPaginationProps, "disabled" | "total" | "currentPage" | "pageSize"> | undefined>, "teleported">— 上游声明枚举:
false/truepagination.size:AsamControlSize | undefined— ASAM 声明枚举:
"large"/"big"/"default"/"small"pagination.layout:string | undefined— 上游声明pagination.defaultPageSize:number | undefined— 上游声明pagination.pageCount:number | undefined— 上游声明pagination.pagerCount:number | undefined— 上游声明pagination.defaultCurrentPage:number | undefined— 上游声明pagination.pageSizes:number[] | undefined— 上游声明pagination.prevText:string | undefined— 上游声明pagination.prevIcon:PublicMember<NonNullable<Omit<AsamPaginationProps, "disabled" | "total" | "currentPage" | "pageSize"> | undefined>, "prevIcon">— 上游声明事件参数:
(props: any, ctx: Parameters<Extract<NonNullable<PublicMember<NonNullable<Omit<AsamPaginationProps, "disabled" | "total" | "currentPage" | "pageSize"> | undefined>, "prevIcon">>, (...args: never[]) => unknown>>[1]): anypagination.prevIcon.__isFragment:undefined— 依赖声明pagination.prevIcon.__isTeleport:undefined— 依赖声明pagination.prevIcon.__isSuspense:undefined— 依赖声明
pagination.nextText:string | undefined— 上游声明pagination.nextIcon:PublicMember<NonNullable<Omit<AsamPaginationProps, "disabled" | "total" | "currentPage" | "pageSize"> | undefined>, "nextIcon">— 上游声明事件参数:
(props: any, ctx: Parameters<Extract<NonNullable<PublicMember<NonNullable<Omit<AsamPaginationProps, "disabled" | "total" | "currentPage" | "pageSize"> | undefined>, "nextIcon">>, (...args: never[]) => unknown>>[1]): anypagination.nextIcon.__isFragment:undefined— 依赖声明pagination.nextIcon.__isTeleport:undefined— 依赖声明pagination.nextIcon.__isSuspense:undefined— 依赖声明
pagination.background:boolean | undefined— 上游声明枚举:
false/truepagination.hideOnSinglePage:boolean | undefined— 上游声明枚举:
false/truepagination.appendSizeTo:string | undefined— 上游声明
style
必填:否;声明来源:上游声明。
style 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游源码工厂(未执行):() => ({})(2.14.7;源码事实,未作为 ASAM 运行实测)
className
必填:否;声明来源:上游声明。
默认值与来源
未知 / 未声明固定上游源码默认表达式:''(2.14.7;源码事实,未作为 ASAM 运行实测)
width
必填:否;声明来源:上游声明。
size
必填:否;声明来源:ASAM 声明。
size 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"large" / "big" / "default" / "small"
height
必填:否;声明来源:上游声明。
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
maxHeight
必填:否;声明来源:上游声明。
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
fit
必填:否;声明来源:上游声明。
fit 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:true(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码默认表达式:true(2.14.7;源码事实,未作为 ASAM 运行实测)
showHeader
必填:否;声明来源:上游声明。
showHeader 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:true(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码默认表达式:true(2.14.7;源码事实,未作为 ASAM 运行实测)
showSummary
必填:否;声明来源:上游声明。
showSummary 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:false(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
sumText
必填:否;声明来源:上游声明。
默认值与来源
未知 / 未声明固定上游文档默认:Sum(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
summaryMethod
必填:否;声明来源:上游声明。
summaryMethod 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(data: { columns: AsamTableColumnCtx<T>[]; data: T[]; }): (string | VNode<import("vue").RendererNode, import("vue").RendererElement, { [key: string]: any; }>)[]
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
rowClassName
必填:否;声明来源:上游声明。
rowClassName 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(data: { row: T; rowIndex: number; }): string
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
rowStyle
必填:否;声明来源:上游声明。
rowStyle 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(data: { row: T; rowIndex: number /** Override the base row array with the existing cross-page keys/rows snapshot. */; }): CSSProperties
嵌套展开边界:大型实例类型;完整类型见公开类型视图。
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
cellClassName
必填:否;声明来源:上游声明。
cellClassName 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(data: { row: T; rowIndex: number; column: AsamTableColumnCtx<T>; columnIndex: number; }): string
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
cellStyle
必填:否;声明来源:上游声明。
cellStyle 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(data: { row: T; rowIndex: number; column: AsamTableColumnCtx<T>; columnIndex: number; }): CSSProperties
嵌套展开边界:大型实例类型;完整类型见公开类型视图。
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
headerRowClassName
必填:否;声明来源:上游声明。
headerRowClassName 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(data: { row: T; rowIndex: number; }): string
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
headerRowStyle
必填:否;声明来源:上游声明。
headerRowStyle 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(data: { row: T; rowIndex: number /** Override the base row array with the existing cross-page keys/rows snapshot. */; }): CSSProperties
嵌套展开边界:大型实例类型;完整类型见公开类型视图。
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
headerCellClassName
必填:否;声明来源:上游声明。
headerCellClassName 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(data: { row: T; rowIndex: number; column: AsamTableColumnCtx<T>; columnIndex: number; }): string
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
headerCellStyle
必填:否;声明来源:上游声明。
headerCellStyle 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(data: { row: T; rowIndex: number; column: AsamTableColumnCtx<T>; columnIndex: number; }): CSSProperties
嵌套展开边界:大型实例类型;完整类型见公开类型视图。
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
highlightCurrentRow
必填:否;声明来源:上游声明。
highlightCurrentRow 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:false(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
emptyText
必填:否;声明来源:上游声明。
默认值与来源
未知 / 未声明固定上游文档默认:No Data(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
defaultExpandAll
必填:否;声明来源:上游声明。
defaultExpandAll 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:false(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
rowExpandable
必填:否;声明来源:上游声明。
rowExpandable 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(row: T, index: number): boolean
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码未声明 default(2.14.7;源码事实,未作为 ASAM 运行实测)
defaultSort
必填:否;声明来源:上游声明。
defaultSort 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
defaultSort.prop:string— 上游声明用作初始排序列的属性。
固定上游文档默认:
未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)defaultSort.order:PublicMember<NonNullable<AsamTableSort | undefined>, "order">— 上游声明初始排序方向;只有提供 prop 且省略 order 时,默认使用升序。
固定上游文档默认:
ascending;条件:已提供 prop,且省略 order。(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)枚举:
"ascending"/"descending"defaultSort.init:any— 上游声明defaultSort.silent:any— 上游声明
默认值与来源
未知 / 未声明固定上游文档默认:if `prop` is set, and `order` is not set, then `order` is default to ascending(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
tooltipEffect
必填:否;声明来源:上游声明。
默认值与来源
未知 / 未声明固定上游文档默认:dark(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
tooltipOptions
必填:否;声明来源:上游声明。
tooltipOptions 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
嵌套展开边界:大型实例类型;完整类型见公开类型视图。
默认值与来源
未知 / 未声明固定上游文档默认:^[object]`{ enterable: true, placement: 'top', showArrow: true, hideAfter: 200, popperOptions: { strategy: 'fixed' } }`(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
spanMethod
必填:否;声明来源:上游声明。
spanMethod 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(data: { row: T; rowIndex: number; column: AsamTableColumnCtx<T>; columnIndex: number; }): number[] | { rowspan: number; colspan: number; } | undefined
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
selectOnIndeterminate
必填:否;声明来源:上游声明。
selectOnIndeterminate 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:true(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码默认表达式:true(2.14.7;源码事实,未作为 ASAM 运行实测)
indent
必填:否;声明来源:上游声明。
默认值与来源
未知 / 未声明固定上游文档默认:16(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码默认表达式:16(2.14.7;源码事实,未作为 ASAM 运行实测)
treeProps
必填:否;声明来源:上游声明。
treeProps 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
treeProps.hasChildren:string | undefined— 上游声明表示存在懒加载子项的行字段名。
固定上游文档默认:
未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)固定上游源码工厂中的字面字段(未执行):
'hasChildren'(2.14.7;源码事实,未作为 ASAM 运行实测)treeProps.children:string | undefined— 上游声明包含嵌套子数据的行字段名。
固定上游文档默认:
未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)固定上游源码工厂中的字面字段(未执行):
'children'(2.14.7;源码事实,未作为 ASAM 运行实测)treeProps.checkStrictly:boolean | undefined— 上游声明为 true 时父子选择相互独立;为 false 时父子选择关联。
固定上游文档默认:
未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)固定上游源码工厂中的字面字段(未执行):
false(2.14.7;源码事实,未作为 ASAM 运行实测)枚举:
false/true
默认值与来源
未知 / 未声明固定上游文档默认:^[object]`{ hasChildren: 'hasChildren', children: 'children', checkStrictly: false }`(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码工厂(未执行):() => {
return {
hasChildren: 'hasChildren',
children: 'children',
checkStrictly: false,
}
}(2.14.7;源码事实,未作为 ASAM 运行实测)
lazy
必填:否;声明来源:上游声明。
lazy 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:false(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
load
必填:否;声明来源:上游声明。
load 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(row: T, treeNode: AsamTableTreeNode, resolve: (data: T[]) => void): void
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
tableLayout
必填:否;声明来源:ASAM 声明。
tableLayout 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"auto" / "fixed"
默认值与来源
未知 / 未声明固定上游文档默认:fixed(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码默认表达式:'fixed'(2.14.7;源码事实,未作为 ASAM 运行实测)
scrollbarAlwaysOn
必填:否;声明来源:上游声明。
scrollbarAlwaysOn 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:false(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
flexible
必填:否;声明来源:上游声明。
flexible 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:false(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
showOverflowTooltip
必填:否;声明来源:上游声明。
showOverflowTooltip 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
嵌套展开边界:大型实例类型;完整类型见公开类型视图。
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码默认表达式:undefined(2.14.7;源码事实,未作为 ASAM 运行实测)
tooltipFormatter
必填:否;声明来源:上游声明。
tooltipFormatter 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(data: { row: T; column: AsamTableColumnCtx<T>; cellValue: any; }): string | VNode<import("vue").RendererNode, import("vue").RendererElement, { [key: string]: any; }>
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
appendFilterPanelTo
必填:否;声明来源:上游声明。
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
scrollbarTabindex
必填:否;声明来源:上游声明。
默认值与来源
未知 / 未声明固定上游文档默认:未声明(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
固定上游源码默认表达式:undefined(2.14.7;源码事实,未作为 ASAM 运行实测)
allowDragLastColumn
必填:否;声明来源:ASAM 声明。
allowDragLastColumn 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)preserveExpandedContent
必填:否;声明来源:ASAM 声明。
preserveExpandedContent 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)nativeScrollbar
必填:否;声明来源:上游声明。
nativeScrollbar 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)固定上游文档默认:false(2.14.7;文档事实,实际默认以 ASAM 源码 / 安装包为准)
reserveSelection
必填:否;声明来源:ASAM 声明。
reserveSelection 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
默认值与来源
false(隐式 Boolean 默认;已安装组件)AsamDataTableProps 公开类型视图
import type { Attrs, VNode, CSSProperties } from 'vue';
import type { AsamDataTableProps, AsamRecord, AsamRowKey, AsamOptionsCache, AsamRequestConfig, AsamPage, AsamPaginationProps, AsamControlSize, AsamTableSummaryMethod, AsamTableColumnCtx, AsamTableColumnCls, AsamTableColumnStyle, AsamTableCellCls, AsamTableCellStyle, AsamTableSort, AsamTableTreeNode, AsamTableOverflowTooltipFormatter, AsamApiRequest, AsamRequestContext, AsamPageRequest } from '@asam-ui/ui';
type PublicMember<T, K extends PropertyKey> = T extends unknown ? K extends keyof T ? T[K] : never : never;
// AsamDataTableProps
type PublicContract<Q = AsamRecord, P = unknown, R = unknown, T extends object = AsamRecord> = AsamDataTableProps<Q, P, R, T>;
参数与事件说明
update:currentPage
必填:是;声明来源:ASAM 声明。
update:pageSize
必填:是;声明来源:ASAM 声明。
page-change
必填:是;声明来源:ASAM 声明。
page-size-change
必填:是;声明来源:ASAM 声明。
selection-change
必填:是;声明来源:ASAM 声明。
success
必填:是;声明来源:ASAM 声明。
error
必填:是;声明来源:ASAM 声明。
select
必填:是;声明来源:ASAM 声明。
select-all
必填:是;声明来源:ASAM 声明。
cell-mouse-enter
必填:是;声明来源:ASAM 声明。
cell-mouse-leave
必填:是;声明来源:ASAM 声明。
cell-dblclick
必填:是;声明来源:ASAM 声明。
cell-contextmenu
必填:是;声明来源:ASAM 声明。
cell-click
必填:是;声明来源:ASAM 声明。
row-dblclick
必填:是;声明来源:ASAM 声明。
row-click
必填:是;声明来源:ASAM 声明。
row-contextmenu
必填:是;声明来源:ASAM 声明。
header-click
必填:是;声明来源:ASAM 声明。
header-contextmenu
必填:是;声明来源:ASAM 声明。
sort-change
必填:是;声明来源:ASAM 声明。
filter-change
必填:是;声明来源:ASAM 声明。
current-change
必填:是;声明来源:ASAM 声明。
header-dragend
必填:是;声明来源:ASAM 声明。
expand-change
必填:是;声明来源:ASAM 声明。
scroll
必填:是;声明来源:ASAM 声明。
AsamDataTableEmits 公开类型视图
import type { AsamDataTableEmits, AsamTableSelection, AsamPage, AsamDataError, AsamTableColumnCtx } from '@asam-ui/ui';
// AsamDataTableEmits
type PublicContract<T extends object> = {
"update:currentPage": [value: number];
"update:pageSize": [value: number];
"page-change": [value: number];
"page-size-change": [value: number];
"selection-change": [selection: AsamTableSelection<T>];
"success": [data: AsamPage<T>];
"error": [error: AsamDataError];
"select": (AsamDataTableEmits<T>)["select"];
"select-all": (AsamDataTableEmits<T>)["select-all"];
"cell-mouse-enter": (AsamDataTableEmits<T>)["cell-mouse-enter"];
"cell-mouse-leave": (AsamDataTableEmits<T>)["cell-mouse-leave"];
"cell-dblclick": (AsamDataTableEmits<T>)["cell-dblclick"];
"cell-contextmenu": (AsamDataTableEmits<T>)["cell-contextmenu"];
"cell-click": (AsamDataTableEmits<T>)["cell-click"];
"row-dblclick": (AsamDataTableEmits<T>)["row-dblclick"];
"row-click": (AsamDataTableEmits<T>)["row-click"];
"row-contextmenu": (AsamDataTableEmits<T>)["row-contextmenu"];
"header-click": (AsamDataTableEmits<T>)["header-click"];
"header-contextmenu": (AsamDataTableEmits<T>)["header-contextmenu"];
"sort-change": (AsamDataTableEmits<T>)["sort-change"];
"filter-change": [filters: Record<string, string[]>];
"current-change": (AsamDataTableEmits<T>)["current-change"];
"header-dragend": (AsamDataTableEmits<T>)["header-dragend"];
"expand-change": (AsamDataTableEmits<T>)["expand-change"];
"scroll": [position: { scrollLeft: number; scrollTop: number; }];
};
参数与事件说明
default
必填:否;声明来源:ASAM 声明。
default 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): unknown
toolbar
必填:否;声明来源:ASAM 声明。
toolbar 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(props: { selection: AsamTableSelection<T>; loading: boolean; stale: boolean; }): unknown
empty
必填:否;声明来源:ASAM 声明。
empty 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): unknown
append
必填:否;声明来源:ASAM 声明。
append 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): unknown
error
必填:否;声明来源:数据 / provider 协议。
error 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(scope: Parameters<Extract<NonNullable<(AsamDataTableSlots<T>)["error"]>, (...args: never[]) => unknown>>[0]): unknown
AsamDataTableSlots 公开类型视图
import type { AsamDataTableSlots, AsamTableSelection, AsamDataError, AsamPage, AsamReloadResult } from '@asam-ui/ui';
// AsamDataTableSlots
type PublicContract<T extends object> = {
"default"?: (() => unknown) | undefined;
"toolbar"?: ((props: { selection: AsamTableSelection<T>; loading: boolean; stale: boolean; }) => unknown) | undefined;
"empty"?: (() => unknown) | undefined;
"append"?: (() => unknown) | undefined;
"error"?: (AsamDataTableSlots<T>)["error"];
};
参数与事件说明
clearSelection
必填:是;声明来源:ASAM 声明。
clearSelection 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): void
getSelection
必填:是;声明来源:ASAM 声明。
getSelection 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): AsamTableSelection<T>
toggleRowSelection
必填:是;声明来源:ASAM 声明。
toggleRowSelection 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(row: T, selected?: boolean | undefined): void
getSelectionRows
必填:是;声明来源:ASAM 声明。
getSelectionRows 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): T[]
toggleAllSelection
必填:是;声明来源:ASAM 声明。
toggleAllSelection 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): void
updateKeyChildren
必填:是;声明来源:ASAM 声明。
updateKeyChildren 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(key: AsamRowKey, children: T[]): void
reload
必填:是;声明来源:数据 / provider 协议。
reload 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(options?: { readonly force?: boolean; } | undefined): Promise<AsamReloadResult<AsamPage<T>>>
cancel
必填:是;声明来源:数据 / provider 协议。
cancel 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): void
sort
必填:是;声明来源:上游声明。
sort 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(prop: string, order: string): void
context
必填:是;声明来源:上游声明。
context 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
context.$ready:boolean— 上游声明枚举:
false/truecontext.hoverState:(NonNullable<AsamTableContext<T>>)["hoverState"]— 上游声明context.hoverState.cell:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.hoverState.column:AsamTableColumnCtx<T>— 上游声明嵌套展开边界:大型实例类型;完整类型见公开类型视图。
context.hoverState.row:T— 上游声明
context.renderExpanded:(NonNullable<AsamTableContext<T>>)["renderExpanded"]— 上游声明事件参数:
(__0: Parameters<Extract<NonNullable<(NonNullable<AsamTableContext<T>>)["renderExpanded"]>, (...args: never[]) => unknown>>[0]): VNode<import("vue").RendererNode, import("vue").RendererElement, { [key: string]: any; }>[] | undefinedcontext.store:(NonNullable<AsamTableContext<T>>)["store"]— 上游声明嵌套展开边界:大型实例类型;完整类型见公开类型视图。
context.layout:AsamDataTableExpose<T>["layout"]— 上游声明context.layout.observers:(NonNullable<(NonNullable<AsamTableContext<T>>)["layout"]>)["observers"]— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.table:AsamTableContext<T>— 上游声明嵌套展开边界:递归引用;完整类型见公开类型视图。
context.layout.store:(NonNullable<(NonNullable<AsamTableContext<T>>)["layout"]>)["store"]— 上游声明嵌套展开边界:大型实例类型;完整类型见公开类型视图。
context.layout.columns:AsamTableColumnCtx<T>[]— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.fit:boolean— 上游声明枚举:
false/truecontext.layout.showHeader:boolean— 上游声明枚举:
false/truecontext.layout.heightMap:Record<string, string | number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.height:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.scrollX:Ref<boolean, boolean>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.scrollY:Ref<boolean, boolean>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.bodyWidth:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.fixedWidth:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.rightFixedWidth:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.tableHeight:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.headerHeight:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.appendHeight:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.footerHeight:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.layout.gutterWidth:number— 上游声明context.layout.updateScrollY:() => boolean— 上游声明事件参数:
(): booleancontext.layout.setHeight:(value: string | number | null, prop?: string) => void— 上游声明事件参数:
(value: string | number | null, prop?: string | undefined): voidcontext.layout.setMaxHeight:(value: string | number | null) => void— 上游声明事件参数:
(value: string | number | null): voidcontext.layout.getFlattenColumns:() => AsamTableColumnCtx<T>[]— 上游声明事件参数:
(): AsamTableColumnCtx<T>[]context.layout.updateElsHeight:() => void— 上游声明事件参数:
(): voidcontext.layout.headerDisplayNone:(elm: HTMLElement) => boolean— 上游声明事件参数:
(elm: HTMLElement): booleancontext.layout.updateColumnsWidth:() => void— 上游声明事件参数:
(): voidcontext.layout.addObserver:(NonNullable<(NonNullable<AsamTableContext<T>>)["layout"]>)["addObserver"]— 上游声明事件参数:
(observer: Parameters<Extract<NonNullable<(NonNullable<(NonNullable<AsamTableContext<T>>)["layout"]>)["addObserver"]>, (...args: never[]) => unknown>>[0]): voidcontext.layout.removeObserver:(NonNullable<(NonNullable<AsamTableContext<T>>)["layout"]>)["removeObserver"]— 上游声明事件参数:
(observer: Parameters<Extract<NonNullable<(NonNullable<(NonNullable<AsamTableContext<T>>)["layout"]>)["removeObserver"]>, (...args: never[]) => unknown>>[0]): voidcontext.layout.notifyObservers:(event: string) => void— 上游声明事件参数:
(event: string): void
context.refs:AsamTableRefs— 上游声明context.refs.tableWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.refs.headerWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.refs.footerWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.refs.fixedBodyWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.refs.rightFixedBodyWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.refs.bodyWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.refs.appendWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.tableId:string— 上游声明context.state:(NonNullable<AsamTableContext<T>>)["state"]— 上游声明context.state.isGroup:Ref<boolean, boolean>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.state.resizeState:Ref<{ width: any; height: any; }, { width: any; height: any; }>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.state.doLayout:() => void— 上游声明事件参数:
(): voidcontext.state.debouncedUpdateLayout:() => void— 上游声明事件参数:
(): void
context.emit:AsamTableNativeEmits<T>— 上游声明事件参数:
(e: "select", selection: T[], row: T): void事件参数:
(e: "select-all", selection: T[]): void事件参数:
(e: "selection-change", newSelection: T[]): void事件参数:
(e: "cell-mouse-enter" | "cell-mouse-leave" | "cell-dblclick", row: T, column: AsamTableColumnCtx<T>, cell: HTMLTableCellElement, event: MouseEvent): void事件参数:
(e: "cell-contextmenu" | "cell-click", row: T, column: AsamTableColumnCtx<T>, cell: HTMLTableCellElement, event: PointerEvent): void事件参数:
(e: "row-dblclick", row: T, column: AsamTableColumnCtx<T> | null, event: MouseEvent): void事件参数:
(e: "row-click" | "row-contextmenu", row: T, column: AsamTableColumnCtx<T> | null, event: PointerEvent): void事件参数:
(e: "header-click" | "header-contextmenu", column: AsamTableColumnCtx<T>, event: PointerEvent): void事件参数完整签名见公开类型视图。
事件参数:
(e: "filter-change", newFilters: Record<string, string[]>): void事件参数:
(e: "current-change", currentRow: T | null, oldCurrentRow: T | null): void事件参数:
(e: "header-dragend", newWidth: number, oldWidth: number, column: AsamTableColumnCtx<T>, event: MouseEvent): void事件参数:
(e: "expand-change", row: T, expandedRows: T[]): void事件参数:
(e: "expand-change", row: T, expanded: boolean): void事件参数:
(e: "scroll", data: { scrollLeft: number; scrollTop: number; }): voidcontext.effect:ReactiveEffect<any>— 依赖声明渲染副作用实例。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.data:(NonNullable<AsamTableContext<T>>)["data"]— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.uid:number— 依赖声明context.type:ConcreteComponent<{}, any, any, ComputedOptions, MethodOptions, {}, any>— 依赖声明事件参数:
(props: {}, ctx: Parameters<Extract<NonNullable<(NonNullable<AsamTableContext<T>>)["type"]>, (...args: never[]) => unknown>>[1]): any嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent:ComponentInternalInstance | null— 依赖声明context.parent.uid:number— 依赖声明context.parent.type:ConcreteComponent<{}, any, any, ComputedOptions, MethodOptions, {}, any>— 依赖声明事件参数:
(props: {}, ctx: Parameters<Extract<NonNullable<PublicMember<NonNullable<(NonNullable<AsamTableContext<T>>)["parent"]>, "type">>, (...args: never[]) => unknown>>[1]): any嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.parent:ComponentInternalInstance | null— 依赖声明嵌套展开边界:递归引用;完整类型见公开类型视图。
context.parent.root:ComponentInternalInstance— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.appContext:AppContext— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.vnode:VNode<RendererNode, RendererElement, { [key: string]: any; }>— 依赖声明父虚拟 DOM 中代表当前组件的 VNode。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.subTree:VNode<RendererNode, RendererElement, { [key: string]: any; }>— 依赖声明当前组件自身虚拟 DOM 树的根 VNode。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.effect:ReactiveEffect<any>— 依赖声明渲染副作用实例。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.update:() => void— 依赖声明强制更新渲染副作用。
事件参数:
(): void嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.job:PublicMember<NonNullable<(NonNullable<AsamTableContext<T>>)["parent"]>, "job">— 依赖声明交给调度器的渲染任务,会检查是否需要更新。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.proxy:ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase<any, any, any, any, any, any, any, any, any, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions>, {}, {}, "", {}, any> | null— 依赖声明context.parent.exposed:Record<string, any> | null— 依赖声明context.parent.exposeProxy:Record<string, any> | null— 依赖声明context.parent.data:PublicMember<NonNullable<(NonNullable<AsamTableContext<T>>)["parent"]>, "data">— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.props:PublicMember<NonNullable<(NonNullable<AsamTableContext<T>>)["parent"]>, "props">— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.attrs:PublicMember<NonNullable<(NonNullable<AsamTableContext<T>>)["parent"]>, "attrs">— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.slots:PublicMember<NonNullable<(NonNullable<AsamTableContext<T>>)["parent"]>, "slots">— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.refs:PublicMember<NonNullable<(NonNullable<AsamTableContext<T>>)["parent"]>, "refs">— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.emit:(event: string, ...args: any[]) => void— 依赖声明事件参数:
(event: string, ...args: any[]): void嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.parent.isMounted:boolean— 依赖声明枚举:
false/truecontext.parent.isUnmounted:boolean— 依赖声明枚举:
false/truecontext.parent.isDeactivated:boolean— 依赖声明枚举:
false/true
context.root:ComponentInternalInstance— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.appContext:AppContext— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.vnode:VNode<RendererNode, RendererElement, { [key: string]: any; }>— 依赖声明父虚拟 DOM 中代表当前组件的 VNode。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.subTree:VNode<RendererNode, RendererElement, { [key: string]: any; }>— 依赖声明当前组件自身虚拟 DOM 树的根 VNode。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.update:() => void— 依赖声明强制更新渲染副作用。
事件参数:
(): void嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.job:(NonNullable<AsamTableContext<T>>)["job"]— 依赖声明交给调度器的渲染任务,会检查是否需要更新。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.proxy:ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase<any, any, any, any, any, any, any, any, any, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions>, {}, {}, "", {}, any> | null— 依赖声明context.exposed:Record<string, any> | null— 依赖声明context.exposeProxy:Record<string, any> | null— 依赖声明context.props:(NonNullable<AsamTableContext<T>>)["props"]— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.attrs:(NonNullable<AsamTableContext<T>>)["attrs"]— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.slots:(NonNullable<AsamTableContext<T>>)["slots"]— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
context.isMounted:boolean— 依赖声明枚举:
false/truecontext.isUnmounted:boolean— 依赖声明枚举:
false/truecontext.isDeactivated:boolean— 依赖声明枚举:
false/true
tableLayout
必填:是;声明来源:上游声明。
tableLayout 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"auto" / "fixed"
allowDragLastColumn
必填:是;声明来源:上游声明。
allowDragLastColumn 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
ns
必填:是;声明来源:上游声明。
ns 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
ns.namespace:(NonNullable<(AsamDataTableExpose<T>)["ns"]>)["namespace"]— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
ns.b:(blockSuffix?: string) => string— 上游声明事件参数:
(blockSuffix?: string | undefined): stringns.e:(element?: string) => string— 上游声明事件参数:
(element?: string | undefined): stringns.m:(modifier?: string) => string— 上游声明事件参数:
(modifier?: string | undefined): stringns.be:(blockSuffix?: string, element?: string) => string— 上游声明事件参数:
(blockSuffix?: string | undefined, element?: string | undefined): stringns.em:(element?: string, modifier?: string) => string— 上游声明事件参数:
(element?: string | undefined, modifier?: string | undefined): stringns.bm:(blockSuffix?: string, modifier?: string) => string— 上游声明事件参数:
(blockSuffix?: string | undefined, modifier?: string | undefined): stringns.bem:(blockSuffix?: string, element?: string, modifier?: string) => string— 上游声明事件参数:
(blockSuffix?: string | undefined, element?: string | undefined, modifier?: string | undefined): stringns.is:{ (name: string, state: boolean | undefined): string; (name: string): string; }— 上游声明事件参数:
(name: string, state: boolean | undefined): string事件参数:
(name: string): stringns.cssVar:(object: Record<string, string>) => Record<string, string>— 上游声明事件参数:
(object: Record<string, string>): Record<string, string>ns.cssVarName:(name: string) => string— 上游声明事件参数:
(name: string): stringns.cssVarBlock:(object: Record<string, string>) => Record<string, string>— 上游声明事件参数:
(object: Record<string, string>): Record<string, string>ns.cssVarBlockName:(name: string) => string— 上游声明事件参数:
(name: string): string
layout
必填:是;声明来源:上游声明。
layout 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
layout.observers:(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["observers"]— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table:AsamTableContext<T>— 上游声明layout.table.$ready:boolean— 上游声明枚举:
false/truelayout.table.hoverState:(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["hoverState"]— 上游声明layout.table.hoverState.cell:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.hoverState.column:AsamTableColumnCtx<T>— 上游声明嵌套展开边界:深度上限;完整类型见公开类型视图。
layout.table.hoverState.row:T— 上游声明嵌套展开边界:深度上限;完整类型见公开类型视图。
layout.table.renderExpanded:(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["renderExpanded"]— 上游声明事件参数:
(__0: Parameters<Extract<NonNullable<(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["renderExpanded"]>, (...args: never[]) => unknown>>[0]): VNode<import("vue").RendererNode, import("vue").RendererElement, { [key: string]: any; }>[] | undefinedlayout.table.store:(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["store"]— 上游声明嵌套展开边界:大型实例类型;完整类型见公开类型视图。
layout.table.layout:AsamDataTableExpose<T>["layout"]— 上游声明嵌套展开边界:递归引用;完整类型见公开类型视图。
layout.table.refs:AsamTableRefs— 上游声明layout.table.refs.tableWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.refs.headerWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.refs.footerWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.refs.fixedBodyWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.refs.rightFixedBodyWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.refs.bodyWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.refs.appendWrapper:HTMLElement— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.tableId:string— 上游声明layout.table.state:(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["state"]— 上游声明layout.table.state.isGroup:Ref<boolean, boolean>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.state.resizeState:Ref<{ width: any; height: any; }, { width: any; height: any; }>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.state.doLayout:() => void— 上游声明事件参数:
(): void嵌套展开边界:深度上限;完整类型见公开类型视图。
layout.table.state.debouncedUpdateLayout:() => void— 上游声明事件参数:
(): void嵌套展开边界:深度上限;完整类型见公开类型视图。
layout.table.emit:AsamTableNativeEmits<T>— 上游声明事件参数:
(e: "select", selection: T[], row: T): void事件参数:
(e: "select-all", selection: T[]): void事件参数:
(e: "selection-change", newSelection: T[]): void事件参数:
(e: "cell-mouse-enter" | "cell-mouse-leave" | "cell-dblclick", row: T, column: AsamTableColumnCtx<T>, cell: HTMLTableCellElement, event: MouseEvent): void事件参数:
(e: "cell-contextmenu" | "cell-click", row: T, column: AsamTableColumnCtx<T>, cell: HTMLTableCellElement, event: PointerEvent): void事件参数:
(e: "row-dblclick", row: T, column: AsamTableColumnCtx<T> | null, event: MouseEvent): void事件参数:
(e: "row-click" | "row-contextmenu", row: T, column: AsamTableColumnCtx<T> | null, event: PointerEvent): void事件参数:
(e: "header-click" | "header-contextmenu", column: AsamTableColumnCtx<T>, event: PointerEvent): void事件参数完整签名见公开类型视图。
事件参数:
(e: "filter-change", newFilters: Record<string, string[]>): void事件参数:
(e: "current-change", currentRow: T | null, oldCurrentRow: T | null): void事件参数:
(e: "header-dragend", newWidth: number, oldWidth: number, column: AsamTableColumnCtx<T>, event: MouseEvent): void事件参数:
(e: "expand-change", row: T, expandedRows: T[]): void事件参数:
(e: "expand-change", row: T, expanded: boolean): void事件参数:
(e: "scroll", data: { scrollLeft: number; scrollTop: number; }): voidlayout.table.effect:ReactiveEffect<any>— 依赖声明渲染副作用实例。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.data:(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["data"]— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.uid:number— 依赖声明layout.table.type:ConcreteComponent<{}, any, any, ComputedOptions, MethodOptions, {}, any>— 依赖声明事件参数:
(props: {}, ctx: Parameters<Extract<NonNullable<(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["type"]>, (...args: never[]) => unknown>>[1]): any嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent:ComponentInternalInstance | null— 依赖声明layout.table.parent.uid:number— 依赖声明嵌套展开边界:深度上限;完整类型见公开类型视图。
layout.table.parent.type:ConcreteComponent<{}, any, any, ComputedOptions, MethodOptions, {}, any>— 依赖声明事件参数:
(props: {}, ctx: Parameters<Extract<NonNullable<PublicMember<NonNullable<(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["parent"]>, "type">>, (...args: never[]) => unknown>>[1]): any嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.parent:ComponentInternalInstance | null— 依赖声明嵌套展开边界:递归引用;完整类型见公开类型视图。
layout.table.parent.root:ComponentInternalInstance— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.appContext:AppContext— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.vnode:VNode<RendererNode, RendererElement, { [key: string]: any; }>— 依赖声明父虚拟 DOM 中代表当前组件的 VNode。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.subTree:VNode<RendererNode, RendererElement, { [key: string]: any; }>— 依赖声明当前组件自身虚拟 DOM 树的根 VNode。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.effect:ReactiveEffect<any>— 依赖声明渲染副作用实例。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.update:() => void— 依赖声明强制更新渲染副作用。
事件参数:
(): void嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.job:PublicMember<NonNullable<(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["parent"]>, "job">— 依赖声明交给调度器的渲染任务,会检查是否需要更新。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.proxy:ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase<any, any, any, any, any, any, any, any, any, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions>, {}, {}, "", {}, any> | null— 依赖声明嵌套展开边界:深度上限;完整类型见公开类型视图。
layout.table.parent.exposed:Record<string, any> | null— 依赖声明嵌套展开边界:深度上限;完整类型见公开类型视图。
layout.table.parent.exposeProxy:Record<string, any> | null— 依赖声明嵌套展开边界:深度上限;完整类型见公开类型视图。
layout.table.parent.data:PublicMember<NonNullable<(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["parent"]>, "data">— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.props:PublicMember<NonNullable<(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["parent"]>, "props">— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.attrs:PublicMember<NonNullable<(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["parent"]>, "attrs">— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.slots:PublicMember<NonNullable<(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["parent"]>, "slots">— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.refs:PublicMember<NonNullable<(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["parent"]>, "refs">— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.emit:(event: string, ...args: any[]) => void— 依赖声明事件参数:
(event: string, ...args: any[]): void嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.parent.isMounted:boolean— 依赖声明枚举:
false/true嵌套展开边界:深度上限;完整类型见公开类型视图。
layout.table.parent.isUnmounted:boolean— 依赖声明枚举:
false/true嵌套展开边界:深度上限;完整类型见公开类型视图。
layout.table.parent.isDeactivated:boolean— 依赖声明枚举:
false/true嵌套展开边界:深度上限;完整类型见公开类型视图。
layout.table.root:ComponentInternalInstance— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.appContext:AppContext— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.vnode:VNode<RendererNode, RendererElement, { [key: string]: any; }>— 依赖声明父虚拟 DOM 中代表当前组件的 VNode。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.subTree:VNode<RendererNode, RendererElement, { [key: string]: any; }>— 依赖声明当前组件自身虚拟 DOM 树的根 VNode。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.update:() => void— 依赖声明强制更新渲染副作用。
事件参数:
(): void嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.job:(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["job"]— 依赖声明交给调度器的渲染任务,会检查是否需要更新。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.proxy:ComponentPublicInstance<{}, {}, {}, {}, {}, {}, {}, {}, false, ComponentOptionsBase<any, any, any, any, any, any, any, any, any, {}, {}, string, {}, {}, {}, string, ComponentProvideOptions>, {}, {}, "", {}, any> | null— 依赖声明layout.table.exposed:Record<string, any> | null— 依赖声明layout.table.exposeProxy:Record<string, any> | null— 依赖声明layout.table.props:(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["props"]— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.attrs:(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["attrs"]— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.slots:(NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["table"]>)["slots"]— 依赖声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.table.isMounted:boolean— 依赖声明枚举:
false/truelayout.table.isUnmounted:boolean— 依赖声明枚举:
false/truelayout.table.isDeactivated:boolean— 依赖声明枚举:
false/true
layout.store:(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["store"]— 上游声明嵌套展开边界:大型实例类型;完整类型见公开类型视图。
layout.columns:AsamTableColumnCtx<T>[]— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.fit:boolean— 上游声明枚举:
false/truelayout.showHeader:boolean— 上游声明枚举:
false/truelayout.heightMap:Record<string, string | number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.height:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.scrollX:Ref<boolean, boolean>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.scrollY:Ref<boolean, boolean>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.bodyWidth:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.fixedWidth:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.rightFixedWidth:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.tableHeight:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.headerHeight:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.appendHeight:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.footerHeight:Ref<number | null, number | null>— 上游声明嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
layout.gutterWidth:number— 上游声明layout.updateScrollY:() => boolean— 上游声明事件参数:
(): booleanlayout.setHeight:(value: string | number | null, prop?: string) => void— 上游声明事件参数:
(value: string | number | null, prop?: string | undefined): voidlayout.setMaxHeight:(value: string | number | null) => void— 上游声明事件参数:
(value: string | number | null): voidlayout.getFlattenColumns:() => AsamTableColumnCtx<T>[]— 上游声明事件参数:
(): AsamTableColumnCtx<T>[]layout.updateElsHeight:() => void— 上游声明事件参数:
(): voidlayout.headerDisplayNone:(elm: HTMLElement) => boolean— 上游声明事件参数:
(elm: HTMLElement): booleanlayout.updateColumnsWidth:() => void— 上游声明事件参数:
(): voidlayout.addObserver:(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["addObserver"]— 上游声明事件参数:
(observer: Parameters<Extract<NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["addObserver"]>, (...args: never[]) => unknown>>[0]): voidlayout.removeObserver:(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["removeObserver"]— 上游声明事件参数:
(observer: Parameters<Extract<NonNullable<(NonNullable<(AsamDataTableExpose<T>)["layout"]>)["removeObserver"]>, (...args: never[]) => unknown>>[0]): voidlayout.notifyObservers:(event: string) => void— 上游声明事件参数:
(event: string): void
store
必填:是;声明来源:上游声明。
store 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
嵌套展开边界:大型实例类型;完整类型见公开类型视图。
columns
必填:是;声明来源:上游声明。
columns 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
嵌套展开边界:框架 / 平台类型;完整类型见公开类型视图。
handleHeaderFooterMousewheel
必填:是;声明来源:ASAM 声明。
handleHeaderFooterMousewheel 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(event: WheelEvent, delta: { pixelX: number; pixelY: number; }): void
handleMouseLeave
必填:是;声明来源:上游声明。
handleMouseLeave 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): void
tableId
必填:是;声明来源:上游声明。
tableSize
必填:是;声明来源:上游声明。
tableSize 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"" / "large" / "default" / "small"
isHidden
必填:是;声明来源:上游声明。
isHidden 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
isEmpty
必填:是;声明来源:上游声明。
isEmpty 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
renderExpanded
必填:是;声明来源:上游声明。
renderExpanded 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(__0: Parameters<Extract<NonNullable<(AsamDataTableExpose<T>)["renderExpanded"]>, (...args: never[]) => unknown>>[0]): VNode<import("vue").RendererNode, import("vue").RendererElement, { [key: string]: any; }>[] | undefined
resizeProxyVisible
必填:是;声明来源:上游声明。
resizeProxyVisible 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
resizeState
必填:是;声明来源:上游声明。
resizeState 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
resizeState.width:number | null— 上游声明resizeState.height:number | null— 上游声明resizeState.headerHeight:number | null— 上游声明
isGroup
必填:是;声明来源:上游声明。
isGroup 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
bodyWidth
必填:是;声明来源:上游声明。
tableBodyStyles
必填:是;声明来源:上游声明。
tableBodyStyles 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
tableBodyStyles.width:string— 上游声明
emptyBlockStyle
必填:是;声明来源:上游声明。
emptyBlockStyle 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
emptyBlockStyle.width:string— 上游声明emptyBlockStyle.height:string— 上游声明
debouncedUpdateLayout
必填:是;声明来源:上游声明。
debouncedUpdateLayout 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(...args: Parameters<Extract<NonNullable<(AsamDataTableExpose<T>)["debouncedUpdateLayout"]>, (...args: never[]) => unknown>> extends [...infer Rest] ? Rest : never): void | undefined
setCurrentRow
必填:是;声明来源:上游声明。
setCurrentRow 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(row?: T | undefined): void
getHalfSelectionRows
必填:是;声明来源:上游声明。
getHalfSelectionRows 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): T[]
clearFilter
必填:是;声明来源:上游声明。
clearFilter 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(columnKeys?: string | string[] | undefined): void
toggleRowExpansion
必填:是;声明来源:上游声明。
toggleRowExpansion 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(row: T, expanded?: boolean | undefined): void
clearSort
必填:是;声明来源:上游声明。
clearSort 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): void
doLayout
必填:是;声明来源:上游声明。
doLayout 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(): void
t
必填:是;声明来源:上游声明。
t 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(path: Parameters<Extract<NonNullable<AsamDataTableExpose<T>["t"]>, (...args: never[]) => unknown>>[0], option?: Parameters<Extract<NonNullable<AsamDataTableExpose<T>["t"]>, (...args: never[]) => unknown>>[1]): string
setDragVisible
必填:是;声明来源:上游声明。
setDragVisible 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(visible: boolean): void
computedSumText
必填:是;声明来源:上游声明。
computedEmptyText
必填:是;声明来源:上游声明。
computedTooltipEffect
必填:是;声明来源:上游声明。
computedTooltipOptions
必填:是;声明来源:上游声明。
computedTooltipOptions 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
嵌套展开边界:大型实例类型;完整类型见公开类型视图。
scrollbarViewStyle
必填:是;声明来源:上游声明。
scrollbarViewStyle 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
scrollbarViewStyle.display:string— 上游声明scrollbarViewStyle.verticalAlign:string— 上游声明
scrollbarStyle
必填:是;声明来源:上游声明。
scrollbarStyle 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
scrollbarStyle.height:string— 上游声明scrollbarStyle.maxHeight:undefined— 上游声明scrollbarStyle.maxHeight:string— 上游声明scrollbarStyle.height:undefined— 上游声明
scrollBarRef
必填:是;声明来源:ASAM 声明。
scrollBarRef 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
嵌套展开边界:复杂类型;完整类型见公开类型视图。
scrollTo
必填:是;声明来源:上游声明。
scrollTo 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(options: number | ScrollToOptions, yCoord?: number | undefined): void
setScrollLeft
必填:是;声明来源:上游声明。
setScrollLeft 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(left?: number | undefined): void
setScrollTop
必填:是;声明来源:上游声明。
setScrollTop 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(top?: number | undefined): void
AsamDataTableExpose 公开类型视图
import type { VNode, Ref, ReactiveEffect, ConcreteComponent, ComputedOptions, MethodOptions, Attrs, ComponentInternalInstance, AppContext, RendererNode, RendererElement, ComponentPublicInstance, ComponentOptionsBase, ComponentProvideOptions } from 'vue';
import type { AsamDataTableExpose, AsamRecord, AsamTableSelection, AsamRowKey, AsamPage, AsamReloadResult, AsamTableContext, AsamTableColumnCtx, AsamTableRefs, AsamTableNativeEmits, AsamTableSort, AsamTableFilter, AsamScrollbarDirection } from '@asam-ui/ui';
type PublicMember<T, K extends PropertyKey> = T extends unknown ? K extends keyof T ? T[K] : never : never;
// AsamDataTableExpose
type PublicContract<T extends object = AsamRecord> = {
"clearSelection": () => void;
"getSelection": () => AsamTableSelection<T>;
"toggleRowSelection": (row: T, selected?: boolean) => void;
"getSelectionRows": () => T[];
"toggleAllSelection": () => void;
"updateKeyChildren": (key: AsamRowKey, children: T[]) => void;
readonly "reload": (options?: { readonly force?: boolean; }) => Promise<AsamReloadResult<AsamPage<T>>>;
readonly "cancel": () => void;
readonly "sort": (prop: string, order: string) => void;
readonly "context": AsamTableContext<T>;
readonly "tableLayout": "auto" | "fixed";
readonly "allowDragLastColumn": boolean;
readonly "ns": (AsamDataTableExpose<T>)["ns"];
readonly "layout": (AsamDataTableExpose<T>)["layout"];
readonly "store": (AsamDataTableExpose<T>)["store"];
readonly "columns": AsamTableColumnCtx<T>[];
readonly "handleHeaderFooterMousewheel": (event: WheelEvent, delta: { pixelX: number; pixelY: number; }) => void;
readonly "handleMouseLeave": () => void;
readonly "tableId": string;
readonly "tableSize": "" | "large" | "default" | "small";
readonly "isHidden": boolean;
readonly "isEmpty": boolean;
readonly "renderExpanded": (AsamDataTableExpose<T>)["renderExpanded"];
readonly "resizeProxyVisible": boolean;
readonly "resizeState": { width: null | number; height: null | number; headerHeight: null | number; };
readonly "isGroup": boolean;
readonly "bodyWidth": string;
readonly "tableBodyStyles": { width: string; };
readonly "emptyBlockStyle": { width: string; height: string; } | undefined;
readonly "debouncedUpdateLayout": (AsamDataTableExpose<T>)["debouncedUpdateLayout"];
readonly "setCurrentRow": (row?: T | undefined) => void;
readonly "getHalfSelectionRows": () => T[];
readonly "clearFilter": (columnKeys?: string[] | string) => void;
readonly "toggleRowExpansion": (row: T, expanded?: boolean) => void;
readonly "clearSort": () => void;
readonly "doLayout": () => void;
readonly "t": AsamDataTableExpose<T>["t"];
readonly "setDragVisible": (visible: boolean) => void;
readonly "computedSumText": string;
readonly "computedEmptyText": string;
readonly "computedTooltipEffect": string | undefined;
readonly "computedTooltipOptions": (AsamDataTableExpose<T>)["computedTooltipOptions"];
readonly "scrollbarViewStyle": { display: string; verticalAlign: string; };
readonly "scrollbarStyle": { height: string; maxHeight?: undefined; } | { maxHeight: string; height?: undefined; } | { height?: undefined; maxHeight?: undefined; };
readonly "scrollBarRef": (AsamDataTableExpose<T>)["scrollBarRef"];
readonly "scrollTo": (options: ScrollToOptions | number, yCoord?: number) => void;
readonly "setScrollLeft": (left?: number) => void;
readonly "setScrollTop": (top?: number) => void;
};
组合契约按分支生效;请同时查看公开类型。
参数与事件说明
data
必填:否;声明来源:数据 / provider 协议。
rowKey
必填:否;声明来源:数据 / provider 协议。
rowKey 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(row: T): AsamRowKey
cache
必填:否;声明来源:数据 / provider 协议。
cache 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false
cache.ttlMs:number— 数据 / provider 协议cache.key:((requestInput: unknown) => string) | undefined— 数据 / provider 协议事件参数:
(requestInput: unknown): string
query
必填:否;声明来源:数据 / provider 协议。
queryKey
必填:否;声明来源:数据 / provider 协议。
queryKey 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(query: Q): string
immediate
必填:否;声明来源:数据 / provider 协议。
immediate 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:false / true
currentPage
必填:否;声明来源:数据 / provider 协议。
pageSize
必填:否;声明来源:数据 / provider 协议。
defaultSelectedRowKeys
必填:否;声明来源:数据 / provider 协议。
api
必填:否;声明来源:数据 / provider 协议。
api 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(params: P, context: AsamRequestContext): Promise<R>
buildPageParams
必填:否;声明来源:数据 / provider 协议。
buildPageParams 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(query: Q, page: AsamPageRequest): P
request
必填:否;声明来源:数据 / provider 协议。
request 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
request.url:string— 数据 / provider 协议request.method:"delete" | "get" | "post" | "put" | undefined— 数据 / provider 协议枚举:
"delete"/"get"/"post"/"put"request.params:Readonly<Record<string, unknown>> | undefined— 数据 / provider 协议request.data:unknown— 数据 / provider 协议
buildPageRequest
必填:否;声明来源:数据 / provider 协议。
buildPageRequest 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(query: Q, page: AsamPageRequest): AsamRequestConfig
pageTarget
必填:否;声明来源:数据 / provider 协议。
pageTarget 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
枚举:"data" / "params"
transform
必填:否;声明来源:数据 / provider 协议。
transform 参数与事件数据
下列为声明可读视图;可导入类型以公开类型视图为准。分支条件与父属性一致。
事件参数:(response: R): AsamPage<T>
事件参数:(response: R): AsamPage<T>
AsamPagedInput 公开类型视图
import type { AsamPagedInput, AsamRecord, AsamRowKey, AsamOptionsCache, AsamApiRequest, AsamRequestContext, AsamPageRequest, AsamPage, AsamRequestConfig } from '@asam-ui/ui';
type PublicMember<T, K extends PropertyKey> = T extends unknown ? K extends keyof T ? T[K] : never : never;
// AsamPagedInput
type PublicContract<Q = AsamRecord, P = unknown, R = unknown, T extends object = AsamRecord> = AsamPagedInput<Q, P, R, T>;