因为目前所在的公司作的是学校管理的项目,功能中就包含课程表定制,学生排序,邮箱回执插入等,都须要用到拖拽的功能,如下就介绍下项目中用的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参数配置,须要的能够参考下: