两天前,客户方提出了便捷工作台的需求,通俗点来说,可通过拖拽、增减等操作,将菜单拖入到想放置的模块中,同时可对大模块拖拽排序,最终以配置的模块或菜单,在首页进行展示。该需求,实现逻辑上其实很简单明了,无非是确定拖拽区域,拖拽对象,按照一定规则操作菜单,但在大的功能做完之后,遗留了一些小的bug,这些小bug容易让人遗漏。因此,在这里记录一下。
一、实现流程
目前项目适用框架:vue2+ant-design-vue
拖拽组件:vuedraggable
-
页面布局
首页以flex布局去实现,完成固定两行,列数自适应的布局。
弹窗采用ant-design提供的模态框组件去封装,同时根据业务需求,接入vuedraggable组件。这里代码比较简单,直接贴图,具体实现不做赘述。
二、遇到的问题
-
2.1、弹框引发滚动穿透
问题描述:在打开的弹窗中,设置滚动区域,当滚动到弹窗底部或头部,同时超出父级元素滚动区域时,会出现父级div同时滚动现象,也就是所谓的滚动穿透。 如何处理:监听弹窗元素滚动开始start方法,在该方法中,将根元素滚动属性隐藏,对弹窗滚动结束end方法监听,恢复根元素的滚动属性。
<draggable @start="start" @end="end"></draggable>
start() {
// 防止拖动时,滚动穿透
document.getElementById('app').style.overflow = 'hidden'
},
end() {
document.getElementById('app').style.overflow = ''
},
-
2.2、关闭弹窗后,首页展示区域局部渲染
问题描述:对弹窗内容操作之后,需要将弹窗中已经拖拽的元素,按照项目需求展示在首页某个区域中,由于vue只会在加载页面时渲染一次,因此关闭弹窗之后,首页区域不会重新渲染,因而出现了展示区域样式混乱现象。 如何处理:vue只有当组件内容获取样式发生改变之后,才会重新进行渲染,因此我们可以为组件设置key属性,key内容为当前时间值,由于时间值随时在改变,因此vue会重新渲染。
<div class="impact-quick-card-container" :key="new Date().getTime()"></div>
-
2.3、拖拽滚动区域
问题描述:需要指定固定区域,进行拖拽操作,如何确认滚动区域? 如何处理:根据vuedraggable文档描述,可以设置handle属性,该属性中定义拖拽的元素,可以为需要退拽的区域dom设置该属性。
-
2.4、多级元素拖动设计
问题描述:需求为一个多层拖拽效果,即菜单可以拖拽到所属模块,同时所属模块也能互相拖动,用于排序,如何设置多级元素关联关系的元素拖动呢? 如何处理:首先将模块与菜单元素分层,内部菜单之间可拖动,设置group值为menu,外层模块元素区域,设置另外的group属性为module,通过group区别,来控制不同的拖拽分组。
<draggable group="module">
<div v-for="(item, index) in modules" :key="index">
<h3>{{ item.modelName }}</h3>
<draggable group="menu">
<div v-for="(element, idx) in item.rows" :key="idx" class="move list-group-item ">
<span class="text" :title="element.benchName">{{ element.benchName }}</span>
<b class="list-group-item-icon" @click="subtractPage(item.rows, idx, element)">-</b>
</div>
</draggable>
</div>
</draggable>
-
2.5、拖拽时,无法同时滚动
问题描述:当拖拽某个菜单元素且超过当前区域时,弹窗不向下滚动。 如何处理:vuedraggable文档提供了scrollSensitivity属性,用于描述拖拽距离滚动区域多远时,滚动条滚动。给draggable设置该属性,可以处理该问题。
<draggable group="menu" @start="start" :scrollSensitivity="250">
</draggable>
-
2.6、隐形滚动条
问题描述:当出现滚动时,如何将滚动条做的更好看一些,也就是隐形滚动条,有滚动效果,但不出现浏览器原生滚动条。 如何处理:利用内外两层div包裹,通过css控制滚动区域,外层div滚动属性设置为hidden,内层利用position:fixed固定区域,同时设置overflow属性,这里要这与,如果想要隐藏浏览器的原生滚动条,需要设置内层区域高度或宽度高于外层区域。
<div class="impact-quick-card-container" :key="new Date().getTime()">
<div class="quick-card-container">
</div>
</div>
<style lang="less" scoped>
.impact-quick-card-container {
position: relative;
height: 250px;
overflow: hidden;
.quick-card-container {
display: flex;
position: absolute;
width: 100%;
overflow-x: auto;
overflow-y: hidden;
}
}
</style>
-
2.7、flex固定行的布局
问题描述:如何利用flex布局,固定行,列随着设置动态变化。 如何处理:使用flex的column排序,解决不了该问题。只能通过writing-mode来处理。flex根元素设置writing-mode为vertical-lr,表示元素会从上到下垂直排列,其次flex子元素div上,设置writing-mode为horizontal-tb,表示该元素将水平从左到右排列,同时一定要设置子元素的flex宽度,使用50%将行划分为两行。
<div class="wrapper-content">
<div class="module-wrapper" v-for="(menu, idx) in item.rows" :key="idx">
</div>
</div>
<style lang="less" scoped>
.wrapper-content {
display: flex;
writing-mode: vertical-lr;
flex-wrap: wrap;
padding: 15px 10px 5px 10px;
height: 200px;
.module-wrapper {
flex: 0 0 50%;
writing-mode: horizontal-tb;
}
}
</style>