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>
posted @ 2024-02-20 15:01  风意不止  阅读(1841)  评论(0编辑  收藏  举报