Table
A table component for displaying structured list data.
Basic Usage
Name | Age | Date of Birth | Address |
|---|
<template>
<b-table :columns="columns" :data="data">
<template #age="{ row }">
<b-tag>{{ row.age }}</b-tag>
</template>
</b-table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', slot: 'age' },
{ title: 'Date of Birth', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const data = ref([
{
name: 'John Brown',
age: 18,
birthday: '1990-04-22',
address: 'New York No. 1 Lake Park'
},
{
name: 'Jim Green',
age: 25,
birthday: '1990-11-11',
address: 'London No. 1 Lake Park'
},
{
name: 'Joe Black',
age: 30,
birthday: '1985-02-05',
address: 'Sydney No. 1 Lake Park'
},
{
name: 'Jon Snow',
age: 26,
birthday: '1993-07-11',
address: 'Ottawa No. 2 Lake Park'
},
{
name: 'Jim Raynor',
age: 33,
birthday: '1999-12-12',
address: 'Moscow No. 3 Lake Park'
}
])
</script>Striped Rows
Set stripe to enable alternating row colors.
Name | Age | Date of Birth | Address |
|---|
<template>
<b-table :columns="columns" :data="data" stripe>
<template #age="{ row }">
<b-tag>{{ row.age }}</b-tag>
</template>
</b-table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', slot: 'age' },
{ title: 'Date of Birth', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const data = ref([
{
name: 'John Brown',
age: 18,
birthday: '1990-04-22',
address: 'New York No. 1 Lake Park'
},
{
name: 'Jim Green',
age: 25,
birthday: '1990-11-11',
address: 'London No. 1 Lake Park'
},
{
name: 'Joe Black',
age: 30,
birthday: '1985-02-05',
address: 'Sydney No. 1 Lake Park'
},
{
name: 'Jon Snow',
age: 26,
birthday: '1993-07-11',
address: 'Ottawa No. 2 Lake Park'
},
{
name: 'Jim Raynor',
age: 33,
birthday: '1999-12-12',
address: 'Moscow No. 3 Lake Park'
}
])
</script>Border
Set border to enable vertical borders.
Name | Age | Date of Birth | Address |
|---|
<template>
<b-table :columns="columns" :data="data" border>
<template #age="{ row }">
<b-tag>{{ row.age }}</b-tag>
</template>
</b-table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', slot: 'age' },
{ title: 'Date of Birth', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const data = ref([
{
name: 'John Brown',
age: 18,
birthday: '1990-04-22',
address: 'New York No. 1 Lake Park'
},
{
name: 'Jim Green',
age: 25,
birthday: '1990-11-11',
address: 'London No. 1 Lake Park'
},
{
name: 'Joe Black',
age: 30,
birthday: '1985-02-05',
address: 'Sydney No. 1 Lake Park'
},
{
name: 'Jon Snow',
age: 26,
birthday: '1993-07-11',
address: 'Ottawa No. 2 Lake Park'
},
{
name: 'Jim Raynor',
age: 33,
birthday: '1999-12-12',
address: 'Moscow No. 3 Lake Park'
}
])
</script>Text Overflow Tooltip
Set tooltip on a column to truncate long text and show the full content on hover. If there is only one table on the page, you can also set tooltip-theme for a more polished tooltip style.
Note: Columns with tooltip enabled should use a fixed width such as width; otherwise text truncation and tooltip alignment may become inconsistent in auto-width layouts.
Note: Table cells use overflow: hidden, so tooltip nodes must be appended to body. When many table instances are cached at the same time, this can create extra nodes and affect performance, so enable it only when needed.
Name | Age | Birthday | Address | Remarks |
|---|
Name | Age | Birthday | Address | Remarks |
|---|
<template>
<div>
<b-divider align="left">Default原生title</b-divider>
<b-table :columns="columns" :data="data" border></b-table>
<b-divider align="left">开启tooltip</b-divider>
<b-table :columns="columns" :data="data" border tooltip-theme="dark"></b-table>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Birthday', key: 'birthday' },
{ title: 'Address', key: 'address' },
{ title: 'Remarks', key: 'remark', width: 300, tooltip: true }
]
const data = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
address: 'BeijingCity朝阳区芍药居',
remark:
'这是一段描述文字,文本长度会超出Column宽,设置tooltipproperty可以设置不换Rowshow并开启mousehovershow所有文字。'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
address: 'BeijingCity海淀区西二旗',
remark:
'这是一段描述文字,文本长度会超出Column宽,设置tooltipproperty可以设置不换Rowshow并开启mousehovershow所有文字。'
},
{
name: '李小红',
age: 30,
birthday: '1985-02-05',
address: 'ShanghaiCity浦东New区世纪大道',
remark:
'这是一段描述文字,文本长度会超出Column宽,设置tooltipproperty可以设置不换Rowshow并开启mousehovershow所有文字。'
},
{
name: '周小伟',
age: 26,
birthday: '1993-07-11',
address: 'ShenzhenCity南山区深南大道',
remark:
'这是一段描述文字,文本长度会超出Column宽,设置tooltipproperty可以设置不换Rowshow并开启mousehovershow所有文字。'
},
{
name: '张小发',
age: 33,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道',
remark:
'这是一段描述文字,文本长度会超出Column宽,设置tooltipproperty可以设置不换Rowshow并开启mousehovershow所有文字。'
}
])
</script>Fixed Header
Use height or max-height to fix the table header. The table body scrolls through the built-in scrollbar, and fixed columns stay in sync with the body scroll position.
Name | Age | Birthday | Address |
|---|
<template>
<b-table :columns="columns" :data="data" height="200" border></b-table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Birthday', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const data = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
address: 'BeijingCity朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
address: 'BeijingCity海淀区西二旗'
},
{
name: '李小红',
age: 30,
birthday: '1985-02-05',
address: 'ShanghaiCity浦东New区世纪大道'
},
{
name: '周小伟',
age: 26,
birthday: '1993-07-11',
address: 'ShenzhenCity南山区深南大道'
},
{
name: '张小发',
age: 33,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道'
}
])
</script>Fixed Header and Columns
Both header and columns can be fixed simultaneously.
<template>
<div style="width: 800px">
<b-table :columns="columns" :data="data" height="200" border></b-table>
</div>
</template>
<script setup lang="ts">
import { ref, h } from 'vue'
const columns = [
{
title: 'Name',
fixed: 'left',
key: 'name',
width: 150
},
{
title: 'Age',
key: 'age',
width: 150
},
{
title: 'Birthday',
key: 'birthday',
width: 150
},
{
title: 'Address',
key: 'address',
width: 350
},
{
title: 'Actions',
fixed: 'right',
width: 100,
render: () => {
return h('a', { style: { cursor: 'pointer' } }, 'Edit')
}
}
]
const data = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
address: 'BeijingCity朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
address: 'BeijingCity海淀区西二旗'
},
{
name: '李小红',
age: 30,
birthday: '1985-02-05',
address: 'ShanghaiCity浦东New区世纪大道'
},
{
name: '周小伟',
age: 26,
birthday: '1993-07-11',
address: 'ShenzhenCity南山区深南大道'
},
{
name: '张小发',
age: 33,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道'
}
])
</script>Auto Height
Name | Age | Birthday | Address | Actions |
|---|
<template>
<div style="padding: 2px">
<b-table :columns="columns" :data="data" max-height="200" border>
<template #ctrl="{ index }">
<b-button type="danger" size="mini" plain @click="removeRow(index)">Delete</b-button>
</template>
</b-table>
<br />
<b-button @click="add">增加一条数据</b-button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age', align: 'center' },
{ title: 'Birthday', key: 'birthday' },
{ title: 'Address', key: 'address', width: 350 },
{ title: 'Actions', slot: 'ctrl', width: 100 }
]
const data = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
address: 'BeijingCity朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
address: 'BeijingCity海淀区西二旗'
}
])
function add() {
data.value.push({
name: '张小发',
age: 33,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道'
})
}
function removeRow(index) {
data.value.splice(index, 1)
}
</script>Single Selection
Name | Age | Birthday | Address |
|---|
Name | Age | Birthday | Address | Actions |
|---|
<template>
<div>
<b-table
ref="currentRowTable"
:columns="columns"
:data="data"
highlight-row
highlight-row-cancel
@current-change="currentRowChange"
></b-table>
<br />
<div>
<b-button @click="clearSelect">清除Single select</b-button>
<b-button @click="clickRow(0)">select第一Row</b-button>
</div>
<br />
<b-table
ref="currentRowTable2"
:columns="columns2"
:data="data2"
highlight-row
@current-change="currentRowChange"
>
<template #ctrl="{ index }">
<b-button type="text" text-color="danger" @click="removeRow(index)">Delete</b-button>
</template>
</b-table>
<br />
<b-button @click="init">初始化table2并Defaultselect第一Row</b-button>
</div>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue'
import { Message } from 'bin-ui-design'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Birthday', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const columns2 = [
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Birthday', key: 'birthday' },
{ title: 'Address', key: 'address' },
{ title: 'Actions', slot: 'ctrl' }
]
const data = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
address: 'BeijingCity朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
address: 'BeijingCity海淀区西二旗'
},
{
name: '李小红',
age: 30,
birthday: '1985-02-05',
address: 'ShanghaiCity浦东New区世纪大道'
},
{
name: '周小伟',
age: 26,
birthday: '1993-07-11',
address: 'ShenzhenCity南山区深南大道'
},
{
name: '张小发',
age: 33,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道'
}
])
const data2 = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
address: 'BeijingCity朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
address: 'BeijingCity海淀区西二旗'
},
{
name: '李小红',
age: 30,
birthday: '1985-02-05',
address: 'ShanghaiCity浦东New区世纪大道'
},
{
name: '周小伟',
age: 26,
birthday: '1993-07-11',
address: 'ShenzhenCity南山区深南大道'
},
{
name: '张小发',
age: 33,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道'
}
])
const currentRowTable = ref(null)
const currentRowTable2 = ref(null)
function currentRowChange(currentRow, oldRow, index) {
console.log(currentRow, oldRow, index)
if (index >= 0) {
Message(`select了第${index + 1}Row`)
}
}
function clearSelect() {
currentRowTable.value.clearCurrentRow()
}
// select某一Row
function clickRow(index) {
currentRowTable.value.clickCurrentRow(index)
}
function init() {
data2.value = JSON.parse(JSON.stringify(data.value))
nextTick(() => {
currentRowTable2.value.clickCurrentRow(0)
})
}
function removeRow(index) {
data2.value.splice(index, 1)
}
</script>Multiple Selection
Enable multi-selection by adding a column with type: 'selection'.
Set the special key _checked: true on a data row to pre-select it.
Set the special key _disabled: true on a data row to disable its selection.
@select: triggers when a row is selected. Returns selection and row — the selected rows and the most recently selected row. @select-all: triggers when all items are selected. Returns selection — all selected rows. @selection-change: triggers whenever the selection changes. Returns selection — all selected rows.
Name | Age | Birthday | Address |
|---|
<template>
<div>
<b-table ref="tableRef" :columns="columns" :data="data" highlight-row></b-table>
<br />
<b-button @click="$refs.tableRef.selectAll(true)">设置全选</b-button>
<b-button @click="$refs.tableRef.selectAll(false)">Cancel全选</b-button>
<b-button @click="getAllSelected">获取select</b-button>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ type: 'selection', width: 60, align: 'center' },
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Birthday', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const data = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
address: 'BeijingCity朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
address: 'BeijingCity海淀区西二旗'
},
{
name: '李小红',
age: 30,
birthday: '1985-02-05',
address: 'ShanghaiCity浦东New区世纪大道'
},
{
name: '周小伟',
age: 26,
birthday: '1993-07-11',
address: 'ShenzhenCity南山区深南大道'
},
{
name: '张小发',
age: 33,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道'
}
])
const tableRef = ref(null)
function getAllSelected() {
const selected = tableRef.value.getSelection()
console.log(selected)
}
</script>Expandable Rows
Enable expandable rows by adding a column with type: 'expand'.
Name | Age | Date of Birth | Detailed Address |
|---|
<template>
<b-table :columns="columns" :data="data"></b-table>
</template>
<script setup lang="ts">
import { ref, h } from 'vue'
const columns = [
{
type: 'expand',
width: 50,
render: params => {
return h('div', 'Detailed Address: ' + params.row.address)
}
},
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Date of Birth', key: 'birthday' },
{ title: 'Detailed Address', key: 'address' }
]
const data = ref([
{
name: 'John Brown',
age: 18,
birthday: '1990-04-22',
address: 'New York No. 1 Lake Park'
},
{
name: 'Jim Green',
age: 25,
birthday: '1990-11-11',
address: 'London No. 1 Lake Park'
},
{
name: 'Joe Black',
age: 30,
birthday: '1985-02-05',
address: 'Sydney No. 1 Lake Park'
},
{
name: 'Jon Snow',
age: 26,
birthday: '1993-07-11',
address: 'Ottawa No. 2 Lake Park'
},
{
name: 'Jim Raynor',
age: 33,
birthday: '1999-12-12',
address: 'Moscow No. 3 Lake Park'
}
])
</script>Tree Table
Tree table mode always uses children as the nested field. It is enabled only when row-key is a string and expand-column-key is also provided.
Supports:
default-expanded-row-keysfor uncontrolled default expansionexpanded-row-keys+@update:expanded-row-keysfor controlled expansion- Sorting within sibling groups while preserving the tree hierarchy
- Fixed columns and fixed-height scrolling
Current limits:
- Not supported together with
type: 'expand' - Not supported together with
draggable - Not recommended together with
mergeMethod
The first table expands East China by default. The second table shows controlled expanded keys with fixed columns.
Department / Member | City | Monthly Deals |
|---|
Department / Member | City | Role | Monthly Deals | Status |
|---|
Department / Member |
|---|
East China |
Retail Team |
Channel Team |
South China |
Status |
|---|
Stable |
Stable |
Stable |
Stable |
<template>
<div>
<p class="mb-12">
The first table expands East China by default. The second table shows controlled
expanded keys with fixed columns.
</p>
<b-table
class="mb-24"
border
:columns="basicColumns"
:data="treeData"
row-key="id"
expand-column-key="name"
:default-expanded-row-keys="[1001]"
></b-table>
<div class="mb-12">
<b-button size="small" @click="expandedRowKeys = [1001, 1002]">Expand all</b-button>
<b-button size="small" class="ml-8" @click="expandedRowKeys = []">Collapse all</b-button>
<b-button size="small" class="ml-8" @click="expandedRowKeys = [1001]">
Reset default
</b-button>
</div>
<div class="mb-12">Expanded keys: {{ expandedRowKeys.join(', ') || '-' }}</div>
<b-table
border
height="260"
:columns="fixedColumns"
:data="treeData"
row-key="id"
expand-column-key="name"
:expanded-row-keys="expandedRowKeys"
@update:expanded-row-keys="expandedRowKeys = $event"
></b-table>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const basicColumns = [
{
title: 'Department / Member',
key: 'name',
minWidth: 220
},
{
title: 'City',
key: 'city',
minWidth: 140
},
{
title: 'Monthly Deals',
key: 'amount',
width: 140,
sortable: true
}
]
const fixedColumns = [
{
title: 'Department / Member',
key: 'name',
minWidth: 220,
fixed: 'left'
},
{
title: 'City',
key: 'city',
minWidth: 140
},
{
title: 'Role',
key: 'role',
minWidth: 140
},
{
title: 'Monthly Deals',
key: 'amount',
width: 140,
sortable: true
},
{
title: 'Status',
key: 'status',
width: 120,
fixed: 'right'
}
]
const treeData = [
{
id: 1001,
name: 'East China',
city: 'Shanghai',
role: 'Region',
amount: 286,
status: 'Stable',
children: [
{
id: 1101,
name: 'Retail Team',
city: 'Shanghai',
role: 'Team',
amount: 168,
status: 'Stable',
children: [
{
id: 1111,
name: 'Mia Wang',
city: 'Shanghai',
role: 'Sales',
amount: 92,
status: 'Active'
},
{
id: 1112,
name: 'Iris Li',
city: 'Suzhou',
role: 'Sales',
amount: 76,
status: 'Active'
}
]
},
{
id: 1102,
name: 'Channel Team',
city: 'Hangzhou',
role: 'Team',
amount: 118,
status: 'Stable',
children: [
{
id: 1121,
name: 'Noah Zhou',
city: 'Hangzhou',
role: 'Sales',
amount: 63,
status: 'Following up'
},
{
id: 1122,
name: 'Lena Chen',
city: 'Ningbo',
role: 'Sales',
amount: 55,
status: 'Following up'
}
]
}
]
},
{
id: 1002,
name: 'South China',
city: 'Shenzhen',
role: 'Region',
amount: 214,
status: 'Stable',
children: [
{
id: 1201,
name: 'Customer Success',
city: 'Shenzhen',
role: 'Team',
amount: 126,
status: 'Stable',
children: [
{
id: 1211,
name: 'Leo Zhang',
city: 'Shenzhen',
role: 'Sales',
amount: 74,
status: 'Active'
},
{
id: 1212,
name: 'Evan Zhao',
city: 'Guangzhou',
role: 'Sales',
amount: 52,
status: 'Active'
}
]
}
]
}
]
const expandedRowKeys = ref([1001])
</script>Grouped Header
Use children in column definitions to group table headers. For merged headers and cells, the border mode is recommended.
Name | 基本Info | EducationInfo | |||
|---|---|---|---|---|---|
Age | Birthday | 详细Address | 毕业院校 | 毕业Day期 | |
<template>
<b-table :columns="columns" height="300" :data="data" border></b-table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{
title: '基本Info',
align: 'center',
children: [
{ title: 'Age', key: 'age' },
{ title: 'Birthday', key: 'birthday' },
{ title: '详细Address', key: 'address' }
]
},
{
title: 'EducationInfo',
align: 'center',
children: [
{ title: '毕业院校', key: 'school' },
{ title: '毕业Day期', key: 'eduDate' }
]
}
]
const data = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
address: 'BeijingCity朝阳区芍药居',
school: 'Nanjing河海大学',
eduDate: '2012-04-22'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
address: 'BeijingCity海淀区西二旗',
school: 'Beijing大学',
eduDate: '2012-04-22'
},
{
name: '李小红',
age: 30,
birthday: '1985-02-05',
address: 'ShanghaiCity浦东New区世纪大道',
school: 'Shanghai复旦',
eduDate: '2012-04-22'
},
{
name: '周小伟',
age: 26,
birthday: '1993-07-11',
address: 'ShenzhenCity南山区深南大道',
school: '广东大学',
eduDate: '2012-04-22'
},
{
name: '张小发',
age: 33,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道',
school: 'Nanjing交通学院',
eduDate: '2012-04-22'
},
{
name: '李晓红',
age: 23,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道',
school: 'Nanjing交通学院',
eduDate: '2012-04-22'
},
{
name: '郭小宁',
age: 23,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道',
school: 'Nanjing交通学院',
eduDate: '2012-04-22'
}
])
</script>Row & Column Merging
Configure mergeMethod to specify row/column merging logic. The method parameters are four objects: row, column, rowIndex, columnIndex. The method returns an array of two elements: the first is rowspan, the second is colspan. It is recommended to use border mode for merged cells.
Name | Age | Birthday | Address |
|---|
<template>
<b-table :columns="columns" :data="data" border :merge-method="handleSpan"></b-table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Birthday', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const data = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
address: 'BeijingCity朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
address: 'BeijingCity海淀区西二旗'
},
{
name: '李小红',
age: 30,
birthday: '1985-02-05',
address: 'ShanghaiCity浦东New区世纪大道'
},
{
name: '周小伟',
age: 26,
birthday: '1993-07-11',
address: 'ShenzhenCity南山区深南大道'
},
{
name: '张小发',
age: 33,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道'
}
])
function handleSpan({ row, column, rowIndex, columnIndex }) {
// 获取相同Name的Row是,0,1
if (rowIndex === 0 && columnIndex === 0) {
return {
rowspan: 2,
colspan: 1
}
} else if (rowIndex === 1 && columnIndex === 0) {
return {
rowspan: 0,
colspan: 1
}
}
// 合并Column,这里将第三Row,周小伟的Day期和Address合并
if (rowIndex === 3 && columnIndex === 2) {
return [1, 2]
} else if (rowIndex === 3 && columnIndex === 3) {
return [0, 0]
}
}
</script>Sortable
Name | Age | Date of Birth | Address |
|---|
<template>
<b-table :columns="columns" :data="data"></b-table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age', sortable: true },
{ title: 'Date of Birth', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const data = ref([
{
name: 'John Brown',
age: 18,
birthday: '1990-04-22',
address: 'New York No. 1 Lake Park'
},
{
name: 'Jim Green',
age: 25,
birthday: '1990-11-11',
address: 'London No. 1 Lake Park'
},
{
name: 'Joe Black',
age: 30,
birthday: '1985-02-05',
address: 'Sydney No. 1 Lake Park'
},
{
name: 'Jon Snow',
age: 26,
birthday: '1993-07-11',
address: 'Ottawa No. 2 Lake Park'
},
{
name: 'Jim Raynor',
age: 33,
birthday: '1999-12-12',
address: 'Moscow No. 3 Lake Park'
}
])
</script>Inline Editing
Name | Age | Birthday | Hobby | Address | Actions |
|---|
<template>
<b-table :columns="columns" :data="data">
<template #name="{ index, row }">
<b-input
v-if="obj.editIndex === index"
v-model="obj.editName"
type="text"
size="small"
clearable
></b-input>
<span v-else>{{ row.name }}</span>
</template>
<template #age="{ index, row }">
<b-input-number
v-if="obj.editIndex === index"
v-model="obj.editAge"
type="text"
size="small"
></b-input-number>
<span v-else>{{ row.age }}</span>
</template>
<template #birthday="{ index, row }">
<b-date-picker
v-if="obj.editIndex === index"
v-model="obj.editBirthday"
size="small"
type="date"
placeholder="选择Day期"
></b-date-picker>
<span v-else>{{ row.birthday }}</span>
</template>
<template #hobby="{ index, row }">
<b-select v-if="obj.editIndex === index" v-model="obj.editHobby" clearable size="small">
<b-option v-for="(val, key) in hobbyMap" :key="key" :value="key" :label="val">
{{ val }}
</b-option>
</b-select>
<span v-else>{{ hobbyMap[row.hobby] }}</span>
</template>
<template #address="{ index, row }">
<b-input
v-if="obj.editIndex === index"
v-model="obj.editAddress"
type="text"
size="small"
></b-input>
<span v-else>{{ row.address }}</span>
</template>
<template #action="{ index, row }">
<div v-if="obj.editIndex === index">
<b-button size="small" type="success" plain @click="handleSave(index)">save</b-button>
<b-button size="small" @click="obj.editIndex = -1">Cancel</b-button>
</div>
<div v-else>
<b-button size="small" @click="handleEdit(row, index)">Actions</b-button>
</div>
</template>
</b-table>
</template>
<script setup lang="ts">
import { reactive, ref } from 'vue'
import dayjs from 'dayjs'
const columns = [
{ title: 'Name', slot: 'name' },
{ title: 'Age', slot: 'age' },
{ title: 'Birthday', slot: 'birthday' },
{ title: 'Hobby', slot: 'hobby' },
{ title: 'Address', slot: 'address' },
{ title: 'Actions', slot: 'action' }
]
const hobbyMap = { '1': '吃饭', '2': '睡觉', '3': '打豆豆' }
const data = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
hobby: '1',
address: 'BeijingCity朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
hobby: '1',
address: 'BeijingCity海淀区西二旗'
},
{
name: '李小红',
age: 30,
birthday: '1985-02-05',
hobby: '3',
address: 'ShanghaiCity浦东New区世纪大道'
},
{
name: '周小伟',
age: 26,
birthday: '1993-07-11',
hobby: '1',
address: 'ShenzhenCity南山区深南大道'
},
{
name: '张小发',
age: 33,
birthday: '1999-12-12',
hobby: '2',
address: 'NanjingCity龙眠大道'
}
])
const obj = reactive({
editName: '',
editAge: '',
editBirthday: '',
editHobby: '',
editAddress: '',
editIndex: -1
})
function handleEdit(row, index) {
obj.editName = row.name
obj.editAge = row.age
obj.editHobby = row.hobby
obj.editAddress = row.address
obj.editBirthday = new Date(row.birthday)
obj.editIndex = index
}
function handleSave(index) {
data.value[index].name = obj.editName
data.value[index].age = obj.editAge
data.value[index].birthday = dayjs(obj.editBirthday).format('YYYY-MM-DD')
data.value[index].hobby = obj.editHobby
data.value[index].address = obj.editAddress
obj.editIndex = -1
}
</script>Edit Mode
Enable the edit table style to hide input borders, making inline table editing easier.
<template>
<b-form ref="formRef" :model="list" label-width="85px" label-position="top">
<b-collapse-wrap title="Edittable" shadow="none">
<div style="padding: 10px 24px">
<b-table
edit-table
:columns="columns"
:data="list"
no-data-text="暂无parameter"
draggable
drag-handle=".handle"
max-height="420"
@drag-drop="handleDragDrop"
>
<template #handle>
<b-icon name="drag" class="handle" />
</template>
<template #name="{ index }">
<b-form-item :rules="validateRules.name" :prop="index + '.name'">
<b-input v-model="list[index].name" type="text" clearable></b-input>
</b-form-item>
</template>
<template #age="{ index }">
<b-form-item :rules="validateRules.age" :prop="index + '.age'">
<b-input-number
v-model="list[index].age"
type="text"
arrow-up-icon="plus"
arrow-down-icon="minus"
></b-input-number>
</b-form-item>
</template>
<template #birthday="{ index }">
<b-form-item :rules="validateRules.birthday" :prop="index + '.birthday'">
<b-date-picker
v-model="list[index].birthday"
type="date"
placeholder="选择Day期"
></b-date-picker>
</b-form-item>
</template>
<template #hobby="{ index }">
<b-form-item :rules="validateRules.hobby" :prop="index + '.hobby'">
<b-select v-model="list[index].hobby" clearable>
<b-option v-for="(val, key) in hobbyMap" :key="key" :value="key" :label="val">
{{ val }}
</b-option>
</b-select>
</b-form-item>
</template>
<template #address="{ index }">
<b-form-item :rules="validateRules.address" :prop="index + '.address'">
<b-input v-model="list[index].address" type="text"></b-input>
</b-form-item>
</template>
<template #action="{ index }">
<b-button type="text" text-color="danger" @click="handleRemove(index)">
<b-icon name="minus-circle" />
</b-button>
</template>
</b-table>
<div class="mt-8">
<b-button icon="plus" dashed style="width: 100%" @click="handleAdd">New增</b-button>
</div>
</div>
</b-collapse-wrap>
<b-collapse-wrap title="Readonly" shadow="none">
<div style="padding: 10px 24px">
<b-table :columns="columns2" :data="list" edit-table edit-table-detail>
<template #hobby="{ row }">
{{ hobbyMap[row.hobby] }}
</template>
</b-table>
</div>
</b-collapse-wrap>
</b-form>
</template>
<script setup lang="ts">
import { reactive, ref, nextTick } from 'vue'
const formRef = ref(null)
const tableRef = ref(null)
const hobbyMap = { 1: '吃饭', 2: '睡觉', 3: '打豆豆' }
const columns = [
{ title: ' ', slot: 'handle', width: 24, align: 'center' },
{ title: 'Name', slot: 'name' },
{ title: 'Age', slot: 'age' },
{ title: 'Birthday', slot: 'birthday' },
{ title: 'Hobby', slot: 'hobby' },
{ title: 'Address', slot: 'address' },
{ title: ' ', slot: 'action', width: 50, align: 'center' }
]
const columns2 = [
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Birthday', key: 'birthday' },
{ title: 'Hobby', slot: 'hobby' },
{ title: 'Address', key: 'address' }
]
const validateRules = reactive({
name: [{ required: true, message: '必填项', trigger: 'blur' }],
age: [{ required: true, message: '必填项', trigger: 'change' }],
birthday: [{ required: true, message: '必填项', trigger: 'change' }],
hobby: [{ required: true, message: '必填项', trigger: 'change' }],
address: [{ required: true, message: '必填项', trigger: 'blur' }]
})
const list = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
hobby: '1',
address: 'BeijingCity朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
hobby: '1',
address: 'BeijingCity海淀区西二旗'
},
{
name: '李小红',
age: 30,
birthday: '1985-02-05',
hobby: '3',
address: 'ShanghaiCity浦东New区世纪大道'
},
{
name: '周小伟',
age: 26,
birthday: '1993-07-11',
hobby: '1',
address: 'ShenzhenCity南山区深南大道'
},
{
name: '张小发',
age: 33,
birthday: '1999-12-12',
hobby: '2',
address: 'NanjingCity龙眠大道'
}
])
function handleAdd() {
const row = {
name: '',
age: null,
birthday: '',
hobby: '',
address: ''
}
list.value.push(row)
}
function handleRemove(index) {
list.value.splice(index, 1)
}
function handleDragDrop(newList, newIndex) {
list.value = [...newList]
nextTick(() => tableRef.value && tableRef.value.clickCurrentRow(newIndex))
}
</script>Drag to Reorder
Set draggable to enable drag sorting.
Note: When drag sorting is enabled, mouse dragging also takes over text selection. You can set handle to limit dragging to a specific element.
To update the data order, use v-model:data for two-way binding, or handle the update manually in the @drag-drop event.
Default Drag
ID | Name | Age | Date of Birth | Address |
|---|
Actual Data: [ "1-John Brown", "2-Jim Green", "3-Joe Black", "4-Jon Snow", "5-Jim Raynor" ]
When combined with single selection, it is recommended to use the drag-drop function for custom control, which better implements custom selection effects
Drag Handle
# | ID | Name | Age | Date of Birth | Address | Actions |
|---|
Actual Data: [ "1-John Brown", "2-Jim Green", "3-Joe Black", "4-Jon Snow", "5-Jim Raynor" ]
Selected Row: {}
<template>
<div>
<div>
<p>Default Drag</p>
<b-table v-model:data="data1" :columns="columns1" draggable></b-table>
<p>Actual Data: {{ data1.map(v => v.id + '-' + v.name) }}</p>
</div>
<b-divider></b-divider>
<div>
<p>When combined with single selection, it is recommended to use the drag-drop function for custom control, which better implements custom selection effects</p>
<p>Drag Handle</p>
<b-table
ref="currentRowTable"
:columns="columns2"
:data="data2"
draggable
drag-handle=".drag-handle"
highlight-row
@drag-drop="handleDragDrop"
@current-change="currentRowChange"
>
<template #handle="{ row }">
<span class="drag-handle" style="cursor: grab"><b-icon name="drag" size="20" /></span>
</template>
<template #ctrl="{ row, index }">
<b-button type="text" @click.stop="handleEdit(row, index)">Edit</b-button>
<b-button type="text" text-color="danger" @click.stop="removeRow(index)">Delete</b-button>
</template>
</b-table>
<p>Actual Data: {{ data2.map(v => v.id + '-' + v.name) }}</p>
<p>Selected Row: {{ currentRow }}</p>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, nextTick } from 'vue'
const columns1 = [
{ title: 'ID', key: 'id', width: 70 },
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Date of Birth', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const columns2 = [
{ slot: 'handle', width: 70 },
{ title: 'ID', key: 'id', width: 70 },
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Date of Birth', key: 'birthday' },
{ title: 'Address', key: 'address' },
{ title: 'Actions', slot: 'ctrl', width: 120 }
]
const data1 = ref([
{
id: 1,
name: 'John Brown',
age: 18,
birthday: '1990-04-22',
address: 'New York No. 1 Lake Park'
},
{
id: 2,
name: 'Jim Green',
age: 25,
birthday: '1990-11-11',
address: 'London No. 1 Lake Park'
},
{
id: 3,
name: 'Joe Black',
age: 30,
birthday: '1985-02-05',
address: 'Sydney No. 1 Lake Park'
},
{
id: 4,
name: 'Jon Snow',
age: 26,
birthday: '1993-07-11',
address: 'Ottawa No. 2 Lake Park'
},
{
id: 5,
name: 'Jim Raynor',
age: 33,
birthday: '1999-12-12',
address: 'Moscow No. 3 Lake Park'
}
])
const data2 = ref([
{
id: 1,
name: 'John Brown',
age: 18,
birthday: '1990-04-22',
address: 'New York No. 1 Lake Park'
},
{
id: 2,
name: 'Jim Green',
age: 25,
birthday: '1990-11-11',
address: 'London No. 1 Lake Park'
},
{
id: 3,
name: 'Joe Black',
age: 30,
birthday: '1985-02-05',
address: 'Sydney No. 1 Lake Park'
},
{
id: 4,
name: 'Jon Snow',
age: 26,
birthday: '1993-07-11',
address: 'Ottawa No. 2 Lake Park'
},
{
id: 5,
name: 'Jim Raynor',
age: 33,
birthday: '1999-12-12',
address: 'Moscow No. 3 Lake Park'
}
])
const currentRow = ref({})
const currentRowTable = ref(null)
function currentRowChange(row, oldRow, index) {
currentRow.value = row
}
function handleDragDrop(newList, newIndex, oldIndex) {
data2.value = [...newList]
nextTick(() => {
currentRowTable.value.clickCurrentRow(newIndex)
})
}
function handleEdit(row, index) {
console.log(row, index)
}
function removeRow(index) {
data2.value.splice(index, 1)
nextTick(() => {
currentRowTable.value.clearCurrentRow()
})
}
</script>Loading State
Name | Age | Birthday | Address |
|---|
<template>
<div>
<b-table :columns="columns" :data="data" :loading="loading"></b-table>
<br />
<b-switch v-model="loading"></b-switch>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Birthday', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const data = ref([
{
name: '王小明',
age: 18,
birthday: '1990-04-22',
address: 'BeijingCity朝阳区芍药居'
},
{
name: '张小刚',
age: 25,
birthday: '1990-11-11',
address: 'BeijingCity海淀区西二旗'
},
{
name: '李小红',
age: 30,
birthday: '1985-02-05',
address: 'ShanghaiCity浦东New区世纪大道'
},
{
name: '周小伟',
age: 26,
birthday: '1993-07-11',
address: 'ShenzhenCity南山区深南大道'
},
{
name: '张小发',
age: 33,
birthday: '1999-12-12',
address: 'NanjingCity龙眠大道'
}
])
const loading = ref(false)
</script>Sizes
Set size to large, default, or small to adjust the table size. The default row height is 40px, and small uses 36px. Leaving it unset is the same as default.
Name | Age | Date of Birth | Address |
|---|
<template>
<div>
<div class="mb-16">
<b-radio-group v-model="tableSize" type="button">
<b-radio label="large">Loose</b-radio>
<b-radio label="default">Default</b-radio>
<b-radio label="small">Compact</b-radio>
</b-radio-group>
</div>
<b-table :columns="columns" :data="data" :size="tableSize"></b-table>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Date of Birth', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const data = ref([
{
name: 'John Brown',
age: 18,
birthday: '1990-04-22',
address: 'New York No. 1 Lake Park'
},
{
name: 'Jim Green',
age: 25,
birthday: '1990-11-11',
address: 'London No. 1 Lake Park'
},
{
name: 'Joe Black',
age: 30,
birthday: '1985-02-05',
address: 'Sydney No. 1 Lake Park'
},
{
name: 'Jon Snow',
age: 26,
birthday: '1993-07-11',
address: 'Ottawa No. 2 Lake Park'
},
{
name: 'Jim Raynor',
age: 33,
birthday: '1999-12-12',
address: 'Moscow No. 3 Lake Park'
}
])
const tableSize = ref('default')
</script>Empty Data
Set noDataText for the empty data state.
Name | Age | Birthday | Address |
|---|
No Data |
<template>
<b-table :columns="columns" :data="data" no-data-text="No Data"></b-table>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: 'Name', key: 'name' },
{ title: 'Age', key: 'age' },
{ title: 'Birthday', key: 'birthday' },
{ title: 'Address', key: 'address' }
]
const data = ref([])
</script>Table props
| Parameter | Description | Type | Options | Default |
|---|---|---|---|---|
| data | Structured data to display. The field cellClassName is reserved for setting custom cell class names, so do not use it in your data. See examples for specific style usage. | Array | — | [] |
| columns | Column configuration, see below for details | Array | — | [] |
| stripe | Show stripe rows alternately | Boolean | false/true | false |
| border | Show vertical border | Boolean | false/true | false |
| show-header | Whether to show the table header | Boolean | false/true | true |
| width | Table width in px | Number/String | — | auto |
| height | Table height in px. When set, the body scrolls through the built-in scrollbar and the header stays fixed | Number/String | — | — |
| max-height | Max table height in px. When exceeded, the body scrolls through the built-in scrollbar and the header stays fixed | Number/String | — | — |
| loading | Table loading state | Boolean | — | false |
| disabled-hover | Disable hover highlight | Boolean | — | false |
| highlight-row | Enable row highlight / single-selection mode. | Boolean | — | false |
| highlight-row-cancel | Whether single-selection highlight can be canceled. If true, clicking the selected row again will deselect it | Boolean | — | false |
| size | Table size | String | large / default / small | default |
| no-data-text | Empty state text | String | — | No Data |
| loading-text | Loading text | String | — | Loading |
| draggable | Enable drag to reorder rows. To sync metadata, use v-model:data or handle the @drag-drop event to update data | Boolean | — | false |
| drag-handle | Drag handle icon | String | — | — |
| tooltip-theme | Tooltip theme used when a column enables tooltip | String | dark / light | — |
| row-key | Whether to force refresh using the built-in row-key; pass a business key field name in tree table mode | Boolean/String | — | false |
| expand-column-key | Column key used to render the tree expand control. Tree mode starts only when this is set and row-key is a string | String | — | '' |
| default-expanded-row-keys | Default expanded row keys in tree table mode, used in uncontrolled mode | Array | — | [] |
| expanded-row-keys | Controlled expanded row keys in tree table mode, used with update:expandedRowKeys | Array | — | — |
| indent-size | Indent width for tree table rows | Number | — | 16 |
| merge-method | Merge method for row/column spanning | Function | — | false |
| edit-table | Enable edit-table styling | Boolean | false/true | false |
| edit-table-detail | Enable a denser detail editing style on top of edit-table | Boolean | false/true | false |
Table events
| Event Name | Description | Return Value |
|---|---|---|
| current-change | Effective when highlight-row is enabled; triggers when the current row changes | currentRow, oldCurrentRow,index |
| select | Effective in multi-select mode; triggers when an item is selected | selected items, recently selected |
| select-cancel | Effective in multi-select mode; triggers when an item is deselected | selected items, deselected |
| select-all | Triggers when all items are selected | selected items |
| select-all-cancel | Triggers when all items are deselected | selected items |
| selection-change | Triggers when the selection changes | selected items |
| sort-change | Effective when sortable; triggers when sorting is clicked | column: current column data, key: sort indicator, order (asc or desc) |
| row-click | Triggers when a row is clicked | Current row data, index |
| row-dblclick | Triggers when a row is double-clicked | Current row data, index |
| expand | Triggers when an expandable row with type: 'expand' is expanded or collapsed | row: current row data, status: current state |
| update:expandedRowKeys | Triggers when the controlled tree expansion keys update. Use @update:expanded-row-keys in templates | expandedRowKeys |
| expand-change | Triggers when a tree row expand state changes | row, expanded, expandedRowKeys |
| drag-drop | Triggers when drag sort is released | The two rows' data indices and updated data: newData, newIndex, oldIndex |
Table slot
| Name | Description |
|---|---|
| header | Table header |
| footer | Table footer |
| loading | Loading content |
Table methods
| Method Name | Description | Parameter |
|---|---|---|
| clickCurrentRow | Select a row by index | index |
| clearCurrentRow | Clear the highlighted row; only works when highlight-row is enabled | — |
| handleResize | Recalculate column widths, fixed panes, and scrollbar layout manually | — |
| getSelection | Get selected rows | — |
| selectAll | Set all currently selectable rows to selected or deselected | status |
column
| Parameter | Description | Type | Options | Default |
|---|---|---|---|---|
| type | Column type | String | index、selection、expand、html String | - |
| title | Column header text | String | - | # |
| key | Field name for column data | String | - | - |
| width | Column width | Number | - | - |
| minWidth | Minimum column width | Number | - | - |
| maxWidth | Maximum column width | Number | - | - |
| align | Alignment | String | right,center | left |
| className | CSS class name for the column | String | - | - |
| fixed | Whether the column is fixed to the left or right | String | left,right | - |
| ellipsis | When enabled, text will not wrap | Boolean | - | false |
| tooltip | When enabled, text will not wrap and shows full content via Tooltip component | Boolean | - | false |
| slot | Render the column with a named slot. Slot params are row, column, and index | String | - | - |
| render | Custom render function for the column; the first parameter is h, the second is an object containing row, column, and index | Function | - | - |
| renderHeader | Custom header render function. Params: { column, index } | Function | - | - |
| indexMethod | Available when type is index. Custom index method; the row parameter is the current row content | Function | - | - |
| sortable | Whether the corresponding column can be sorted | Boolean ,'custom' | - | false |
| sortMethod | Custom sort method; three parameters: a, b, and type | Function | - | - |
| sortType | Set initial sort order. Accepted values: asc, desc | String | - | - |
| children | Child column definitions for grouped headers; parent fixed is inherited by children | Array | - | - |