介绍:vue2拖拽插件,一对一拖拽,一个格子只有一个数据,当然也可以多个(多个更简单)各种拖拽吧。先看gif格式的动态图片,![](https://i-blog.csdnimg.cn/blog_migrate/4b1e96f7fa841dac0a742e8efc94802a.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/07f44e816165abb974b95239ff8206f7.png)
先介绍插件(不会介绍必定插件不是我写的我只是用):我看的这个文档还挺详细的
中文地址vue.draggable中文文档 - itxst.com
- npm下载
npm install vuedraggable
- 那个页面要用在那个页面做引入
import draggable from 'vuedraggable' // 引入 components: {draggable},// 注册
- 最基础的简单使用(里面有不算详细的介绍)(这里只介绍了一个因为我感觉有很多东西需要咱们自己看文档,我么办法一个个介绍,主要是简单啊)
<div class="itxst"> <div class="col"> <div class="title">A组</div> <!-- <transition-group>标签不知道干啥用的带上跟不带我没发现什么区别 !! 切记 <draggable>或者说<transition-group>标签下面只能单纯的一个div循环出来别瞎写不然报错!! !只能写一个div让他循环就行了! v-model表示数据源 (不必多说) group 这个想象成一个组 相同的组之间才可以相互拖拽 animation 是过渡动画 。。。还有不少东西,不过基础的够用了,需要更多的请查看官网快速入门里面的介绍 --> <draggable v-model="arr1" group="site" animation="300"> <transition-group> <div class="item" v-for="item in arr1" :key="item.id">{{item.name}}</div> <div class="item">0</div> </transition-group> </draggable> </div> <div class="col"> <div class="title">B组(本组不能拖入A组)</div> <draggable v-model="arr2" group="site" animation="100"> <transition-group> <div class="item" v-for="item in arr2" :key="item.id">{{item.name}}</div> </transition-group> </draggable> </div> </div> <script> import draggable from 'vuedraggable' export default { components: {draggable,}, data() { return { arr1: [{id: 1,name: 'name1'}, {id: 2,name: 'name2'}, {id: 3,name: 'name3'}, {id: 4,name: 'name4'} ], arr2: [], }; }, methods: {} } </script> <style> .itxst {margin: 10px;} .title {padding: 6px 12px;} .col { width: 40%; flex: 1; padding: 10px; border: solid 1px #eee; border-radius: 5px; float: left; } .col+.col {margin-left: 10px;} .item { padding: 6px 12px; margin: 0px 10px 0px 10px; border: solid 1px #eee; background-color: #f1f1f1; } .item:hover {background-color: #fdfdfd;cursor: move;} .item+.item {border-top: none;margin-top: 6px;} </style>
- 基础的已经没什么了(带你入个门而已别的可以看官网就行)。我介绍一下我写的吧比较复杂一点(不想听可以直接复制代码纯前端的demo vue2+Element写的别的框架的话直接改el-即可),我这边业务需求是有三个数据源,患者+两个设备(都是独立的),并且每个框框只能放一条数据。这样的话就有点麻烦用到了动态的group(代码里面有写)一个动态里面的name对应一个静态的。并且在动态的里面可以设置是否能拖进去或者拖出来.....(累了直接看代码吧,有不足请指教,谢谢)
- 纯前端demo,只要安装了插件就能用
<template> <div style="display: flex;padding: 10px;"> <div class="patient" style=""> <div class="tetx_nav">患者</div> <div class="nav"> <div class="text">待分配</div> <div class="oveCC"> <draggable v-model="userList" group="user" animation="300"> </transition-group> <div class="list" v-for="(item,ind) in userList" :key="ind" @click="userItem(item)" @mousedown="startDragging" @mouseup="stopDragging" @dragstart="startDragging" @dragend="stopDragging"> <div class="list_info"> <svg class="icon" aria-hidden="true"> <use v-if="item.xb" xlink:href="#icon-portrait"></use> <use v-else xlink:href="#icon--man-"></use> </svg> <span class="list_name">{{item.name}}</span> <span>{{item.xb?'女':'男'}}</span> <span>{{item.age}}岁</span> </div> <div class="list_time"> <span>{{item.time}}</span> <span>开始监护</span> </div> </div> </transition-group> </draggable> </div> </div> </div> <div class="info"> <div class="tetx_nav"> 入科:0 | 在科:0 | 出科:0 </div> <div class="nav"> <div class="nav_wrap"> <div class="dra_box" v-for="(it,ind) in allList" :key="ind"> <div class="box_text">{{ind}}床位</div> <div class="box_div"> <div class="notNull dox_sty" :class="{ 'glow-effect': isDraggingUser&&it.userObj.length==0 }"> <div class="list_dra"> <draggable v-model="it.userObj" :group="it.userB" @add="userAdd(it,ind)" @end="userEnd(it,ind)" animation="300"> <div style="width: 100%;height: 80px;" v-if="it.userObj.length==0"> <span v-if="!isDraggingUser">请分配患者至床位</span> </div> <div v-else :class="it.userObj.length==1?'':'forbid'" v-for="(item,index) in it.userObj" :key="index" class="list" @mousedown="startDragging" @mouseup="stopDragging" @dragstart="startDragging" @dragend="stopDragging"> <div class="list_info"> <svg class="icon" aria-hidden="true"> <use v-if="item.xb" xlink:href="#icon-portrait"></use> <use v-else xlink:href="#icon--man-"></use> </svg> <span class="list_name">{{item.name}}</span> <span>{{item.xb?'女':'男'}}</span> <span>{{item.age}}岁</span> </div> <div class="list_time"> <span>{{item.time}}</span> <span>开始监护</span> </div> </div> </draggable> </div> </div> <div class="device_div dox_sty" :class="{ 'glow-effect': isDraggingDevjhy&&it.devJhy.length==0 }"> <draggable v-model="it.devJhy" :group="it.devJhyB" @add="devJhyAdd(it,ind)" @end="devJhyEnd(it,ind)" animation="300"> <div style="width: 100%;height: 80px;" v-if="it.devJhy.length==0"><span v-if="!isDraggingDevjhy">监护仪</span></div> <div v-else class="deviceCode" v-for="(item,index) in it.devJhy" :key="item.id" @mousedown="startDevjhy" @mouseup="stopDevjhy" @dragstart="startDevjhy" @dragend="stopDevjhy"> <div class="dev_text"> {{item.text}} </div> <div class="dev_id"> {{item.id}} </div> </div> </draggable> </div> <div class="device_div dox_sty" :class="{ 'glow-effect': isDraggingDevhxj&&it.devHxj.length==0 }"> <draggable v-model="it.devHxj" :group="it.devHxjB" @add="devHxjAdd(it,ind)" @end="devHxjEnd(it,ind)" animation="300"> <div style="width: 100%;height: 80px;" v-if="it.devHxj.length==0"><span v-if="!isDraggingDevhxj">呼吸机</span></div> <div v-else class="deviceCode" v-for="(item,index) in it.devHxj" :key="item.id" @mousedown="startDevhxj" @mouseup="stopDevhxj" @dragstart="startDevhxj" @dragend="stopDevhxj"> <div class="dev_text"> {{item.text}} </div> <div class="dev_id"> {{item.id}} </div> </div> </draggable> </div> </div> </div> </div> </div> </div> <div class="device"> <div class="tetx_nav"> 设备 </div> <div class="nav"> <div class="nav_tab" style="padding-right: 5px;"> <div class="text">监护仪</div> <div class="oveCC"> <draggable v-model="jhydev" group="devJhy" animation="300" @start="startDevjhy" @unchoose="stopDevjhy"> <div v-if="jhydev.length===0" style="width: 100px;height: 100px;"> <el-empty description="暂无设备"></el-empty> </div> <div v-else class="deviceCode" v-for="(item,ind) in jhydev" :key="item.id"> <div class="dev_text"> {{item.text}} </div> <div class="dev_id"> {{item.id}} </div> </div> </draggable> </div> </div> <div class="nav_tab" style="padding-left: 5px;"> <div class="text">呼吸机{{hxjdev.length}}</div> <div class="oveCC"> <draggable v-model="hxjdev" group="devHxj" animation="300" @start="startDevhxj" @unchoose="stopDevhxj"> <div v-if="hxjdev.length===0" style="width: 100px;height: 100px;"> <el-empty description="暂无设备"></el-empty> </div> <div v-else class="deviceCode" v-for="(item,ind) in hxjdev" :key="item.id"> <div class="dev_text"> {{item.text}} </div> <div class="dev_id"> {{item.id}} </div> </div> </draggable> </div> </div> </div> </div> <div class="details"> <div class="tetx_nav"> 患者信息 </div> <div class="nav"> <div style="display: flex;line-height: 40px;"> <div class="nav_left">姓名:</div> <div style="flex: 1;">{{userInfo.name!=''?userInfo.name:'-'}}</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">性别:</div> <div style="flex: 1;">{{userInfo.xb===''?'-':userInfo.xb==1?'男':'女'}}</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">年龄:</div> <div style="flex: 1;">{{userInfo.age!=''?userInfo.age:'-'}}</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">科室:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">病区:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">床位号:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">住院号:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">责任医生:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">诊断信息:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">护理等级:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">病情等级:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">通气模式:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">起搏模式:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">设备号(监护仪):</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">设备号(呼吸机):</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">监护状态:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">监护开始时间:</div> <div style="flex: 1;">-</div> </div> <div style="display: flex;line-height: 40px;"> <div class="nav_left">监护时长:</div> <div style="flex: 1;">-</div> </div> </div> </div> </div> </template> <script> import draggable from 'vuedraggable' export default { components: { draggable }, data() { return { isDraggingUser: false, isDraggingDevjhy: false, isDraggingDevhxj: false, userInfo: { name: '', xb: '', age: '', time: '', }, userList: [{ name: '张三', xb: 1, age: 75, time: '2023-10-13 10:44:30' }, { name: '李四', xb: 0, age: 65, time: '2023-10-13 10:44:30' }, { name: '王*麻', xb: 1, age: 45, time: '2023-10-13 10:44:30' }, { name: '狂徒', xb: 0, age: 58, time: '2023-10-13 10:44:30' }], jhydev: [{ id: 'MB02A91006', text: '1' }, { id: 'MB02A91007', text: '2' }, { id: 'ME02B22032', text: '3' }, { id: 'ME02B22033', text: '4' }, { id: 'ME02B22035', text: '5' }, { id: 'ME02B22036', text: '6' }, { id: 'ME26B19026', text: '7' }, ], hxjdev: [{ id: 'VE03B1A001', text: '-' }, { id: 'VE03B1A002', text: '-' }, { id: 'VE03B1A003', text: '-' }, { id: 'VE03B1A004', text: '-' }], allList: [], } }, mounted() { for (let i = 0; i < 40; i++) { this.allList.push({ userObj: [], devJhy: [], devHxj: [], userB: { name: "user", pull: false, put: true, }, devJhyB: { name: "devJhy", pull: true, //是否可以拖出去 put: true, //是否可以拖进来 }, devHxjB: { name: "devHxj", pull: true, //拖出去 put: true, //拖进来 }, }) } }, methods: { // 患者信息详情 userItem(item) { this.userInfo = item }, // 新增数据 -- 患者 userAdd(data, i) { this.$set(this.allList[i].userB, 'pull', true) this.$set(this.allList[i].userB, 'put', false) }, // 移动结束 -- 患者 userEnd(data, i) { console.log(JSON.parse(JSON.stringify(this.allList)), JSON.parse(JSON.stringify(this.allList[0]))) if (data.userObj.length == 0) { this.$set(this.allList[i].userB, 'pull', false) this.$set(this.allList[i].userB, 'put', true) } else { this.$set(this.allList[i].userB, 'pull', true) this.$set(this.allList[i].userB, 'put', false) } }, // 新增数据 -- 呼吸机 devHxjAdd(data, i) { this.$set(this.allList[i].devHxjB, 'pull', true) this.$set(this.allList[i].devHxjB, 'put', false) }, // 移动结束 -- 呼吸机 devHxjEnd(data, i) { if (data.devHxj.length == 0) { this.$set(this.allList[i].devHxjB, 'pull', false) this.$set(this.allList[i].devHxjB, 'put', true) } else { this.$set(this.allList[i].devHxjB, 'pull', true) this.$set(this.allList[i].devHxjB, 'put', false) } }, // 新增数据 -- 监护仪 devJhyAdd(data, i) { this.$set(this.allList[i].devJhyB, 'pull', true) this.$set(this.allList[i].devJhyB, 'put', false) }, // 移动结束 -- 监护仪 devJhyEnd(data, i) { if (data.devJhy.length == 0) { this.$set(this.allList[i].devJhyB, 'pull', false) this.$set(this.allList[i].devJhyB, 'put', true) } else { this.$set(this.allList[i].devJhyB, 'pull', true) this.$set(this.allList[i].devJhyB, 'put', false) } }, // 《------------------------------外发光效果 内部文字消失 startDragging() { // 开始拖动时触发的事件 -- 患者 this.isDraggingUser = true; this.isClose('user',true) }, stopDragging() { // 选中后松开鼠标的事件 -- 患者 this.isDraggingUser = false; // this.open() }, startDevjhy() { // 开始拖动时触发的事件 -- 监护仪 this.isDraggingDevjhy = true; this.isClose('Devjhy',true) }, stopDevjhy() { // 选中后松开鼠标的事件 -- 监护仪 this.isDraggingDevjhy = false; // this.open() }, startDevhxj() {// 开始拖动时触发的事件 -- 呼吸机 this.isDraggingDevhxj = true; this.isClose('Devhxj',true) }, stopDevhxj() { // 选中后松开鼠标的事件 -- 呼吸机 this.isDraggingDevhxj = false; // this.open() }, // 外发光效果-------------------------------》 // 由于自定义然而name的效果好像失效了(bug:可以任意的拖拽),所以只能禁用拖进的事件 isClose(name,e){ if(name === 'user'){ this.allList.forEach(item=>{ if(item.userObj.length == 0){ item.userB.put = true }else{ item.userB.put = false } item.devJhyB.put = false item.devHxjB.put = false }) }else if(name === 'Devjhy'){ this.allList.forEach(item=>{ item.userB.put = false if(item.devJhy.length == 0){ item.devJhyB.put = true }else{ item.devJhyB.put = false } item.devHxjB.put = false }) }else if(name === 'Devhxj'){ this.allList.forEach(item=>{ item.userB.put = false item.devJhyB.put = false if(item.devHxj.length == 0){ item.devHxjB.put = true }else{ item.devHxjB.put = false } }) } }, // open(){ // this.allList.forEach(item=>{ // item.userB.put = true // item.devJhyB.put = true // item.devHxjB.put = true // }) // } }, } </script> <style lang="scss" scoped> .glow-effect { animation: glow 1s infinite alternate; border: 1px dashed rgba(0, 255, 0, 0.5) !important; } @keyframes glow { 0% { box-shadow: 0 0 10px rgba(0, 255, 0, 0.5); } 100% { box-shadow: 0 0 20px rgba(0, 255, 0, 0.8); } } ::-webkit-scrollbar { width: 4px; height: 4px; } ::-webkit-scrollbar-track { background-color: transparent; -webkit-border-radius: 2em; -moz-border-radius: 2em; border-radius: 2em; } ::-webkit-scrollbar-thumb { background-color: rgb(147, 147, 153, 0.5); -webkit-border-radius: 2em; -moz-border-radius: 2em; border-radius: 2em; } $customHeight: 100%; $boxHeight: calc(100vh - 100px); .tetx_nav { text-align: center; color: #333; font-size: 16px; font-weight: 600; line-height: 30px; } .patient { height: $boxHeight; width: 230px; min-width: 230px; .nav { height: $customHeight ; padding: 10px 5px; border-radius: 10px; background-image: linear-gradient(180deg, #ebf2fa, #fff); .text { color: #758491; font-family: PingFangSC-Semibold; font-size: 14px; font-weight: 600; text-align: center; } .oveCC { overflow: hidden; overflow: auto; height: 100%; .list { background: #fff; border: 1px solid #dde6ed; border-radius: 10px; height: 78px; padding: 10px; position: relative; margin-top: 5px; cursor: pointer; user-select: none; //文字不可被选中 .list_info { padding-bottom: 10px; border-bottom: #bdbdbd 1px solid; display: flex; align-items: center; font-size: 14px; color: #969696; span { margin-left: 10px; } svg { width: 30px; height: 30px; } .list_name { font-size: 16px; font-weight: 600; color: #000; } } .list_time { line-height: 25px; display: flex; align-items: center; justify-content: space-between; font-size: 12px; color: #969696; } } } } } .info { height: $boxHeight; min-width: 500px; flex: 1; padding: 0 10px; .nav { height: $customHeight; border-radius: 10px; background-color: #8f9ad5; padding: 7px; ::-webkit-scrollbar-thumb { background-color: rgb(103, 127, 213); -webkit-border-radius: 2em; -moz-border-radius: 2em; border-radius: 2em; } .nav_wrap { display: flex; flex-wrap: wrap; overflow: hidden; overflow: auto; height: 100%; .dra_box { background: #fff; border: 1px solid #dde6ed; border-radius: 8px; border-radius: 12px; float: left; height: 160px; margin: 5px; padding: 1px; user-select: none; flex: 1; min-width: 440px; .box_text { height: 36px; text-align: center; width: 100%; } .box_div { background: #fff; border-radius: 12px; padding: 10px; display: flex; justify-content: space-between; .notNull { width: 220px; min-width: 220px; .list_dra { position: relative; cursor: pointer; user-select: none; //文字不可被选中 .list_info { line-height: normal; padding-bottom: 10px; border-bottom: #bdbdbd 1px solid; display: flex; align-items: center; font-size: 14px; color: #969696; span { margin-left: 10px; } svg { width: 45px; height: 45px; } .list_name { font-size: 16px; font-weight: 600; color: #000; } } .list_time { line-height: 35px; display: flex; align-items: center; justify-content: space-between; font-size: 12px; color: #969696; } } } .device_div { width: 100px; min-width: 100px; .deviceCode { font-size: 12px; .dev_text { height: 40px; text-align: center; line-height: 40px; } .dev_id { color: #758491; height: 40px; text-align: center; line-height: 40px; } } } .dox_sty { height: 100px; line-height: 78px; color: #c6cfd7; font-size: 16px; font-weight: 400; text-align: center; padding: 10px; border-radius: 10px; border: 1px solid #dde6ed; background: #fff; } } } } } } .device { height: $boxHeight; width: 240px; min-width: 240px; .nav { height: $customHeight; padding: 10px; border-radius: 10px; background-image: linear-gradient(180deg, #ebf2fa, #fff); display: flex; .nav_tab { width: 110px; min-width: 110px; .text { color: #758491; font-family: PingFangSC-Semibold; font-size: 14px; font-weight: 600; text-align: center; } .oveCC { overflow: hidden; overflow: auto; height: 100%; .deviceCode { cursor: pointer; margin-top: 5px; background: #fff; border: 1px solid #dde6ed; border-radius: 10px; height: 100px; padding: 10px; position: relative; .dev_text { height: 60px; text-align: center; line-height: 60px; } .dev_id { color: #758491; } } } } } } .details { height: $boxHeight; width: 240px; min-width: 240px; padding-left: 10px; .nav { height: $customHeight; border-radius: 10px; background-image: linear-gradient(180deg, #ebf2fa, #fff); padding: 10px; .nav_left { width: 120px; } } } </style>