拖拽draggable
大约 3 分钟
拖拽draggable
案例

end(event) {
const mouseX = event.originalEvent.x;
const mouseY = event.originalEvent.y;
const plumbBox = document.querySelector('.plumbBox');
const tableRect = plumbBox.getBoundingClientRect();
if (
mouseX >= tableRect.left &&
mouseX <= tableRect.right &&
mouseY >= tableRect.top &&
mouseY <= tableRect.bottom &&
) {
const cellElement = document.elementFromPoint(mouseX, mouseY); // 能知道,当前拖拽过来的元素所在父元素
const tdElement = cellElement.closest('td'); // 拿到最近的td父元素(也可能是自身)
if (cellElement && cellElement.tagName === 'TD' ) { // 验证是否是拖拽到指定的td单元格内
//const rowId = cellElement.getAttribute('data-row-id');// 通过getAttribute 获取自定义属性
// 拖拽后执行
}
}
},
<template>
<div class="flowBox flow-content bk main-wrap">
<div class="leftMenu">
<div class="menu-title df_center">左侧菜单</div>
<draggable @start="start" @end="end" :sort="false" class="node-wrap df_fdc_ac">
<div v-for="(item, index) in leftMenuList" :key="item.id" @mousedown="(el) => downNode(el, item)"
class="leftNode node" :class="item.type"> {{ item.name }}
</div>
<h4>操作提示</h4>
<hr/>
<p>左侧拖拽至右侧画布</p>
<p>右键节点是删除节点,左键线条是删除线条</p>
<el-button size="meduim" type="primary" @click="saveFlow">保存流程</el-button>
</draggable>
</div>
<div class="plumbBox">
<table class="bordered-table">
<thead>
<tr>
<th :colspan="tableHeaders.length">防山火现场处置方案</th>
</tr>
<tr>
<th v-for="header in tableHeaders" :key="header.ename">{{ header.name }}</th>
</tr>
</thead>
<tbody>
<tr v-for="(row, rowIndex) in tableData" :key="row.id">
<td :id="'row_'+rowIndex+'_col_'+colIndex" v-for="(column, colIndex) in tableHeaders"
:key="column.ename" :class="['row_'+rowIndex,'col_'+colIndex]"
@drop="drop($event, row,column)" @dragover="allowDrop($event)">
{{ row[column.ename] }}
</td>
</tr>
</tbody>
</table>
</div>
</div>
</template>
<script>
import {leftMenuList} from "./js/data";
import draggable from "vuedraggable";
export default {
name: 'demo1',
components: {draggable},
data() {
return {
leftMenuList: leftMenuList,
dataList: [],
tableHeaders: [
{ name: '角色1', ename: 'juese1' },
{ name: '角色2', ename: 'juese2' },
{ name: '角色3', ename: 'juese3' },
{ name: '角色4', ename: 'juese4' },
{ name: '角色5', ename: 'juese5' },
{ name: '角色6', ename: 'juese6' },
],
tableData: [
{id: 0, firstColumn: '火情报出', juese1:[],juese2:[],juese3:[],juese4:[],juese5:[],juese6:[],},
{id: 1, firstColumn: '先期处理', juese1:[],juese2:[],juese3:[],juese4:[],juese5:[],juese6:[],},
{id: 2, firstColumn: '启动相应', juese1:[],juese2:[],juese3:[],juese4:[],juese5:[],juese6:[],},
],
selectedNode: null,
currentRow: null, // 行
currentCol: null,// 当前选中的列
}
},
methods: {
start() {
},
end(event) {
const mouseX = event.originalEvent.x;
const mouseY = event.originalEvent.y;
const plumbBox = document.querySelector('.plumbBox');
const tableRect = plumbBox.getBoundingClientRect();
if (
mouseX >= tableRect.left &&
mouseX <= tableRect.right &&
mouseY >= tableRect.top &&
mouseY <= tableRect.bottom &&
this.selectedNode
) {
const cellElement = document.elementFromPoint(mouseX, mouseY);
console.log(cellElement)
if (cellElement && cellElement.tagName === 'TD') {
const rowId = cellElement.getAttribute('data-row-id');
// const selectedRow = this.tableData.find(row => row.id === Number(rowId));
console.log(this.currentRow,this.currentCol)
this.currentRow[this.currentCol.ename].push(this.selectedNode)
console.log(this.tableData)
}
}
},
downNode(event, nodeItem) {
this.selectedNode = nodeItem;
},
drop(event, rowItem,colItem) {
event.preventDefault();
this.currentRow = rowItem;
this.currentCol = colItem;
},
allowDrop(event) {
event.preventDefault();
},
saveFlow() {
// 保存流程的逻辑
}
}
}
</script>
<style lang="scss" scoped>
.main-wrap {
color: #fff !important;
}
.bordered-table {
border-collapse: collapse;
width: 100%;
}
.bordered-table th,
.bordered-table td {
border: 1px solid #fff;
padding: 8px;
}
.col_0 {
line-height: 1.5; /* 设置合适的值,使字体垂直居中 */
writing-mode: vertical-rl; /* 第一列的文字垂直布局*/
width: 50px;
}
.flowBox {
display: flex;
height: 100%;
color: #fff;
}
.leftMenu {
/*width: 300px;*/
width: 200px;
/* height: 100%; */
border: 1px solid #fff;
padding: 20px 10px;
.menu-title {
font-size: 18px;
font-weight: 600;
}
.leftNode {
/* width: 150px;*/
margin-bottom: 30px;
}
}
.plumbBox {
flex: 1;
/* height: 100%; */
position: relative;
/*background: url(./icon/grid-background.png);
background-size: 50px 50px;*/
}
/* 节点 样式 目前总共分为 2类,一类是 时间节点,一类是 流转节点*/
.node {
width: 150px;
height: 60px;
display: flex;
justify-content: center;
align-items: center;
border: dashed 1px #fff;
cursor: move;
&.timeNode {
border-radius: 50%;
border: 1px solid #fff;
}
&.flowNode {
border: 1px solid #fff;
border-radius: 4px;
}
&:hover {
border: 2px dashed #1f77f3;
}
&.check {
border: 2px solid #1f77f3;
/* 最好加个阴影啥的*/
}
}
// 连线的节点
.pointNode {
border-radius: 50%;
width: 10px;
height: 10px;
position: absolute;
bottom: -5px;
left: 50%;
transform: translateX(-50%);
-o-border-image: initial;
/* border-image: initial; */
background: white;
-webkit-box-sizing: border-box;
box-sizing: border-box;
border: 3px solid royalblue;
&:hover {
background: royalblue;
border: none;
cursor: pointer;
}
}
</style>
案例
https://blog.csdn.net/weixin_56718509/article/details/133863083
https://huaweicloud.csdn.net/653f71c734bf9e25c799bcc1.html
https://stackblitz.com/edit/vitejs-vite-rkwugn?file=README.md
类似app端拖拽排序 https://blog.csdn.net/sinat_28071063/article/details/102372442