vue 下拉树组件 vxe-table-select 实现分页多选

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:输入框内显示的文本,支持自定义。
  • 其余字段可在表格列中展示,不影响选中逻辑。

https://vxeui.com

相关推荐
大熊程序猿1 小时前
用 ASP.NET Core + SignalR 做一个带实时对战房间的打字练习平台
javascript
OpsEye2 小时前
大量无效 Token 消耗,是很多企业被忽略的隐形成本
javascript·ai编程
用户621007587022 小时前
Web BIM 开发实践:如何将构件选择、属性查询接入业务系统?
前端
羲云2 小时前
AI 分站搭完就能发链接?上线前先过这 5 关
前端
葡萄城技术团队2 小时前
一个 F4 快捷键,为什么能决定 Web 表格是否像 Excel?
前端
王川7082 小时前
商协会后台前端的模块聚合实践
前端·后端
jerrywus2 小时前
Codex、Claude Code、DSH 怎么用手机管?我试了试 Agents Anywhere
前端·claude·deepseek
twj_5022 小时前
农业商业计划书PPT制作完全指南
前端