Table 表格
用于展示多条结构类似的数据,并支持排序、选择、展开、分页等交互。
基础用法
最基本的表格用法,只需传入 data 数据和 columns 列配置即可。columns 是一个对象数组,每个对象描述一列,key 对应 data 中的字段名,title 为列头显示文字。
工号 | 姓名 | 部门 | 职位 | 城市 | 入职日期 | 状态 |
|---|---|---|---|---|---|---|
1001 | 张三 | 技术部 | 前端工程师 | 北京 | 2022-01-15 | 在职 |
1002 | 李四 | 产品部 | 产品经理 | 上海 | 2021-06-20 | 在职 |
1003 | 王五 | 设计部 | UI设计师 | 广州 | 2023-03-10 | 在职 |
1004 | 赵六 | 市场部 | 市场专员 | 深圳 | 2024-01-05 | 实习 |
<template>
<Table :data="tableData" :columns="columns" />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100 },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '城市', key: 'city', width: 100 },
{ title: '入职日期', key: 'date', width: 120 },
{ title: '状态', key: 'status', width: 80 }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', city: '北京', date: '2022-01-15', status: '在职' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', city: '上海', date: '2021-06-20', status: '在职' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', city: '广州', date: '2023-03-10', status: '在职' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', city: '深圳', date: '2024-01-05', status: '实习' }
])
</script>带边框
通过 border 属性可以给表格添加纵向边框,使单元格边界更加清晰。适合数据列较多、需要在视觉上明确区分单元格时使用。
工号 | 姓名 | 部门 | 职位 | 城市 | 入职日期 | 状态 |
|---|---|---|---|---|---|---|
1001 | 张三 | 技术部 | 前端工程师 | 北京 | 2022-01-15 | 在职 |
1002 | 李四 | 产品部 | 产品经理 | 上海 | 2021-06-20 | 在职 |
1003 | 王五 | 设计部 | UI设计师 | 广州 | 2023-03-10 | 在职 |
1004 | 赵六 | 市场部 | 市场专员 | 深圳 | 2024-01-05 | 实习 |
<template>
<Table :data="tableData" :columns="columns" border />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100 },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '城市', key: 'city', width: 100 },
{ title: '入职日期', key: 'date', width: 120 },
{ title: '状态', key: 'status', width: 80 }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', city: '北京', date: '2022-01-15', status: '在职' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', city: '上海', date: '2021-06-20', status: '在职' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', city: '广州', date: '2023-03-10', status: '在职' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', city: '深圳', date: '2024-01-05', status: '实习' }
])
</script>斑马纹
通过 stripe 属性可以呈现斑马纹效果,即奇偶行使用不同的背景色,能够有效引导视觉流,方便用户横向阅读数据。适合行数较多的表格。
工号 | 姓名 | 部门 | 职位 | 城市 | 入职日期 | 状态 |
|---|---|---|---|---|---|---|
1001 | 张三 | 技术部 | 前端工程师 | 北京 | 2022-01-15 | 在职 |
1002 | 李四 | 产品部 | 产品经理 | 上海 | 2021-06-20 | 在职 |
1003 | 王五 | 设计部 | UI设计师 | 广州 | 2023-03-10 | 在职 |
1004 | 赵六 | 市场部 | 市场专员 | 深圳 | 2024-01-05 | 实习 |
1005 | 钱七 | 运营部 | 运营总监 | 杭州 | 2020-11-30 | 在职 |
<template>
<Table :data="tableData" :columns="columns" stripe />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100 },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '城市', key: 'city', width: 100 },
{ title: '入职日期', key: 'date', width: 120 },
{ title: '状态', key: 'status', width: 80 }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', city: '北京', date: '2022-01-15', status: '在职' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', city: '上海', date: '2021-06-20', status: '在职' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', city: '广州', date: '2023-03-10', status: '在职' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', city: '深圳', date: '2024-01-05', status: '实习' },
{ id: '1005', name: '钱七', dept: '运营部', role: '运营总监', city: '杭州', date: '2020-11-30', status: '在职' }
])
</script>排序
通过设置列的 sortable 属性可以开启该列的点击排序功能。排序规则为三轮循环:未排序 → 升序 → 降序 → 未排序。点击排序图标或列头均可触发排序。可以通过 defaultSort 属性设置初始排序状态,接受 prop(排序字段名)和 order(排序方向 'ascending' 或 'descending')两个参数。
工号 | 姓名 | 部门 | 职位 | 年龄 | 城市 | 入职日期 |
|---|---|---|---|---|---|---|
1005 | 钱七 | 运营部 | 运营总监 | 35 | 杭州 | 2020-11-30 |
1002 | 李四 | 产品部 | 产品经理 | 30 | 上海 | 2021-06-20 |
1003 | 王五 | 设计部 | UI设计师 | 28 | 广州 | 2023-03-10 |
1001 | 张三 | 技术部 | 前端工程师 | 25 | 北京 | 2022-01-15 |
1004 | 赵六 | 市场部 | 市场专员 | 22 | 深圳 | 2024-01-05 |
<template>
<Table
:data="tableData"
:columns="columns"
border
:defaultSort="{ prop: 'age', order: 'descending' }"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100, sortable: true },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '年龄', key: 'age', width: 80, sortable: true, align: 'center' },
{ title: '城市', key: 'city', width: 100 },
{ title: '入职日期', key: 'date', width: 120 }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', age: 25, city: '北京', date: '2022-01-15' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', age: 30, city: '上海', date: '2021-06-20' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', age: 28, city: '广州', date: '2023-03-10' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', age: 22, city: '深圳', date: '2024-01-05' },
{ id: '1005', name: '钱七', dept: '运营部', role: '运营总监', age: 35, city: '杭州', date: '2020-11-30' }
])
</script>行选择
通过设置列的 type: 'selection' 可以添加一列复选框,用于行选择。表头复选框为全选操作,选中某行后会高亮显示。行选择状态可在外部通过 selectedRows 等方式访问(后续版本将提供 selection-change 事件)。
工号 | 姓名 | 部门 | 职位 | 城市 | 入职日期 | 状态 | |
|---|---|---|---|---|---|---|---|
1001 | 张三 | 技术部 | 前端工程师 | 北京 | 2022-01-15 | 在职 | |
1002 | 李四 | 产品部 | 产品经理 | 上海 | 2021-06-20 | 在职 | |
1003 | 王五 | 设计部 | UI设计师 | 广州 | 2023-03-10 | 在职 | |
1004 | 赵六 | 市场部 | 市场专员 | 深圳 | 2024-01-05 | 实习 |
<template>
<Table :data="tableData" :columns="columns" border />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '选择', type: 'selection', width: 50 },
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100 },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '城市', key: 'city', width: 100 },
{ title: '入职日期', key: 'date', width: 120 },
{ title: '状态', key: 'status', width: 80 }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', city: '北京', date: '2022-01-15', status: '在职' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', city: '上海', date: '2021-06-20', status: '在职' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', city: '广州', date: '2023-03-10', status: '在职' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', city: '深圳', date: '2024-01-05', status: '实习' }
])
</script>索引列
通过设置列的 type: 'index' 可以显示一列行号。默认显示从 1 开始的序号,配合分页使用时,序号会根据当前页和每页条数自动计算。可以通过 title 属性自定义列头文字。
序号 | 工号 | 姓名 | 部门 | 职位 | 城市 | 入职日期 |
|---|---|---|---|---|---|---|
| 1 | 1001 | 张三 | 技术部 | 前端工程师 | 北京 | 2022-01-15 |
| 2 | 1002 | 李四 | 产品部 | 产品经理 | 上海 | 2021-06-20 |
| 3 | 1003 | 王五 | 设计部 | UI设计师 | 广州 | 2023-03-10 |
| 4 | 1004 | 赵六 | 市场部 | 市场专员 | 深圳 | 2024-01-05 |
<template>
<Table :data="tableData" :columns="columns" border />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '序号', type: 'index', width: 60 },
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100 },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '城市', key: 'city', width: 100 },
{ title: '入职日期', key: 'date', width: 120 }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', city: '北京', date: '2022-01-15' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', city: '上海', date: '2021-06-20' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', city: '广州', date: '2023-03-10' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', city: '深圳', date: '2024-01-05' }
])
</script>行展开
通过设置列的 type: 'expand' 可以添加一个展开列,点击后可以展开/收起该行的详细信息。展开的内容通过具名插槽渲染,插槽名称由 expandSlot 属性指定(默认为 'expand'),作用域参数 { row, rowIndex } 可访问当前行数据。expandKey 属性用于指定行数据的唯一标识字段,默认为 'id'。
展开 | 工号 | 姓名 | 部门 | 职位 | 城市 | 状态 |
|---|---|---|---|---|---|---|
1001 | 张三 | 技术部 | 前端工程师 | 北京 | 在职 | |
1002 | 李四 | 产品部 | 产品经理 | 上海 | 在职 | |
1003 | 王五 | 设计部 | UI设计师 | 广州 | 在职 | |
1004 | 赵六 | 市场部 | 市场专员 | 深圳 | 实习 |
<template>
<Table :data="tableData" :columns="columns" border>
<template #expand="{ row }">
<div class="expand-panel">
<div class="expand-row">
<span class="expand-item"><em>工号</em><span>{{ row.id }}</span></span>
<span class="expand-item"><em>姓名</em><span>{{ row.name }}</span></span>
<span class="expand-item"><em>部门</em><span>{{ row.dept }}</span></span>
<span class="expand-item"><em>职位</em><span>{{ row.role }}</span></span>
</div>
<div class="expand-row">
<span class="expand-item"><em>城市</em><span>{{ row.city }}</span></span>
<span class="expand-item"><em>入职日期</em><span>{{ row.date }}</span></span>
<span class="expand-item full"><em>备注</em><span>该员工入职以来表现良好,按时完成各项工作任务,积极参与团队协作,适合承担核心业务开发工作。</span></span>
</div>
</div>
</template>
</Table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '展开', type: 'expand', expandSlot: 'expand', width: 60 },
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100 },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '城市', key: 'city', width: 100 },
{ title: '状态', key: 'status', width: 80 }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', city: '北京', date: '2022-01-15', status: '在职' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', city: '上海', date: '2021-06-20', status: '在职' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', city: '广州', date: '2023-03-10', status: '在职' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', city: '深圳', date: '2024-01-05', status: '实习' }
])
</script>
<style scoped>
.expand-panel {
padding: 14px 16px;
background: #f5f7fa;
}
.expand-row {
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
}
.expand-row:last-child {
margin-bottom: 0;
}
.expand-item {
display: flex;
align-items: baseline;
min-width: 160px;
margin-right: 32px;
}
.expand-item em {
font-style: normal;
color: #909399;
font-size: 13px;
width: 56px;
flex-shrink: 0;
}
.expand-item span {
color: #303133;
font-size: 13px;
}
.expand-item.full {
flex-basis: 100%;
min-width: 0;
}
</style>分页
通过 pagination 属性可以为表格添加分页功能。传入一个对象,包含 pageSize(每页条数)和 currentPage(当前页码)。表格会自动根据配置对数据进行分页,切页后排序状态会保留。如果不传入该属性,则不分页,显示全部数据。
工号 | 姓名 | 部门 | 职位 | 城市 | 入职日期 | 状态 |
|---|---|---|---|---|---|---|
1001 | 张三 | 技术部 | 前端工程师 | 北京 | 2022-01-15 | 在职 |
1002 | 李四 | 产品部 | 产品经理 | 上海 | 2021-06-20 | 在职 |
1003 | 王五 | 设计部 | UI设计师 | 广州 | 2023-03-10 | 在职 |
<template>
<Table
:data="tableData"
:columns="columns"
border
:pagination="{ pageSize: 3, currentPage: 1 }"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100 },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '城市', key: 'city', width: 100 },
{ title: '入职日期', key: 'date', width: 120 },
{ title: '状态', key: 'status', width: 80 }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', city: '北京', date: '2022-01-15', status: '在职' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', city: '上海', date: '2021-06-20', status: '在职' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', city: '广州', date: '2023-03-10', status: '在职' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', city: '深圳', date: '2024-01-05', status: '实习' },
{ id: '1005', name: '钱七', dept: '运营部', role: '运营总监', city: '杭州', date: '2020-11-30', status: '在职' },
{ id: '1006', name: '孙八', dept: '财务部', role: '财务经理', city: '南京', date: '2022-08-15', status: '在职' },
{ id: '1007', name: '周九', dept: '人事部', role: '人事专员', city: '成都', date: '2023-05-20', status: '实习' }
])
</script>加载状态
通过 loading 属性可以控制表格的加载状态。当设为 true 时,表格上方会覆盖一层半透明遮罩并显示旋转加载动画。常用于数据异步加载时给用户明确的等待反馈。设为 false 可关闭加载状态。
工号 | 姓名 | 部门 | 职位 | 城市 | 入职日期 | 状态 |
|---|---|---|---|---|---|---|
1001 | 张三 | 技术部 | 前端工程师 | 北京 | 2022-01-15 | 在职 |
1002 | 李四 | 产品部 | 产品经理 | 上海 | 2021-06-20 | 在职 |
1003 | 王五 | 设计部 | UI设计师 | 广州 | 2023-03-10 | 在职 |
1004 | 赵六 | 市场部 | 市场专员 | 深圳 | 2024-01-05 | 实习 |
<template>
<div class="demo-wrapper">
<button class="trigger-btn" @click="loading = !loading">
{{ loading ? '关闭加载' : '开启加载' }}
</button>
<Table
:data="tableData"
:columns="columns"
border
:loading="loading"
/>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const loading = ref(true)
const columns = ref([
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100 },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '城市', key: 'city', width: 100 },
{ title: '入职日期', key: 'date', width: 120 },
{ title: '状态', key: 'status', width: 80 }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', city: '北京', date: '2022-01-15', status: '在职' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', city: '上海', date: '2021-06-20', status: '在职' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', city: '广州', date: '2023-03-10', status: '在职' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', city: '深圳', date: '2024-01-05', status: '实习' }
])
</script>
<style scoped>
.demo-wrapper {
display: flex;
flex-direction: column;
gap: 12px;
}
.trigger-btn {
align-self: flex-start;
padding: 6px 14px;
border: 1px solid #409eff;
background: #fff;
border-radius: 4px;
color: #409eff;
cursor: pointer;
font-size: 13px;
}
.trigger-btn:hover {
background: #ecf5ff;
}
</style>固定列
通过设置列的 fixed 属性可以将列固定在左侧或右侧。固定列在横向滚动时始终保持可见,非常适合列数较多且有关键列(如姓名、操作列)需要始终展示的场景。fixed 可选值为 'left'(固定左侧)、'right'(固定右侧)或 true(等同于 'left')。建议同时为固定列设置明确的 width,否则可能无法正确固定。
工号 | 姓名 | 部门 | 职位 | 省份 | 城市 | 详细地址 | 邮编 | 联系电话 | 邮箱 | 入职日期 | 状态 | 操作 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
1001 | 张三 | 技术部 | 前端工程师 | 北京市 | 北京市 | 朝阳区建国路88号SOHO现代城A座1201室 | 100022 | 13800138001 | zhangsan@example-company.com | 2022-01-15 | 在职 | |
1002 | 李四 | 产品部 | 产品经理 | 上海市 | 上海市 | 浦东新区世纪大道100号环球金融中心B座802室 | 200120 | 13800138002 | lisi@example-company.com | 2021-06-20 | 在职 | |
1003 | 王五 | 设计部 | UI设计师 | 广东省 | 广州市 | 天河区天河路123号太古汇写字楼18层 | 510620 | 13800138003 | wangwu@example-company.com | 2023-03-10 | 在职 | |
1004 | 赵六 | 市场部 | 市场专员 | 浙江省 | 杭州市 | 西湖区文一路88号浙江大学科技园 | 310012 | 13800138004 | zhaoliu@example-company.com | 2024-01-05 | 实习 |
<template>
<Table :data="tableData" :columns="columns" border>
<template #action="{ row }">
<button class="btn-action" @click="handleEdit(row)">编辑</button>
<button class="btn-action btn-danger" @click="handleDelete(row)">删除</button>
</template>
</Table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '工号', key: 'id', width: 80, fixed: 'left' },
{ title: '姓名', key: 'name', width: 100, fixed: 'left' },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '省份', key: 'province', width: 100 },
{ title: '城市', key: 'city', width: 100 },
{ title: '详细地址', key: 'address', width: 240 },
{ title: '邮编', key: 'zip', width: 100 },
{ title: '联系电话', key: 'phone', width: 130 },
{ title: '邮箱', key: 'email', width: 220 },
{ title: '入职日期', key: 'date', width: 120 },
{ title: '状态', key: 'status', width: 80 },
{ title: '操作', key: 'action', slot: 'action', width: 160, fixed: 'right', ellipsis: false }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', province: '北京市', city: '北京市', address: '朝阳区建国路88号SOHO现代城A座1201室', zip: '100022', phone: '13800138001', email: 'zhangsan@example-company.com', date: '2022-01-15', status: '在职' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', province: '上海市', city: '上海市', address: '浦东新区世纪大道100号环球金融中心B座802室', zip: '200120', phone: '13800138002', email: 'lisi@example-company.com', date: '2021-06-20', status: '在职' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', province: '广东省', city: '广州市', address: '天河区天河路123号太古汇写字楼18层', zip: '510620', phone: '13800138003', email: 'wangwu@example-company.com', date: '2023-03-10', status: '在职' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', province: '浙江省', city: '杭州市', address: '西湖区文一路88号浙江大学科技园', zip: '310012', phone: '13800138004', email: 'zhaoliu@example-company.com', date: '2024-01-05', status: '实习' }
])
const handleEdit = (row: any) => console.log('编辑', row)
const handleDelete = (row: any) => console.log('删除', row)
</script>
<style scoped>
button {
padding: 4px 10px;
margin-right: 4px;
border: 1px solid #dcdfe6;
background: #fff;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
color: #606266;
}
button:last-child { margin-right: 0; }
button:hover {
color: #409eff;
border-color: #c6e2ff;
background: #ecf5ff;
}
.btn-danger:hover {
color: #f56c6c;
border-color: #fbc4c4;
background: #fef0f0;
}
</style>多级表头
通过在列配置中使用 children 属性可以创建多级表头结构。含有 children 的列会作为父级表头,其下的子列会在下方渲染为多行表头。子列同样支持 sortable、fixed、width 等属性。多级表头常用于对列进行分组归类,提升大表格的可读性。
基本信息 | 职位信息 | 详细地址 | 入职日期 | |||
|---|---|---|---|---|---|---|
工号 | 姓名 | 部门 | 职位 | 城市 | ||
1001 | 张三 | 技术部 | 前端工程师 | 北京 | 北京市朝阳区建国路88号SOHO现代城 | 2022-01-15 |
1002 | 李四 | 产品部 | 产品经理 | 上海 | 上海市浦东新区世纪大道100号 | 2021-06-20 |
1003 | 王五 | 设计部 | UI设计师 | 广州 | 广州市天河区天河路123号太古汇 | 2023-03-10 |
1004 | 赵六 | 市场部 | 市场专员 | 深圳 | 深圳市南山区科技路66号高新产业园 | 2024-01-05 |
<template>
<Table :data="tableData" :columns="columns" border />
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{
title: '基本信息',
children: [
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100 }
]
},
{
title: '职位信息',
children: [
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '城市', key: 'city', width: 100 }
]
},
{ title: '详细地址', key: 'address', width: 220 },
{ title: '入职日期', key: 'date', width: 120 }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', city: '北京', address: '北京市朝阳区建国路88号SOHO现代城', date: '2022-01-15' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', city: '上海', address: '上海市浦东新区世纪大道100号', date: '2021-06-20' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', city: '广州', address: '广州市天河区天河路123号太古汇', date: '2023-03-10' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', city: '深圳', address: '深圳市南山区科技路66号高新产业园', date: '2024-01-05' }
])
</script>自定义列模板
通过设置列的 slot 属性指定插槽名称后,即可在 Table 组件上使用具名插槽自定义该列的渲染内容。插槽作用域提供 { row, column, rowIndex } 三个参数,分别对应当前行数据、列配置和行索引。这种方式适合在单元格中渲染按钮、标签、图标等复杂内容。操作列是典型的自定义列模板场景。
工号 | 姓名 | 部门 | 职位 | 城市 | 入职日期 | 状态 | 操作 |
|---|---|---|---|---|---|---|---|
1001 | 张三 | 技术部 | 前端工程师 | 北京 | 2022-01-15 | 在职 | |
1002 | 李四 | 产品部 | 产品经理 | 上海 | 2021-06-20 | 在职 | |
1003 | 王五 | 设计部 | UI设计师 | 广州 | 2023-03-10 | 在职 | |
1004 | 赵六 | 市场部 | 市场专员 | 深圳 | 2024-01-05 | 实习 |
<template>
<Table :data="tableData" :columns="columns" border>
<template #status="{ row }">
<span :class="row.status === '在职' ? 'tag-success' : 'tag-warning'">
{{ row.status }}
</span>
</template>
<template #action="{ row }">
<button class="btn-action" @click="handleEdit(row)">编辑</button>
<button class="btn-action btn-danger" @click="handleDelete(row)">删除</button>
</template>
</Table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100 },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '城市', key: 'city', width: 100 },
{ title: '入职日期', key: 'date', width: 120 },
{ title: '状态', key: 'status', slot: 'status', width: 90, align: 'center' },
{ title: '操作', key: 'action', slot: 'action', width: 160, ellipsis: false }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', city: '北京', date: '2022-01-15', status: '在职' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', city: '上海', date: '2021-06-20', status: '在职' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', city: '广州', date: '2023-03-10', status: '在职' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', city: '深圳', date: '2024-01-05', status: '实习' }
])
const handleEdit = (row: any) => console.log('编辑', row)
const handleDelete = (row: any) => console.log('删除', row)
</script>
<style scoped>
.tag-success {
display: inline-block;
padding: 2px 8px;
background: #f0f9eb;
color: #67c23a;
border-radius: 4px;
font-size: 12px;
}
.tag-warning {
display: inline-block;
padding: 2px 8px;
background: #fdf6ec;
color: #e6a23c;
border-radius: 4px;
font-size: 12px;
}
button {
padding: 4px 10px;
margin-right: 4px;
border: 1px solid #dcdfe6;
background: #fff;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
color: #606266;
}
button:last-child { margin-right: 0; }
button:hover {
color: #409eff;
border-color: #c6e2ff;
background: #ecf5ff;
}
.btn-danger:hover {
color: #f56c6c;
border-color: #fbc4c4;
background: #fef0f0;
}
</style>单元格省略与 Tooltip
当单元格内容过长时,默认会进行文本省略(ellipsis 默认为 true)。同时会自动使用 Tooltip 在鼠标悬停时显示完整内容。可以通过设置 tooltip: false 关闭 Tooltip,或设置 tooltip: true 强制开启。tooltipConfig 属性可以统一配置 Tooltip 的行为,如 placement(位置)、showDelay(显示延迟毫秒数)。
工号 | 姓名 | 部门 | 职位 | 邮箱 | 公司地址 | 操作 |
|---|---|---|---|---|---|---|
1001 | 张三 | 技术部 | 前端工程师 | zhangsan.developer@example-company.com | 北京市朝阳区建国路88号SOHO现代城A座1201室 | |
1002 | 李四 | 产品部 | 产品经理 | lisi.product@example-company.com | 上海市浦东新区世纪大道100号上海环球金融中心B座802室 | |
1003 | 王五 | 设计部 | UI设计师 | wangwu.design@example-company.com | 广州市天河区天河路123号太古汇写字楼18层 | |
1004 | 赵六 | 市场部 | 市场专员 | zhaoliu.marketing@example-company.com | 深圳市南山区科技路66号高新产业园3号楼5层 |
<template>
<Table :data="tableData" :columns="columns" border>
<template #action="{ row }">
<button @click="handleEdit(row)">编辑</button>
<button @click="handleDelete(row)">删除</button>
</template>
</Table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '工号', key: 'id', width: 80 },
{ title: '姓名', key: 'name', width: 100 },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '邮箱', key: 'email', width: 220 },
{ title: '公司地址', key: 'address', width: 260 },
{ title: '操作', key: 'action', slot: 'action', width: 160, ellipsis: false }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', email: 'zhangsan.developer@example-company.com', address: '北京市朝阳区建国路88号SOHO现代城A座1201室' },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', email: 'lisi.product@example-company.com', address: '上海市浦东新区世纪大道100号上海环球金融中心B座802室' },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', email: 'wangwu.design@example-company.com', address: '广州市天河区天河路123号太古汇写字楼18层' },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', email: 'zhaoliu.marketing@example-company.com', address: '深圳市南山区科技路66号高新产业园3号楼5层' }
])
const handleEdit = (row: any) => console.log('编辑', row)
const handleDelete = (row: any) => console.log('删除', row)
</script>
<style scoped>
button {
padding: 4px 10px;
margin-right: 4px;
border: 1px solid #dcdfe6;
background: #fff;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
color: #606266;
}
button:last-child { margin-right: 0; }
button:hover {
color: #409eff;
border-color: #c6e2ff;
background: #ecf5ff;
}
</style>复杂示例
综合运用排序、选择、索引、展开、固定列、分页、斑马纹等特性。该示例展示了 Table 组件在真实业务场景中的典型用法,包含了操作列的状态渲染、行操作按钮以及展开行的详细内容。
展开 | 序号 | 工号 | 姓名 | 部门 | 职位 | 城市 | 详细地址 | 入职日期 | 年龄 | 状态 | 操作 | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 1005 | 钱七 | 运营部 | 运营总监 | 杭州 | 杭州市西湖区文一路88号浙江大学科技园 | 2020-11-30 | 35 | 休假中 | |||
| 2 | 1002 | 李四 | 产品部 | 产品经理 | 上海 | 上海市浦东新区世纪大道100号上海环球金融中心 | 2021-06-20 | 30 | 休假中 | |||
| 3 | 1003 | 王五 | 设计部 | UI设计师 | 广州 | 广州市天河区天河路123号太古汇写字楼 | 2023-03-10 | 28 | 正常 |
<template>
<Table
:data="tableData"
:columns="columns"
border
stripe
:pagination="{ pageSize: 3, currentPage: 1 }"
:defaultSort="{ prop: 'age', order: 'descending' }"
>
<template #status="{ row }">
<span :class="`tag tag-${row.status}`">{{ statusMap[row.status] }}</span>
</template>
<template #action="{ row }">
<button class="btn-action" @click="handleView(row)">查看</button>
<button class="btn-action" @click="handleEdit(row)">编辑</button>
<button class="btn-action btn-danger" @click="handleDelete(row)">删除</button>
</template>
<template #expand="{ row }">
<div class="expand-box">
<p><strong>详细地址:</strong>{{ row.address }}</p>
<p><strong>入职时间:</strong>{{ row.date }}</p>
<p><strong>员工备注:</strong>该员工入职以来表现良好,积极配合工作,按时完成任务,具备良好的团队协作精神。</p>
</div>
</template>
</Table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import Table from '@/components/Table/Table.vue'
const columns = ref([
{ title: '选择', type: 'selection', width: 50 },
{ title: '序号', type: 'index', width: 60 },
{ title: '展开', type: 'expand', expandSlot: 'expand', width: 60 },
{ title: '工号', key: 'id', width: 80, fixed: 'left' },
{ title: '姓名', key: 'name', width: 100, fixed: 'left', sortable: true },
{ title: '部门', key: 'dept', width: 120 },
{ title: '职位', key: 'role', width: 120 },
{ title: '城市', key: 'city', width: 100 },
{ title: '详细地址', key: 'address', width: 220, ellipsis: true },
{ title: '入职日期', key: 'date', width: 120 },
{ title: '年龄', key: 'age', width: 80, sortable: true, align: 'center' },
{ title: '状态', key: 'status', slot: 'status', width: 100, align: 'center' },
{ title: '操作', key: 'action', slot: 'action', width: 220, fixed: 'right', ellipsis: false }
])
const tableData = ref([
{ id: '1001', name: '张三', dept: '技术部', role: '前端工程师', city: '北京', address: '北京市朝阳区建国路88号SOHO现代城A座1201室', date: '2022-01-15', age: 25, status: 1 },
{ id: '1002', name: '李四', dept: '产品部', role: '产品经理', city: '上海', address: '上海市浦东新区世纪大道100号上海环球金融中心', date: '2021-06-20', age: 30, status: 2 },
{ id: '1003', name: '王五', dept: '设计部', role: 'UI设计师', city: '广州', address: '广州市天河区天河路123号太古汇写字楼', date: '2023-03-10', age: 28, status: 1 },
{ id: '1004', name: '赵六', dept: '市场部', role: '市场专员', city: '深圳', address: '深圳市南山区科技路66号高新产业园', date: '2024-01-05', age: 22, status: 3 },
{ id: '1005', name: '钱七', dept: '运营部', role: '运营总监', city: '杭州', address: '杭州市西湖区文一路88号浙江大学科技园', date: '2020-11-30', age: 35, status: 2 },
{ id: '1006', name: '孙八', dept: '财务部', role: '财务经理', city: '南京', address: '南京市鼓楼区中山北路100号鼓楼医院旁', date: '2022-08-15', age: 27, status: 1 }
])
const statusMap: Record<number, string> = { 1: '正常', 2: '休假中', 3: '离职' }
const handleView = (row: any) => console.log('查看', row)
const handleEdit = (row: any) => console.log('编辑', row)
const handleDelete = (row: any) => console.log('删除', row)
</script>
<style scoped>
.expand-box {
padding: 12px 16px;
background: #f5f7fa;
}
.expand-box p {
margin: 0 0 6px;
font-size: 13px;
color: #606266;
}
.expand-box p:last-child { margin-bottom: 0; }
.tag {
display: inline-block;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
}
.tag-1 { background: #f0f9eb; color: #67c23a; }
.tag-2 { background: #fdf6ec; color: #e6a23c; }
.tag-3 { background: #fef0f0; color: #f56c6c; }
button {
padding: 4px 10px;
margin-right: 4px;
border: 1px solid #dcdfe6;
background: #fff;
border-radius: 4px;
cursor: pointer;
font-size: 12px;
color: #606266;
}
button:last-child { margin-right: 0; }
button:hover {
color: #409eff;
border-color: #c6e2ff;
background: #ecf5ff;
}
.btn-danger:hover {
color: #f56c6c;
border-color: #fbc4c4;
background: #fef0f0;
}
</style>API
Table Attributes
| Name | Description | Type | Default |
|---|---|---|---|
| data | 表格数据,为一个对象数组,每个对象代表一行数据 | array | [] |
| columns | 列配置数组,每个对象描述一列的基本属性和行为 | array | [] |
| border | 是否添加纵向边框 | boolean | false |
| stripe | 是否显示斑马纹效果 | boolean | false |
| loading | 是否显示加载状态遮罩 | boolean | false |
| size | 表格尺寸,影响单元格内边距大小,可选值为 'large'、'default'、'small' | string | default |
| height | 固定表头高度,传入数值时单位为 px,也可传入带单位的字符串 | number / string | — |
| maxHeight | 表格最大高度,超出后在表格容器内显示滚动条 | number / string | — |
| defaultSort | 初始排序状态,prop 为排序字段名,order 为 'ascending' 或 'descending' | object | — |
| pagination | 分页配置,对象包含 pageSize(每页条数)和 currentPage(当前页) | object | — |
| expandKey | 展开行的唯一标识字段名,用于追踪展开状态 | string | id |
| rowClass | 自定义行类名,传入字符串或函数,函数参数为 (row, rowIndex) | string / function | — |
| rowStyle | 自定义行样式,传入样式对象或函数,函数参数为 (row, rowIndex) | object / function | — |
| cellClass | 自定义单元格类名,函数参数为 (row, column, rowIndex, colIndex) | function | — |
| cellStyle | 自定义单元格样式,函数参数为 (row, column, rowIndex, colIndex) | function | — |
| tooltipConfig | 单元格 Tooltip 配置,可配置 placement(位置)、showDelay、hideDelay | object | — |
TableColumn Attributes
| Name | Description | Type | Default |
|---|---|---|---|
| title | 列头显示的文字 | string | — |
| key | 对应 data 中的字段名,用于取单元格值 | string | — |
| width | 列宽度,单位为 px,建议为固定列和关键列设置明确宽度 | number | — |
| align | 单元格文本对齐方式,可选 'left'(居左)、'center'(居中)、'right'(居右) | string | left |
| slot | 插槽名称,定义后可在 Table 上使用具名插槽自定义该列渲染内容 | string | — |
| sortable | 是否启用该列的点击排序功能 | boolean | false |
| fixed | 固定列位置,可选 'left'(固定左侧)、'right'(固定右侧)或 true(固定左侧) | string / boolean | false |
| type | 列类型,可选 'selection'(复选框)、'index'(行号)、'expand'(展开列) | string | — |
| expandSlot | 展开行的插槽名称,与 type: 'expand' 配合使用 | string | expand |
| ellipsis | 单元格文本过长时是否省略并用 Tooltip 显示完整内容,默认为 true | boolean | true |
| tooltip | 是否强制启用 Tooltip 显示溢出内容,覆盖 ellipsis 的默认行为 | boolean | — |
| className | 自定义该列所有单元格的类名 | string | — |
| style | 自定义该列所有单元格的样式对象 | object | — |
| children | 子列配置数组,用于构建多级表头,含此属性的列作为父级表头 | array | — |
Table Events
| Name | Description | Type |
|---|---|---|
| — | Table 组件当前版本暂未暴露事件,后续版本将补充 selection-change 等事件 | — |
Table Slots
| Name | Description | Scope |
|---|---|---|
| (自定义列插槽) | 通过 columns 中各列的 slot 属性指定插槽名称后,在此定义对应插槽的内容 | { row, column, rowIndex } |
| expand | 行展开后的详细内容插槽,与 type: 'expand' 列配合使用 | { row, rowIndex } |
Column Type 可选值
| Value | Description |
|---|---|
| selection | 复选框列,用于多行选择,表头复选框为全选功能 |
| index | 索引列,显示从 1 开始的行号,配合分页时自动计算偏移 |
| expand | 展开列,点击可展开/收起该行,显示详细信息 |
Column Fixed 可选值
| Value | Description |
|---|---|
| left | 固定在左侧,横向滚动时始终可见 |
| right | 固定在右侧,横向滚动时始终可见 |
| true | 等同于 'left',固定在左侧 |
| false | 不固定,随表格横向滚动(默认值) |
Column Align 可选值
| Value | Description |
|---|---|
| left | 文本居左对齐,单元格内容靠左显示 |
| center | 文本居中对齐,单元格内容居中显示 |
| right | 文本居右对齐,单元格内容靠右显示 |
Table Size 可选值
| Value | Description |
|---|---|
| large | 大尺寸,单元格内边距较大,适合数据密度低的场景 |
| default | 默认尺寸,中等边距,适合大多数场景 |
| small | 小尺寸,单元格内边距紧凑,适合数据密度高的场景 |