Table 数据表格
用于数据表格场景的 MoriUI 组件。
导入
import { Table } from 'moriui'
示例
概览
综合数据表格。
| INV-001 | 已支付 | [email protected] | ¥316 |
| INV-002 | 已支付 | [email protected] | ¥242 |
| INV-003 | 处理中 | [email protected] | ¥837 |
| INV-004 | 已支付 | [email protected] | ¥874 |
| INV-005 | 失败 | [email protected] | ¥721 |
共 15 条
<script setup lang="ts">
import type { ColumnDef, ColumnFiltersState, PaginationState, SortingState } from 'moriui'
import { ref } from 'vue'
import { Table } from 'moriui'
interface Invoice {
invoice: string
status: string
email: string
amount: number
}
const columns: ColumnDef<Invoice>[] = [
{
accessorKey: 'invoice',
header: '发票号',
meta: { filter: { placeholder: '筛选发票' } }
},
{
accessorKey: 'status',
header: '状态',
cell: ctx => String(ctx.getValue()),
meta: { filter: { placeholder: '筛选状态' } }
},
{
accessorKey: 'email',
header: '邮箱',
cell: ctx => String(ctx.getValue()),
meta: { filter: { placeholder: '筛选邮箱' } }
},
{
accessorKey: 'amount',
header: '金额',
cell: ctx => `¥${(ctx.getValue() as number).toLocaleString()}`
}
]
const data: Invoice[] = [
{ invoice: 'INV-001', status: '已支付', email: '[email protected]', amount: 316 },
{ invoice: 'INV-002', status: '已支付', email: '[email protected]', amount: 242 },
{ invoice: 'INV-003', status: '处理中', email: '[email protected]', amount: 837 },
{ invoice: 'INV-004', status: '已支付', email: '[email protected]', amount: 874 },
{ invoice: 'INV-005', status: '失败', email: '[email protected]', amount: 721 },
{ invoice: 'INV-006', status: '待处理', email: '[email protected]', amount: 150 },
{ invoice: 'INV-007', status: '已支付', email: '[email protected]', amount: 450 },
{ invoice: 'INV-008', status: '处理中', email: '[email protected]', amount: 680 },
{ invoice: 'INV-009', status: '失败', email: '[email protected]', amount: 325 },
{ invoice: 'INV-010', status: '已支付', email: '[email protected]', amount: 960 },
{ invoice: 'INV-011', status: '待处理', email: '[email protected]', amount: 180 },
{ invoice: 'INV-012', status: '已支付', email: '[email protected]', amount: 540 },
{ invoice: 'INV-013', status: '失败', email: '[email protected]', amount: 410 },
{ invoice: 'INV-014', status: '处理中', email: '[email protected]', amount: 775 },
{ invoice: 'INV-015', status: '已支付', email: '[email protected]', amount: 633 }
]
const sorting = ref<SortingState>([])
const columnFilters = ref<ColumnFiltersState>([])
const pagination = ref<PaginationState>({ pageIndex: 0, pageSize: 5 })
</script>
<template>
<Table
v-model:sorting="sorting"
v-model:column-filters="columnFilters"
v-model:pagination="pagination"
:data="data"
:columns="columns"
/>
</template>
操作
表格行内操作。
| 操作 | |||
|---|---|---|---|
| 无线鼠标 | ¥29.99 | 128 | |
| 机械键盘 | ¥129.99 | 56 | |
| USB-C 集线器 | ¥49.99 | 203 | |
| 4K 显示器 | ¥399.99 | 32 | |
| 降噪耳机 | ¥89.99 | 78 |
共 5 条
<script setup lang="ts">
import type { ColumnDef } from 'moriui'
import { h } from 'vue'
import { createColumnHelper } from '@tanstack/vue-table'
import { Copy, Eye, MoreHorizontal, Pencil, Trash2 } from '@lucide/vue'
import {
Button,
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuSeparator,
DropdownMenuTrigger,
Table
} from 'moriui'
interface Product {
name: string
price: number
stock: number
}
const columnHelper = createColumnHelper<Product>()
const columns: ColumnDef<Product>[] = [
columnHelper.accessor('name', {
header: '产品名称',
cell: ctx => String(ctx.getValue())
}),
columnHelper.accessor('price', {
header: '单价',
cell: ctx => `¥${(ctx.getValue() as number).toFixed(2)}`
}),
columnHelper.accessor('stock', {
header: '库存',
cell: ctx => String(ctx.getValue())
}),
columnHelper.display({
id: 'actions',
header: '操作',
cell: () => h(DropdownMenu, {}, {
default: () => [
h(DropdownMenuTrigger, {
render: () => h(Button, { 'variant': 'ghost', 'size': 'icon-sm', 'class': 'size-8', 'aria-label': '操作菜单' })
}, { default: () => h(MoreHorizontal) }),
h(DropdownMenuContent, { align: 'end' }, {
default: () => [
h(DropdownMenuItem, { onClick: () => {} }, { default: () => [h(Eye, { class: 'mr-2 size-4' }), '查看详情'] }),
h(DropdownMenuItem, { onClick: () => {} }, { default: () => [h(Pencil, { class: 'mr-2 size-4' }), '编辑'] }),
h(DropdownMenuItem, { onClick: () => {} }, { default: () => [h(Copy, { class: 'mr-2 size-4' }), '复制'] }),
h(DropdownMenuSeparator),
h(DropdownMenuItem, { variant: 'destructive', onClick: () => {} }, { default: () => [h(Trash2, { class: 'mr-2 size-4' }), '删除'] })
]
})
]
})
})
]
const data: Product[] = [
{ name: '无线鼠标', price: 29.99, stock: 128 },
{ name: '机械键盘', price: 129.99, stock: 56 },
{ name: 'USB-C 集线器', price: 49.99, stock: 203 },
{ name: '4K 显示器', price: 399.99, stock: 32 },
{ name: '降噪耳机', price: 89.99, stock: 78 }
]
</script>
<template>
<Table :data="data" :columns="columns" />
</template>
底部
表格底部分页。
| INV-001 | 已支付 | ¥250.00 |
| INV-002 | 待支付 | ¥150.00 |
| INV-003 | 未支付 | ¥350.00 |
| INV-004 | 已支付 | ¥450.00 |
| INV-005 | 已支付 | ¥550.00 |
合计:¥2250.00 共 7 条
<script setup lang="ts">
import type { ColumnDef, PaginationState, SortingState } from 'moriui'
import { Table } from 'moriui'
import { computed, ref } from 'vue'
interface Invoice {
invoice: string
status: string
amount: number
}
const columns: ColumnDef<Invoice>[] = [
{ accessorKey: 'invoice', header: '发票号', cell: ctx => String(ctx.getValue()) },
{
accessorKey: 'status',
header: '状态',
cell: ctx => String(ctx.getValue())
},
{
accessorKey: 'amount',
header: '金额',
cell: ctx => `¥${(ctx.getValue() as number).toFixed(2)}`
}
]
const data: Invoice[] = [
{ invoice: 'INV-001', status: '已支付', amount: 250 },
{ invoice: 'INV-002', status: '待支付', amount: 150 },
{ invoice: 'INV-003', status: '未支付', amount: 350 },
{ invoice: 'INV-004', status: '已支付', amount: 450 },
{ invoice: 'INV-005', status: '已支付', amount: 550 },
{ invoice: 'INV-006', status: '待支付', amount: 200 },
{ invoice: 'INV-007', status: '未支付', amount: 300 }
]
const sorting = ref<SortingState>([])
const pagination = ref<PaginationState>({ pageIndex: 0, pageSize: 5 })
const filteredTotal = computed(() =>
data.reduce((sum, inv) => sum + inv.amount, 0)
)
</script>
<template>
<Table
v-model:sorting="sorting"
v-model:pagination="pagination"
:data="data"
:columns="columns"
class="max-w-lg"
>
<template #pagination="{ table }">
<div class="flex w-full items-center justify-between px-4 py-3 text-sm">
<span class="text-muted-foreground">
合计:<strong class="text-foreground">¥{{ filteredTotal.toFixed(2) }}</strong>
</span>
<span class="text-muted-foreground">
共 {{ table.getFilteredRowModel().rows.length }} 条
</span>
</div>
</template>
</Table>
</template>
RTL
从右到左的表格布局。
| INV-001 | مدفوع | $316 |
| INV-002 | معلق | $242 |
| INV-003 | قيد المعالجة | $837 |
| INV-004 | مدفوع | $874 |
| INV-005 | فشل | $721 |
共 12 条
<script setup lang="ts">
import type { ColumnDef, PaginationState, SortingState } from 'moriui'
import { ref } from 'vue'
import { DirectionProvider, Table } from 'moriui'
interface Invoice {
invoice: string
status: string
amount: number
}
const columns: ColumnDef<Invoice>[] = [
{
accessorKey: 'invoice',
header: 'رقم الفاتورة',
cell: ctx => String(ctx.getValue())
},
{
accessorKey: 'status',
header: 'الحالة',
cell: ctx => String(ctx.getValue())
},
{
accessorKey: 'amount',
header: 'المبلغ',
cell: ctx => `$${(ctx.getValue() as number).toLocaleString()}`
}
]
const data: Invoice[] = [
{ invoice: 'INV-001', status: 'مدفوع', amount: 316 },
{ invoice: 'INV-002', status: 'معلق', amount: 242 },
{ invoice: 'INV-003', status: 'قيد المعالجة', amount: 837 },
{ invoice: 'INV-004', status: 'مدفوع', amount: 874 },
{ invoice: 'INV-005', status: 'فشل', amount: 721 },
{ invoice: 'INV-006', status: 'معلق', amount: 150 },
{ invoice: 'INV-007', status: 'مدفوع', amount: 450 },
{ invoice: 'INV-008', status: 'قيد المعالجة', amount: 680 },
{ invoice: 'INV-009', status: 'فشل', amount: 325 },
{ invoice: 'INV-010', status: 'مدفوع', amount: 960 },
{ invoice: 'INV-011', status: 'معلق', amount: 180 },
{ invoice: 'INV-012', status: 'مدفوع', amount: 540 }
]
const sorting = ref<SortingState>([])
const pagination = ref<PaginationState>({ pageIndex: 0, pageSize: 5 })
</script>
<template>
<DirectionProvider dir="rtl">
<Table
v-model:sorting="sorting"
v-model:pagination="pagination"
:data="data"
:columns="columns"
/>
</DirectionProvider>
</template>
使用说明
组合方式
Table 基于 TanStack Table 构建,接收 data 和 columns 作为输入,内置排序、筛选、分页和行选择能力。
尺寸
size 支持 default 和 sm,控制行的密度。
行选择
selectable 启用行选择复选框。
RTL
表格内容沿 dir 排列。
变体
Table 的 size 支持 default 与 sm;支持排序、列筛选、全局搜索、分页和行选择。
可访问性
Table 输出原生 table 元素,配合 TanStack Table 的排序、选择和分页 ARIA。表头使用 scope="col",排序按钮有 aria-sort。
API 参考
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
Table data | TData[] | — | 必填,表格数据数组。 |
Table columns | ColumnDef<TData>[] | — | 必填,列定义数组。 |
Table size | 'default' | 'sm' | 'default' | 表格行密度。 |
Table selectable | boolean | false | 是否显示行选择复选框。 |
Table pageSizeOptions | number[] | undefined | 可选的每页条数选项。 |
Table searchPlaceholder | string | undefined | 全局搜索占位提示。 |
Table emptyText | string | undefined | 空数据显示文本。 |