欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

基于vue-draggable 实现三级拖动排序效果

程序员文章站 2022-03-25 15:49:42
vue-draggable 之前项目中需要用到拖动排序,就去网上找资料,本来最开始是想用jquery-ui里的拖动的,后面发现不符合我的预期也不知道能不能跟vue.js兼容,后面我...

vue-draggable

之前项目中需要用到拖动排序,就去网上找资料,本来最开始是想用jquery-ui里的拖动的,后面发现不符合我的预期也不知道能不能跟vue.js兼容,后面我试过了,单个的可以但是层级太多就不一样了。

废话少说直接上代码

基于vue-draggable 实现三级拖动排序效果

基于vue-draggable 实现三级拖动排序效果

先看数据结构,和页面的呈现,等会再来上代码。

这就是三层结构渲染出来的图。那个海锚一样的东西是可以点击的,点击后会出现当前类型所带的产品。等会会说的

我们现在来看下我实现后的拖动效果,如下

基于vue-draggable 实现三级拖动排序效果

所有父类型里面的产品拖动如下

基于vue-draggable 实现三级拖动排序效果

控制台的打印

基于vue-draggable 实现三级拖动排序效果

好了,放了那么多图,数据结构也发了。接下来我们来上代码和思路。

先上html的代码,这里我的页面是jsp,但是不影响html兼容,项目中途接手,很古老的jsp我也没办法


<%@ page contenttype="text/html;charset=utf-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<c:set var="ctx" value="${pagecontext.request.contextpath}"/>
<link rel="stylesheet" href="${ctx}/res/ifw/plugins/datatables/datatables.bootstrap.css" rel="external nofollow" />
<style>
 [v-cloak] {
 display: none;
 }
 .flip-list-move {
 transition: transform 0.5s;
 }
 .handle {
 float: right;
 padding-top: 2px;
 padding-bottom: 8px;
 }
 .no-move {
 transition: transform 0s;
 }
 .ghost {
 opacity: 0.5;
 background: #c8ebfb;
 }
 .list-group {
 min-height: 20px;
 }
 .list-group-item {
 cursor: move;
 }
 .list-group-item i {
 cursor: pointer;
 }
