您好,登錄后才能下訂單哦!
效果圖如下所示:
源碼地址
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 為可拖動元素的選擇器名稱。
下面簡單介紹下常用的配置項:
采用相關配置如下:
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.兩欄內列表只顯示標題元素 這里我借助兩個事件實現。
<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實現一個禪道主頁拖拽效果,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!
如果你覺得本文對你有幫助,歡迎轉載,煩請注明出處,謝謝!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。