vue拖拽组件draggable

article/2025/8/15 0:44:21

1、安装vuedraggable依赖

npm i vuedraggable

2、在页面中引入

import Draggable from 'vuedraggable'
components: {Draggable
},

下面为实图使用,代码直接参考下面的,拷贝即可使用
在这里插入图片描述

//主页面
<template><div class="ctnr"><div class="station-area"><div class="text-area"><el-button v-if="disabledDrag && section.length < 4" type="text" @click="changeAdd">新建</el-button><div class="edit-area"><el-button v-if="disabledDrag" type="text" @click="changeDrag">编辑</el-button><div v-else><el-button type="text" @click="changeDrag('updateList')">完成</el-button><el-button type="text" @click="changeDrag('clearList')">取消</el-button></div></div></div></div><draggable :disabled="disabledDrag" class="box" v-model="section" @start="onStartBlock" @end="onEndBlock"animation="300"><transition-group><div class="section" v-for="(item,index) in section" :key="index"><div class="section-card"><h1 class="card-title"><el-input class="title-inp" v-if="!disabledDrag" v-model="item.name"></el-input><span v-else>{{item.name}}</span><i v-if="!disabledDrag" class="el-icon-circle-plus i-plus"@click="bindAddSuit(item.id,item.suites.length)"></i></h1><draggable :disabled="disabledDrag" class="card-box" v-model="item.suites" @start="onStartSuite(item.id)"@end="onEndSuite(item.id)" animation="300"><transition-group><div class="item" v-for="(item2,index2) in item.suites" :key="index2"><div class="item-card" :style="'background:'+item2.color" :class="{'href-type':disabledDrag}"@click="goPreview(item2)"><div class="op-box" v-if="!disabledDrag"><i class="el-icon-edit" @click="bindEditSuit(item2,item.id)"></i><i class="el-icon-delete" @click="bindRemoveSuit(item2.id)"></i></div><span class="card-name">{{item2.name}}</span></div></div></transition-group></draggable></div></div></transition-group></draggable><suit-box ref="SuitBox" @refreshList="refreshList"></suit-box></div>
</template><script>import Draggable from 'vuedraggable'import SuitBox from '@/components/CommonForm/SuitBox'export default {data() {return {disabledDrag: true,oSection: [],section: [],isUpdateBlock: false,isUpdateSuit: [],};},components: {Draggable,SuitBox},watch: {section: {handler(newVal, oldVal) {oldVal.length > 0 && (this.isUpdateBlock = true)},deep: true}},computed: {},mounted() {this.getBlockList()},created() {},methods: {getBlockList() {var data = [{"create_time": "2022-04-01 15:12:33","id": 2,"modify_time": "2022-06-29 19:44:23","name": "测试区域2133213122355","order_num": 1,"suites": [{"color": "#A47373","create_time": "2022-04-01 17:48:32","fillable": false,"id": 4,"modify_time": "2022-07-29 17:55:52","name": "日常签到","order_num": 1,"template_id": 423},{"color": "#9013FE","create_time": "2022-10-09 11:01:02","fillable": true,"id": 22,"modify_time": "2022-10-09 11:01:02","name": "接口","order_num": 2,"template_id": 369}]},{"create_time": "2022-04-01 15:12:41","id": 3,"modify_time": "2022-07-29 17:38:01","name": "防冻液防冻液111","order_num": 2,"suites": []},{"create_time": "2022-06-28 15:16:56","id": 41,"modify_time": "2022-07-27 14:06:52","name": "额3v","order_num": 3,"suites": [{"color": "#F9F115","create_time": "2022-07-27 14:07:02","fillable": false,"id": 18,"modify_time": "2022-07-27 14:07:02","name": "43关闭","order_num": 1,"template_id": 407}]},{"create_time": "2022-06-28 15:16:52","id": 36,"modify_time": "2022-09-05 11:31:14","name": "供电局","order_num": 4,"suites": [{"color": "#BD10E0","create_time": "2022-07-27 14:08:20","fillable": true,"id": 19,"modify_time": "2022-07-27 14:08:20","name": "看给客户","order_num": 1,"template_id": 369}]},{"create_time": "2022-06-28 15:16:56","id": 42,"modify_time": "2022-06-29 19:44:23","name": "","order_num": 5,"suites": []},{"create_time": "2022-06-28 15:16:52","id": 39,"modify_time": "2022-06-29 19:44:23","name": "","order_num": 6,"suites": []},{"create_time": "2022-06-28 15:16:56","id": 40,"modify_time": "2022-08-25 17:05:46","name": "saca","order_num": 7,"suites": [{"color": "#CB2929","create_time": "2022-08-25 17:05:52","fillable": false,"id": 21,"modify_time": "2022-08-25 17:05:52","name": "sacas","order_num": 1,"template_id": 423}]},{"create_time": "2022-06-28 15:16:52","id": 37,"modify_time": "2022-06-29 19:44:23","name": "","order_num": 8,"suites": []},{"create_time": "2022-04-01 15:12:25","id": 1,"modify_time": "2022-06-29 19:44:23","name": "时分为非就不能用鼠标全选","order_num": 9,"suites": [{"color": "#F8E71C","create_time": "2022-04-01 16:35:25","fillable": false,"id": 2,"modify_time": "2022-04-01 17:59:12","name": "套件2","order_num": 2,"template_id": 363},{"color": "#B33434","create_time": "2022-04-01 18:42:04","fillable": true,"id": 6,"modify_time": "2022-04-01 18:42:04","name": "卡片222","order_num": 2,"template_id": 298},{"color": "#DC17DC","create_time": "2022-04-22 14:21:48","fillable": false,"id": 10,"modify_time": "2022-04-24 18:27:38","name": "vr","order_num": 3,"template_id": 372}]},{"create_time": "2022-06-28 15:16:56","id": 43,"modify_time": "2022-06-29 19:44:23","name": "","order_num": 10,"suites": []},{"create_time": "2022-06-28 15:16:52","id": 38,"modify_time": "2022-06-29 19:44:23","name": "","order_num": 11,"suites": []},{"create_time": "2022-04-01 15:12:48","id": 4,"modify_time": "2022-06-29 19:44:23","name": "测试区域4","order_num": 12,"suites": [{"color": "#994545","create_time": "2022-04-02 10:19:00","fillable": true,"id": 8,"modify_time": "2022-04-02 10:19:00","name": "车控室","order_num": 1,"template_id": 350}]}
]this.section = datathis.oSection = JSON.parse(JSON.stringify(data))},refreshList() {this.getBlockList()},onStartBlock() {},onEndBlock(e) {console.log(e)this.isUpdateBlock = true},onStartSuite() {},onEndSuite(blockId) {// 对那个块中的卡片进行了挪动!this.isUpdateSuit.includes(blockId) && this.isUpdateSuit.push(blockId)},changeAdd() {for (var i = 0; i < 4; i++) {let obj = {name: '',order_num: i + 1,station_id: this.station_id}blockAdd(obj).then(res => {})}this.getBlockList();},changeDrag(type) {this.disabledDrag = !this.disabledDragswitch (type) {case 'clearList':this.section = JSON.parse(JSON.stringify(this.oSection))break;case 'updateList':if (this.isUpdateBlock) {this.updateBlock()}if (this.isUpdateSuit.length > 0) {this.updateSuit()}break;}},bindAddSuit(id, orderNum) {if (this.isUpdateBlock || this.isUpdateSuit.length > 0) {this.$confirm("区域或卡片有更新是否保存后再新建卡片?", "提示", {confirmButtonText: "确认",cancelButtonText: "取消",type: "warning"}).then(() => {this.isUpdateBlock && this.updateBlock()this.isUpdateSuit.length > 0 && this.updateSuit()this.$refs.SuitBox.showModal({block_id: id,order_num: orderNum + 1})}).catch(() => {this.$refs.SuitBox.showModal({block_id: id,order_num: orderNum + 1})})} else {this.$refs.SuitBox.showModal({block_id: id,order_num: orderNum + 1})}},bindEditSuit(info, id) {this.$refs.SuitBox.showModal(Object.assign({block_id: id}, info), 'edit')},updateBlock() {let data = []for (const [index, item] of this.section.entries()) {const obj = {id: item.id,name: item.name,order_num: index + 1}data.push(obj)}blockModify(data).then(res => {this.$message.success('区域更新成功')this.isUpdateBlock = false})},updateSuit() {const data = []for (const item of this.isUpdateSuit) {const nowBlock = this.section.filter(res => res.id === item)[0]for (const [index2, item2] of nowBlock.suites.entries()) {const obj = Object.assign(item2, {block_id: item,order_num: index2 + 1})delete obj.create_timedelete obj.modify_timedata.push(obj)}}suiteUpdate(data).then(res => {this.$message.success('卡片更新成功')this.isUpdateSuit = []})console.log(data)},bindRemoveSuit(id) {this.$confirm("是否删除此卡片?", "提示", {confirmButtonText: "确认",cancelButtonText: "取消",type: "warning"}).then(() => {suiteDelete({id}).then(res => {this.$message.success('卡片删除成功')this.getBlockList()})}).catch(() => {})},async goPreview(item) {if (!this.disabledDrag) returnconst detail = await templateDetail({id: item.template_id}).then(res => {return res.data})localStorage.previewForm = JSON.stringify(detail.content)let url = this.$router.resolve({path: `/preview?type=${item.fillable?'fill':''}&template_id=${item.template_id}`});window.open(url.href, '_blank');}}}</script>
<style lang='scss' scoped>.ctnr {height: calc(100vh - 50px);background: #f6f7f9;margin: -40px -50px;padding: 20px 0 20px 20px;overflow-y: scroll;}.station-area {padding: 0 20px;display: flex;align-items: center;.text-area {display: flex;align-items: center;margin-left: auto;.edit-area {margin-left: 15px;}}}/deep/ .el-form-item--small.el-form-item {margin-bottom: 0 !important;}.box {width: 100%;height: 100%;}.section {width: 50%;height: 50%;display: inline-block;padding: 20px 20px 0 0;&-card {background: #fff;border-radius: 14px;height: 100%;padding: 20px;display: flex;flex-wrap: wrap;// padding: ;.card-title {font-size: 16px;margin: 0;position: relative;width: 100%;color: #606266;.title-inp {width: 50%;}.i-plus {color: #448ef7;position: absolute;right: 0;cursor: pointer;font-size: 25px;}}.card-box {width: 100%;height: calc(100% - 30px);}}.item {width: 33.3%;padding: 10px 10px 0 0;height: 33.3%;display: inline-block;&-card {width: 100%;height: 100%;background: #eee;border-radius: 11px;position: relative;display: flex;align-items: center;justify-content: center;user-select: none;&.href-type {cursor: pointer;}.op-box {position: absolute;top: 4px;right: 5px;color: #fff}.card-name {color: #fff;font-size: 16px;width: 80%;overflow: hidden;text-overflow: ellipsis;white-space: nowrap;text-align: center;}}}}</style>
//组件
<template><div><el-dialog @click.native="bindClosePicker" :title="type==='edit'?'编辑卡片':'添加卡片'" :visible.sync="dialogFormVisible":before-close="bindClose"><el-form :model="form" ref="ruleForm" size="small" :rules="rules" class="form-style"><el-form-item label="卡片名称" :label-width="formLabelWidth" prop="name"><el-input v-model="form.name" autocomplete="off"></el-input></el-form-item><el-form-item label="选择模板" :label-width="formLabelWidth" prop="template_id"><el-select v-model="form.template_id" placeholder="请选择"><el-option v-for="item in templateData" :key="item.id" :label="item.name" :value="item.id"></el-option></el-select></el-form-item><el-form-item label="选择背景色" :label-width="formLabelWidth" prop="color"><div class="picker-input" style="position:relative"><div @click.stop="bindShowPicker" class="color-tip" :style="'background:'+form.color"></div><el-input v-model="form.color" autocomplete="off"></el-input></div><div class="picker-box" v-if="showPicker" tabindex="0" @click.stop=""><sketch-picker v-model="pickerColor" @input="updatePickerColor"></sketch-picker></div></el-form-item></el-form><div slot="footer" class="dialog-footer"><el-button @click="bindClose(()=>dialogFormVisible = false)" size="small">取 消</el-button><el-button :loading="postLoading" type="primary" @click="bindPost" size="small">确 定</el-button></div></el-dialog><!-- :disableAlpha="true" --></div>
</template><script>import {Sketch} from 'vue-color'import {suiteUpdate} from '@/api/commonForm'import {listTemplate} from '@/api/form/form'import {number} from '@/utils/validate'export default {data() {const validateNumber = (rule, value, callback) => {console.log(rule, value, callback)if (value && !number(value)) {callback(new Error('排序值输入错误'));} else {callback()}};const validateColor = (rule, value, callback) => {console.log(rule, value, callback)const reg = /^#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})$/if (!value) {callback(new Error('请选择色值'));} else if (value && !reg.test(value)) {callback(new Error('色值输入错误'));} else {callback()}};return {pickerColor: '',dialogFormVisible: false,formLabelWidth: '100px',form: {},type: 'add',postLoading: false,templateData: [],showPicker: false,rules: {name: [{required: true,message: '请输入卡片名称',trigger: 'blur'}],template_id: [{required: true,message: '请选择模板',trigger: 'change'}],color: [{required: true,validator: validateColor,trigger: 'blur'}],order_num: [{validator: validateNumber,trigger: 'blur'}],}};},components: {'sketch-picker': Sketch,},computed: {},watch: {},mounted() {this.getListTemplate()},methods: {async showModal(info, type) {this.type = type || 'add'if (info) {const {name,id,template_id,order_num,block_id,color} = infothis.form = {name,template_id,id,order_num,block_id,color: !color ? '' : color}} else {this.form = {}}this.dialogFormVisible = true},getListTemplate() {listTemplate({page: 1,page_size: 100000}).then(res => {this.templateData = res.data})},bindPost() {console.log(this.form)this.postLoading = truethis.$refs.ruleForm.validate(async (valid) => {if (valid) {this.form.order_num && (this.form.order_num = parseInt(this.form.order_num))await suiteUpdate([this.form]).then(res => {})this.postLoading = falsethis.$message.success(this.type === 'edit' ? '编辑成功' : '添加成功')this.bindClose(() => this.dialogFormVisible = false)this.$emit('refreshList')} else {this.postLoading = falsereturn false}});},bindClose(done) {this.pickerColor = ''this.$refs.ruleForm.resetFields()done()},bindShowPicker() {this.showPicker = true},bindClosePicker() {this.showPicker = false},updatePickerColor(e) {console.log(e)this.$set(this.form, 'color', e.hex)}}}</script>
<style lang='scss' scoped>/deep/.dialog-footer {text-align: center;.el-button {width: 120px;}.el-button+.el-button {margin-left: 70px;}}.form-style {width: 70%;margin: 0 auto;}/deep/ .el-select {width: 100%;}.permission-ctnr {height: 200px;overflow-y: scroll;border: 1px solid #DCDFE6;border-radius: 4px;}.picker-box {position: absolute;top: 40px;left: -25%;}/deep/.picker-input {.color-tip {width: 15px;height: 15px;position: absolute;z-index: 1;top: 9px;left: 10px;border: 1px solid #eeeff2;border-radius: 3px;}.el-input__inner {padding-left: 30px;}}</style>

http://chatgpt.dhexx.cn/article/4JhTgHFH.shtml

相关文章

vue3 draggable拖拽

&#xff01;&#xff01;首先安装 vuedraggable npm i -S vuedraggablenext一定要带上next&#xff0c;不然就会报错 很恶心&#xff01;使用建议看官网。 官网&#xff1a;vue.draggable中文文档 - itxst.comVue.Draggable是一款基于Sortable.js实现的vue拖拽插件。支持移动…

vue-draggable-resizable定制化可拖动控件

最近遇到一个需求&#xff1a;前端定制化生成合同模板&#xff0c;生成时可以在指定位置拖放指定的控件&#xff0c;可动态编辑指定控件的属性和位置&#xff0c;最后将控件的位置等属性传给后台&#xff0c;后续使用模板签署合同时&#xff0c;乙方可在模板上指定位置签署。 点…

使用 vue.draggable 实现拖拽、克隆

这是大佬的博客 https://blog.csdn.net/zjiang1994/article/details/79809687 里面对vue.draggable的一些方法和属性&#xff0c;进行了详解&#xff0c;非常的详细&#xff0c;比官网的还要详细哦&#xff01;给大佬点赞&#xff01;&#xff01;&#xff01; 这是vue.draggab…

React Draggable 实现拖拽 - 最详细中文教程 - 卡拉云

本文首发&#xff1a;《React Draggable 实现拖拽 - 最详细中文教程 - 卡拉云》 React Draggable 是 react 生态中&#xff0c;最好用的拖拽实现库之一。如果你的应用中需要实现拖拽功能&#xff0c;可以尝试用 react-draggable&#xff0c;它可以满足多数情况下的拖拽需求&am…

draggable属性的应用

draggable属性用来定义元素是否可以拖动。 效果图&#xff1a; 代码如下&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>draggable属性的应用</title> </head> <body> <h3>元素拖…

draggable 和 sortable的JS原生实现

概要 本文主要利用html 5的draggable原生特性&#xff0c;实现一个可拖拽的效果。我们可以创建包含多个页面节点的容器&#xff0c;每个容器可以包含多个节点。通过拖拽&#xff0c;可以移动一个容器内的节点到其他容器&#xff0c;每个容器内的节点和以通过拖拽改变排列顺序。…

vue3使用拖拽组件draggable.next的使用教程【保姆级】

环境&#xff1a;vue3setup语法 首先放官方文档的链接&#xff1a; 中文版本&#xff1a; https://www.itxst.com/vue-draggable-next/tutorial.html &#xff08;民间翻译&#xff09; 英文版本&#xff1a;https://github.com/SortableJS/vue.draggable.next 因为自己写的过程…

vue的拖拽插件: vue.draggable

中文文档地址: vue.draggable中文文档 - itxst.comVue.Draggable是一款基于Sortable.js实现的vue拖拽插件。支持移动设备、拖拽和选择文本、智能滚动&#xff0c;可以在不同列表间拖拽、不依赖jQuery为基础、vue 2过渡动画兼容、支持撤销操作&#xff0c;总之是一款非常优秀的…

react-draggable实现拖拽详解

react-draggable 属性常用属性属性列表 事件列表举例首先安装 react-draggable实现移动 希望小编写的能够帮助到你&#x1f618; 属性 常用属性 属性默认值介绍axisxhandle拖动的方向&#xff0c;可选值 x ,y,bothhandle无指定拖动handle的classposition无handle的位置&#…

draggable拖拽组件使用

项目开发中需要用到拖拽组件&#xff0c;因为前端技术框架是vue&#xff0c;这里就使用了vue的一款拖拽插件vue.draggable&#xff0c;一般基本的需求都能满足&#xff0c;这里使用了多个draggable嵌套&#xff0c;达到两级之前相互拖拽的功能。 以下是类似teambition的效果图…

原生JS的拖拽属性draggable(详解)

摘要 作为h5新增的属性draggable&#xff0c;它能够给与一切的html元素拖动的效果。而在这个属性之下&#xff0c;也有着关于拖动效果的各个方法。 而这一篇文章&#xff0c;主要就是说一下关于draggable属性的使用以及工作场景。 1.了解draggable属性的使用 对我来讲&#…

EasyUI基础入门之Draggable(拖拽)

前面学习了easyui基础的解析器,加载器。对于他们入门阶段我们只需简单的了解下即可&#xff0c;毕竟先阶段并不会太过深入。接下来根据easyui官网文档的顺序安排学习下Draggable插件。 Draggable是什么 Draggable是easyui中用于实现拖拽功能的一个插件。利用它&#xff0c;我们…

jts-core 使用说明(二)

jts-core 使用说明 示例代码库 JTS源码底层使用说明&#xff0c;通过一下章节介绍说明 层次结构 org.locationtech.jts: algorithm - 算法包jts-io-common - I/O classes for open spatial formatsgeom - geom基础包geom.prep - 对适当准备的几何图形执行优化的几何操作类e…

java jts获取线上任意一点到起点的距离

java jts获取线上任意一点到起点的距离 近期项目要求计算某段公路上一辆车的运行轨迹&#xff0c;通过路上的设备实时获取车辆的经纬度信息并发送到后台接收。 抽象出来就是获取线上任意一点到起点的距离&#xff0c;按照一定每秒一次的频率去计算就获取该点的运动轨迹了。 主要…

JTS-Geometry 使用说明(五)

org.locationtech.jts.geom.Geometry 使用说明 示例代码库 Geometry 经纬度操作类 Geometry类继承关系 说明 平面、线性几何操作抽象类 提供的相关方法: 1.基础方法&#xff1a; 1.1 getLength:获取长度&#xff0c;线几何返回点与点之间的长度之和&#xff1b;闭合几何返回…

JAVA使用JTS 判断坐标点是否在坐标多边形内部

JAVA使用JTS 判断坐标点是否在坐标多边形内部 思路Geometry之间的关系API及参考博客代码依赖工具类测试类 思路 判断坐标点是否在坐标多边形内部&#xff0c;首先不能直接计算坐标点&#xff0c;是需要字符串坐标点转化为地理空间数据Geometry&#xff0c;然后使用JTS包中提供…

JTS学习笔记

JTS学习笔记 基础的类 Geometry geom对象Coordinate坐标类Point Point对象MultiPoint 基本对象MultiPoint等等GeometryFactory工厂对象PreparedGeometryFactoryPreparedGeometry 几何对象Geometry public abstract class Geometry implements Cloneable, Comparable, Seria…

JTS-Angle GIS几何角度计算使用说明(十八)

org.locationtech.jts.algorithm.Angle 角度计算使用说明 示例代码库 Angle 角度计算 1.Angle.angle(p0,p1) public static double angle(Coordinate p0, Coordinate p1) {double dx p1.x - p0.x;double dy p1.y - p0.y;return Math.atan2(dy, dx); }返回与x轴正方向的夹…

java jts_Java Topology Suite (JTS)与空间数据模型

JTS是Java的处理地理数据的API&#xff0c;它提供以下功能&#xff1a; 实现了OGC关于简单要素SQL查询规范定义的空间数据模型 一个完整的、一致的、基本的二维空间算法的实现&#xff0c;包括二元运算(例如touch和overlap)和空间分析方法(例如intersection和buffer) 一个显示的…

java jts点到面的距离_jts-空间索引

前言&#xff1a; 如果您对JTS这三个词还是没有一个概念&#xff0c;那么推荐您关注一下sinoly的博客。这个我能够找到为数不多的关于jts的中文资料。 http://www.blogjava.net/sinoly/archive/2007/02/09/99042.html 下面这段话就是摘抄自sinoly老兄的博客&#xff1a; ......…