效果圖如下所示:

源碼地址
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進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。