因爲目前所在的公司作的是學校管理的項目,功能中就包含課程表定製,學生排序,郵箱回執插入等,都須要用到拖拽的功能,如下就介紹下項目中用的draggable實現的拖拽排序css
功能描述:搜索框輸入首字母或者姓名可快速定位到該學生,根據鍵盤上鍵和下鍵可進行學生的選擇,enter鍵或者鼠標點擊即選中學生(選中後定位到該學生,該學生變色,方便查找),點擊學生便可進行拖拽排序html
如下爲一個彈窗vue
下拉框列表選擇學生html5
選中學生定位到該學生並變色ajax
拖拽效果vuex
npm install vuedraggable複製代碼
import draggable from 'vuedraggable'複製代碼
具體函數功能會在下面附上完整代碼,太多了就不截圖了~~~npm
根據輸入框的內容對下拉列表的內容進行篩選,後臺返回的數據以下api
佈局bash
參數配置less
以上就是大致的結構,下面附上全部代碼,註釋的很詳細了哦
<template>
<div class="draggable-roster">
<my-dialog ref="draggable_roster_dialog" :width="studentInfo.length > 63 ? 1012 : 1005" @handle="changeMuster">
<div class="draggable-title" slot="title">自定義排序</div>
<div class="draggable-content" slot="content">
<!-- 下拉框搜索內容 -->
<v-menu offset-y v-model="show" fixed max-height="240" class="search-student">
<header slot="activator" class="select-header">
<input ref="input" type="text" v-model.trim="inputValue" placeholder="請輸入學生姓名或者首字母" @keydown.delete="deleteInputVal" @keydown="onKeyDown" @keyup.enter="onKeyEnter">
</header>
<main class="select-wrap">
<ul v-if="filterList && filterList.length > 0" ref="content">
<li v-for="(item, i) in filterList" :key="i" @click="selectListOne(item)" @keydown.delete="deleteInputVal" :class="{'key-down-select': i === listIndex}">
<span>{{item.name}}</span>
</li>
</ul>
<div v-else class="empty-data">沒有符合條件的結果</div>
</main>
</v-menu>
<ul class="studentContainer clear">
<!-- 拖拽內容 -->
<draggable :options="studentTable" v-model="studentInfo">
<li v-for="(student,stIndex) in studentInfo" :key="stIndex" :class="{selected: student.id === inputValueId}">
<i></i>
<div>
<span>{{student.name}}</span>
<var>{{student.id}}</var>
</div>
</li>
</draggable>
</ul>
</div>
</my-dialog>
</div>
</template>
<script>
import myDialog from '@/components/myDialog'
import draggable from 'vuedraggable'
import {mapState} from 'vuex'
export default {
name: 'draggableRoster',
props: {
// 學生列表
studentList: {
type: Array,
default: ()=>{
return []
}
}
},
components: {
draggable,
myDialog
},
data(){
return {
kSchoolId: this.$common.getSession("userData").school.id,
kSchoolYearId: this.$common.getSession("userData").schoolYear.id,
studentInfo: [], //學生信息
studentTable: {
group: {name: 'draggableRoster'},
sort: true,
animation: 150,
// ghostClass: 'rosterDraggableGhost', // 拖動影子元素class
chosenClass: 'rosterDraggableChosen', // 選中的元素class
dragClass: 'rosterDrag', //拖動過程class
},//拖拽參數設置
show: false, //一開始隱藏搜索框列表
inputValue: '', //搜索框內容
inputValueId: '', //選中的學生id
courseInfomation: {}, //班級信息
listIndex: 0, // 當前keydown的項
}
},
computed: {
// 下拉列表內的數據 (根據輸入框的內容進行篩選)
filterList() {
if (this.inputValue === '') return this.studentInfo
let filterList = [];
filterList = this.studentInfo.filter(val => {
let {name, firstName} = val;
if (name.includes(this.inputValue) || firstName.includes(this.inputValue)) {
return true
}
})
return filterList
},
...mapState('semester',['newSemester'])
},
methods: {
//打開vuetify的v-dialog彈窗
open(courseInfo) {
this.studentInfo = this.studentList; //學生信息賦值
this.courseInfomation = courseInfo; //班級信息賦值
this.$refs.draggable_roster_dialog.open(); //打開vuetify的v-dialog彈窗
},
// 選擇下拉列表的一個名字
selectListOne(item) {
this.inputValue = item.name;
this.inputValueId = item.id;
this.show = false;
},
// 刪除輸入框中的內容
deleteInputVal() {
this.inputValueId = '';
this.show = true;
},
// 列表向下向上鍵
onKeyDown (e) {
const keyCodes = Object.freeze({
enter: 13,
up: 38,
down: 40,
})
if (e.keyCode === keyCodes.down && this.listIndex < this.filterList.length - 1) {
this.listIndex++
} else if (e.keyCode === keyCodes.up && this.listIndex > 0) {
this.listIndex--
}
},
// enter鍵選中
onKeyEnter(){
let item = this.filterList[this.listIndex]
if (!item) {
// 沒有選擇項,點擊enter提示
this.$message.warning('請選擇學生');
return
} else {
// 有選擇項,點擊enter則選擇此項
this.selectListOne(item);
return
}
},
// 更改排序
changeMuster() {
let studentArr = [];
this.studentInfo.forEach(item=>{
studentArr.push(item.id)
})
let params = {
kSchoolId: this.kSchoolId,
kSchoolYearId: this.kSchoolYearId,
kSemesterId: this.newSemester.id,
kGradeId: this.courseInfomation.kGradeId,
classType: this.courseInfomation.classType,
studentIdList: studentArr
}
if(this.courseInfomation.classType === 0) {
params.kClassesGroupId = this.courseInfomation.kClassesGroupId;
}else{
params.kCourseId = this.courseInfomation.kCourseId;
if(this.courseInfomation.classType === 1 || this.courseInfomation.classType === 6) {
params.kClassesGroupId = this.courseInfomation.kClassesGroupId;
}else{
params.kClassId = this.courseInfomation.kClassId;
}
}
this.$ajax.post(this.$api.SAVE_MUSTER, params).then(res => {
if (res.code == 200) {
this.$refs.draggable_roster_dialog.close(); //關閉彈窗
this.$message.success('自定義排序成功!');
this.$emit('refreshRoster'); //給父級發送請求,告知父級從新渲染列表
}
});
}
},
watch: {
// 輸入搜索詞,index重置爲0
inputValue (next, prev) {
this.listIndex = 0; //輸入框變化時置0.默認選中第一個
}
}}
</script>
<style lang="less" scoped>
@import '~@/style/common';
.draggable-content {
.search-student {
margin-bottom: 20px;
input {
width: 215px;
height: 24px;
border-bottom: 1px solid #BEBEBE;
color: #4A4A4A;
cursor: text;
}
}
.studentContainer {
overflow: auto;
width: 100%;
max-height: 335px;
border: 1px solid #C1D4D8;
li {
float: left;
width: 107px;
height: 46px;
cursor: pointer;
border-right: 1px solid #d8d8d8;
border-bottom: 2px solid #BDCBB3;
i {
float: left;
width: 16px;
height: 44px;
margin-right: 1px;
}
div {
float: right;
width: 89px;
padding-top: 5px;
font-size: 14px;
line-height: 18px;
span {
display: block;
color: #4A4A4A;
.ellipsis1;
}
var {
display: block;
color: #A2A2A2;
font-style: normal;
.ellipsis1;
}
}
}
li:nth-child(9n){
border-right: 0;
}
//拖拽列表鼠標移入時的灰色樣式
li:hover {
i {
background: #D8D8D8 url('~@/assets/yidong.png') center center no-repeat;
}
}
//拖拽列表學生被選中的樣式
li.selected {
background: #E4F2F3;
}
//拖拽的綠色樣式
.rosterDraggableChosen,
.rosterDrag {
i {
background: #9EC583 url('~@/assets/yidong.png') center center no-repeat !important;
}
}
}
}
//搜索下拉框的樣式
.select-wrap {
background: #fff;
>ul {
>li {
cursor: pointer;
padding-left: 10px;
height: 36px;
line-height: 36px;
&:hover {
background: #F5FAFB;
}
}
.key-down-select {
background: #F5FAFB;
color: #0E858E;
}
}
.empty-data {
text-align: center;
color: #8C8C8C;;
padding-top: 10px;
padding-bottom: 10px;
}
}
</style>複製代碼
option配置項
option配置項
group: string or array 分組用的,同一組的不一樣list能夠相互拖動
sort: boolean 定義是否能夠拖拽
delay:number 定義鼠標選中列表單元能夠開始拖動的延遲時間
disabled: boolean 定義是否此sortable對象是否可用,爲true時sortable對象不能拖放排序等功能
animation: umber 單位:ms 動畫時間
handle: selector 格式爲簡單css選擇器的字符串,使列表單元中符合選擇器的元素成爲拖動的手柄,只有按住拖動手柄才能使列表單元進行拖動
filter: selector 格式爲簡單css選擇器的字符串,定義哪些列表單元不能進行拖放,可設置爲多個選擇器,中間用「,」分隔
preventOnFilter: 當拖動filter時是否觸發event.preventDefault()默認觸發
draggable: selector 格式爲簡單css選擇器的字符串,定義哪些列表單元能夠進行拖放
ghostClass: selector 格式爲簡單css選擇器的字符串,當拖動列表單元時會生成一個副本做爲影子單元來模擬被拖動單元排序的狀況,此配置項就是來給這個影子單元添加一個class,咱們能夠經過這種方式來給影子元素進行編輯樣式
chosenClass: selector 格式爲簡單css選擇器的字符串,目標被選中時添加
dragClass:selector 格式爲簡單css選擇器的字符串,目標拖動過程當中添加
forceFallback: boolean 若是設置爲true時,將不使用原生的html5的拖放,能夠修改一些拖放中元素的樣式等
fallbackClass: string 當forceFallback設置爲true時,拖放過程當中鼠標附着單元的樣式
dataIdAttr: data-id
scroll:boolean當排序的容器是個可滾動的區域,拖放能夠引發區域滾動
scrollFn:function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) { … } 用於自定義滾動條的適配
scrollSensitivity: number 就是鼠標靠近邊緣多遠開始滾動默認30
scrollSpeed: number 滾動速度複製代碼
函數配置
setData: 設置值時的回調函數
onChoose: 選擇單元時的回調函數
onStart: 開始拖動時的回調函數
onEnd: 拖動結束時的回調函數
onAdd: 添加單元時的回調函數
onUpdate: 排序發生變化時的回調函數
onRemove: 單元被移動到另外一個列表時的回調函數
onFilter: 嘗試選擇一個被filter過濾的單元的回調函數
onMove: 移動單元時的回調函數
onClone: clone時的回調函數
以上函數對象的屬性:
to: 移動到的列表的容器
from:來源列表容器
item: 被移動的單元
clone: 副本的單元
oldIndex:移動前的序號
newIndex:移動後的序號複製代碼
這篇文章中有更詳細的draggable參數配置,須要的能夠參考下: