news 2026/6/10 21:04:18

draggable组件实现两层拖拽面板

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
draggable组件实现两层拖拽面板

遇见情况:现在的数据层层叠叠,有的要求不仅简单的单个盒子拖拽,而且要求:父盒子里面的子盒子也要能拖拽。

实现要求:拖拽父输入框只能与属于父输入框的交换位置;拖拽子输入框只能与子输入框交换位置

实现图片:

完整代码实现:

<template> <div class="custom no-select"> <el-form :model="formtwo" ref="formRef" label-width="auto" style="max-width: 475px" > <draggable v-model="formtwo.dimensionalInfoList" item-key="id" handle=".drag-handle" animation="300" @end="onEnd" > <template #item="{ element: group, index: groupIndex }"> <div class="group_card"> <!-- 父维度 --> <div class="father_box"> <span class="iconfont icon-hengxian drag-handle"></span> <el-form-item :prop="'dimensionalInfoList.' + groupIndex + '.content'" :rules="{ required: true, message: '父维度不能为空', trigger: 'blur', }" > <el-input v-model="group.content" placeholder="请输入父分析维度" class="desc__content" /> </el-form-item> <SisternodeOutlined class="icon_Share_css" :style="{ marginRight: formtwo.dimensionalInfoList.length === 1 ? '24px' : '10px', }" @click="addNextInput(groupIndex)" /> <el-icon class="icon_height_css" v-if="formtwo.dimensionalInfoList.length !== 1" > <CircleClose @click="delInput(groupIndex)" /> </el-icon> </div> <!-- 子维度 --> <draggable v-model="group.childInfo" item-key="id" handle=".drag-handle" animation="300" @end="onEnd" > <template #item="{ element: groupChild, index: groupIndexChild }"> <div class="groupChild_card"> <!-- 前方样式引导,用于连接父节点和子节点的连线--> <div style=" width: 30px; height: 50px; float: left; margin-top: -24px; " > <div style="display: flex"> <div :style="{ height: groupIndexChild !== group.childInfo.length - 1 ? '40px' : '35px', paddingLeft: '7px', borderRight: '1px solid #979797', }" ></div> <div style=" width: 20px; height: 6px; margin-top: 34px; border-left: 1px solid #979797; border-bottom: 1px solid #979797; border-radius: 0 0 0 5px; margin-left: -0.5px; " ></div> </div> <div style=" width: 0.5px; height: 10px; background-color: #979797; float: left; margin-left: 7px; " v-if="groupIndexChild !== group.childInfo.length - 1" ></div> </div> <span class="iconfont icon-hengxian drag-handle"></span> <el-form-item :prop=" 'dimensionalInfoList.' + groupIndex + '.childInfo.' + groupIndexChild + '.content' " :rules="{ required: true, message: '子维度不能为空', trigger: 'blur', }" > <el-input v-model="groupChild.content" placeholder="请输入子分析维度" class="desc__content desc__content_son" /> </el-form-item> <el-icon class="icon_height_css"> <CircleClose @click="delInputSon(child)" /> </el-icon> </div> </template> </draggable> </div> </template> </draggable> </el-form> </div> </template> <script setup> import { ref } from "vue"; import { CircleClose } from "@element-plus/icons-vue"; import { SisternodeOutlined } from "@ant-design/icons-vue"; import draggable from "vuedraggable"; let i = 1; //自定义id const formRef = ref(); //定义分析维度表单 // 分析维度,初始化就显示三个框 const formtwo = ref({ dimensionalInfoList: [ { content: "", dimId: ++i, childInfo: [] }, { content: "", dimId: ++i, childInfo: [] }, { content: "", dimId: ++i, childInfo: [] }, ], }); //拖拽结束事件,重新更新数据 const onEnd = () => { }; // 添加子联动输入框 const addNextInput = (index) => { // 获取当前维度对象 const currentItem = formtwo.value.dimensionalInfoList[index]; if (!currentItem.childInfo) { currentItem.childInfo = []; } // 向当前维度对象的 childInfo 数组中添加一个新的子维度对象 currentItem.childInfo.push({ content: "", }); }; // 删除维度输入框 const delInput = (index) => { formtwo.value.dimensionalInfoList.splice(index, 1); }; // 删除子维度输入框 const delInputSon = (child) => { // 找到当前子维度对应的父维度对象 const parentIndex = formtwo.value.dimensionalInfoList.findIndex((item) => item.childInfo.includes(child), ); const parentItem = formtwo.value.dimensionalInfoList[parentIndex]; // 从父维度对象的 childInfo 数组中删除当前子维度对象 parentItem.childInfo = parentItem.childInfo.filter((item) => item !== child); }; </script> <style lang="scss" scoped> .father_box { display: flex; justify-content: start; width: 100%; .icon_Share_css { text-align: center; margin-top: 15px; color: rgb(151, 151, 151); cursor: pointer; margin-right: 10px; } .icon_Share_css :hover { color: #1363df; } } .icon_height_css { text-align: center; line-height: 30px; margin-top: 15px; color: rgb(151, 151, 151) !important; cursor: pointer; } .icon_height_css :hover { color: red; } .desc__content { margin-left: 10px; margin-right: 10px; width: 100%; :deep .el-textarea__inner { background-color: rgba(246, 246, 246); min-height: 50px; max-height: 200px; } :deep .el-input__wrapper { background-color: rgba(246, 246, 246); } } .adddome { width: 100%; font-size: 12px; line-height: 12px; float: left; display: flex; justify-content: space-between; padding-bottom: 16px; .icon-gengxin { color: #979797; font-size: 15px; margin-right: 0; } .icon-gengxin:hover { color: #1363df; } } .icon-hengxian { font-size: 15px; color: #979797; text-align: center; line-height: 30px; cursor: move; } :deep .el-form-item__error { margin-left: 25px; } .son :deep .el-form-item__error { margin-left: 50px !important; } .prependInput { margin-bottom: 15px; } :deep .el-input-group--prepend > .el-input__wrapper, .el-input-group__append { background-color: rgba(246, 246, 246) !important; } :deep .el-input-group__prepend { background-color: rgba(246, 246, 246) !important; } .no-select { user-select: none; } .group_card { .groupChild_card { display: flex; width: 95%; } } .el-form-item { width: 100%; } </style>

