效果图如下所示:
源码地址
bb两句
最近在做一个基于vue的后台管理项目。平时项目进度统计就在上禅道上进行。so~ 然后领导就感觉这个拖拽效果还行,能不能加到咱们项目里面。 既然领导发话,那就开干。。
所有技术:vue + vuedraggable
拖动的实现基于 vuedraggable 的插件开发。
主页为两栏流式布局,每一个组件可以在上下拖动,也可以左右拖动。
基本步骤
布局
这块布局为最为普通的两栏布局,这里采用flex布局。左边自适应,右边为固定宽。
.layout-container { display: flex; .left { flex: 1; margin-right: 40px; } .right { width: 550px; } }
拖拽实现
这里使用 vuedraggable
插件。需要在组件里面引入使用。 draggable 相当于拖拽容器,这块很明显需要两个拖拽的容器。所以分别在 .left .right 中添加两个拖拽容器。在默认情况下,这里已经可以进行拖拽了。插件的效果还是很强大。
<div class="layout-container"> <!--左栏--> <div class="left"> <draggable v-bind="dragOptions" class="list-group" :list="item" > // ... 拖拽元素或组件 </draggable> </div> <!--右栏--> <div class="right"> <draggable v-bind="dragOptions" class="list-group" :list="item" > // ... 拖拽元素或组件 </draggable> </div> </div> <script> import draggable from "vuedraggable"; export default { components: {draggable}, computed: { dragOptions() { return { animation: 30, handle: ".drag-handle", group: "description", ghostClass: "ghost", chosenClass: "sortable", forceFallback: true }; } } }; </script>
但是, 和我想要的效果还是相差一点。
左右拖动 与 仅标题栏拖动
这块只需要配置相关的配置项就可以比较简单。 左右拖动需要给拖拽容器指定相同的 group 属性。指定标题元素拖动需要配置 handle 为可拖动元素的选择器名称。
下面简单介绍下常用的配置项:
- disabled :boolean 定义是否此sortable对象是否可用,为true时sortable对象不能拖放排序等功能,为false时为可以进行排序,相当于一个开关;
- group : 用处是为了设置可以拖放容器时使用,若两个容器该配置项相同,则可以相互拖动;
- animation :number 单位:ms,定义排序动画的时间;
- handle :selector 格式为简单css选择器的字符串,使列表单元中符合选择器的元素成为拖动的手柄,只有按住拖动手柄才能使列表单元进行拖动;
- filter :selector 格式为简单css选择器的字符串,定义哪些列表单元不能进行拖放,可设置为多个选择器,中间用“,”分隔;
- draggable :selector 格式为简单css选择器的字符串,定义哪些列表单元可以进行拖放
- ghostClass :selector 格式为简单css选择器的字符串,当拖动列表单元时会生成一个副本作为影子单元来模拟被拖动单元排序的情况,此配置项就是来给这个影子单元添加一个class,我们可以通过这种方式来给影子元素进行编辑样式;
- chosenClass :selector 格式为简单css选择器的字符串,当选中列表单元时会给该单元增加一个class;
- forceFallback :boolean 如果设置为true时,将不使用原生的html5的拖放,可以修改一些拖放中元素的样式等;
- fallbackClass :string 当forceFallback设置为true时,拖放过程中鼠标附着单元的样式;
采用相关配置如下:
computed: { dragOptions() { return { animation: 30, handle: ".drag-handle", group: "description", ghostClass: "ghost", chosenClass: "sortable", forceFallback: true }; } }
拖动时样式调整
在拖动的时候,我们需要做三个事情。拖动时,拖动元素只显示标题栏,两栏内列表只显示标题元素以及将要移动的位置变灰。
1.拖动元素只显示标题栏: 在默认情况下,会开启 html5 元素的拖动效果。这里明显不需要。 forceFallback 改为 false 则可以关闭 html5 的默认效果。顺便通过 chosenClass: "sortable" 修改拖动元素class 类名。直接用css进行隐藏
.sortable { .component-box { display: none; height: 0; } }
2.两栏内列表只显示标题元素 这里我借助两个事件实现。
- onStart:function 列表单元拖动开始的回调函数
- onEnd:function 列表单元拖放结束后的回调函数
<div class="layout-container" :class="{drag:dragging}"> //... </div> data() { return { dragging: false }; }, methods: { onStart() { this.dragging = true; }, onEnd() { this.dragging = false; } } .drag { .component-box { display: none; } }
在开始拖动的时候给 .layout-container
添加 .drag 的 class 名。拖动结束时,移除class名。
将要移动的位置变灰
这里需要用到上面 ghostClass: "ghost"
配置项。并添加相应的css。
.ghost { .drag-handle { background: rgb(129, 168, 187); } }
好了基本已经实现了。。。
展示动态组件
接下来就是数据的动态展示了。 这里需要vue中的动态组件了。。附上官方文档连接点击查看。
然后里面每个拖动的元素的内容都写成组件,搭配动态组件实现自由拖动。
// 将所用组件引入 import { timeline, calendar, welcome, carousel, imgs, KonList } from "@/components/DragComponents"; components: { draggable, timeline, calendar, welcome, carousel, imgs, KonList }
配合 v-for 对数据进行循环,然后进行动态展示。
<component :is="element.name"/>
这块涉及到数据格式相关的,可以直接看文末的代码。。。 这里就就不展开说了。。
数据保持
在拖动结束后,我们需要将拖动的顺序缓存在前端,当下次进入后,可以继续使用拖动后的数据。
// 获取新的布局 getLayout() { let myLayout = JSON.parse(window.localStorage.getItem("kon")); if (!myLayout || Object.keys(myLayout).length === 0) myLayout = this.layout; const newLayout = {}; for (const side in myLayout) { newLayout[side] = myLayout[side].map(i => { return this.componentList.find(c => c.id === i); }); } this.mainData = newLayout; }, // 设置新的布局 setLayout() { const res = {}; for (const side in this.mainData) { const item = this.mainData[side].map(i => i.id); res[side]=item; } window.localStorage.setItem("kon", JSON.stringify(res)); }
这样我只需要在 mounted 中获取新的布局。。
mounted() { this.getLayout(); }
在拖动结束后,设置新的布局
onEnd() { this.dragging = false; this.setLayout(); }
在项目中,还是建议配合后端进行用户布局的数据存储,每次拖动后将新的布局数据请求接口保存在数据库,同时存入缓存中。当再次进入页面的时候,读取缓存中的数据,没有的话请求后端的接口拿到用户的布局,然后再次存入缓存中。有的话直接读取缓存中的数据。
最后说两句
其实上面的效果也不是特别难,简单花点时间,看看相关文档,就能做出来,,记录在掘金上面,只是想和大家分享我的思路。同时希望和大家一起交流,一起进步。
生活不易,大家加油
附上源码: 项目地址
<template> <div :class="{drag:dragging}"> <div class="layout-container"> <div :class="key" v-for="(item, key) in mainData" :key="key"> <draggable v-bind="dragOptions" class="list-group" :list="item" @end="onEnd" @start="onStart" > <transition-group name="list"> <div class="list-group-item" v-for="(element, index) in item" :key="index"> <div class="drag-handle">{{ element.title }}</div> <div class="component-box"> <component :is="element.name"/> </div> </div> </transition-group> </draggable> </div> </div> </div> </template> <script> import draggable from "vuedraggable"; import { timeline, calendar, welcome, carousel, imgs, KonList } from "@/components/DragComponents"; export default { components: { draggable, timeline, calendar, welcome, carousel, imgs, KonList }, data() { return { dragging: false, componentList: [ { name: "KonList", title: "追番地址", id: "5" }, { name: "imgs", title: "五月最强新番", id: "4" }, { name: "timeline", title: "日程组件", id: "2" }, { name: "carousel", title: "走马灯组件", id: "1" }, { name: "calendar", title: "日历组件", id: "3" } ], layout: { left: ["5", "4"], right: ["2", "1", "3"] }, mainData: {} }; }, computed: { dragOptions() { return { animation: 30, handle: ".drag-handle", group: "description", ghostClass: "ghost", chosenClass: "sortable", forceFallback: true }; } }, mounted() { this.getLayout(); }, methods: { onStart() { this.dragging = true; }, onEnd() { this.dragging = false; this.setLayout(); }, getLayout() { let myLayout = JSON.parse(window.localStorage.getItem("kon")); if (!myLayout || Object.keys(myLayout).length === 0) myLayout = this.layout; const newLayout = {}; for (const side in myLayout) { newLayout[side] = myLayout[side].map(i => { return this.componentList.find(c => c.id === i); }); } this.mainData = newLayout; }, setLayout() { const res = {}; for (const side in this.mainData) { const item = this.mainData[side].map(i => i.id); res[side]=item; } window.localStorage.setItem("kon", JSON.stringify(res)); } } }; </script> <style lang="scss" scoped> .layout-container { height: 100%; display: flex; .left { flex: 1; margin-right: 40px; } .right { width: 550px; } .list-group-item { margin-bottom: 20px; border-radius: 6px; overflow: hidden; background: #fff; } .component-box { padding: 20px; } .drag-handle { cursor: move; height: 40px; line-height: 40px; color: #fff; font-weight: 700; font-size: 16px; padding: 0 20px; background: #6cf; } } .drag { .component-box { display: none; } } .list-enter-active { transition: all .3s linear; } .list-enter, .list-leave-to { opacity: .5; } .sortable { .component-box { display: none; height: 0; } } .list-group { > span { display: block; min-height: 20px; } } .ghost { .drag-handle { background: rgb(129, 168, 187); } } </style>
总结
以上所述是小编给大家介绍的基于vue实现一个禅道主页拖拽效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!
vue主页拖拽,vue,拖拽
免责声明:本站文章均来自网站采集或用户投稿,网站不提供任何软件下载或自行开发的软件! 如有用户或公司发现本站内容信息存在侵权行为,请邮件告知! 858582#qq.com
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。