基于vue-draggable 實現(xiàn)三級拖動排序效果
vue-draggable
之前項目中需要用到拖動排序,就去網(wǎng)上找資料,本來最開始是想用jquery-ui里的拖動的,后面發(fā)現(xiàn)不符合我的預期也不知道能不能跟vue.js兼容,后面我試過了,單個的可以但是層級太多就不一樣了。
廢話少說直接上代碼
先看數(shù)據(jù)結(jié)構(gòu),和頁面的呈現(xiàn),等會再來上代碼。
這就是三層結(jié)構(gòu)渲染出來的圖。那個海錨一樣的東西是可以點擊的,點擊后會出現(xiàn)當前類型所帶的產(chǎn)品。等會會說的
我們現(xiàn)在來看下我實現(xiàn)后的拖動效果,如下
所有父類型里面的產(chǎn)品拖動如下
控制臺的打印
好了,放了那么多圖,數(shù)據(jù)結(jié)構(gòu)也發(fā)了。接下來我們來上代碼和思路。
先上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('通知', '異常數(shù)據(jù)', '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) { //排序產(chǎn)品類型 _this.listProductType[i].productTypes = _this.listProductType[i].productTypes.sort((one, two) => { return one.displaySeq - two.displaySeq; }) ; //排序產(chǎn)品 _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把vuedraggable模塊引入,上面圖最下面的js是我剛剛發(fā)過的代碼文件。
Vue.component('vue-draggable', vuedraggable)
這句話顯得尤為重要。注冊成vue的組件,雖然它本身就是vue的一個組件了。
當然最后我們會進行排序后的順序的保存。這里就不得不說vue的雙向綁定了,你對象只要在頁面改變位置,在內(nèi)存地址里的位置順序也會被改變的,所有我們只需要再次將整個對象回傳就行。后臺去解析保存,當然這種方式我覺得會很繁瑣。比如:我貼個獲取數(shù)據(jù)的代碼
/** * 獲取排序數(shù)據(jù) * * @param merchantId * @return */ public List<SortProductTypeVo> treeSorting(Long merchantId) { //獲取所有的連接項 List<ProductTypeRef> typeRefList = productTypeRefService.findAll(); //獲取所有的產(chǎn)品 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); // //獲取產(chǎn)品類型和產(chǎn)品關(guān)聯(lián) 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 -> { //獲取產(chǎn)品和產(chǎn)品類型之間的連接關(guān)系 List<ProductTypeRef> l = parentIdChildrenMap.getOrDefault(d.getId(), new ArrayList<>()); //排序產(chǎn)品關(guān)聯(lián)就相當于產(chǎn)品排序 l.sort((q, b) -> Integer.compare(q.getDisplaySeq(), b.getDisplaySeq())); //根據(jù)排序產(chǎn)品關(guān)聯(lián)去找到產(chǎn)品 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()));//數(shù)組中過濾空的產(chǎn)品 // 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語法,可能還有需要改進的地方。反正目前來說,功能是實現(xiàn)了。
其實本來想代碼一點點講解的,奈何實在是有事。
關(guān)于怎么讓3級菜單組件相互拖動,你只需要在父級相互拖動這里就能找到答案,
加上這個屬性就行,理論上。我沒試過,因為我懶,hhhh
總結(jié)
以上所述是小編給大家介紹的基于vue-draggable 實現(xiàn)三級拖動排序效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對我們網(wǎng)站的支持!
如果你覺得本文對你有幫助,歡迎轉(zhuǎn)載,煩請注明出處,謝謝!
上一篇:沒有了
欄 目:JavaScript
下一篇:沒有了
本文標題:基于vue-draggable 實現(xiàn)三級拖動排序效果
本文地址:http://mengdiqiu.com.cn/a1/JavaScript/9226.html
您可能感興趣的文章
- 04-02java后端代碼分頁 java后端實現(xiàn)分頁page
- 01-10Echarts實現(xiàn)單條折線可拖拽效果
- 01-10在Vue項目中使用Typescript的實現(xiàn)
- 01-10js實現(xiàn)上傳圖片并顯示圖片名稱
- 01-10Vue中使用Lodop插件實現(xiàn)打印功能的簡單方法
- 01-10echarts實現(xiàn)折線圖的拖拽效果
- 01-10d3.js實現(xiàn)圖形縮放平移
- 01-10小程序簡單兩欄瀑布流效果的實現(xiàn)
- 01-10H5實現(xiàn)手機拍照和選擇上傳功能
- 01-10Echarts實現(xiàn)多條折線可拖拽效果


閱讀排行
本欄相關(guān)
- 04-02javascript點線,點線的代碼
- 04-02javascript潛力,javascript強大嗎
- 04-02javascript替換字符串,js字符串的替換
- 04-02javascript移出,js 移入移出
- 04-02包含javascript舍的詞條
- 04-02javascript并行,深入理解并行編程 豆瓣
- 04-02javascript匿名,js匿名方法
- 04-02javascript警報,JavaScript警告
- 04-02javascript遮蓋,JavaScript遮蓋PC端頁面
- 04-02javascript前身,javascript的前身
隨機閱讀
- 04-02jquery與jsp,用jquery
- 01-11ajax實現(xiàn)頁面的局部加載
- 08-05dedecms(織夢)副欄目數(shù)量限制代碼修改
- 08-05DEDE織夢data目錄下的sessions文件夾有什
- 08-05織夢dedecms什么時候用欄目交叉功能?
- 01-10SublimeText編譯C開發(fā)環(huán)境設置
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-10使用C語言求解撲克牌的順子及n個骰子
- 01-10delphi制作wav文件的方法
- 01-10C#中split用法實例總結(jié)