注意点:

1、拖拽的id必须要有,要不然不能进行拖拽

2、handle=".drag-handle"为draggable组件的拖拽盒子的css,所以要在导入的icon图标上添加上该className

3、且template下方必须只有一个item的slot——必须只有一个子节点

4、子维度在父维度内部,

5、关于图片上父节点与子节点的连线是html手动调整实现,所以你们要写的时候需要根据实际情况进行调整

<!-- 前方样式引导,用于连接父节点和子节点的连线--> <div style=" width: 30px; height: 50px; float: left; margin-top: -24px; " > <div style="display: flex"> <div :style="{ height: groupIndexChild !== group.childInfo.length - 1 ? '40px' : '35px', paddingLeft: '7px', borderRight: '1px solid #979797', }" ></div> <div style=" width: 20px; height: 6px; margin-top: 34px; border-left: 1px solid #979797; border-bottom: 1px solid #979797; border-radius: 0 0 0 5px; margin-left: -0.5px; " ></div> </div> <div style=" width: 0.5px; height: 10px; background-color: #979797; float: left; margin-left: 7px; " v-if="groupIndexChild !== group.childInfo.length - 1" ></div> </div>
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/10 14:33:44

降重去 AI 双 buff!虎贲等考 AI:让学术原创性 “零争议” 通关

在学术审核 “双重严卡” 的当下&#xff0c;“查重超标” 和 “AI 生成痕迹” 成了科研人、毕业生的两大 “拦路虎”。传统降重工具越改越乱&#xff0c;语义不通&#xff1b;普通去 AI 工具简单删减&#xff0c;学术质量下滑。而虎贲等考 AI&#xff08;官网&#xff1a;http…

作者头像 李华
网站建设 2026/6/9 22:15:21

Conda install -c pytorch pytorch详解

Conda install -c pytorch pytorch 详解 在人工智能项目开发中&#xff0c;一个看似简单的命令往往承载着一整套工程实践的沉淀。比如这条安装指令&#xff1a; conda install -c pytorch pytorch它不只是“装个 PyTorch”这么简单——背后是一整套关于环境隔离、依赖管理、跨…

作者头像 李华
网站建设 2026/6/10 9:46:05

大模型温度系数调节:Miniconda环境实现多样化输出

大模型温度系数调节&#xff1a;Miniconda环境实现多样化输出 在当前大语言模型&#xff08;LLM&#xff09;广泛应用的背景下&#xff0c;如何让同一个模型既能写出严谨的技术文档&#xff0c;又能生成富有想象力的诗歌&#xff0c;成了开发者面临的一个现实挑战。更棘手的是&…

作者头像 李华
网站建设 2026/6/10 9:46:45

数据结构 b树(b-)树

头文件#pragma on #include<stdio.h> #include<iostream> #include<stdlib.h> #include<string.h> #include<assert.h> using namespace std; #define M 5//阶数 typedef struct btnode {int keynum; // 当前关键字数量int keys…

作者头像 李华
网站建设 2026/6/10 9:43:47

还在熬夜凑论文?7款AI工具20分钟生成万字+真实参考文献!

别再用这些“自杀式”论文写作方法了&#xff01;你正在踩的3个致命坑 还在对着空白文档发呆到凌晨3点&#xff1f; 还在用百度百科拼拼凑凑当“参考文献”&#xff1f; 还在因为导师的红色批注改到崩溃大哭&#xff1f; 如果你点头的频率比论文的参考文献还多&#xff0c;那…

作者头像 李华