vxe-table-select 是 vxe-table 体系中的下拉选择组件,下拉面板本身就是一个表格,继承了 vxe-table 的配置化能力。通过 multiple 开启多选、配置 gridConfig.pagerConfig 启用分页,再监听 page-change 事件换页取数,即可实现分页多选。
但需要注意一个关键限制:分页多选在独立使用时完美支持;若用在表格单元格内,未分页加载的数据无法完成 value → label 的转换,因为对应数据尚未加载。下文会详细说明这一限制及规避方式。
vxe-table-select 如何实现分页多选?
组件上加 multiple,在 gridConfig 中配置 pagerConfig 与列定义,监听 page-change 事件在页码变化时更新 options 数据,即可实现带分页的多选下拉表格。
- 生效前提:
- options 数据每项必须包含 value 与 label 字段,分别作为选中值与显示文本。
- multiple 必须显式设置,否则为单选模式。
- 分页依赖 pagerConfig,且需要自行在 page-change 中更新当前页数据。
- 预期结果:下拉面板以表格形式展示数据,带复选框列与分页器;勾选多行后收起面板,输入框内显示已选项标签;翻页后已选项不丢失。
代码
下面的配置用于实现下拉表格分页多选,采用前端分页模拟

html
<template>
<div>
<vxe-table-select
v-model="val1"
multiple
:options="tableData"
:popup-config="popupConfig"
:grid-config="gridConfig"
@page-change="pageChangeEvent"
>
</vxe-table-select>
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
const allList = [
{ value: 'Test1', label: 'Test1', nickname: 'T1', role: 'Develop', sex: 'Man', age: 28, address: 'Shenzhen' },
{ value: 'Test2', label: 'Test2', nickname: 'T2', role: 'Test', sex: 'Women', age: 22, address: 'Guangzhou' },
{ value: 'Test3', label: 'Test3', nickname: 'T3', role: 'PM', sex: 'Man', age: 32, address: 'Shanghai' },
{ value: 'Test4', label: 'Test4', nickname: 'T4', role: 'Designer', sex: 'Women', age: 23, address: 'test abc' },
{ value: 'Test5', label: 'Test5', nickname: 'T5', role: 'Develop', sex: 'Women', age: 30, address: 'Shanghai' },
{ value: 'Test6', label: 'Test6', nickname: 'T6', role: 'Designer', sex: 'Women', age: 21, address: 'Shenzhen' },
{ value: 'Test7', label: 'Test7', nickname: 'T7', role: 'Test', sex: 'Man', age: 29, address: 'Shenzhen' },
{ value: 'Test8', label: 'Test8', nickname: 'T8', role: 'Develop', sex: 'Man', age: 35, address: 'test abc' },
{ value: 'Test9', label: 'Test9', nickname: 'T9', role: 'Develop', sex: 'Man', age: 35, address: 'Shenzhen' },
{ value: 'Test10', label: 'Test10', nickname: 'T10', role: 'Develop', sex: 'Man', age: 35, address: 'Guangzhou' },
{ value: 'Test11', label: 'Test11', nickname: 'T11', role: 'Develop', sex: 'Man', age: 49, address: 'Guangzhou' },
{ value: 'Test12', label: 'Test12', nickname: 'T12', role: 'Develop', sex: 'Women', age: 45, address: 'Shanghai' },
{ value: 'Test13', label: 'Test13', nickname: 'T13', role: 'Test', sex: 'Women', age: 35, address: 'Guangzhou' },
{ value: 'Test14', label: 'Test14', nickname: 'T14', role: 'Test', sex: 'Man', age: 29, address: 'Shanghai' },
{ value: 'Test15', label: 'Test15', nickname: 'T15', role: 'Develop', sex: 'Man', age: 39, address: 'Guangzhou' },
{ value: 'Test16', label: 'Test16', nickname: 'T16', role: 'Test', sex: 'Women', age: 35, address: 'Guangzhou' },
{ value: 'Test17', label: 'Test17', nickname: 'T17', role: 'Test', sex: 'Man', age: 39, address: 'Shanghai' },
{ value: 'Test18', label: 'Test18', nickname: 'T18', role: 'Develop', sex: 'Man', age: 44, address: 'Guangzhou' },
{ value: 'Test19', label: 'Test19', nickname: 'T19', role: 'Develop', sex: 'Man', age: 39, address: 'Guangzhou' },
{ value: 'Test20', label: 'Test20', nickname: 'T20', role: 'Test', sex: 'Women', age: 35, address: 'Guangzhou' },
{ value: 'Test21', label: 'Test21', nickname: 'T21', role: 'Test', sex: 'Man', age: 39, address: 'Shanghai' },
{ value: 'Test22', label: 'Test22', nickname: 'T22', role: 'Develop', sex: 'Man', age: 44, address: 'Guangzhou' }
]
const val1 = ref()
const tableData = ref([])
const popupConfig = reactive({
width: 600,
height: 400
})
const pagerConfig = reactive({
total: 0,
currentPage: 1,
pageSize: 10
})
const gridConfig = reactive({
border: true,
loading: false,
pagerConfig,
columns: [
{ type: 'checkbox', width: 70 },
{ field: 'label', title: 'Name' },
{ field: 'role', title: 'Role' },
{ field: 'sex', title: 'Sex' },
{ field: 'address', title: 'Address' }
]
})
// 模拟前端分页
const handlePageData = () => {
gridConfig.loading = true
setTimeout(() => {
const { pageSize, currentPage } = pagerConfig
pagerConfig.total = allList.length
tableData.value = allList.slice((currentPage - 1) * pageSize, currentPage * pageSize)
gridConfig.loading = false
}, 100)
}
const pageChangeEvent = ({ pageSize, currentPage }) => {
pagerConfig.currentPage = currentPage
pagerConfig.pageSize = pageSize
handlePageData()
}
handlePageData()
</script>
- 预期结果:
- 下拉面板展开后为表格形态,带复选框列与分页器,每页显示 10 条。
- 勾选多行后收起面板,输入框显示已选项标签。
- 翻页后已选项保持不变,再次回到之前页时勾选状态仍在。
使用限制:独立使用 vs 表格单元格内使用
这是最需要关注的部分。分页多选在不同使用场景下的支持程度不同
| 使用场景 | 是否分页 | 支持情况 | 说明 |
|---|---|---|---|
| 独立使用 | 否 | ✅ 完美支持 | 单选、多选均正常,支持大数据量虚拟滚动 |
| 独立使用 | 是 | ✅ 完美支持 | 分页多选正常,value 与 label 可不同 |
| 表格单元格内 | 否 | ✅ 完美支持 | 无论单选多选,均可正常显示 label,支持虚拟滚动 |
| 表格单元格内 | 是 | ⚠️ 受限 | 未分页加载的数据无法完成 value → label 转换 |
为什么表格单元格内分页会受限?
- 组件在单元格内显示时,会根据当前 v-model 保存的 value 去 options 中查找对应的 label 来展示。启用分页后,options 只包含当前页数据:
- 如果已选中的 value 恰好不在当前页,options 中找不到对应项,组件内部也没有相关的数据缓存,所有单元格就无法显示其 label。
- 独立使用时,组件能通过下拉面板的交互上下文或已加载数据完成转换,受影响较小。
- 单元格内使用时,表格渲染是批量且脱离下拉面板上下文的,缺失数据的问题会被放大。
规避建议
- 保持 value 与 label 值一致(官方建议)
- 让 value 直接等于展示文本,例如 { value: 'Test1', label: 'Test1' }。这样即使 options 中没有该数据,单元格也能直接显示 value,不会出现空白。示例代码中 allList 已按此方式构造。
- 单元格内使用时不启用分页
- 如果必须在表格单元格内使用,建议去掉 pagerConfig,一次性加载全部选项。未分页时无论单选多选都完美支持,且支持大数据量虚拟滚动,性能上并非不可接受。
- 分页场景改为独立使用
- 如果数据量确实需要分页,建议将该选择器放在表单区域独立使用,而不是嵌入表格单元格。
关键配置参数
| 配置项 | 类型 | 默认值 | 说明 |
|---|---|---|---|
| v-model | Any | --- | 选中值;多选时为数组 |
| multiple | Boolean | false | 是否多选,必须显式设置 |
| options | Array | \[\] | 下拉数据,每项需含 value 与 label |
| popupConfig.width | Number | --- | 下拉面板宽度,单位 px |
| popupConfig.height | Number | --- | 下拉面板高度,单位 px |
| gridConfig | Object | --- | 下拉面板表格配置,与 vxe-grid 配置一致 |
| gridConfig.pagerConfig | Object | --- | 分页配置,配置即启用分页 |
| gridConfig.columns | Array | --- | 表格列定义 |
| page-change | Event | --- | 页码变化事件,参数含 pageSize、currentPage |
事件与数据流
1. page-change 事件
javascript
const pageChangeEvent = ({ pageSize, currentPage }) => {
pagerConfig.currentPage = currentPage
pagerConfig.pageSize = pageSize
handlePageData()
}
- 参数为对象,包含 pageSize 与 currentPage。
- 需自行根据新页码重新取数并更新 options,组件不会自动请求。
2. 选项数据结构
javascript
{ value: 'Test1', label: 'Test1', role: 'Develop', sex: 'Man', age: 28, address: 'Shenzhen' }
- value:选中值,v-model 中保存的是它,支持自定义。
- label:输入框内显示的文本,支持自定义。
- 其余字段可在表格列中展示,不影响选中逻辑。