这是大佬的博客 https://blog.csdn.net/zjiang1994/article/details/79809687
里面对vue.draggable的一些方法和属性,进行了详解,非常的详细,比官网的还要详细哦!给大佬点赞!!!
这是vue.draggable的官网 https://www.itxst.com/vue-draggable/jirneq6b.html
虽然官网说的不是很详细,但是有一些例子还不错
如图,想要实现这样的效果,左侧列表有一堆框框,要把左侧的列表拖拽到右侧列表,然后就去了官网
官网列子 未拖拽之前是这样的
拖拽后是这样的
首先用的这个方法,多个列表之间进行拖拽,但是用了这个方法后,发现左侧的一些项到右侧去了,但是同时左侧的一些项也消失了,若是想在右侧列表存在两个单选框就无法实现,所以换了个方法,使用了 clone拷贝
未拖拽前是这样的
拖拽后是这样的(有个点需要注意下,从左侧向右侧进行拖拽时,记得从右侧的一列数据上面拖进去,不要从下面往上面拖,否则拖不进去,而且右侧列表需要保留一列数据,否则左侧数据也会拖不过来
)
可以发现 ,初步实现了效果
接下来就是一些比较细的注意事项了
可以发现,左侧可以向右侧拖拽,但是同时右侧也可以往左侧拖拽,这会导致左侧列表混乱起来,这和我的需求就不符了,我是想着左侧的列表项是固定的,这样就只是单纯的把左侧的一些项复制到右侧
然后就去看了大佬的博客
可以看到 pull:‘clone’
时可以进行克隆,所以 pull:''
时,就相当于禁止克隆了
以下是代码截图
但是又有一个问题 我只想 右侧列表实现内部拖拽,左侧列表禁止内部拖拽,现在是两边都可以进行拖拽 ,这无疑又导致了左侧列表发生了混乱
然后又去仔细看了下大佬的博客,有这么段话
发现了这个 sort
属性,sort:true
时,可以进行内部拖拽,sort:false
时,禁止拖拽
ok,这下舒服了
监测右侧列表内部拖拽后的下标
以下是目前的全部代码
<template><div><div class="body_top"><el-row :gutter="20"><el-col :span="24"><el-card shadow="always"><div style="background-color: #fff"><el-form :inline="true"><el-form-item label="模块标识:" prop="molds"><el-input v-model="top_form.molds" controls-position="right" placeholder="请输入模块标识" class="ele-fluid ele-text-left"/></el-form-item> <el-form-item label="模块名称:" prop="name"><el-input v-model="top_form.name" controls-position="right" placeholder="请输入模块名称" class="ele-fluid ele-text-left"/></el-form-item><el-form-item label="状态码:" prop="state"><el-input v-model="top_form.state" controls-position="right" placeholder="请输入状态码" class="ele-fluid ele-text-left"/></el-form-item><el-form-item><el-button @click="showEdit=false">取消</el-button><el-button type="primary" @click="formSave">保存</el-button></el-form-item></el-form></div></el-card></el-col></el-row></div><div class="ele-body"><el-row :gutter="20"><el-col :span="7"><el-card shadow="never"><div class="col"><el-row><div class="base_title">基础字段</div><el-divider></el-divider><draggable v-model="arr1" @end="end1" :options="{ group: { name: 'tt', pull: 'clone' },sort: false,}" animation="300" style="width: 100%; min-height: 330px"><!-- <transition-group> --><div v-for="item in arr1" :key="item.id"><div class="base_style"><span class="el-icon-circle-check icon"></span>{{ item.name }}</div></div><!-- </transition-group> --></draggable></el-row><el-row><div class="base_title remark_margin">特殊字段</div><el-divider></el-divider><draggable v-model="arr2" @end="end1" :options="{ group: { name: 'tt', pull: 'clone' }, sort: false,}" animation="300" style="width: 100%; min-height: 330px"><!-- <transition-group> --><div v-for="item in arr2" :key="item.id"><div class="base_style"><span class="el-icon-circle-check icon"></span>{{ item.name }}</div></div><!-- </transition-group> --></draggable></el-row></div></el-card></el-col><el-col :span="10"><el-card shadow="never"><div class="col"><draggable v-model="arr3" :options="{ group: { name: 'tt', pull: '' }, sort: true,}" animation="300" :move="onMove" style="width: 100%; min-height: 330px"><!-- <transition-group> --><div v-for="(item, index) in arr3" :key="index"><div v-if="item.type == 'text'" class="base_margin" @click="showMenu(item)"><div class="static_wrap">{{ item.name }} <div class="static_border"></div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><div v-if="item.type == 'textarea'" class="base_margin" @click="showMenu(item)"><div class="static_wrap"> {{ item.name }}<div class="static_border static_textarea"><span class="static_bias">//</span></div></div><div> <el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><div v-if="item.type == 'select'" class="base_margin" @click="showMenu(item)"><div class="static_wrap">{{ item.name }}<div class="static_border static_textarea"><span class="el-icon-arrow-down static_select"></span></div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'number'"class="base_margin"@click="showMenu(item)"><div class="static_wrap">{{ item.name }}<div class="static_border"></div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'radio'"class="base_margin"@click="showMenu(item)"><div class="circle_wrap">{{ item.name }}<span class="static_circle"></span></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'checkbox'"class="base_margin"@click="showMenu(item)"><div class="circle_wrap">{{ item.name }}<span class="static_react"></span></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'datetime'"class="base_margin"@click="showMenu(item)"><div class="static_wrap"><span>{{ item.name }}</span><div class="static_border static_textarea"><span class="el-icon-time static_time"></span></div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'daterange'"class="base_margin"@click="showMenu(item)"><div class="static_wrap">{{ item.name }}<div class="static_border static_textarea"><span class="el-icon-date static_time"></span></div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'place'"class="base_margin"@click="showMenu(item)"><div class="static_wrap">{{ item.name }}<div class="static_border"></div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'image'"class="base_margin"@click="showMenu(item)"><div class="image_wrap">{{ item.name }}<spanstyle="font-size: 40px; line-height: 60px"class="el-icon-picture"></span></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'file'"class="base_margin"@click="showMenu(item)"><div class="file_wrap">{{ item.name }}<div class="static_file">点击上传</div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'tel'"class="base_margin"@click="showMenu(item)"><div class="static_wrap">{{ item.name }}<div class="static_border"></div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'button'"class="base_margin"@click="showMenu(item)"><div class="file_wrap">{{ item.name }}<div class="static_file"></div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'signature'"class="base_margin"@click="showMenu(item)"><div class="static_wrap">{{ item.name }}<div class="static_border"></div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'content'"class="base_margin"@click="showMenu(item)"><div class="static_wrap">{{ item.name }}<div class="static_border"></div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><divv-if="item.type == 'city'"class="base_margin"@click="showMenu(item)"><div class="static_wrap">{{ item.name }}<div class="static_border static_textarea"><span class="el-icon-arrow-down static_select"></span></div></div><div><el-button type="text" @click="deleteDomain(index)"><i class="el-icon-delete"></i></el-button></div></div><div v-if="item.type == 'example'" class="base_margin" style="visibility:hidden;"><div class="static_wrap"><span style="color: red">{{ item.name }}</span><div class="static_border"></div></div><div></div></div></div><!-- </transition-group> --></draggable></div></el-card></el-col><el-col :span="7"><el-card shadow="never"><div class="col"><divstyle="text-align: center;margin-bottom: 10px;font-weight: bold;color: rgb(2, 167, 240);">字段属性</div><el-divider></el-divider><div v-if="isShow"><el-form:model="controlForm"ref="controlForm"label-width="100px;"><div class="remark_margin">控件说明</div><div class="remark_margin">此控件 {{ controlForm.remark }}</div><div class="remark_margin">大小:</div><divstyle="display: flex;justify-content: space-around;margin-bottom: 10px;"><div>长<el-inputclass="base_input"v-model="controlForm.height"></el-input></div><div>宽<el-inputclass="base_input"v-model="controlForm.width"></el-input></div></div><el-form-item label="标题:"><el-inputv-model="controlForm.label"style="width: calc(100% - 20px)"></el-input></el-form-item><el-form-item><el-checkbox-group v-model="controlForm.checkList"><el-checkbox label="1">显示标题</el-checkbox><el-checkbox label="2">换行</el-checkbox></el-checkbox-group></el-form-item><el-form-item label="提示文字:"><el-inputv-model="controlForm.placeholder"style="width: calc(100% - 20px)"></el-input></el-form-item><el-form-item label="默认值:"><el-select v-model="controlForm.default"><el-option label="自定义" :value="1" /><el-option label="关联系统表" :value="2" /> </el-select></el-form-item><el-form-item label="选格式:"><el-select v-model="controlForm.format"><el-option label="文本" :value="1" /><el-option label="手机号" :value="2" /><el-option label="邮箱" :value="3" /><el-option label="身份证号" :value="4" /><el-option label="邮政编码" :value="5" /> </el-select></el-form-item><div style="margin-bottom: 20px">校验:</div><el-form-item><el-checkbox-group v-model="controlForm.checked"><el-checkbox label="1">必填</el-checkbox><el-checkbox label="2">不允许重复</el-checkbox><el-checkbox label="3">脱敏</el-checkbox></el-checkbox-group></el-form-item><div style="margin-bottom: 20px">字段权限:</div><el-form-item><el-checkbox-group v-model="controlForm.force"><el-checkbox label="3">指定人员可见</el-checkbox><el-checkbox label="1">可见</el-checkbox><el-checkbox label="2">可编辑</el-checkbox><el-checkbox label="4">指定人员可编辑</el-checkbox><el-checkbox label="5">导入</el-checkbox><el-checkbox label="6">导出</el-checkbox></el-checkbox-group></el-form-item></el-form><divslot="footer"style="display: flex; justify-content: center"><el-button icon="el-icon-close">重置</el-button><el-button type="primary" @click="fieldSave">保存</el-button></div></div></div></el-card></el-col></el-row></div></div>
</template>
<script>
//导入draggable组件
import draggable from "vuedraggable";
export default {//注册draggable组件name:'oa_template_add',components: {draggable,},data() {return {//基本字段arr1: [{ id: 1, icon: "", name: "单行文本框", type: "text" },{ id: 2, icon: "", name: "多行文本框", type: "textarea" },{ id: 3, icon: "", name: "下拉列表框", type: "select" },{ id: 4, icon: "", name: "数字文本框", type: "number" },{ id: 5, icon: "", name: "单选按钮组", type: "radio" },{ id: 6, icon: "", name: "复选框组", type: "checkbox" },{ id: 7, icon: "", name: "日期时间框", type: "datetime" },{ id: 8, icon: "", name: "日期区间", type: "daterange" },],//特殊字段arr2: [{ id: 9, icon: "", name: "地址框", type: "place" },{ id: 10, icon: "", name: "图片", type: "image" },{ id: 12, icon: "", name: "附件", type: "file" },{ id: 13, icon: "", name: "手机号", type: "tel" },{ id: 14, icon: "", name: "按钮", type: "button" },{ id: 16, icon: "", name: "手写签名", type: "signature" },{ id: 15, icon: "", name: "内容联动", type: "content" },{ id: 17, icon: "", name: "城市级联", type: "city" },],// arr3: [{ id: 100, icon: "", name: "示例框", type: "example" }],arr3: [],controlForm: {width: "",height: "",label: "",checkList: [],checked: [],force: [],placeholder: "",default: "",format: "",type: "",remark: "",id: "",},//顶部表单top_form: {name: "",molds:"",state:"",field: [],},formArr: [],isShow: false, //字段属性是否显示};},methods: {//左侧拖动结束时的事件end1() {let arr5 = [];//对数组进行重新赋值,使其id等于下标this.arr3.forEach((d, index) => {arr5.push({id: index,name: d.name,type: d.type,});});this.arr3 = arr5;// console.log(this.arr3);},//内部拖拽时触发的方法onMove(e) {//e.draggedContext.index为拖拽前的下标,e.draggedContext.futureIndex为拖拽后的下标// console.log(e);// console.log(e.draggedContext.index, e.draggedContext.futureIndex);},//删除按钮deleteDomain(index) {this.arr3.splice(index, 1);},//字段表单的显示与隐藏showMenu(e) {this.isShow = true;// console.log(e);this.controlForm.remark = e.name;this.controlForm.id = e.id;this.controlForm.type = e.type;},//提交字段表单fieldSave() {this.formArr.push(this.controlForm)// console.log(this.controlForm);// let newArr = this.top_form.field;// newArr.push(this.controlForm);// if (newArr.length == 0) {// newArr.push(this.controlForm);// } else {// for (var i = 0; i < newArr.length; i++) {// if (newArr[i].id == this.controlForm.id) {// newArr[i] = this.controlForm;// this.$message.warning("警告");// } else {// newArr.push(this.controlForm);// this.$message.success("添加");// }// }// }// console.log(this.top_form);},//提交form表单formSave() {this.top_form.field=this.formArr;console.log(this.top_form);},},
};
</script>
<style scoped>
.col {background-color: #fff;height: 750px;overflow: scroll;
}
.base_title {font-size: 18px;width: 80px;height: 20px;line-height: 20px;text-align: center;color: #000;font-weight: bold;margin-bottom: 10px;
}
.base_style {width: 135px;height: 35px;border: 1px solid #999;line-height: 35px;text-align: center;float: left;margin-left: 10px;margin-top: 10px;margin-right: 10px;margin-bottom: 10px;box-sizing: border-box;border-radius: 5px;text-align: left;cursor: pointer;
}
.icon {color: rgb(86, 169, 251);margin-right: 5px;margin-left: 10px;
}
.base_input {width: 160px;
}
.base_margin {margin-top: 10px;margin-bottom: 10px;background-color: rgba(235, 248, 251, 1);height: 80px;line-height: 60px;border-radius: 10px;padding: 10px;box-sizing: border-box;display: flex;justify-content: space-between;cursor: pointer;
}
.remark_margin {margin-top: 10px;margin-bottom: 10px;
}
.body_top {padding: 15px 15px 0 15px;
}
/* 中间静态框css样式 */
.static_wrap {width: 300px;display: flex;justify-content: space-between;align-items: center;
}
.static_border {width: 200px;height: 36px;background-color: #fff;border-radius: 5px;border: 1px solid #c0c4cc;cursor: pointer;
}
.static_textarea {position: relative;
}
.static_bias {position: absolute;right: 2px;top: 0;font-size: 12px;
}
.static_select {position: absolute;right: 5px;top: 10px;
}
.circle_wrap {width: 110px;display: flex;justify-content: space-between;align-items: center;
}
.static_circle {width: 10px;height: 10px;border-radius: 50%;border: 1px solid #c0c4cc;cursor: pointer;
}
.static_react {width: 10px;height: 10px;border: 1px solid #c0c4cc;cursor: pointer;
}
.static_time {position: absolute;left: 10px;top: 10px;
}
.image_wrap {width: 135px;display: flex;justify-content: space-between;align-items: center;cursor: pointer;
}
.file_wrap {width: 180px;display: flex;justify-content: space-between;align-items: center;
}
.static_file {width: 80px;height: 40px;background-color: rgb(86, 169, 251);text-align: center;line-height: 40px;color: #fff;border-radius: 5px;font-size: 12px;
}
</style>
还有一点 拖拽 group
里的 name
记得改成 一致 的,否则拖拽不过去
<----------------------------------------->
2021.10.29更
之前提到过,右侧列表数据为空时,左侧的标签拖拽不到右侧列表,所以当时就想着给右侧列表加个默认的数据,并且给这个数据背景颜色设为空白,但是这会有很多问题,比如说必须从默认的数据上面拖,才能拖拽到右侧,而且原地会有个空白区,用户体验不好,今日碰巧看到了大佬的博客,解决了!!感谢感谢
这是之前的效果图,可以看到第一次拖拽的时候拖不过去,需要从右侧隐藏的那个示例框上面拖才行,而且拖过去了也会有空白,
这是现在的图
这是大佬的博客地址,说的很详细哦 vue.draggable多列表间拖拽列表数据为空时,拖拽失效bug解决