
基于 Element Plus 2.10 的el-cascader实现一个「前两级单选 后续层级同父多选」的教材选择器涵盖 props 配置、选中逻辑、UI 定制和数据提交。一、需求背景在资源上传场景中教材数据是一个多级树结构出版社第1级 └── 教材第2级 └── 章第3级 └── 节第4级业务要求第1、2级出版社、教材单选用于定位教材第3级起章、节同父多选用于关联具体章节点击节点文字即可选中不需要精确点击 checkbox前两级隐藏 checkboxUI 更简洁二、Cascader Props 配置el-cascader v-modeltextbookCascaderValue :optionstextbookCascaderList :props{ expandTrigger: click, checkStrictly: true, multiple: true, checkOnClickNode: true } popper-classtextbook-cascader-popper clearable filterable placeholder请选择 changeonTextbookCascaderChange /Props 逐项说明Prop值作用expandTriggerclick点击节点展开子级默认是hovercheckStrictlytrue父子节点不关联勾选任意层级都可选multipletrue开启多选模式显示 checkboxcheckOnClickNodetrue2.10.5点击节点文字即勾选不用精确点 checkboxcheckOnClickNode是 Element Plus 2.10.5 新增的 prop解决了「必须点击 checkbox 才能选中」的痛点。在此之前需要通过 CSS hack 或 JS 模拟实现。三、选中逻辑前两级单选 后续同父多选核心处理函数constonTextbookCascaderChange(meta:any){constvalmeta.textbookCascaderValueif(!val||val.length0){meta.textbookIds[]return}constlastPathval[val.length-1]// 前两级出版社、教材单选只保留最后选择的项if(lastPath.length2){meta.textbookCascaderValue[lastPath]meta.textbookIds[lastPath[lastPath.length-1]]return}// 第三级起章节同级 同父多选constparentKeyJSON.stringify(lastPath.slice(0,-1))constsameParentval.filter((path:any[])JSON.stringify(path.slice(0,-1))parentKey)if(sameParent.length!val.length){meta.textbookCascaderValuesameParent}meta.textbookIdssameParent.map((path:any[])path[path.length-1])}数据结构说明el-cascader多选模式下v-model的值是二维数组每个元素是一条从根到选中节点的路径// 选中了「北京大学出版社 → 必修一 → 第一章」和「北京大学出版社 → 必修一 → 第二章」textbookCascaderValue[[1,10,100],// 路径出版社ID1, 教材ID10, 章ID100[1,10,101],// 路径出版社ID1, 教材ID10, 章ID101]逻辑分两段1. 前两级path.length 2单选if(lastPath.length2){meta.textbookCascaderValue[lastPath]// 只保留最后选的meta.textbookIds[lastPath[lastPath.length-1]]return}用户选了出版社A再选出版社B → 清掉A只保留B。2. 第三级起path.length 3同父多选constparentKeyJSON.stringify(lastPath.slice(0,-1))constsameParentval.filter((path:any[])JSON.stringify(path.slice(0,-1))parentKey)取最后一次选择的父路径去掉最后一个元素作为基准过滤掉父路径不一致的旧选项例如选了「必修一 → 第一章」再选「必修二 → 第三章」→ 清掉旧的只保留后者四、UI 定制隐藏前两级 Checkbox问题multiple: true会在所有层级显示 checkbox但前两级出版社、教材是单选显示 checkbox 会让用户困惑。方案通过popper-class给教材级联的下拉面板加一个专属 class再用 CSS 隐藏前两级的 checkboxel-cascader popper-classtextbook-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-classel-cascader的下拉面板是teleport 到body的不在组件 DOM 树内。直接用组件的 class如.l-cascader选择器够不到弹窗内的元素。popper-class会把自定义 class 加到弹窗根元素上是定位 teleported 内容的标准做法。DOM 结构!-- teleport 到 body 的弹窗 --divclassel-popper textbook-cascader-popper el-cascader__dropdowndivclassel-cascader-paneldivclassel-cascader-menu!-- :nth-child(1) 出版社 --ulliclassel-cascader-nodespanclassel-checkbox.../span!-- 隐藏 --spanclassel-cascader-node__label北京大学出版社/spaniclassel-cascader-node__postfix→/i/li/ul/divdivclassel-cascader-menu!-- :nth-child(2) 教材 --ulliclassel-cascader-nodespanclassel-checkbox.../span!-- 隐藏 --spanclassel-cascader-node__label必修一/spaniclassel-cascader-node__postfix→/i/li/ul/divdivclassel-cascader-menu!-- :nth-child(3) 章 --!-- checkbox 正常显示 --/div/div/div五、数据提交后端接收ListLong类型FormData 提交时用重复 key// 教材ID列表if(meta.textbookIds?.length){meta.textbookIds.forEach((id:number){fd.append(textbookIds,String(id))})}Spring Boot 会自动将多个同名参数绑定到ListLongSchema(description教材ID列表多选)privateListLongtextbookIds;六、完整流程图用户操作 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.5checkOnClickNodepropVue 3v-model响应式绑定Spring BootListLong自动绑定重复 key 参数