多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

cube-ui Drawer 抽屉组件实战指南:从级联选择到自定义 Panel 的完整实现

cube-ui Drawer 抽屉组件实战指南:从级联选择到自定义 Panel 的完整实现 前端UI组件移动开发【免费下载链接】cube-ui:large_orange_diamond: A fantastic mobile ui lib implement by Vue项目地址https://gitcode.com/gh_mirrors/cu/cube-ui点击查看免费下载Drawer抽屉是 cube-ui仓库根目录中一个从侧边滑出的满屏级联选择容器在省市区联动、多级分类等“大范围层级选择”场景中非常实用。本文将基于 Drawer 官方文档 结合组件源码系统讲解其 Props、事件、实例方法并通过默认配置与自定义插槽两套完整示例带你掌握从数据驱动到异步级联的完整开发能力。一、Drawer 是什么定位与适用场景Drawer 是 cube-ui 在1.7.0版本新增的组件文档将其定位为“抽屉”主要用于需要大范围层级进行选择的场景一般情况下应该是满屏状态。与弹出层Popup不同Drawer 的交互模型是从屏幕右侧滑出主面板默认max-width: 90%内部按列Panel组织数据每列宽约 170px多列之间以层叠margin-left: -67px的方式排布实现“抽屉拉开逐层选择”的视觉效果点击某一列中的条目后下一列自动填充并滑出直到选中最后一个 Panel 中的项完成整个选择。关键依赖务必记住__注组件依赖父容器相对定位或者绝对定位因为 Drawer 是绝对定位的。从 drawer.vue 的样式可以看到.cube-drawer使用position: absolute; top/right/bottom/left: 0铺满父容器因此父容器必须设置position: relative或position: absolute否则 Drawer 会定位到最近的定位祖先甚至视口造成布局错乱。参考官方示例 example/pages/drawer/default.vue其.view-wrapper使用position: fixed即为此目的。组件由三部分组成对应三个子组件| 子组件 | 职责 | | - | - | |cube-drawer| 整体容器负责标题、Panel 的编排、滑出动画与事件分发 | |cube-drawer-panel| 单列面板内嵌cube-scroll实现滚动 | |cube-drawer-item| 面板中的单个选项行 |三者在 src/modules/drawer/index.js 中一并注册因此Vue.use(Drawer)后即可同时使用这三个组件模块同时暴露Drawer.Panel、Drawer.Item便于按需引用。二、默认配置使用一个省市区联动 Demo文档给出了最典型的“默认配置使用”示例下面的代码与 example/pages/drawer/default.vue 完全一致可直接在项目中复制运行cube-button clickshowDrawerShow Drawer/cube-button cube-drawer refdrawer title请选择 :datadata :selected-indexselectedIndex changechangeHandler selectselectHandler cancelcancelHandler/cube-drawerimport { provinceList, cityList, areaList } from ../../data/area export default { data() { return { selectedIndex: [], data: [ provinceList, [], [] ] } }, methods: { showDrawer() { this.$refs.drawer.show() }, changeHandler(index, item, selectedVal, selectedIndex, selectedText) { // fake request setTimeout(() { let data if (index 0) { // procince change, get city data data cityList[item.value] } else { // city change, get area data data areaList[item.value] } // refill panel(index 1) data this.$refs.drawer.refill(index 1, data) }, 200) }, selectHandler(selectedVal, selectedIndex, selectedText) { this.$createDialog({ type: warn, content: Selected Item: br/ - value: ${selectedVal.join(, )} br/ - index: ${selectedIndex.join(, )} br/ - text: ${selectedText.join( )}, icon: cubeic-alert }).show() }, cancelHandler() { console.log(cancel) } } }关键点逐一解读title就是标题可选不传则标题栏不渲染源码中通过v-show$slots.title || title控制见 drawer.vue。data数据源二维数组长度决定了抽屉的 Panel 数初始长度一定要确定外层数组的每一项对应一列 Panel。上例中[provinceList, [], []]表示三列省/市/区后两列初始为空等待联动填充。初始长度必须确定是硬性要求——show()时组件会按data.length逐列推进并计算位移见 drawer.vue若初始长度不足会导致后续列无法展示。selected-index是初始选择的索引值传入数组例如[0]表示打开抽屉后第一列默认选中第 0 项。源码中通过selected: [...this.selectedIndex]拷贝并响应selectedIndex变化drawer.vue。三个事件change选择发生改变即选中非最后一个 Panel 中的项时触发select选中最后一个 Panel 中的项完成最终选择后触发cancel点击左侧空白蒙层触发。异步级联的核心refill方法文档明确指出“你可以在change中通过 Drawer 的refill方法更新下一个 Panel 的数据可以是同步更新也可以是异步更新。”上例中用setTimeout(..., 200)模拟网络请求拿到数据后调用this.$refs.drawer.refill(index 1, data)填充下一列。从源码看refill做了三件事drawer.vuethis.$set(this.data, panelIndex, data)——响应式替换对应列的数据截断已选中的索引、值、文本集合slice(0, panelIndex)把层级停留在当前列若传入第三个参数index默认选中项文档建议不填则同步触发选中逻辑。三、自定义使用插槽 子组件定制面板“你可以通过插槽来自定义结构。”当默认样式无法满足需求时可在cube-drawer内使用cube-drawer-panel与cube-drawer-item自由组合。以下代码与 example/pages/drawer/custom.vue 一致cube-drawer refdrawer :datadata :selected-indexselectedIndex changechangeHandler selectselectHandler cancelcancelHandler span slottitle{{province.text}}/span cube-drawer-panel v-for(panel, index) in data :keyindex :indexindex :datapanel cube-drawer-item v-for(item, i) in panel :itemitem :keyi :indexi i classcubeic-round-border/i span{{item.text}}/span /cube-drawer-item /cube-drawer-panel /cube-drawerimport { provinceList, cityList, areaList } from ../../data/area export default { data() { return { province: {}, selectedIndex: [], data: [ [], [] ] } }, methods: { showDrawer() { // get radom province const randomIndex Math.round(Math.random() * provinceList.length) const randomProvince provinceList[randomIndex] this.province randomProvince this.$refs.drawer.refill(0, cityList[randomProvince.value]) this.$refs.drawer.show() }, changeHandler(index, item, selectedVal, selectedIndex, selectedText) { setTimeout(() { // city change, get area data const data areaList[item.value] this.$refs.drawer.refill(index 1, data) }, 200) }, selectHandler(selectedVal, selectedIndex, selectedText) { this.$createDialog({ type: warn, content: Selected Item: br/ - value: ${selectedVal.join(, )} br/ - index: ${selectedIndex.join(, )} br/ - text: ${selectedText.join( )}, icon: cubeic-alert }).show() }, cancelHandler() { console.log(cancel) } } }自定义使用要点slottitle自定义标题可注入任意内容上例为随机省份名未提供插槽时回退为titleprop。cube-drawer-panel接收index在 data 中的位置与data该列数据面板内默认使用cube-scroll包裹列表可滚动。cube-drawer-item接收item数据项与index在面板内的位置其默认插槽渲染{{item.text || item}}自定义时可注入图标如cubeic-round-border等任意内容。自定义时的联动填充此例在showDrawer里先随机选一个省份refill(0, cityList[...])填充第一列后再show()与默认示例的“先 show 后联动”路径不同但同样合法。值得注意的是若自定义面板需要覆盖选中高亮样式可像 custom.vue 一样为.cube-drawer-item_active追加样式默认高亮色来自主题变量$drawer-item-active-bgc。四、Props 配置详解CubeDrawer| 参数 | 说明 | 类型 | 可选值 | 默认值 | | - | - | - | - | - | | title | 标题 | String | - | | | data | 数据源 | Array | - | [] | | selectedIndex | 初始选择索引 | Array | - | [] | | visible1.8.1| 显示状态是否可见。v-model绑定值 | Boolean | true/false | false |其中visible是1.8.1新增的受控 prop通过v-model双向绑定控制显隐。其实现来自 visibility.js 混入组件内部维护isVisible数据并监听visible的变化为true时调用show()同时通过toggle事件把内部状态同步出去从而支持v-modelvisible这种写法。注意show()/hide()与visible是同一套状态的两种控制入口混用时要避免状态不一致。此外Drawer 还混入了 popup.js提供zIndex、maskClosable两个可选 prop但组件自身的显隐主要依赖 visibilityMixin 与isVisible。data子配置项data是一个二维数组数组中每一项仍然为数组结构类似于[ [ { text: text, value: value }, ... ], [ text, text2, ... ] ]里层数组的每一项可以是对象包含 text 和 value也可以是纯字符串。从 drawer.vue 的changeHandler可以看到当数据项为字符串时selectedVal与selectedText都取该字符串本身当为对象时值集合取item.value、文本集合取item.text。这也是select回调中selectedText拼接展示“省 市 区”的底层依据。CubeDrawerPanel| 参数 | 说明 | 类型 | 可选值 | 默认值 | | - | - | - | - | - | | data | 数据源 | Array | - | [] | | index | 该数据源在 CubeDrawer 的 data 中的索引值 | Number | - | -1 |CubeDrawerItem| 参数 | 说明 | 类型 | 可选值 | 默认值 | | - | - | - | - | - | | data | 数据项 | String/Object | - | | | index | 该数据项在 CubeDrawerPanel 的 data 中的索引值 | Number | - | -1 |说明文档 Props 表中 CubeDrawerItem 的写法为data对应源码 drawer-item.vue 中实际 prop 名为item类型[String, Object]。使用自定义插槽结构时请以源码实际 prop 名item为准传入数据项例如cube-drawer-item :itemitem :indexi。五、事件详解| 事件名 | 说明 | 参数1 | 参数2 | 参数3 | 参数4 | 参数5 | | - | - | - | - | - | - | - | | change | 选择发生改变选中是非最后一个 Panel 中的项的时候触发 | 发生改变的 Panel 的索引 | 发生改变的数据项 | 已选中的值集合 | 已选中的索引集合 | 已选中的文本集合 | | select | 选择了最后一个 Panel 中的项触发 | 已选中的值集合 | 已选中的索引集合 | 已选中的文本集合 | - | - | | cancel | 点击左侧空白区域触发 | - | - | - | - | - |从源码看三个事件的分发集中在 drawer.vuechangeHandler(panelIndex, item, index)在不是最后一列时$emit(change, ...)供业务方异步拉取下一列数据当panelIndex data.length - 1时$emit(select, ...)并自动调用hide()——即选中最后一个 Panel 的项后抽屉自动收起无需手动关闭drawerClick点击.cube-drawer空白蒙层时hide()并$emit(cancel)。六、实例方法| 方法名 | 说明 | 参数1 | 参数2 | 参数3 | | - | - | - | - | - | | refill | 填充数据改变某个 Panel 数据 | 要改变的 Panel 的索引 | 填充数据 | 默认选中项可选建议不填 | | show | 显示 | - | - | - | | hide | 隐藏 | - | - | - |refill(panelIndex, data, index?)核心联动方法。前两个参数必填第三个参数为默认选中项文档明确“建议不填”因为默认行为会保留当前选中并仅更新数据传入 index 会额外触发一次选中变更一般用于初始化。show()显示抽屉。源码中会按data.length遍历 Panel若某列缺少选中项且不是第一列会以当前已选索引触发一次changeHandler以推进级联drawer.vue。hide()隐藏抽屉。通过修改translate3d位移触发过渡transitionend后才真正把isVisible置为falsedrawer.vue。对应的 TypeScript 声明位于 types/components/Drawer.d.tsrefill: (index?: number, data?: any[], item?: any[]) void、show()、hide()。七、源码原理与测试验证层级切换与位移计算Drawer 的多列展示并非“并排平铺”而是通过translate3d负位移让当前列滑到最右侧可点区域。核心逻辑在computedStyle()drawer.vue遍历当前index之前的所有 Panel累加其offsetWidth与 margin再对.cube-drawer-main设置translate3d(-allWidth, 0, 0)配合transition: transform .3s ease-in-out产生平滑的抽拉动画。Panel 间默认margin-left: -67px、进入/离开动画位移 67pxdrawer-panel.vue形成层叠抽屉的视觉层次。滚动与联动细节每个cube-drawer-panel内部用cube-scroll承载列表并监听data变化回滚到顶部scrollToTop监听isVisible变化后refresh()以重算滚动区域drawer-panel.vuePanel 在mounted时通过$parent.addPanel(this)注册到 DrawerbeforeDestroy时注销保证位移计算始终基于真实 DOM。单元测试佐证组件行为在 test/unit/specs/drawer.spec.js 中有完整覆盖可作为行为规范参考渲染与联动构造data: [[1, 2], [], []]三列数据断言标题渲染、Panel 数量为 3、初始隐藏display: noneshow()后第一列显示且首项高亮含cube-drawer-item_activeclass模拟change回调中refill(index 1, [...])后第二列、第三列依次填充并显示。事件触发顺序点击非末列条目仅触发change点到最后列条目触发select并自动隐藏点击空白蒙层触发cancel。回退联动选中第一列后回点第一列其他项第二列数据被重新填充。测试代码中的交互模式dispatchTap点击选项、refill异步填充与文档示例完全一致可直接对照验证业务写法。八、实践注意事项父容器定位Drawer 是绝对定位父容器必须是相对/绝对定位如position: fixed的.view-wrapper否则抽屉位置错乱。data初始长度必须确定二维数组外层长度决定 Panel 数量联动只负责“填充内容”而非“新增列”需要更多层级时初始化就要预留对应长度的空数组。数据项两种形态对象{ text, value }或纯字符串均可混用于不同列但注意对象形态下selectedVal取value、selectedText取text。异步填充放change在change回调里refill(index 1, data)同步或异步如setTimeout/请求返回后都支持。visible与 v-model1.8.1需要外部控制显隐时用v-model绑定visible方法调用与 v-model 混用需注意状态同步。选中末列自动收起select触发后组件自动hide()无需手动关闭若需要选择后留在抽屉内做额外操作可自行结合refill与selectedIndex设计。按需引入通过import Drawer from cube-ui/lib/drawer对应 lib/drawer 构建产物或全局Vue.use(Drawer)均可Drawer 依赖cube-scroll引入时需确保 scroll 组件可用。通过本文的示例与源码对照你可以直接在业务中落地 Drawer 的级联选择能力——从简单的两三级联动到结合自定义插槽打造完全符合设计稿的多列抽屉均能从容实现。赞分享前端UI组件移动开发【免费下载链接】cube-ui:large_orange_diamond: A fantastic mobile ui lib implement by Vue项目地址https://gitcode.com/gh_mirrors/cu/cube-ui点击查看免费下载相关推荐Naive UI Drawer 抽屉组件完全指南从 Props 到源码实现Naive UI Drawer 抽屉组件完全指南从 Props 到源码实现 抽屉Drawer是 Naive UI 中与 Modal 对话框 https:前端UI组件Ant Design Drawer 抽屉组件完全指南从 API 用法到源码级实现原理Ant Design Drawer 抽屉组件完全指南从 API 用法到源码级实现原理 Drawer抽屉是 Ant Design 中用于「屏幕边缘滑出浮层面前端UI组件设计系统wvp-GB28181-pro一套平台统管混杂品牌摄像头浏览器免插件直接看wvp GB28181 pro一套平台统管混杂品牌摄像头浏览器免插件直接看 wvp GB28181 pro 是一款开箱即用的视频平台统一接入 GB2818后端音视频前端上一篇Vercel 上的 React Router v7 Single-Fetch .data 路由端到端 fixture 实战与源码解析下一篇国家中小学智慧教育平台电子课本下载工具Python技术实现深度解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表