当前位置: 首页 > news >正文

Element Plus 级联选择器实战:仿学科网教材多级选择的完整方案

基于 Element Plus 2.10+ 的el-cascader,实现一个「前两级单选 + 后续层级同父多选」的教材选择器,涵盖 props 配置、选中逻辑、UI 定制和数据提交。

一、需求背景

在资源上传场景中,教材数据是一个多级树结构:

出版社(第1级) └── 教材(第2级) └── 章(第3级) └── 节(第4级)

业务要求:

  • 第1、2级(出版社、教材):单选,用于定位教材
  • 第3级起(章、节):同父多选,用于关联具体章节
  • 点击节点文字即可选中,不需要精确点击 checkbox
  • 前两级隐藏 checkbox,UI 更简洁

二、Cascader Props 配置

<el-cascader v-model="textbookCascaderValue" :options="textbookCascaderList" :props="{ expandTrigger: 'click', checkStrictly: true, multiple: true, checkOnClickNode: true }" popper-class="textbook-cascader-popper" clearable filterable placeholder="请选择" @change="onTextbookCascaderChange" />

Props 逐项说明

Prop作用
expandTrigger'click'点击节点展开子级(默认是'hover'
checkStrictlytrue父子节点不关联勾选,任意层级都可选
multipletrue开启多选模式,显示 checkbox
checkOnClickNodetrue2.10.5+点击节点文字即勾选,不用精确点 checkbox

checkOnClickNode是 Element Plus 2.10.5 新增的 prop,解决了「必须点击 checkbox 才能选中」的痛点。在此之前需要通过 CSS hack 或 JS 模拟实现。

三、选中逻辑:前两级单选 + 后续同父多选

核心处理函数

constonTextbookCascaderChange=(meta:any)=>{constval=meta.textbookCascaderValueif(!val||val.length===0){meta.textbookIds=[]return}constlastPath=val[val.length-1]// 前两级(出版社、教材):单选,只保留最后选择的项if(lastPath.length<=2){meta.textbookCascaderValue=[lastPath]meta.textbookIds=[lastPath[lastPath.length-1]]return}// 第三级起(章节):同级 + 同父多选constparentKey=JSON.stringify(lastPath.slice(0,-1))constsameParent=val.filter((path:any[])=>JSON.stringify(path.slice(0,-1))===parentKey)if(sameParent.length!==val.length){meta.textbookCascaderValue=sameParent}meta.textbookIds=sameParent.map((path:any[])=>path[path.length-1])}

数据结构说明

el-cascader多选模式下,v-model的值是二维数组,每个元素是一条从根到选中节点的路径:

// 选中了「北京大学出版社 → 必修一 → 第一章」和「北京大学出版社 → 必修一 → 第二章」textbookCascaderValue=[[1,10,100],// 路径:出版社ID=1, 教材ID=10, 章ID=100[1,10,101],// 路径:出版社ID=1, 教材ID=10, 章ID=101]

逻辑分两段

1. 前两级(path.length <= 2):单选

if(lastPath.length<=2){meta.textbookCascaderValue=[lastPath]// 只保留最后选的meta.textbookIds=[lastPath[lastPath.length-1]]return}

用户选了出版社A,再选出版社B → 清掉A,只保留B。

2. 第三级起(path.length >= 3):同父多选

constparentKey=JSON.stringify(lastPath.slice(0,-1))constsameParent=val.filter((path:any[])=>JSON.stringify(path.slice(0,-1))===parentKey)
  • 取最后一次选择的父路径(去掉最后一个元素)作为基准
  • 过滤掉父路径不一致的旧选项
  • 例如:选了「必修一 → 第一章」,再选「必修二 → 第三章」→ 清掉旧的,只保留后者

四、UI 定制:隐藏前两级 Checkbox

问题

multiple: true会在所有层级显示 checkbox,但前两级(出版社、教材)是单选,显示 checkbox 会让用户困惑。

方案

通过popper-class给教材级联的下拉面板加一个专属 class,再用 CSS 隐藏前两级的 checkbox:

<el-cascader popper-class="textbook-cascader-popper" ... />
/* 教材级联:前两级(出版社、教材)隐藏 checkbox */.textbook-cascader-popper .el-cascader-menu:nth-child(1) .el-checkbox, .textbook-cascader-popper .el-cascader-menu:nth-child(2) .el-checkbox{display:none;}

为什么用popper-class

el-cascader的下拉面板是teleport 到<body>的,不在组件 DOM 树内。直接用组件的 class(如.l-cascader)选择器够不到弹窗内的元素。popper-class会把自定义 class 加到弹窗根元素上,是定位 teleported 内容的标准做法。

DOM 结构

<!-- teleport 到 body 的弹窗 --><divclass="el-popper textbook-cascader-popper el-cascader__dropdown"><divclass="el-cascader-panel"><divclass="el-cascader-menu"><!-- :nth-child(1) = 出版社 --><ul><liclass="el-cascader-node"><spanclass="el-checkbox">...</span><!-- 隐藏 --><spanclass="el-cascader-node__label">北京大学出版社</span><iclass="el-cascader-node__postfix"></i></li></ul></div><divclass="el-cascader-menu"><!-- :nth-child(2) = 教材 --><ul><liclass="el-cascader-node"><spanclass="el-checkbox">...</span><!-- 隐藏 --><spanclass="el-cascader-node__label">必修一</span><iclass="el-cascader-node__postfix"></i></li></ul></div><divclass="el-cascader-menu"><!-- :nth-child(3) = 章 --><!-- checkbox 正常显示 --></div></div></div>

五、数据提交

后端接收List<Long>类型,FormData 提交时用重复 key

// 教材ID列表if(meta.textbookIds?.length){meta.textbookIds.forEach((id:number)=>{fd.append('textbookIds',String(id))})}

Spring Boot 会自动将多个同名参数绑定到List<Long>

@Schema(description="教材ID列表(多选)")privateList<Long>textbookIds;

六、完整流程图

用户操作 cascaderValue 变化 提交数据 ───────────────────────────────────────────────────────────────────── 点击出版社 A → [[A]] → textbookIds: [] 点击教材 B → [[A, B]] → textbookIds: [] 点击章节 1 → [[A, B, 1]] → textbookIds: [1] 点击章节 2 → [[A,B,1], [A,B,2]] → textbookIds: [1,2] 点击章节 3(不同教材) → [[A,C,3]] ← 清掉旧的 → textbookIds: [3] 点击出版社 D → [[D]] ← 清掉旧的 → textbookIds: []

七、踩坑记录

问题原因解决
点击 label 无法选中checkStrictly模式下点击 label 只高亮不勾选升级 Element Plus 2.10.5+,使用checkOnClickNode: true
前两级 checkbox 隐藏不生效dropdown 被 teleport 到 body,组件 class 选择器够不到popper-class定位弹窗
跨父级选择时旧数据未清空多选模式下新选择是追加而非替换@change中手动过滤,以最后选择的父路径为基准
提交时后端收不到 ListFormData 用逗号分隔的字符串改为重复 key 方式fd.append('textbookIds', id)

八、版本要求

  • Element Plus >= 2.10.5checkOnClickNodeprop
  • Vue 3v-model响应式绑定
  • Spring BootList<Long>自动绑定重复 key 参数
http://www.jsqmd.com/news/1099899/

相关文章:

  • Java计算机毕设之基于 SpringBoot+Vue 的 4S 店客户跟进与购车管理系统的设计与实现 基于 SpringBoot+Vue 的汽车门店车辆(完整前后端代码+说明文档+LW,调试定制等)
  • 专访大晓机器人王飞:世界模型是“进化型基础设施”
  • 基于51/STM32单片机温度控制系统 恒温箱 水温控制 温度采集 成品1(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • HarmonyOS律愈实战02:ArkTS五音数据模型设计
  • esp32s3+ws2812灯条控制
  • 公开课紧张到忘词?老教师都在用的3个临场应对方法
  • LeetCode 热题 100 —— 7.接雨水(Javascript解法)
  • 别再盲目试用了!AI编程助手采购决策树:按团队规模、语言栈、安全等级自动匹配最优组合(含SaaS/私有化/混合部署ROI计算表)
  • 2026 年有哪些真正适合学生写开题的 AI 辅助写作工具,实测无套路分享
  • 【VMware磁盘扩容终极指南】:20年运维专家亲授5种零宕机扩容方案,99%的人不知道第3种!
  • Antigravity Manager:把多个 AI 账号管明白的桌面工具
  • Debian 12 编译安装网讯网卡驱动详细教程
  • Dism++深度解析:现代化Windows系统维护架构与技术实现
  • SCI投稿AI绘图避坑全攻略:AI打草稿+人工转矢量,彻底告别撤稿风险!
  • 从H100的异步执行和线程块集群,聊聊如何榨干GPU的每一分算力
  • 2026年技术方向怎么选?机器视觉、PLC、AI大模型、嵌入式深度对比
  • 宝塔面板部署 Spring Boot 项目全流程
  • Python爬虫经典案例018:爬虫性能优化与调优——从慢到快的全面优化指南
  • VisualCppRedist AIO:终极Windows运行库一体化智能管理解决方案深度解析
  • 【open harmony/harmonyos】HarmonyOS 应用中的数据模型分层:以星图节点 Store 为例
  • 2026年论文查重免费网站靠谱吗?这5个平台实测对比
  • 基于STM32单片机智能窗帘窗户光敏定时遥控温湿度语音物联网设计1(设计源文件+万字报告+讲解)(支持资料、图片参考_相关定制)_
  • 09502黄大年茶思屋榜文95期 第2题 高性能、适用于NPU硬件的Training-free大模型剪枝算法
  • openGauss 还原成功了,用户却喊“数据库里啥也没有“:一个 search_path 坑实录
  • 国家标准起草单位是什么?有什么价值?企业如何申请参与国标制定
  • Claude Code 深度实战指南:从环境配置到 Agent 自动化进阶
  • 开源AI绘画工作台infinite-canvas:本地部署与高效工作流构建指南
  • SIM 卡克隆工具指南:安全移动 SIM 卡数据
  • 上门按摩APP小程序开发公司,获客新思路:酒店渠道为什么值得做
  • 如何在一部手机上实现工作与生活数据的完全隔离?