安裝
Terminal
pnpm dlx shadcn-vue add @admin/vxe-tableimport { AdminVxeTable, AdminVxeTableCheckbox, AdminVxeTableDragIcon, AdminVxeTableSorter, AdminVxeTableText, AdminVxeTableTextButton, AdminVxeTableTextButtonGroup, AdminVxeTableTextImage, useVxeTable} from '@/components/admin/vxe-table'用法
基本欄位與資料即可渲染表格。
<script setup lang="ts">import type { VxeTableDefines } from 'vxe-table'import { ref } from 'vue'import { AdminVxeTable } from '@/components/admin/vxe-table'interface User { id: number name: string role: string email: string}const columns = [ { type: 'seq', width: 60, title: '#' }, { field: 'name', title: '名稱', minWidth: 120 }, { field: 'role', title: '角色', minWidth: 120 }, { field: 'email', title: 'Email', minWidth: 200 }] as const satisfies readonly VxeTableDefines.ColumnOptions<User>[]const data = ref<User[]>([ { id: 1, name: 'Alice', role: 'Admin', email: 'alice@example.com' }, { id: 2, name: 'Bob', role: 'Editor', email: 'bob@example.com' }, { id: 3, name: 'Carol', role: 'Viewer', email: 'carol@example.com' }])</script><template> <admin-vxe-table class="w-full" :columns="columns" :data="data" /></template>場景
排序
以 AdminVxeTableSorter 取代上游預設排序圖示,並關閉 sort-config.showIcon。
<script setup lang="ts">import type { VxeTableDefines } from 'vxe-table'import { ref } from 'vue'import { AdminVxeTable, AdminVxeTableSorter } from '@/components/admin/vxe-table'interface Product { id: number name: string price: number stock: number}const columns = [ { type: 'seq', width: 60, title: '#' }, { field: 'name', title: '商品', minWidth: 160, sortable: true, slots: { header: 'nameHeader' } }, { field: 'price', title: '價格', minWidth: 120, sortable: true, slots: { header: 'priceHeader' } }, { field: 'stock', title: '庫存', minWidth: 100, sortable: true, slots: { header: 'stockHeader' } }] as const satisfies readonly VxeTableDefines.ColumnOptions<Product>[]const data = ref<Product[]>([ { id: 1, name: '無線耳機', price: 3200, stock: 42 }, { id: 2, name: '藍牙鍵盤', price: 2500, stock: 18 }, { id: 3, name: '螢幕支架', price: 1800, stock: 73 }, { id: 4, name: 'USB-C Hub', price: 1600, stock: 120 }])</script><template> <admin-vxe-table class="w-full" :columns="columns" :data="data" :sort-config="{ showIcon: false }" > <template #nameHeader="{ column }"> <span>商品</span> <admin-vxe-table-sorter :column="column" /> </template> <template #priceHeader="{ column }"> <span>價格</span> <admin-vxe-table-sorter :column="column" /> </template> <template #stockHeader="{ column }"> <span>庫存</span> <admin-vxe-table-sorter :column="column" /> </template> </admin-vxe-table></template>勾選與多選
啟用 checkbox 欄並監聽勾選變更,顯示目前選取筆數。
<script setup lang="ts">import type { VxeTableDefines } from 'vxe-table'import { ref } from 'vue'import { AdminVxeTable } from '@/components/admin/vxe-table'interface Task { id: number title: string owner: string status: string}const columns = [ { type: 'checkbox', width: 56 }, { type: 'seq', width: 60, title: '#' }, { field: 'title', title: '任務', minWidth: 200 }, { field: 'owner', title: '負責人', minWidth: 120 }, { field: 'status', title: '狀態', minWidth: 100 }] as const satisfies readonly VxeTableDefines.ColumnOptions<Task>[]const data = ref<Task[]>([ { id: 1, title: '撰寫 RFC 文件', owner: 'Alice', status: 'In progress' }, { id: 2, title: '補齊單元測試', owner: 'Bob', status: 'Todo' }, { id: 3, title: '檢查無障礙', owner: 'Carol', status: 'Done' }, { id: 4, title: '部署到 staging', owner: 'David', status: 'Blocked' }])const selected = ref<Task[]>([])function onCheckboxChange({ records }: { records: Task[] }) { selected.value = records}</script><template> <div class="flex flex-col gap-3"> <admin-vxe-table class="w-full" :columns="columns" :data="data" @checkbox-all="onCheckboxChange" @checkbox-change="onCheckboxChange" /> <p class="typo-caption-small text-text-50"> 已選 {{ selected.length }} 筆:{{ selected.map((t) => t.title).join('、') || '—' }} </p> </div></template>分頁
需要底部頁碼與每頁筆數選擇時,開啟 pagination 並綁定目前頁資料與全量筆數。
<script setup lang="ts">import type { VxeTableDefines } from 'vxe-table'import { computed, ref } from 'vue'import { AdminVxeTable } from '@/components/admin/vxe-table'interface Order { id: number title: string owner: string amount: number}const columns = [ { type: 'seq', width: 60, title: '#' }, { field: 'title', title: '訂單', minWidth: 200 }, { field: 'owner', title: '負責人', minWidth: 120 }, { field: 'amount', title: '金額', minWidth: 100 }] as const satisfies readonly VxeTableDefines.ColumnOptions<Order>[]const allRows = Array.from({ length: 48 }, (_, i) => ({ id: i + 1, title: `訂單 #${String(i + 1).padStart(3, '0')}`, owner: ['Alice', 'Bob', 'Carol', 'David'][i % 4]!, amount: 1000 + i * 50}))const page = ref(1)const itemsPerPage = ref(10)const pageRows = computed(() => { const start = (page.value - 1) * itemsPerPage.value return allRows.slice(start, start + itemsPerPage.value)})</script><template> <admin-vxe-table v-model:items-per-page="itemsPerPage" v-model:page="page" class="w-full" pagination :columns="columns" :data="pageRows" :total="allRows.length" /></template>所有子組件總覽
全部搭配子元件的版型一覽。
<script setup lang="ts">import type { VxeTableDefines } from 'vxe-table'import { ref } from 'vue'import { AdminVxeTable, AdminVxeTableCheckbox, AdminVxeTableDragIcon, AdminVxeTableSorter, AdminVxeTableText, AdminVxeTableTextButton, AdminVxeTableTextButtonGroup, AdminVxeTableTextImage} from '@/components/admin/vxe-table'interface Product { id: number image: string name: string sku: string category: string price: number stock: number}const columns = [ { type: 'checkbox', width: 56, slots: { checkbox: 'rowCheckbox', header: 'headerCheckbox' } }, { width: 44, title: '', dragSort: true }, { field: 'product', title: '商品', minWidth: 240, slots: { default: 'product' } }, { field: 'category', title: '分類', minWidth: 180, slots: { default: 'category' } }, { field: 'stock', title: '庫存', minWidth: 120, sortable: true, slots: { header: 'stockHeader' } }, { field: 'actions', title: '操作', minWidth: 160, slots: { default: 'actions' } }] as const satisfies readonly VxeTableDefines.ColumnOptions<Product>[]const data = ref<Product[]>([ { id: 1, image: 'https://picsum.photos/seed/headphones/80/60', name: '無線降噪耳機', sku: 'SKU-00001', category: '音訊', price: 4990, stock: 32 }, { id: 2, image: 'https://picsum.photos/seed/keyboard/80/60', name: '機械式鍵盤', sku: 'SKU-00002', category: '周邊', price: 2890, stock: 18 }, { id: 3, image: 'https://picsum.photos/seed/monitor/80/60', name: '4K 顯示器', sku: 'SKU-00003', category: '顯示', price: 12990, stock: 7 }])function onView(_row: Product) {}function onEdit(_row: Product) {}</script><template> <div class="w-full overflow-x-auto"> <admin-vxe-table class="w-full" :columns="columns" :data="data" :row-config="{ keyField: 'id', drag: true }" :sort-config="{ showIcon: false }" > <template #headerCheckbox="{ checked, indeterminate }"> <admin-vxe-table-checkbox :checked="checked" :indeterminate="indeterminate" /> </template> <template #rowCheckbox="{ row, checked, indeterminate }"> <admin-vxe-table-checkbox :checked="checked" :indeterminate="indeterminate" :row="row" /> </template> <template #rowDragIcon> <admin-vxe-table-drag-icon /> </template> <template #product="{ row }"> <admin-vxe-table-text-image :image="row.image" :name="row.name" /> </template> <template #category="{ row }"> <admin-vxe-table-text> <template #title>{{ row.category }}</template> <template #subtitle>{{ row.sku }}</template> </admin-vxe-table-text> </template> <template #stockHeader="{ column }"> <span>庫存</span> <admin-vxe-table-sorter :column="column" /> </template> <template #actions="{ row }"> <admin-vxe-table-text-button-group> <admin-vxe-table-text-button @click="onView(row)">檢視</admin-vxe-table-text-button> <admin-vxe-table-text-button @click="onEdit(row)">編輯</admin-vxe-table-text-button> </admin-vxe-table-text-button-group> </template> </admin-vxe-table> </div></template>API 參考
組成
<template> <admin-vxe-table :columns="columns" :data="data" :sort-config="{ showIcon: false }"> <template #headerCheckbox="{ checked, indeterminate }"> <admin-vxe-table-checkbox :checked="checked" :indeterminate="indeterminate" /> </template> <template #rowCheckbox="{ row, checked, indeterminate }"> <admin-vxe-table-checkbox :checked="checked" :indeterminate="indeterminate" :row="row" /> </template> <template #rowDragIcon> <admin-vxe-table-drag-icon /> </template> <template #stockHeader="{ column }"> <span>庫存</span> <admin-vxe-table-sorter :column="column" /> </template> <template #product="{ row }"> <admin-vxe-table-text-image :image="row.image" :name="row.name" /> </template> <template #category="{ row }"> <admin-vxe-table-text> <template #title>{{ row.category }}</template> <template #subtitle>{{ row.sku }}</template> </admin-vxe-table-text> </template> <template #actions> <admin-vxe-table-text-button-group> <admin-vxe-table-text-button>編輯</admin-vxe-table-text-button> <admin-vxe-table-text-button>刪除</admin-vxe-table-text-button> </admin-vxe-table-text-button-group> </template> </admin-vxe-table></template>admin-vxe-table
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
class | ClassValue | — | 根元素之自訂 class |
columns | C | undefined | — | 表格欄位定義(型別會被泛型 C 收斂以推導 column slots 名稱) |
pagination | boolean | undefined | — | 啟用分頁 |
page | number | undefined | — | 當前頁碼 (v-model:page) |
itemsPerPage | number | undefined | — | 每頁筆數 (v-model:items-per-page) |
total | number | undefined | — | 總筆數 |
perPageOptions | number[] | undefined | [10, 20, 50, 100] | 每頁筆數選項 |
showPaginationInfo | boolean | undefined | true | 顯示分頁資訊 |
showPerPageSelector | boolean | undefined | true | 顯示每頁筆數選擇器 |
Events
| 名稱 | Payload | 說明 |
|---|---|---|
update:page | number | 使用者切換頁碼時觸發 |
update:itemsPerPage | number | 使用者切換每頁筆數時觸發;切換後 `page` 會自動重置為 1 |
admin-vxe-table-checkbox
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
checked | boolean | undefined | — | 顯示為已勾選狀態 |
indeterminate | boolean | undefined | — | 顯示為「部分選取」中間態 |
row | Record<string, any> | undefined | — | 帶入該列資料時點擊切換該列;省略則切換全選 |
class | unknown | — | — |
admin-vxe-table-sorter
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
column* | VxeTableDefines.ColumnInfo<any> | — | 帶入 header slot 提供的 `column`,驅動三階段排序切換 |
class | unknown | — | — |
* 代表必填
admin-vxe-table-text-image
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
image* | string | — | 圖片 URL |
name* | string | — | 圖片右側顯示的名稱文字 |
class | unknown | — | — |
* 代表必填
admin-vxe-table-text-button
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
class | ClassValue | — | 根元素之自訂 class |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | any | 按鈕文字或內容 |
admin-vxe-table-text-button-group
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
class | ClassValue | — | 根元素之自訂 class |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
default | any | 一組文字按鈕 |
admin-vxe-table-drag-icon
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
class | unknown | — | — |
admin-vxe-table-text
Props
| 名稱 | 型別 | 預設值 | 說明 |
|---|---|---|---|
class | unknown | — | — |
Slots
| 名稱 | Bindings | 說明 |
|---|---|---|
title | any | 主標題文字 |
subtitle | any | 副標題文字 |
注意事項
- 不支援 SSR:
vxe-table依賴瀏覽器 DOM;元件殼不含<ClientOnly>。Nuxt 請自行以<ClientOnly>(或其他框架等價機制)包覆;純 CSR 不需處理。見 安裝 · SSR。 loading由 admin overlay 接管:傳入loading時會顯示v-admin-loading遮罩,不會轉發至vxe-grid原生 loading/loading slot。- 自訂 cell 子元件須置於
AdminVxeTable內:AdminVxeTableCheckbox、AdminVxeTableSorter等透過useVxeTable取得內部gridRef;寫自訂 cell 時也可直接呼叫useVxeTable()。 - 分頁資料由消費端切片:
pagination只負責 UI 與page/itemsPerPage雙向綁定;表格data仍須傳入當前頁資料,total為全量筆數。