基于vue实现一个禅道主页拖拽效果


Posted in Javascript onMay 27, 2019

效果图如下所示:

基于vue实现一个禅道主页拖拽效果

源码地址

bb两句

最近在做一个基于vue的后台管理项目。平时项目进度统计就在上禅道上进行。so~ 然后领导就感觉这个拖拽效果还行,能不能加到咱们项目里面。 既然领导发话,那就开干。。

所有技术:vue + vuedraggable

拖动的实现基于 vuedraggable 的插件开发。

主页为两栏流式布局,每一个组件可以在上下拖动,也可以左右拖动。

基于vue实现一个禅道主页拖拽效果

基本步骤

布局

这块布局为最为普通的两栏布局,这里采用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实现一个禅道主页拖拽效果

展示动态组件

接下来就是数据的动态展示了。 这里需要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();
}

在项目中,还是建议配合后端进行用户布局的数据存储,每次拖动后将新的布局数据请求接口保存在数据库,同时存入缓存中。当再次进入页面的时候,读取缓存中的数据,没有的话请求后端的接口拿到用户的布局,然后再次存入缓存中。有的话直接读取缓存中的数据。

最后说两句

其实上面的效果也不是特别难,简单花点时间,看看相关文档,就能做出来,,记录在掘金上面,只是想和大家分享我的思路。同时希望和大家一起交流,一起进步。

基于vue实现一个禅道主页拖拽效果

生活不易,大家加油

附上源码: 项目地址

<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实现一个禅道主页拖拽效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对三水点靠木网站的支持!
如果你觉得本文对你有帮助,欢迎转载,烦请注明出处,谢谢!

Javascript 相关文章推荐
jQuery弹出层插件简化版代码下载
Oct 16 Javascript
用JS做的简单的可折叠的两级树形菜单
Sep 21 Javascript
javascript中的作用域和上下文使用简要概述
Dec 05 Javascript
JavaScript DOM事件(笔记)
Apr 08 Javascript
1秒50万字!js实现关键词匹配
Aug 01 Javascript
微信小程序 window_x64环境搭建
Sep 30 Javascript
微信小程序进行微信支付的步骤昂述
Dec 01 Javascript
利用JS实现简单的瀑布流加载图片效果
Apr 22 Javascript
EasyUI Datebox 日期验证之开始日期小于结束时间
May 19 Javascript
浅谈angular2 组件的生命周期钩子
Aug 12 Javascript
微信小程序 云开发模糊查询实现解析
Sep 02 Javascript
关于Js中new操作符的作用详解
Feb 21 Javascript
jQuery实现input输入框获取焦点与失去焦点时提示的消失与显示功能示例
May 27 #jQuery
初学node.js中实现删除用户路由
May 27 #Javascript
jquery实现动态创建form并提交的方法示例
May 27 #jQuery
jquery实现动态改变css样式的方法分析
May 27 #jQuery
Vue scrollBehavior 滚动行为实现后退页面显示在上次浏览的位置
May 27 #Javascript
通过jQuery学习js类型判断的技巧
May 27 #jQuery
实现高性能javascript的注意事项
May 27 #Javascript
You might like
php数组函数序列之array_key_exists() - 查找数组键名是否存在
2011/10/29 PHP
php array_filter除去数组中的空字符元素
2020/06/21 PHP
基于php设计模式中单例模式的应用分析
2013/05/15 PHP
PHP的消息通信机制测试实例
2016/11/10 PHP
php连接MSsql server的五种方法总结
2018/03/04 PHP
gearman中任务的优先级和返回状态实例分析
2020/02/27 PHP
tp5.1 框架数据库高级查询技巧实例总结
2020/05/25 PHP
点击文章内容处弹出页面代码
2009/10/01 Javascript
理解Javascript_13_执行模型详解
2010/10/20 Javascript
javascript表单验证 - Parsley.js使用和配置
2013/01/25 Javascript
jQuery中:only-child选择器用法实例
2015/01/03 Javascript
javascript实现限制上传文件大小
2015/02/06 Javascript
gameboy网页闯关游戏(riddle webgame)--仿微信聊天的前端页面设计和难点
2016/02/21 Javascript
AngularJS解决ng界面长表达式(ui-set)的方法分析
2016/11/07 Javascript
深入理解vue $refs的基本用法
2017/07/13 Javascript
从vue基础开始创建一个简单的增删改查的实例代码(推荐)
2018/02/11 Javascript
微信小程序踩坑记录之解决tabBar.list[3].selectedIconPath大小超过40kb
2018/07/04 Javascript
用Vue编写抽象组件的方法
2019/05/06 Javascript
只有 20 行的 JavaScript 模板引擎实例详解
2020/05/11 Javascript
JavaScript中遍历的十种方法总结
2020/12/15 Javascript
图文详解WinPE下安装Python
2016/05/17 Python
Python用list或dict字段模式读取文件的方法
2017/01/10 Python
Python 数据结构之旋转链表
2017/02/25 Python
Python实现删除排序数组中重复项的两种方法示例
2019/01/31 Python
python判断无向图环是否存在的示例
2019/11/22 Python
Python urlopen()和urlretrieve()用法解析
2020/01/07 Python
Hotels.com南非:酒店预订
2017/11/02 全球购物
以特惠价提供在线奢侈品购物:FRMODA.com
2018/01/25 全球购物
澳大利亚最大的百货公司:Myer
2018/12/21 全球购物
EJB包括(SessionBean,EntityBean)说出他们的生命周期,及如何管理事务的
2015/07/24 面试题
经理秘书找工作求职信
2013/12/19 职场文书
优秀求职信范文分享
2013/12/19 职场文书
农村党支部先进事迹
2014/01/14 职场文书
中专毕业生个人职业生涯规划
2014/02/19 职场文书
纪委书记群众路线整改措施思想汇报
2014/10/09 职场文书
谢师宴邀请函
2015/02/02 职场文书