AiTable 表格组件
基于 Naive UI DataTable 封装的增强型数据表格组件,支持表格/卡片双模式渲染、列设置、列宽拖拽、密度调整、分页、行展开、树形表等功能。通常由 AiCrudPage 内部调用,也可独立使用。
基础用法
vue
<template>
<AiTable
ref="tableRef"
:columns="columns"
:data-source="dataSource"
:loading="loading"
:pagination="pagination"
@page-change="handlePageChange"
@refresh="handleRefresh"
/>
</template>
<script setup>
import { ref } from 'vue'
import { AiTable } from '@/components/ai-form'
const tableRef = ref(null)
const columns = [
{ prop: 'name', label: '姓名', width: 120 },
{ prop: 'age', label: '年龄', width: 80, align: 'center' },
{ prop: 'status', label: '状态', width: 100, slot: 'status' },
{ prop: 'action', label: '操作', width: 180, fixed: 'right', slot: 'action' }
]
const dataSource = ref([])
const loading = ref(false)
const pagination = ref({ page: 1, pageSize: 10, itemCount: 0 })
function handlePageChange(page) {
pagination.value.page = page
fetchData()
}
function handleRefresh() {
fetchData()
}
</script>Props
基础属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| columns | Array<object> | [] | 必填,表格列配置 |
| dataSource | Array | [] | 表格数据源 |
| loading | Boolean | false | 是否显示加载状态 |
| pagination | Object | Boolean | {} | 分页配置,传 false 隐藏分页 |
| rowKey | String | Function | 'id' | 行唯一标识字段名或函数 |
表格样式
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| striped | Boolean | false | 是否显示斑马纹 |
| bordered | Boolean | true | 是否显示边框 |
| size | String | 'medium' | 表格尺寸:'small' | 'medium' | 'large' |
| rowGap | Number | 0 | 行间距附加高度(px) |
| maxHeight | Number | String | - | 表格最大高度,超出后纵向滚动 |
| scrollX | Number | - | 横向滚动最小宽度(px) |
| resizable | Boolean | true | 列宽是否可拖拽调整 |
渲染模式
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| renderMode | String | 'table' | 渲染模式:'table'(表格)| 'card'(卡片) |
| cardProps | Object | {} | 卡片模式配置,如 { columns: 4, gap: 16, hoverable: true } |
| showRenderModeSwitch | Boolean | false | 是否显示列表/卡片切换按钮 |
选择与行操作
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| hideSelection | Boolean | false | 是否隐藏多选框列 |
| defaultSelection | Array | [] | 默认选中行(键值数组) |
| expandConfig | Object | {} | 行展开面板配置(详见下方) |
| treeConfig | Object | {} | 树形表配置(详见下方) |
| tableProps | Object | {} | 透传给 Naive UI DataTable 的额外属性 |
工具栏控制
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| showToolbar | Boolean | true | 是否显示工具栏(刷新/密度/列设置) |
| showRefresh | Boolean | true | 是否显示刷新按钮 |
| showDensity | Boolean | true | 是否显示密度调整下拉 |
| showColumnFilter | Boolean | true | 是否显示列设置按钮 |
| showSearchToggle | Boolean | false | 是否显示搜索切换按钮 |
| searchVisible | Boolean | false | 搜索区域是否可见 |
列配置
每列配置对象支持以下属性:
| 属性 | 类型 | 说明 |
|---|---|---|
| prop | String | 字段名(对应 dataSource 行数据中的 key) |
| label | String | 列标题 |
| width | Number | String | 列宽(px 或 CSS 值) |
| minWidth | Number | 最小宽度 |
| fixed | String | 固定列:'left' 或 'right' |
| align | String | 对齐方式:'left' | 'center' | 'right' |
| sortable | Boolean | 是否可排序 |
| sortOrder | String | 默认排序方式:'ascend' | 'descend' | false |
| slot | String | 自定义插槽名(仅在父组件中通过 <template #slotName> 渲染) |
| render | Function | 自定义渲染函数:(row, index) => VNode | string |
| formatter | Function | 格式化函数:(value, row) => string |
| filter | Boolean | Object | 是否启用列筛选及筛选配置 |
| hide | Boolean | 是否隐藏该列 |
| ellipsis | Boolean | Object | 文本溢出省略配置 |
列筛选:启用
filter: true后,表头会出现筛选图标。也可传{ type: 'multiple', options: [...] }自定义筛选器。
展开面板配置(expandConfig)
行展开面板支持多种渲染类型,配置如下:
js
const expandConfig = {
type: 'table', // 'table' | 'descriptions' | 'form' | 'tabs' | 'custom'
title: '关联单据', // 展开区域标题
// 当 type='table' 时需配置:
columns: [...], // 子表格列配置
api: 'get@/api/order/:id/items', // 子数据接口(支持占位符)
rowKey: 'itemId', // 子表行键
// 当 type='descriptions' 时需配置:
schema: [...], // 描述列表字段
// 当 type='tabs' 时需配置:
tabs: [ // 页签配置
{ key: 'detail', label: '详情', type: 'descriptions', schema: [...] },
{ key: 'history', label: '变更记录', type: 'table', columns: [...], api: '...' }
]
}树形表配置(treeConfig)
js
const treeConfig = {
key: 'id', // 主键字段名
parentKey: 'parentId', // 父节点字段名
childrenKey: 'children', // 子节点字段名(后端返回树形结构时使用)
defaultExpandAll: true // 是否默认展开全部
}事件
| 事件名 | 参数 | 说明 |
|---|---|---|
page-change | (page: number) | 页码变化 |
page-size-change | (pageSize: number) | 每页条数变化 |
refresh | - | 点击刷新按钮 |
density-change | (size: 'small' | 'medium' | 'large') | 密度变化 |
filter-change | (visibleColumns: Array) | 列筛选变化(设置的可见列数组) |
selection-change | (keys: Array, rows: Array) | 选中行变化 |
row-click | (row, index, event) | 行点击 |
expand-change | (row, expanded) | 行展开/收起 |
render-mode-change | (mode: 'table' | 'card') | 渲染模式切换 |
search-visible-change | (visible: boolean) | 搜索区域显隐切换 |
方法
通过 ref 获取组件实例:
js
const tableRef = ref(null)
// 清除所有选中行
tableRef.value.clearSelection()
// 获取当前选中的行数据
const rows = tableRef.value.getCheckedRows()
// 获取当前选中的行键
const keys = tableRef.value.getCheckedKeys()
// 通过键值数组设置选中行
tableRef.value.setCheckedKeys([1, 2, 3])
// 刷新(重新触发 refresh 事件)
tableRef.value.refresh()
// 重置表格到初始状态(清除排序、筛选、选中)
tableRef.value.reset()
// 滚动到指定行(传入 rowKey 值)
tableRef.value.scrollTo(123)插槽
vue
<AiTable :columns="columns" :data-source="dataSource">
<!-- 列插槽 — slot 名对应 columns 中的 slot 属性 -->
<template #status="{ row, index }">
<n-tag :type="row.status === 1 ? 'success' : 'error'">
{{ row.status === 1 ? '启用' : '禁用' }}
</n-tag>
</template>
<!-- 操作列 -->
<template #action="{ row }">
<n-space>
<n-button size="small" @click="handleEdit(row)">编辑</n-button>
<n-button size="small" type="error" @click="handleDelete(row)">删除</n-button>
</n-space>
</template>
<!-- 工具栏左侧 -->
<template #toolbar-left>
<n-button type="primary" @click="handleAdd">新增</n-button>
</template>
<!-- 工具栏右侧 -->
<template #toolbar-right>
<n-button @click="exportData">导出</n-button>
</template>
<!-- 卡片模式的自定义卡片渲染 -->
<template #card="{ row, index }">
<div class="custom-card">
<h4>{{ row.title }}</h4>
<p>{{ row.description }}</p>
</div>
</template>
<!-- 展开行自定义内容(expandConfig.type='custom' 时生效) -->
<template #expand="{ row }">
<div class="expand-content">
<p>自定义展开内容: {{ row.id }}</p>
</div>
</template>
</AiTable>卡片模式
vue
<template>
<AiTable
:columns="productColumns"
:data-source="products"
:render-mode="'card'"
:show-render-mode-switch="true"
:card-props="{ columns: 3, gap: 16, hoverable: true }"
>
<template #card="{ row }">
<n-card :title="row.name" hoverable>
<template #cover>
<img :src="row.image" style="height: 160px; object-fit: cover" />
</template>
<n-space vertical>
<span class="price">¥{{ row.price }}</span>
<n-tag>{{ row.category }}</n-tag>
</n-space>
</n-card>
</template>
</AiTable>
</template>