</style>
<div id="vuesort" class="box box-darkness">
 <div class="box-header with-border">
 <h3 class="box-title">排序</h3>
 <div class="box-tools pull-right">
  <button type="button" class="btn btn-box-tool" data-widget="collapse"><i class="fa fa-minus"></i>
  </button>
 </div>
 </div>
 <div class="box-body" style="position: relative">
 <div class="col-md-3">
  <ul id="main-nav1" class="nav nav-tabs nav-stacked">
  <draggable class="list-group" tag="ul" v-model="listproducttype":move="getdata" @update="datadragend">
   <transition-group type="transition" :name="'flip-list'">
   <li class="list-group-item" v-for="(element,index) in listproducttype" :key="element.id">
    <a :href="'#'+forid(element.uuid)" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="nav-header collapsed" data-toggle="collapse"><i
     v-show="element.productlist.length>0"
     aria-hidden="true"
     :class="{'fa fa-anchor':isactive,'glyphicon glyphicon-pushpin':!isactive}"
     @click="submenu"></i></a>
    {{element.name}}
    <i class="fa fa-align-justify handle" v-show="element.producttypes.length>0"
    @click="showleve2(index)"></i>
    <template v-if="element.productlist.length>0">
    <ur :id="forid(element.uuid)" class="nav nav-list collapse secondmenu">
     <draggable class="list-group" tag="ul":move="getdata" v-model="element.productlist"
      @update="datadragends">
     <transition-group type="transition" :name="'flip-list'">
   <li class="list-group-item" v-for="e in element.productlist" :key="e.id">
    <a> {{e.name}}</a>
   </li>
   </transition-group>
  </draggable>
  </ur>
  </template>
  </li>
  </transition-group>
  </draggable>
  </ul>
 </div>
 <div class="col-md-3" v-show="one.producttypes.length>0&&showone">
  <span><h3 style="color: red">--->>>{{one.name}}</h3></span>
  <ul id="main-nav2" class="nav nav-tabs nav-stacked">
  <draggable class="list-group" tag="ul" v-model="one.producttypes" :move="getdata"@update="datadragend">
   <transition-group type="transition" :name="'flip-list'">
   <li class="list-group-item" v-for="(element,index) in one.producttypes" :key="element.id">
    <a :href="'#'+forid(element.uuid)" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="nav-header collapsed" data-toggle="collapse"><i
     v-show="element.productlist.length>0"
     aria-hidden="true"
     :class="{'fa fa-anchor':isactive,'glyphicon glyphicon-pushpin':!isactive}"
     @click="submenu"></i></a>
    {{element.name}}
    <i class="fa fa-align-justify handle" v-show="element.producttypes.length>0"
    @click="showleve3(index)"></i>
    <template v-if="element.productlist.length>0">
    <ur :id="forid(element.uuid)" class="nav nav-list collapse secondmenu">
     <draggable class="list-group" tag="ul":move="getdata" v-model="element.productlist"
      @update="datadragends">
     <transition-group type="transition" :name="'flip-list'">
   <li class="list-group-item" v-for="e in element.productlist" :key="e.id">
    <a> {{e.name}}</a>
   </li>
   </transition-group>
  </draggable>
  </ur>
  </template>
  </li>
  </transition-group>
  </draggable>
  </ul>
 </div>
 <div class="col-md-3" v-show="two.producttypes.length>0&&showtwo">
  <span><h3 style="color: red">--->>>{{two.name}}</h3></span>
  <ul id="main-nav3" class="nav nav-tabs nav-stacked">
  <draggable class="list-group" tag="ul" v-model="two.producttypes":move="getdata" @update="datadragend">
   <transition-group type="transition" :name="'flip-list'">
   <li class="list-group-item" v-for="(element,index) in two.producttypes" :key="element.id">
    <a :href="'#'+forid(element.uuid)" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="nav-header collapsed" data-toggle="collapse"><i
     v-show="element.productlist.length>0"
     aria-hidden="true"
     :class="{'fa fa-anchor':isactive,'glyphicon glyphicon-pushpin':!isactive}"
     @click="submenu"></i></a>
    {{element.name}}
    <template v-if="element.productlist.length>0">
    <ur :id="forid(element.uuid)" class="nav nav-list collapse secondmenu">
     <draggable class="list-group":move="getdata" tag="ul" v-model="element.productlist"
      @update="datadragends">
     <transition-group type="transition" :name="'flip-list'">
   <li class="list-group-item" v-for="e in element.productlist" :key="e.id">
    <a> {{e.name}}</a>
   </li>
   </transition-group>
  </draggable>
  </ur>
  </template>
  </li>
  </transition-group>
  </draggable>
  </ul>
 </div>
 </div>
 <div class="box-footer">
 <button type="button" class="btn btn-darkness pull-right" id="dosearch"
  style="margin-left: 20px;"@click="save">保存
 </button>
 <button type="button" class="btn btn-default pull-right" @click="reset" id="resetsearch">重置</button>
 </div>
