Table 表格
用于展示多条结构类似的数据。
基础用法
姓名 | 年龄 | 城市 |
|---|---|---|
| 张三 | 28 | 北京 |
| 李四 | 32 | 上海 |
| 王五 | 25 | 广州 |
vue
<my-table :data="data" :columns="columns" border stripe hover />次要变体
variant="minor" 提供更轻量的视觉风格,适合在卡片或侧栏中使用。
姓名 | 年龄 | 城市 |
|---|---|---|
| 张三 | 28 | 北京 |
| 李四 | 32 | 上海 |
| 王五 | 25 | 广州 |
vue
<my-table :data="data" :columns="columns" variant="minor" hover />排序
设置列的 sortable: true 启用排序,点击表头切换升序/降序/无排序。设为 'custom' 则只触发事件不自行排序。
姓名 | 年龄 | 城市 |
|---|---|---|
| 张三 | 28 | 北京 |
| 李四 | 32 | 上海 |
| 王五 | 25 | 广州 |
vue
<my-table :data="data" :columns="columns" border @sort-change="onSortChange" />行选择
selection 启用复选框,通过 v-model:selection-model 双向绑定选中行。
员工ID | 成员 | 角色 | 部门 | 状态 | |
|---|---|---|---|---|---|
| 1 | 张三 zhangsan@example.com | 前端开发 | 技术部 | 在职 | |
| 2 | 李四 lisi@example.com | 后端开发 | 技术部 | 在职 | |
| 3 | 王五 wangwu@example.com | UI设计师 | 设计部 | 试用 | |
| 4 | 赵六 zhaoliu@example.com | 产品经理 | 产品部 | 离职 | |
| 5 | 孙七 sunqi@example.com | 测试工程师 | 质量部 | 在职 | |
| 6 | 周八 zhouba@example.com | 前端开发 | 技术部 | 试用 | |
| 7 | 吴九 wujiu@example.com | 后端开发 | 技术部 | 在职 | |
| 8 | 郑十 zhengshi@example.com | 运维工程师 | 运维部 | 离职 |
vue
<my-table
:data="employees"
:columns="columns"
selection
v-model:selection-model="selectedRows"
:row-key="'id'"
border
hover
/>自定义单元格
通过列的 render 函数或具名插槽自定义单元格内容。支持头像、状态标签等。
员工ID | 成员 | 角色 | 部门 | 状态 |
|---|---|---|---|---|
| 1 | 张三 zhangsan@example.com | 前端开发 | 技术部 | 在职 |
| 2 | 李四 lisi@example.com | 后端开发 | 技术部 | 在职 |
| 3 | 王五 wangwu@example.com | UI设计师 | 设计部 | 试用 |
| 4 | 赵六 zhaoliu@example.com | 产品经理 | 产品部 | 离职 |
| 5 | 孙七 sunqi@example.com | 测试工程师 | 质量部 | 在职 |
| 6 | 周八 zhouba@example.com | 前端开发 | 技术部 | 试用 |
| 7 | 吴九 wujiu@example.com | 后端开发 | 技术部 | 在职 |
| 8 | 郑十 zhengshi@example.com | 运维工程师 | 运维部 | 离职 |
ts
const columns = [
{ key: 'id', title: '员工ID', width: 90 },
{
key: 'member', title: '成员', width: 220,
render: (row) => h('div', { style: 'display:flex;align-items:center;gap:10px' }, [
h('img', { src: row.avatar, style: 'width:32px;height:32px;border-radius:50%' }),
h('div', [
h('div', { style: 'font-weight:500' }, row.name),
h('div', { style: 'font-size:12px;color:#999' }, row.email),
]),
]),
},
{ key: 'role', title: '角色' },
{
key: 'status', title: '状态', align: 'center',
render: (row) => {
const s = statusMap[row.status]
return h('span', {
style: `padding:2px 10px;border-radius:12px;font-size:12px;font-weight:500;background:${s.bg};color:${s.color}`,
}, s.text)
},
},
]可展开行
expandable 启用行展开,通过 expand 插槽自定义展开内容。accordion 限制同时只展开一行。
ID | 姓名 | 角色 | |
|---|---|---|---|
| 1 | 张三 | 前端开发 | |
| 2 | 李四 | 后端开发 | |
| 3 | 王五 | UI设计师 | |
| 4 | 赵六 | 产品经理 |
vue
<my-table :data="employees" :columns="columns" :row-key="'id'" expandable>
<template #expand="{ row }">
<div>邮箱:{{ row.email }} | 部门:{{ row.department }}</div>
</template>
</my-table>分页
传入 pagination 配置开启内置分页,自动对数据进行切片。
ID | 姓名 | 角色 |
|---|---|---|
| 1 | 员工1 | 前端 |
| 2 | 员工2 | 后端 |
| 3 | 员工3 | 设计 |
| 4 | 员工4 | 前端 |
| 5 | 员工5 | 后端 |
| 6 | 员工6 | 设计 |
| 7 | 员工7 | 前端 |
| 8 | 员工8 | 后端 |
| 9 | 员工9 | 设计 |
| 10 | 员工10 | 前端 |
共 30 条
vue
<my-table
:data="data"
:columns="columns"
:pagination="{ total: 30, currentPage: 1, pageSize: 10 }"
@page-change="handlePageChange"
/>加载中
loading 展示加载遮罩层。
姓名 | 年龄 | 城市 |
|---|---|---|
| 张三 | 28 | 北京 |
| 李四 | 32 | 上海 |
| 王五 | 25 | 广州 |
vue
<my-table :data="data" :columns="columns" :loading="loading" loading-text="加载中..." />虚拟滚动
virtualScroll + bodyHeight + rowHeight 开启虚拟滚动,只渲染可视区域的行。
ID | 姓名 | 邮箱 | 角色 |
|---|---|---|---|
| 1 | 用户1 | user1@example.com | 开发 |
| 2 | 用户2 | user2@example.com | 设计 |
| 3 | 用户3 | user3@example.com | 产品 |
| 4 | 用户4 | user4@example.com | 开发 |
| 5 | 用户5 | user5@example.com | 设计 |
| 6 | 用户6 | user6@example.com | 产品 |
| 7 | 用户7 | user7@example.com | 开发 |
| 8 | 用户8 | user8@example.com | 设计 |
| 9 | 用户9 | user9@example.com | 产品 |
| 10 | 用户10 | user10@example.com | 开发 |
| 11 | 用户11 | user11@example.com | 设计 |
| 12 | 用户12 | user12@example.com | 产品 |
| 13 | 用户13 | user13@example.com | 开发 |
| 14 | 用户14 | user14@example.com | 设计 |
| 15 | 用户15 | user15@example.com | 产品 |
| 16 | 用户16 | user16@example.com | 开发 |
| 17 | 用户17 | user17@example.com | 设计 |
vue
<my-table
:data="largeData"
:columns="columns"
virtual-scroll
:body-height="300"
:row-height="44"
/>列宽调整
列的 resizable 不为 false 时(默认开启),拖拽列右侧边界可调整列宽。
姓名 | 年龄 | 城市 |
|---|---|---|
| 张三 | 28 | 北京 |
| 李四 | 32 | 上海 |
| 王五 | 25 | 广州 |
组件结构
根元素 <div class="my-table"> 包含:loading 遮罩 __loading-overlay、滚动容器 __scroll(内为标准 <table> 结构:<thead class="my-table__head"> > <tr> > <th> 表头单元格;<tbody class="my-table__body"> > <tr> 数据行 > <td> 数据单元格,支持展开行 __expanded-row)、底部分页 __pagination-wrapper。
CSS 类名
| 类名 | 说明 |
|---|---|
.my-table | 根元素 |
.is-large | 大尺寸 |
.is-small | 小尺寸 |
.is-minor | 次要变体 |
.is-border | 带边框 |
.is-stripe | 斑马纹 |
.is-hover | 行悬停高亮 |
.is-loading | 加载中 |
.my-table__scroll | 滚动容器 |
.my-table__scroll--virtual | 虚拟滚动容器 |
.my-table__inner | 内部 <table> 元素 |
.my-table__head | 表头 <thead> |
.my-table__header-cell | 表头单元格 |
.is-sortable | 可排序列 |
.is-left | 固定左侧 |
.is-right | 固定右侧 |
.my-table__header-cell--checkbox | 复选框列表头 |
.my-table__header-cell--expand | 展开列表头 |
.my-table__cell-inner | 表头单元格内部布局 |
.my-table__sort-icons | 排序图标组 |
.is-active | 排序激活 |
.is-asc | 升序 |
.is-desc | 降序 |
.my-table__sort-arrow--up | 升序箭头 |
.my-table__sort-arrow--down | 降序箭头 |
.my-table__body | 表体 <tbody> |
.my-table__row | 数据行 |
.is-stripe-row | 斑马行 |
.my-table__cell | 数据单元格 |
.my-table__cell--checkbox | 复选框单元格 |
.my-table__cell--expand | 展开按钮单元格 |
.my-table__expand-icon | 展开图标 |
.is-expanded | 已展开 |
.my-table__expanded-row | 展开内容行 |
.my-table__expanded-cell | 展开内容单元格 |
.my-table__expanded-json | 展开的 JSON 展示 |
.my-table__resize-handle | 列宽调整手柄 |
.is-resizing | 调整中 |
.my-table__empty | 空数据提示 |
.my-table__loading-overlay | 加载遮罩层 |
.my-table__loading-text | 加载文字 |
.my-table__loading-spinner | 加载旋转图标 |
.my-table__pagination-wrapper | 分页容器 |
API
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| data | 表格数据 | Record<string, any>[] | [] |
| columns | 列配置 | TableColumn[] | [] |
| variant | 视觉变体 | 'default' | 'minor' | 'default' |
| border | 是否带边框 | boolean | false |
| stripe | 是否斑马纹 | boolean | false |
| hover | 是否行悬停高亮 | boolean | false |
| loading | 是否加载中 | boolean | false |
| loadingText | 加载文字 | string | '加载中...' |
| size | 表格尺寸 | 'large' | 'default' | 'small' | 'default' |
| emptyText | 空数据时文字 | string | '暂无数据' |
| rowKey | 行唯一标识字段名或函数 | string | (row) => string | '' |
| defaultSort | 默认排序 | { key: string, order: SortOrder } | {} |
| selection | 是否开启行选择 | boolean | false |
| selectionModel | 已选行(v-model) | Record<string, any>[] | [] |
| selectable | 行是否可选 | (row, index) => boolean | — |
| expandable | 是否开启行展开 | boolean | false |
| expandedRows | 已展开行 key 列表 | (string | number)[] | [] |
| defaultExpandAll | 默认展开所有行 | boolean | false |
| accordion | 手风琴模式(同时只展开一行) | boolean | false |
| pagination | 分页配置,false 禁用 | TablePaginationConfig | false | false |
| virtualScroll | 是否开启虚拟滚动 | boolean | false |
| rowHeight | 行高(虚拟滚动需设置) | number | 48 |
| bodyHeight | 滚动容器高度 | number | string | — |
TableColumn
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| key | 列标识(对应 data 字段) | string | — |
| title | 列标题 | string | — |
| width | 列宽 | string | number | — |
| minWidth | 最小列宽(调整时约束) | string | number | — |
| align | 对齐方式 | 'left' | 'center' | 'right' | 'left' |
| sortable | 可排序('custom' 仅触发事件) | boolean | 'custom' | — |
| fixed | 固定列 | 'left' | 'right' | — |
| resizable | 可调整列宽 | boolean | true |
| render | 自定义渲染函数 | (row, col, idx) => VNode | string | — |
Events
| 事件 | 说明 | 回调参数 |
|---|---|---|
| cell-click | 单元格点击 | (row, column) |
| row-click | 行点击 | (row, index) |
| sort-change | 排序改变 | (sort: SortState) |
| selection-change | 选择改变 | (rows) |
| update:selectionModel | 选择 v-model | (rows) |
| expand-change | 展开改变 | (row, expanded) |
| update:expandedRows | 展开 v-model | (keys) |
| page-change | 页码改变 | (page) |
| page-size-change | 每页条数改变 | (size) |
| column-resize | 列宽调整结束 | (column, newWidth) |
Slots
| 名称 | 说明 | 作用域 |
|---|---|---|
[col.key] | 自定义列单元格 | { row, value, index } |
expand | 展开行内容 | { row, expanded } |
empty | 空数据内容 | — |
default | 额外列(表头右侧) | — |