demo
<template>
<el-dialog :visible="dragAndDropFormDialog" title="测试拖拽" fullscreen center @close="btnCancel">
<div class="dropBox">
<!--使用draggable组件-->
<div class="itxst">
<div class="col">
<div class="title">元素</div>
<draggable
v-model="arr1"
group="site"
animation="300"
drag-class="dragClass"
ghost-class="ghostClass"
chosen-class="chosenClass"
@start="onStart"
@end="onEnd"
@change="change"
>
<transition-group>
<div v-for="(item, index) in arr1" :key="index" class="item">
<div v-if="item.innerHTML == 'title1'" class="title1">{{ item.name }}</div>
<div v-if="item.innerHTML == 'title2'" class="title2">{{ item.name }}</div>
<div v-if="item.innerHTML == 'title3'" class="title3">{{ item.name }}</div>
<el-collapse v-if="item.innerHTML == 'All'" v-model="activeNames" @change="handleChange">
<el-collapse-item title="添加字段" name="1">
<div v-for="(collapse, coIndx) in item.demoList" :key="coIndx" style="padding-right: 10px">
<el-input
v-if="collapse.innerHTML == 'input'"
v-model="collapse.value"
:placeholder="collapse.name"
class="inptmar"
/>
<el-radio v-if="collapse.innerHTML == 'radio'" v-model="item.radio" :label="collapse.value">
{{ collapse.value }}
</el-radio>
</div>
<el-button style="width: 100%" type="primary" @click="adddemoList(item)">+</el-button>
</el-collapse-item>
</el-collapse>
</div>
</transition-group>
</draggable>
</div>
</div>
<div class="dropCenter">
<div class="col">
<div class="title">你可以把左边的元素拖到右边</div>
<draggable
v-model="arr2"
group="site"
animation="300"
drag-class="dragClass"
ghost-class="ghostClass"
chosen-class="chosenClass"
@start="onStart"
@end="onEnd"
>
<transition-group class="dropminheigth">
<div v-for="(item, index) in arr2" :key="index" class="Formitem" :class="isRemove ? 'flexForm' : ''">
<div v-if="item.innerHTML == 'title1'" class="title1">
<el-row type="flex" justify="center">
<el-input v-model="item.value" v-focus :placeholder="item.name" />
</el-row>
</div>
<div v-if="item.innerHTML == 'title2'" class="title2">
<el-row type="flex" justify="center">
<el-input v-model="item.value" v-focus :placeholder="item.name" />
</el-row>
</div>
<div v-if="item.innerHTML == 'title3'" class="title3">
<el-row type="flex" justify="center">
<el-input v-model="item.value" v-focus :placeholder="item.name" />
</el-row>
</div>
<div v-if="item.innerHTML == 'All'" class="inputFlex">
<draggable
v-model="item.demoList"
group="rowDemo"
animation="300"
drag-class="dragClass"
ghost-class="ghostClass"
chosen-class="chosenClass"
@start="onStart"
@change="change2"
@end="onEnd"
>
<transition-group class="inputFlex">
<div v-for="(collapse, coIndx) in item.demoList" :key="coIndx" style="padding-right: 10px">
<el-input
v-if="collapse.innerHTML == 'input'"
v-model="collapse.value"
:placeholder="collapse.name"
class="inptmar"
/>
<el-radio v-if="collapse.innerHTML == 'radio'" v-model="item.radio" :label="collapse.value">
{{ collapse.value }}
</el-radio>
</div>
</transition-group>
</draggable>
</div>
<div v-if="isRemove">
<el-button type="danger" icon="el-icon-delete" @click="removeList(index)" />
</div>
</div>
</transition-group>
</draggable>
</div>
</div>
<div class="rigth-edit">
<el-button :type="isRemove ? 'success' : 'primary'" @click="isRemove = !isRemove">{{
isRemove ? '确定' : '修改'
}}</el-button>
</div>
</div>
<!-- 添加字段弹窗 -->
<el-dialog
:visible="isShowDemo"
title="添加字段"
width="400px"
center
:modal="false"
:close-on-click-modal="false"
@close="addFieldCancel"
>
<el-form ref="fieldForm" inline label-width="100px" :model="FieldForm" :rules="FieldRule">
<el-form-item label="类型" prop="type">
<el-select v-model="FieldForm.type" clearable class="w220" @change="serleChanBtn">
<el-option :value="1" label="输入框" />
<el-option :value="2" label="单选框" />
</el-select>
</el-form-item>
<div v-if="FieldForm.type == 1">
<el-form-item v-for="(indemo, n) in FieldForm.inputList" :key="n" label="字段名" prop="name">
<el-input v-model="indemo.value" clearable class="w220" />
</el-form-item>
<el-form-item label=" ">
<el-button style="width: 100%" type="primary" @click="addInput">添加输入框</el-button>
</el-form-item>
</div>
<div v-if="FieldForm.type == 2">
<el-form-item
v-for="(tarmr, n) in FieldForm.radioList"
:key="n"
class="typeRequired"
:label="'选项' + (n + 1)"
>
<el-input v-model="tarmr.value" clearable class="w220" />
</el-form-item>
<el-form-item label=" ">
<el-button style="width: 100%" type="primary" @click="addBtnArea">添加选项</el-button>
</el-form-item>
</div>
</el-form>
<template #footer>
<el-row type="flex" justify="center">
<el-button type="primary" @click="addFieldSave">确定</el-button>
<el-button @click="addFieldCancel">取消</el-button>
</el-row>
</template>
</el-dialog>
</el-dialog>
</template>
<script>
// 导入draggable组件
import draggable from 'vuedraggable'
export default {
name: 'DragAndDropForm',
components: { draggable },
props: {
dragAndDropFormDialog: {
type: Boolean,
required: true
}
},
data() {
return {
drag: false,
// 定义要被拖拽对象的数组
arr1: [
{ name: 'title1', innerHTML: 'title1', value: '' },
{ name: 'title2', innerHTML: 'title2', value: '' },
{ name: 'title3', innerHTML: 'title3', value: '' },
{
innerHTML: 'All',
demoList: []
}
],
arr2: [
{
innerHTML: 'All',
demoList: [
{
name: '输入框',
innerHTML: 'input',
value: '测试input1'
},
{
name: '输入框',
innerHTML: 'input',
value: '测试input2'
},
{
value: '测试radio1',
innerHTML: 'radio',
defaultAr: false
},
{
value: '测试radio2',
innerHTML: 'radio',
defaultAr: false
}
]
},
{
innerHTML: 'All',
demoList: [
{
name: '输入框',
innerHTML: 'input',
value: '测试input1'
},
{
name: '输入框',
innerHTML: 'input',
value: '测试input2'
},
{
value: '测试radio1',
innerHTML: 'radio',
defaultAr: false
},
{
value: '测试radio2',
innerHTML: 'radio',
defaultAr: false
}
]
}
],
arr3: [],
activeNames: ['1'],
isRemove: false,
isShowDemo: false,
FieldForm: {
demoList: [],
radioList: [],
inputList: []
},
FieldRule: {},
letDemoItem: {}
}
},
computed: {}, // 计算属性
watch: {},
created() {},
mounted() {},
methods: {
// 开始拖拽事件
onStart(start) {
console.log(start)
this.drag = true
},
// 拖拽结束事件
onEnd(event) {
// event.item 拖拽的本身
// event.to 拖拽的目标列表
// event.from 拖拽之前的列表
// event.oldIndex 拖拽前的位置
// event.newIndex 拖拽后的位置
console.log(event)
this.drag = false
},
// 监听拖拽
change(event) {
console.log(event)
this.arr1 = [
{ name: 'title1', innerHTML: 'title1', value: '' },
{ name: 'title2', innerHTML: 'title2', value: '' },
{ name: 'title3', innerHTML: 'title3', value: '' },
{
innerHTML: 'All',
demoList: []
}
]
},
change2(change) {
console.log(this.arr2)
},
adddemoList(item) {
this.letDemoItem = item
this.isShowDemo = true
// item.demoList.push({ name: '输入框', type: 'input', value: '' })
},
btnCancel() {
this.$emit('update:dragAndDropFormDialog', false)
},
handleChange(val) {
console.log(val)
},
async removeList(i) {
try {
await this.$confirm('是否删除?', { type: 'warning' })
} catch (error) {
return
}
this.arr2.splice(i, 1)
},
serleChanBtn() {
if (this.FieldForm.type === 1) {
this.FieldForm = {
...this.FieldForm,
name: '',
inputList: [
{
name: '输入框',
innerHTML: 'input',
value: ''
}
],
radioList: []
}
} else if (this.FieldForm.type === 2) {
this.FieldForm = {
...this.FieldForm,
radioList: [
{
value: undefined,
innerHTML: 'radio',
defaultAr: false
}
],
inputList: []
}
}
this.$refs['fieldForm'].resetFields()
},
// 添加字段弹层
addInput() {
this.FieldForm.inputList.push({ name: '输入框', innerHTML: 'input', value: '' })
},
addBtnArea() {
this.FieldForm.radioList.push({
value: undefined,
innerHTML: 'radio',
defaultAr: false
})
},
// 添加字段保存按钮
async addFieldSave() {
try {
await this.$refs['fieldForm'].validate()
if (this.FieldForm.type === 2) {
for (let i = 0; i < this.FieldForm.radioList.length; i++) {
if (!this.FieldForm.radioList[i].value) {
return this.$message.warning('请将当前添加的选项填写完整')
}
}
}
if (this.FieldForm.radioList) {
this.FieldForm.radioList.forEach((item) => {
this.letDemoItem.demoList.push(item)
})
}
if (this.FieldForm.inputList) {
this.FieldForm.inputList.forEach((item) => {
this.letDemoItem.demoList.push(item)
})
}
this.addFieldCancel()
} catch (err) {
console.log(err)
}
},
// 添加字段取消按钮
addFieldCancel() {
this.FieldForm = {}
this.$nextTick(() => {
this.$refs['fieldForm'].resetFields()
})
this.isShowDemo = false
}
}
}
</script>
<style lang="scss" scoped>
.dropBox {
display: flex;
justify-content: center;
.dropCenter {
width: 828px;
}
}
.inptmar {
margin: 5px;
}
.title1 {
font-size: 24px;
}
.title2 {
font-size: 22px;
}
.title3 {
font-size: 20px;
}
.backgroundDiv {
background-color: #eee;
height: 30px;
line-height: 30px;
margin: 2px;
}
/*定义要拖拽元素的样式*/
.ghostClass {
// background-color: blue !important;
}
.chosenClass {
// background-color: red !important;
opacity: 1 !important;
}
.dragClass {
// background-color: blueviolet !important;
opacity: 1 !important;
box-shadow: none !important;
outline: none !important;
background-image: none !important;
}
.dropminheigth {
display: block;
border: 1px solid #000;
min-height: 300px;
}
.itxst {
width: 300px;
}
.title {
padding: 6px 12px;
}
.col {
width: 100%;
border-radius: 5px;
float: left;
}
.col + .col {
margin-left: 10px;
}
.item {
padding: 6px 12px;
margin: 0px 10px 0px 10px;
border: solid 1px #eee;
}
.Formitem {
border-bottom: 1px solid #000;
background-color: skyblue;
}
.flexForm {
display: flex;
justify-content: space-between;
align-items: center;
}
::v-deep .Formitem {
.el-input__inner {
border: 0;
text-align: center;
}
.title1 {
border-color: aqua;
.el-input {
font-size: 24px !important;
}
}
.title2 {
.el-input {
font-size: 22px !important;
}
}
.title3 {
.el-input {
font-size: 20px !important;
}
}
}
.inputFlex {
display: flex;
justify-content: space-around;
align-items: center;
}
.item:hover {
background-color: #fdfdfd;
cursor: move;
}
.item + .item {
border-top: none;
margin-top: 6px;
}
.rigth-edit {
margin-left: 10px;
margin-top: 25px;
}
</style>
配置项
group: "name", // or { name: "...", pull: [true, false, clone], put: [true, false, array] } name相同的组可以互相拖动
sort: true, // 内部排序列表
delay: 0, // 以毫秒为单位定义排序何时开始。
touchStartThreshold: 0, // px,在取消延迟拖动事件之前,点应该移动多少像素?
disabled: false, // 如果设置为真,则禁用sortable。
store: null, // @see Store
animation: 150, // ms, 动画速度运动项目排序时,' 0 ' -没有动画。
handle: ".my-handle", // 在列表项中拖动句柄选择器。
filter: ".ignore-elements", // 不导致拖拽的选择器(字符串或函数)
preventOnFilter: true, // 调用“event.preventDefault()”时触发“filter”
draggable: ".item", // 指定元素中的哪些项应该是可拖动的。
ghostClass: "sortable-ghost", // 设置拖动元素的class的占位符的类名。
chosenClass: "sortable-chosen", // 设置被选中的元素的class
dragClass: "sortable-drag", //拖动元素的class。
dataIdAttr: 'data-id',
forceFallback: false, // 忽略HTML5的DnD行为,并强制退出。(h5里有个属性也是拖动,这里是为了去掉H5拖动对这个的影响)
fallbackClass: "sortable-fallback", // 使用forceFallback时克隆的DOM元素的类名。
fallbackOnBody: false, // 将克隆的DOM元素添加到文档的主体中。(默认放在被拖动元素的同级)
fallbackTolerance: 0, // 用像素指定鼠标在被视为拖拽之前应该移动的距离。
scroll: true, // or HTMLElement
scrollFn: function(offsetX, offsetY, originalEvent, touchEvt, hoverTargetEl) { ... }, // if you have custom scrollbar scrollFn may be used for autoscrolling
scrollSensitivity: 30, // px, how near the mouse must be to an edge to start scrolling.
scrollSpeed: 10, // px