vue3拖拽详细记录,记录项目实现过程,以及vuedraggable拖拽组件的注意事项
vue3拖拽详细记录,记录项目实现过程,以及vuedraggable拖拽组件的注意事项
项目需求
参考文档 --官方确实有demo,但是挂在github上,需要FQ!!!!淦!!!
这里有个很好的演示
https://blog.csdn.net/qq_43751614/article/details/132975785
更全的文档是
https://www.itxst.com/vue-draggable-next/tutorial.html
最终实现的效果如下
细节:监听move事件,使用节流函数,去判断,落地位置,是否重复。
具体代码
<template>
<div class="flex-1 flex" style="width: 100%;">
<div class="box" style="width:150px;height:420px;overflow-y:auto">
<div class="box-tit">字段区</div>
<div class="box-body">
<Draggable :list="list" class="list-left" :sort="false" itemKey="id" :move="checkMove"
chosen-class="chosenClass" :group="{ name: 'componentsGroup', pull: 'clone', put: false }">
<template #item="{ element }">
<div class="items">
<div class="title" style="text-align:left;padding:2px 5px;">{{ element.label }}</div>
</div>
</template>
</Draggable>
</div>
</div>
<div class="box flex-1 flex flex-column" style="height: 420px;">
<div class="box-tit">层级结构区</div>
<div class="box-body flex-1" style="min-height:300px;overflow-y:auto">
<el-form :model="levelList" ref="ruleFormRef" :rules="rules">
<div class="card" v-for="(item, indx) in levelList" :key="indx">
<el-form-item label="" :prop="`${indx}.name`" :rules="rules.name">
<el-input v-model="levelList[indx].name" placeholder="请输入" />
</el-form-item>
<Draggable :list="item.cloneList" itemKey="id" :move="checkMove" :animation="100"
:class="['list-group', 'right' + indx]" group="componentsGroup">
<template #item="{ element, index }">
<div class="items flex align-center">
<div class="title flex" style="margin-right:5px">
<el-button type="primary" plain size="small">
<span style="margin-right:10px ;">{{ element.label }}</span>
<el-icon @click="delTags(indx, index)">
<Close />
</el-icon>
</el-button>
</div>
<el-icon v-if="index < (item.cloneList.length - 1)">
<Right />
</el-icon>
</div>
</template>
</Draggable>
</div>
</el-form>
</div>
<div class="box-footer">
<el-button @click="addCengji" type="primary" plain>+ 添加层次结构</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { reactive, ref, defineProps, toRefs, defineExpose } from "vue";
import Draggable from "vuedraggable";
import { throttle } from 'lodash-es';
import { getDetail, addEditLevel } from '../api'
const props = defineProps({
tableId: Number,
})
const { tableId } = toRefs(props)
// const input = ref('层级名称')
getDetail(Number(tableId.value)).then(res => {
console.log(res.data.fieldDetailsList)
list.value = res.data.fieldDetailsList.map(item => {
return { id: item.id, label: item.fieldCName }
})
}).catch(err => {
console.log('tableid', tableId)
console.log(err)
})
const list = ref([
{
label: "模块1",
id: 1,
},
{
label: "模块2",
id: 2,
},
{
label: "模块3",
id: 3,
},
]);
const levelList = ref([{
id: 1,
name: '层级关系1',
cloneList: []
}])
const addCengji = () => {
levelList.value.push({
id: levelList.value.length + 1,
name: '层级关系' + (levelList.value.length + 1),
cloneList: []
})
}
const delTags = (indx, index) => {
console.log(indx, index)
levelList[indx].cloneList.splice(index, 1)
console.log('层级结构', levelList)
}
const checkMove = throttle((e) => {
// console.log('移动',e);
if (e.from == e.to) {
return true
} else {
// 移动的数据
let moveObj = e.draggedContext.element
// 着陆位置的数据
let nowObj = e.relatedContext.list
if (nowObj.indexOf(moveObj) > -1) {
console.log('着陆位置已存在该数据,不能重置放置')
return false
}
}
}, 300)
// 表单校验和提交
const ruleFormRef = ref(null)
const rules = reactive({
name: [
{ required: true, message: '请输入', trigger: 'blur' },
{ min: 2, max: 20, message: '长度需要在2到20之间', trigger: 'blur' },
]
})
const submitForm = async () => {
if (!ruleFormRef.value) return
console.log('levelList',levelList.value)
await ruleFormRef.value.validate((valid, fields) => {
if (valid) {
console.log('submit!')
submit()
} else {
console.log('error submit!', fields)
}
})
}
const submit = () => {
let data=levelList.value.map(item=>{
return {
id:null,
name:item.name,
dimensionId:Number(tableId.value),
fieldDetailIds:item.cloneList.map(item=>item.id).join(',')
}
})
addEditLevel({
list:data
})
.then(res => {
console.log(res)
})
.catch(err => {
console.log(err)
})
console.log('提交了')
}
//暴露state和play方法
defineExpose({
submitForm
})
</script>
<style scoped>
.flex {
display: flex;
}
.align-center {
align-items: center;
}
.box {
border: 1px solid #ebeef5;
min-height: 240px;
/* margin-bottom: 20px; */
/* display: inline-block; */
}
.box-tit {
padding: 10px 0px;
border-bottom: 1px solid #ebeef5;
}
.box-body {
padding: 15px;
}
.box-footer {
margin: 10px 0px;
}
.items {
border-radius: 5px;
padding: 3px 5px;
line-height: 1.5;
/* box-sizing: border-box; */
/* background: #cecece; */
margin-bottom: 10px;
}
.chosenClass {
background: #409eff !important;
color: #fff;
}
.card {
border: 1px solid #dbdcde;
padding: 10px 10px 0px 10px;
margin-bottom: 15px;
border-radius: 3px;
}
.list-group {
padding: 5px;
min-height: 30px;
display: flex;
align-items: flex-start;
flex-wrap: wrap;
align-content: flex-start;
}
.list-group .items {
height: auto;
padding: 5px;
position: relative;
margin-bottom: 0;
}
</style>