</div>
<script type="text/javascript" src="${ctx}/res/js/vue/vue.js"></script>
<!-- cdnjs :: sortable (https://cdnjs.com/) -->
<script src="${ctx}/res/js/vue/sortable.min.js"></script>
<!-- cdnjs :: vue.draggable (https://cdnjs.com/) -->
<script src="${ctx}/res/js/vue/vuedraggable.umd.min.js"></script>
<script type="text/javascript" src="${ctx}/res/js/sort/combinationsort.js"></script>

接下来是js。


vue.component('vue-draggable', vuedraggable)
var vm = new vue({
 el: '#vuesort',
 data: {
 isactive: true,
 queryobject: {},
 listproducttype: [],
 showson: false,
 index: 0,
 one: {producttypes: []},
 two: {producttypes: []},
 showone: false,
 showtwo: false
 },
 methods: {
 init: function () {
  var _this = this;
  $.ajax({
  url: '../../mt/combinationsort/sortingdata',
  data: null,
  type: 'post',
  contenttype: "application/json",
  datatype: 'json',
  success: function (data) {
   if (data.success = true) {
   if (data.dataobject.length == 0) {
    util.alert('通知', '异常数据', 'info');
    return;
   }
   _this.listproducttype = data.dataobject;
   }
   console.log(data)
  }
  })
 },
 reset: function () {
  var _this = this;
  _this.listproducttype = _this.listproducttype.sort((one, two) => {
  return one.displayseq - two.displayseq;
 })
  ;
  for (var i in _this.listproducttype) {
  //排序产品类型
  _this.listproducttype[i].producttypes = _this.listproducttype[i].producttypes.sort((one, two) => {
   return one.displayseq - two.displayseq;
  })
  ;
  //排序产品
  _this.listproducttype[i].productlist = _this.listproducttype[i].productlist.sort((one, two) => {
   return one.displayseq - two.displayseq;
  })
  ;
  for (var a in _this.listproducttype[i].producttypes) {
   _this.listproducttype[i].producttypes[a].producttypes = _this.listproducttype[i].producttypes[a].producttypes.sort((one, two) => {
   return one.displayseq - two.displayseq;
  })
   ;
   _this.listproducttype[i].producttypes[a].productlist = _this.listproducttype[i].producttypes[a].productlist.sort((one, two) => {
   return one.displayseq - two.displayseq;
  })
   ;
   for (var c in _this.listproducttype[i].producttypes[a].producttypes) {
   _this.listproducttype[i].producttypes[a].producttypes[c].productlist = _this.listproducttype[i].producttypes[a].producttypes[c].productlist.sort((one, two) => {
    return one.displayseq - two.displayseq;
   })
   ;
   }
  }
  }
 },
 datadragend: function (evt) {
  console.log('拖动前的索引:' + evt.oldindex);
  console.log('拖动后的索引:' + evt.newindex);
  var obj = evt.item;
  obj.style.backgroundcolor = '#fff';
 },
 submenu: function () {
  var _this = this;
  if (_this.isactive) _this.isactive = false;
  else _this.isactive = true;
  if (_this.showson) _this.showson = false;
  else _this.showson = true;
 },
 datadragends: function (evt) {
  console.log('拖动前的索引:' + evt.oldindex);
  console.log('拖动后的索引:' + evt.newindex);
  var obj = evt.item;
  obj.style.backgroundcolor = '#fff';
 },
 forid: function (index) {
  return "uuid_" + index
 },
 
 showleve2: function (index) {
  var _this = this;
  _this.index = index;
  // if (_this.one.producttypes.length > 0) _this.one.producttypes = [];
  // else
  _this.one = _this.listproducttype[index];
  console.log(_this.one)
  if (_this.showone) {
  _this.showone = false;
  _this.showtwo = false;
  }
  else _this.showone = true;
 },
 showleve3: function (index) {
  var _this = this;
  // if (_this.two.producttypes.length > 0) _this.two.producttypes = [];
  // else
  _this.two = _this.listproducttype[_this.index].producttypes[index];
  console.log(_this.two.producttypes)
  if (_this.showtwo) _this.showtwo = false;
  else _this.showtwo = true;
 },
 getdata: function (event) {
  console.log("下来了");
  var obj = event.dragged;
  obj.style.backgroundcolor = '#11cc17';
 },
 save: function () {
  var _this = this;
  util.confirm('提示', '您确定要保存排序吗?', function (isok) {
  if (isok) {
   console.log(_this.listproducttype);
   $.ajax({
   type: "post",
   url: "../../mt/combinationsort/savesortingdata",
   data: {list: json.stringify(_this.listproducttype)},
   success: function (json) {
    console.log(json);
   }
   });
   util.alert("提示", '保存成功', 'info');
  }
  }, 'info');
 
 }
 },
 created: function () {
 var _this = this;
 _this.init();
 // _this.heartbeat();
 }
});

最重要的是这几行代码

基于vue-draggable 实现三级拖动排序效果

然后是使用vue把vuedraggable模块引入,上面图最下面的js是我刚刚发过的代码文件。

vue.component('vue-draggable', vuedraggable)

这句话显得尤为重要。注册成vue的组件,虽然它本身就是vue的一个组件了。

基于vue-draggable 实现三级拖动排序效果

当然最后我们会进行排序后的顺序的保存。这里就不得不说vue的双向绑定了,你对象只要在页面改变位置,在内存地址里的位置顺序也会被改变的,所有我们只需要再次将整个对象回传就行。后台去解析保存,当然这种方式我觉得会很繁琐。比如:我贴个获取数据的代码

/**
 * 获取排序数据
 * * @param merchantid
 * @return
 */
 public list<sortproducttypevo> treesorting(long merchantid) {
 //获取所有的连接项
 list<producttyperef> typereflist = producttyperefservice.findall();
 //获取所有的产品
 map<long, productvo> productlist = productservice.sortfindproduct(merchantid).stream().collect(
  collectors.tomap(w -> w.getid(), w -> w));
 //最上级父级
 list<sortproducttypevo> parentlist = byparentproduct(merchantid, 0);
 //平均未分类
 list<sortproducttypevo> typelist = byparentproduct(merchantid, 1);
 //
 //获取产品类型和产品关联
 map<long, list<producttyperef>> parentidchildrenmap = typereflist.stream().filter(producttyperef -> producttyperef.getproducttypeid() != null).collect(collectors.groupingby(producttyperef::getproducttypeid));
 parentlist.foreach(p -> {
  //筛选第二级菜单
  list<sortproducttypevo> districtsone = typelist.stream().filter(sortproducttypevo -> sortproducttypevo.getparenttypeid().equals(p.getid())).collect(collectors.tolist());
  districtsone.foreach(a -> {
  //第三层菜单
  list<sortproducttypevo> districtstwo = typelist.stream().filter(producttype -> producttype.getparenttypeid().equals(a.getid())).collect(collectors.tolist());
  districtstwo.stream().foreach(d -> {
   //获取产品和产品类型之间的连接关系
   list<producttyperef> l = parentidchildrenmap.getordefault(d.getid(), new arraylist<>());
   //排序产品关联就相当于产品排序
   l.sort((q, b) -> integer.compare(q.getdisplayseq(), b.getdisplayseq()));
   //根据排序产品关联去找到产品
   d.setproductlist(l.stream().map(e -> {
   productvo products = productlist.get(e.getproductid());
   if (null != products) products.setdisplayseq(e.getdisplayseq());
   return products;
   }).collect(collectors.tolist()).stream().filter(s -> s != null).collect(collectors.tolist()));//数组中过滤空的产品
//   d.setproducttyperefs(parentidchildrenmap.getordefault(d.getid(), new arraylist<>()));
  });
  list<producttyperef> l = parentidchildrenmap.getordefault(a.getid(), new arraylist<>());
  l.sort((q, b) -> integer.compare(q.getdisplayseq(), b.getdisplayseq()));
  a.setproductlist(l.stream().map(c -> {
   productvo products = productlist.get(c.getproductid());
   if (null != products) products.setdisplayseq(c.getdisplayseq());
   return products;
  }).collect(collectors.tolist()).stream().filter(s -> s != null).collect(collectors.tolist()));
  districtstwo.sort((q, b) -> integer.compare(q.getdisplayseq(), b.getdisplayseq()));
  a.setproducttypes(districtstwo);
//  a.setproducttyperefs(parentidchildrenmap.getordefault(a.getid(), new arraylist<>()));
  });
  list<producttyperef> l = parentidchildrenmap.getordefault(p.getid(), new arraylist<>());
  l.sort((q, b) -> integer.compare(q.getdisplayseq(), b.getdisplayseq()));
  p.setproductlist(l.stream().map(a -> {
  productvo products = productlist.get(a.getproductid());
  if (null != products) products.setdisplayseq(a.getdisplayseq());
  return products;
  }).collect(collectors.tolist()).stream().filter(s -> s != null).collect(collectors.tolist()));
//  p.setproducttyperefs(parentidchildrenmap.getordefault(p.getid(), new arraylist<>()));
  districtsone.sort((q, b) -> integer.compare(q.getdisplayseq(), b.getdisplayseq()));
  p.setproducttypes(districtsone);
 });
 parentlist.sort((q, b) -> integer.compare(q.getdisplayseq(), b.getdisplayseq()));
 return parentlist;
 }

jdk8语法,可能还有需要改进的地方。反正目前来说,功能是实现了。

其实本来想代码一点点讲解的,奈何实在是有事。

关于怎么让3级菜单组件相互拖动,你只需要在父级相互拖动这里就能找到答案,

基于vue-draggable 实现三级拖动排序效果

加上这个属性就行,理论上。我没试过,因为我懒,hhhh

总结

以上所述是小编给大家介绍的基于vue-draggable 实现三级拖动排序效果,希望对大家有所帮助