vue2拖拽 vue.draggable拖拽插件 多功能拖拽

520 阅读8分钟

介绍:vue2拖拽插件,一对一拖拽,一个格子只有一个数据,当然也可以多个(多个更简单)各种拖拽吧。

先看gif格式的动态图片,最后有我写的纯前端demo,只要安装了插件就能用

先介绍插件(不会介绍必定插件不是我写的我只是用):我看的这个文档还挺详细的

中文地址vue.draggable中文文档 - itxst.com

  1. npm下载 npm install vuedraggable
  2. 那个页面要用在那个页面做引入 import draggable from 'vuedraggable' // 引入 components: {draggable},// 注册
  3. 最基础的简单使用(里面有不算详细的介绍)(这里只介绍了一个因为我感觉有很多东西需要咱们自己看文档,我么办法一个个介绍,主要是简单啊)
	<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>
————————————————
版权声明:本文为CSDN博主「卑信-吾天」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_56718509/article/details/133863083

4.基础的已经没什么了(带你入个门而已别的可以看官网就行)。我介绍一下我写的吧比较复杂一点(不想听可以直接复制代码纯前端的demo vue2+Element写的别的框架的话直接改el-即可),我这边业务需求是有三个数据源,患者+两个设备(都是独立的),并且每个框框只能放一条数据。这样的话就有点麻烦用到了动态的group(代码里面有写)一个动态里面的name对应一个静态的。并且在动态的里面可以设置是否能拖进去或者拖出来.....(累了直接看代码吧,有不足请指教,谢谢)

5.纯前端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>