基于vue實(shí)現(xiàn)一個(gè)禪道主頁拖拽效果
效果圖如下所示:
bb兩句
最近在做一個(gè)基于vue的后臺(tái)管理項(xiàng)目。平時(shí)項(xiàng)目進(jìn)度統(tǒng)計(jì)就在上禪道上進(jìn)行。so~ 然后領(lǐng)導(dǎo)就感覺這個(gè)拖拽效果還行,能不能加到咱們項(xiàng)目里面。 既然領(lǐng)導(dǎo)發(fā)話,那就開干。。
所有技術(shù):vue + vuedraggable
拖動(dòng)的實(shí)現(xiàn)基于 vuedraggable 的插件開發(fā)。
主頁為兩欄流式布局,每一個(gè)組件可以在上下拖動(dòng),也可以左右拖動(dòng)。
基本步驟
布局
這塊布局為最為普通的兩欄布局,這里采用flex布局。左邊自適應(yīng),右邊為固定寬。
.layout-container { display: flex; .left { flex: 1; margin-right: 40px; } .right { width: 550px; } }
拖拽實(shí)現(xiàn)
這里使用 vuedraggable
插件。需要在組件里面引入使用。 draggable 相當(dāng)于拖拽容器,這塊很明顯需要兩個(gè)拖拽的容器。所以分別在 .left .right 中添加兩個(gè)拖拽容器。在默認(rèn)情況下,這里已經(jīng)可以進(jìn)行拖拽了。插件的效果還是很強(qiáng)大。
<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>
但是, 和我想要的效果還是相差一點(diǎn)。
左右拖動(dòng) 與 僅標(biāo)題欄拖動(dòng)
這塊只需要配置相關(guān)的配置項(xiàng)就可以比較簡單。 左右拖動(dòng)需要給拖拽容器指定相同的 group 屬性。指定標(biāo)題元素拖動(dòng)需要配置 handle 為可拖動(dòng)元素的選擇器名稱。
下面簡單介紹下常用的配置項(xiàng):
- disabled :boolean 定義是否此sortable對(duì)象是否可用,為true時(shí)sortable對(duì)象不能拖放排序等功能,為false時(shí)為可以進(jìn)行排序,相當(dāng)于一個(gè)開關(guān);
- group : 用處是為了設(shè)置可以拖放容器時(shí)使用,若兩個(gè)容器該配置項(xiàng)相同,則可以相互拖動(dòng);
- animation :number 單位:ms,定義排序動(dòng)畫的時(shí)間;
- handle :selector 格式為簡單css選擇器的字符串,使列表單元中符合選擇器的元素成為拖動(dòng)的手柄,只有按住拖動(dòng)手柄才能使列表單元進(jìn)行拖動(dòng);
- filter :selector 格式為簡單css選擇器的字符串,定義哪些列表單元不能進(jìn)行拖放,可設(shè)置為多個(gè)選擇器,中間用“,”分隔;
- draggable :selector 格式為簡單css選擇器的字符串,定義哪些列表單元可以進(jìn)行拖放
- ghostClass :selector 格式為簡單css選擇器的字符串,當(dāng)拖動(dòng)列表單元時(shí)會(huì)生成一個(gè)副本作為影子單元來模擬被拖動(dòng)單元排序的情況,此配置項(xiàng)就是來給這個(gè)影子單元添加一個(gè)class,我們可以通過這種方式來給影子元素進(jìn)行編輯樣式;
- chosenClass :selector 格式為簡單css選擇器的字符串,當(dāng)選中列表單元時(shí)會(huì)給該單元增加一個(gè)class;
- forceFallback :boolean 如果設(shè)置為true時(shí),將不使用原生的html5的拖放,可以修改一些拖放中元素的樣式等;
- fallbackClass :string 當(dāng)forceFallback設(shè)置為true時(shí),拖放過程中鼠標(biāo)附著單元的樣式;
采用相關(guān)配置如下:
computed: { dragOptions() { return { animation: 30, handle: ".drag-handle", group: "description", ghostClass: "ghost", chosenClass: "sortable", forceFallback: true }; } }
拖動(dòng)時(shí)樣式調(diào)整
在拖動(dòng)的時(shí)候,我們需要做三個(gè)事情。拖動(dòng)時(shí),拖動(dòng)元素只顯示標(biāo)題欄,兩欄內(nèi)列表只顯示標(biāo)題元素以及將要移動(dòng)的位置變灰。
1.拖動(dòng)元素只顯示標(biāo)題欄: 在默認(rèn)情況下,會(huì)開啟 html5 元素的拖動(dòng)效果。這里明顯不需要。 forceFallback 改為 false 則可以關(guān)閉 html5 的默認(rèn)效果。順便通過 chosenClass: "sortable" 修改拖動(dòng)元素class 類名。直接用css進(jìn)行隱藏
.sortable { .component-box { display: none; height: 0; } }
2.兩欄內(nèi)列表只顯示標(biāo)題元素 這里我借助兩個(gè)事件實(shí)現(xiàn)。
- onStart:function 列表單元拖動(dòng)開始的回調(diào)函數(shù)
- onEnd:function 列表單元拖放結(jié)束后的回調(diào)函數(shù)
<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; } }
在開始拖動(dòng)的時(shí)候給 .layout-container
添加 .drag 的 class 名。拖動(dòng)結(jié)束時(shí),移除class名。
將要移動(dòng)的位置變灰
這里需要用到上面 ghostClass: "ghost"
配置項(xiàng)。并添加相應(yīng)的css。
.ghost { .drag-handle { background: rgb(129, 168, 187); } }
好了基本已經(jīng)實(shí)現(xiàn)了。。。
展示動(dòng)態(tài)組件
接下來就是數(shù)據(jù)的動(dòng)態(tài)展示了。 這里需要vue中的動(dòng)態(tài)組件了。。附上官方文檔連接點(diǎn)擊查看。
然后里面每個(gè)拖動(dòng)的元素的內(nèi)容都寫成組件,搭配動(dòng)態(tài)組件實(shí)現(xiàn)自由拖動(dòng)。
// 將所用組件引入 import { timeline, calendar, welcome, carousel, imgs, KonList } from "@/components/DragComponents"; components: { draggable, timeline, calendar, welcome, carousel, imgs, KonList }
配合 v-for 對(duì)數(shù)據(jù)進(jìn)行循環(huán),然后進(jìn)行動(dòng)態(tài)展示。
<component :is="element.name"/>
這塊涉及到數(shù)據(jù)格式相關(guān)的,可以直接看文末的代碼。。。 這里就就不展開說了。。
數(shù)據(jù)保持
在拖動(dòng)結(jié)束后,我們需要將拖動(dòng)的順序緩存在前端,當(dāng)下次進(jìn)入后,可以繼續(xù)使用拖動(dòng)后的數(shù)據(jù)。
// 獲取新的布局 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; }, // 設(shè)置新的布局 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(); }
在拖動(dòng)結(jié)束后,設(shè)置新的布局
onEnd() { this.dragging = false; this.setLayout(); }
在項(xiàng)目中,還是建議配合后端進(jìn)行用戶布局的數(shù)據(jù)存儲(chǔ),每次拖動(dòng)后將新的布局?jǐn)?shù)據(jù)請(qǐng)求接口保存在數(shù)據(jù)庫,同時(shí)存入緩存中。當(dāng)再次進(jìn)入頁面的時(shí)候,讀取緩存中的數(shù)據(jù),沒有的話請(qǐng)求后端的接口拿到用戶的布局,然后再次存入緩存中。有的話直接讀取緩存中的數(shù)據(jù)。
最后說兩句
其實(shí)上面的效果也不是特別難,簡單花點(diǎn)時(shí)間,看看相關(guān)文檔,就能做出來,,記錄在掘金上面,只是想和大家分享我的思路。同時(shí)希望和大家一起交流,一起進(jìn)步。
生活不易,大家加油
附上源碼: 項(xiàng)目地址
<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: "五月最強(qiáng)新番", 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>
總結(jié)
以上所述是小編給大家介紹的基于vue實(shí)現(xiàn)一個(gè)禪道主頁拖拽效果,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)腳本之家網(wǎng)站的支持!
如果你覺得本文對(duì)你有幫助,歡迎轉(zhuǎn)載,煩請(qǐng)注明出處,謝謝!
相關(guān)文章
Vux+Axios攔截器增加loading的問題及實(shí)現(xiàn)方法
這篇文章主要介紹了Vux+Axios攔截器增加loading的問題及實(shí)現(xiàn)方法,文中通過實(shí)例代碼介紹了vue中使用axios的相關(guān)知識(shí),需要的朋友可以參考下2018-11-11vue2.0構(gòu)建單頁應(yīng)用最佳實(shí)戰(zhàn)
這篇文章主要為大家分享了vue2.0構(gòu)建單頁應(yīng)用最佳實(shí)戰(zhàn)案例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04Vue3.4中v-model雙向數(shù)據(jù)綁定新玩法詳解
defineModel?是一個(gè)新的?<script?setup>?宏,旨在簡化支持?v-model?的組件的實(shí)現(xiàn),?這個(gè)宏用來聲明一個(gè)雙向綁定?prop,下面我們就來看看他的具體使用吧2024-03-03基于Vue中this.$options.data()的this指向問題
這篇文章主要介紹了基于Vue中this.$options.data()的this指向問題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03詳解element-ui動(dòng)態(tài)限定的日期范圍選擇器代碼片段
這篇文章主要介紹了element-ui動(dòng)態(tài)限定的日期范圍選擇器代碼片段,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07vue?webpack打包原理解析(全網(wǎng)最新最全)
webpack是讓我們可以進(jìn)行模塊化開發(fā),并且會(huì)幫助我們處理模塊間的依賴關(guān)系,這篇文章主要介紹了vue?webpack打包原理,本篇介紹的有點(diǎn)長,希望大家耐心閱讀2023-02-02React和Vue實(shí)現(xiàn)路由懶加載的示例代碼
路由懶加載是一項(xiàng)關(guān)鍵技術(shù),它可以幫助我們提高Web應(yīng)用的加載速度,本文主要介紹了React和Vue實(shí)現(xiàn)路由懶加載的示例代碼,具有一定的參考價(jià)值,感興趣的可以了解一下2024-01-01詳解關(guān)于vue-area-linkage走過的坑
這篇文章主要介紹了詳解關(guān)于vue-area-linkage走過的坑,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2018-06-06