Appearance
Vue 剪贴板
由 vtClipboard 插件提供:把当前框选区域复制成 TSV(可直接粘到 Excel),也支持从外部粘贴写回。插件与框架无关,@virt-table/vue 直接透传 vanilla 的实现与类型。
装载
ts
import { VirtTableVue, vtCellSelection, vtClipboard } from '@virt-table/vue';
const options = {
list,
itemKey: 'id',
estimatedSize: 40,
// vtClipboard 声明了 requires: ['vtCellSelection'],缺了它会被跳过并告警
plugins: [vtCellSelection(), vtClipboard()],
};自定义 payload
ts
vtClipboard<Row>({
mimeType: 'application/x-vt-demo+json',
onCopy: (ctx) => ({ text: ctx.rows.map((l) => l.join('\t')).join('\n'), payload }),
onPaste: (ctx) => false, // 返回 false 交回插件走默认 TSV 写回
});插件在构造时装载
plugins 在建表时生效,运行时替换该字段不会重新装载。所以回调里要读的开关量放在 ref 里(示例中的「携带结构化 payload」),而不是靠重建插件。
完整语义(默认 TSV 规则、自动补空行、焦点让位、与编辑浮层的关系)见 Vanilla · 剪贴板。
示例
微应用尚未挂载。
源码
点击查看源码
vue
<template>
<div class="demo-wrapper">
<h3 class="demo-title">Vue 剪贴板</h3>
<div class="demo-hint">
拖选一片单元格 → <b>Ctrl/Cmd+C</b> 复制(TSV,可粘到 Excel); 选中目标左上角单元格 →
<b>Ctrl/Cmd+V</b> 粘贴。粘贴行数超出数据时会自动补空行。
</div>
<div class="virt-table-controls">
<label>
<input v-model="usePayload" type="checkbox" />
携带结构化 payload(自定义 MIME,保留原始类型)
</label>
<button type="button" class="virt-table-btn" @click="reset">重置数据</button>
</div>
<div class="status-text">{{ status }}</div>
<div style="width: 760px; height: 420px" class="demo-container">
<VirtTableVue ref="tableRef" :columns="columns" :options="options" />
</div>
</div>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue';
import {
VirtTableVue,
type VueTableColumn,
vtClipboard,
vtCellSelection,
} from '@virt-table/vue';
import { faker } from '@faker-js/faker';
import type { VirtTableVueInstance } from '../../virt-table-ref';
interface Row extends Record<string, unknown> {
id: number;
name: string;
dept: string;
city: string;
score: number;
}
const depts = ['工程部', '设计部', '市场部', '财务部'];
const makeList = (): Row[] =>
Array.from({ length: 200 }, (_, i) => ({
id: i + 1,
name: faker.person.fullName(),
dept: depts[i % depts.length]!,
city: faker.location.city(),
score: faker.number.int({ min: 0, max: 100 }),
}));
/** 结构化 payload:演示自定义 MIME 往返(表内复制粘贴可保留原始类型) */
interface DemoPayload {
kind: 'vt-demo';
rowCount: number;
colCount: number;
cells: unknown[][];
}
const columns: VueTableColumn<Row>[] = [
{ key: 'id', title: 'ID', width: 70 },
{ key: 'name', title: '姓名', width: 170 },
{ key: 'dept', title: '部门', width: 130 },
{ key: 'city', title: '城市', width: 150 },
{ key: 'score', title: '分数', width: 100, align: 'right' },
];
const tableRef = ref<VirtTableVueInstance | null>(null);
const status = ref('等待复制/粘贴…');
/** 开启后复制会额外写入自定义 MIME 的结构化数据,粘贴时优先用它 */
const usePayload = ref(false);
const options = {
list: makeList(),
itemKey: 'id',
estimatedSize: 40,
buffer: 6,
border: true,
// 剪贴板以框选区域为单位,必须同时启用框选
plugins: [
vtCellSelection(),
vtClipboard<Row>({
mimeType: 'application/x-vt-demo+json',
onCopy: (ctx) => {
const size = `${ctx.rows.length} 行 × ${ctx.columnIndexes.length} 列`;
if (!usePayload.value) {
status.value = `已复制 ${size}(默认 TSV)`;
return; // 返回 undefined → 用插件默认的 TSV
}
const payload: DemoPayload = {
kind: 'vt-demo',
rowCount: ctx.rows.length,
colCount: ctx.columnIndexes.length,
cells: ctx.rows.map((line) => [...line]),
};
status.value = `已复制 ${size}(TSV + 结构化 payload)`;
return { text: ctx.rows.map((l) => l.join('\t')).join('\n'), payload };
},
onPaste: (ctx) => {
const payload = ctx.payload as DemoPayload | null;
if (!payload || payload.kind !== 'vt-demo') {
// 没有自定义 payload(例如从 Excel 粘来)→ 交回插件走默认 TSV 写回
status.value = `粘贴 ${ctx.rows.length} 行(默认 TSV 解析)`;
return false;
}
status.value = `粘贴 ${payload.rowCount} 行 × ${payload.colCount} 列(走结构化 payload)`;
return false; // 本示例仍交回默认写回,只是演示 payload 已拿到
},
}),
],
};
watch(usePayload, (on) => {
status.value = on ? '复制将携带结构化 payload' : '复制只写 TSV';
});
const reset = () => {
tableRef.value?.setList(makeList());
tableRef.value?.clearCellSelection();
status.value = '数据已重置';
};
</script>