添加元素(主要参考了layout插件)
List组件内容
<div class="body">
<div
draggable
class="component-item"
v-for="item in dragIconList"
:key="item.type"
@drag="handleDragStart($event, item)"
@dragend="handleDragEnd($event, item)"
>
<div class="component-img">
<img :src="item.url" />
</div>
<div class="component-name">
{{item.title}}
</div>
</div>
</div>
// dragIconList要添加项的数据列表 参数自行添加 如{title:"名称", type:"类型", url:"iconUrl地址"}
document.addEventListener("dragover", function (e) {
mouseXY.x = e.clientX;
mouseXY.y = e.clientY;
}, false);
添加drag事件
@drag="handleDragStart($event, item)"
@dragend="handleDragEnd($event, item)"
let mouseXY = { "x": null, "y": null }; // 当前鼠标所在的位置
// x为组件所在的x轴下标0-n,y位组件所在的y轴下标0-n,w为组件所占的宽度1-n,h为组件的高度1-n,i为组件的id,也可以为组件添加data参数配置所需数据
let DragPos = { "x": null, "y": null, "w": 1, "h": 1, "i": null }; // 临时拖拽项的参数
// dragStrat事件
handleDragStart (e, item) {
let type = item.type; // 多种不同的组件数据可能不同 如下所示
this.gridInFocus = true;
// 拖拽过程中计算临时的item位置
// 当前拖拽项的所占格子数量 不同组件配置不同的参数 按序配置
if (type == "MyType") {
DragPos.w = 3;
DragPos.h = 2;
}
// 获取总网格数据
let parentRect = document.getElementById('content').getBoundingClientRect();
// 判断是否在容器内
let mouseInGrid = false;
if (((mouseXY.x > parentRect.left) && (mouseXY.x < parentRect.right)) && ((mouseXY.y > parentRect.top) && (mouseXY.y < parentRect.bottom))) {
mouseInGrid = true;
}
// 将临时项加入layout中 drop项会在后续移除掉,此时判断主要是防止多次添加
if (mouseInGrid && (this.realValue.findIndex(item => item.i === 'drop')) === -1) {
let { x, y } = this.getCurrentIndex(parentRect)
let canChange = checkCanOccupy(this.realValue, 'drop', x, y, DragPos.w, DragPos.h);
// 判断当前位置是否可以插入
if (canChange) {
DragPos.x = x;
DragPos.y = y;
this.realValue.push({
x: x,
y: y,
w: DragPos.w,
h: DragPos.h,
i: 'drop',
data: {
x: x,
y: y,
w: DragPos.w,
h: DragPos.h,
}
});
}
}
// 已经加入一个临时数据 realValue是数组 用于存储layout数据
let index = this.realValue.findIndex(item => item.i === 'drop');
if (index != -1) {
// 实时移动位置
let { x, y } = this.getCurrentIndex(parentRect)
// 判读当前位置是否有模板
let canChange = checkCanOccupy(this.realValue, 'drop', x, y, DragPos.w, DragPos.h);
if (canChange) {
this.realValue.forEach((item) => {
if (item.i == 'drop') {
item.x = x;
item.y = y;
}
})
}
}
},
// 拖拽结束,开始项layout中添加真实数据
handleDragEnd (e, item) {
this.gridInFocus = false;
let type = item.type;
// 根据组件类型进行一次判断,只能存储一次的组件不能二次添加
if (item.type == "myType") {
let isHasNews = this.realValue.findIndex((item) => {
return item.data.type == 'myType';
})
if (isHasNews != -1) {
this.realValue = this.realValue.filter(obj => obj.i != 'drop');
this.$forceUpdate();
return this.$modal.msgWarning("当前组件已存在")
}
}
let parentRect = document.getElementById('content').getBoundingClientRect();
let mouseInGrid = false;
if (((mouseXY.x > parentRect.left) && (mouseXY.x < parentRect.right)) && ((mouseXY.y > parentRect.top) && (mouseXY.y < parentRect.bottom))) {
mouseInGrid = true;
}
let dropItem = this.realValue.find((item) => {
return item.i == 'drop'
})
// 鼠标在网格中并且已有drop项
if (mouseInGrid && dropItem) {
let x = dropItem.x, y = dropItem.y
let flag = this.checkCanInsert(x, y, type);
if (flag) {
let obj = {
x: x,
y: y,
w: DragPos.w,
h: DragPos.h,
i: uuidv4(), // const { v4: uuidv4 } = require('uuid'); uuid插件自行引入
data: {
id: uuidv4(),
type: type,
showType: 1
}
}
// 这里可以根据类型的不同配置不同的数据参数
this.realValue.push(obj);
this.$emit("addSuccess")
} else {
this.$modal.msgWarning("当前组件不能放在该位置");
}
}
this.gridInFocus = false;
this.realValue = this.realValue.filter(obj => obj.i != 'drop');
this.$forceUpdate();
},
其他方法
// 根据鼠标所在的x,y获取组件的下标 131/117为组件默认的宽高
getCurrentIndex (rect) {
let x_num = Math.floor((mouseXY.x - rect.left) / 131);
let y_num = Math.floor((mouseXY.y - rect.top) / 117);
let x = Math.floor((mouseXY.x - rect.left - (18 * x_num)) / 131);
let y = Math.floor((mouseXY.y - rect.top - (18 * y_num)) / 117);
if (x < 0) {
x = 0;
}
if (y < 0) {
y = 0;
}
if (DragPos.w + x > 6) {
x = 6 - DragPos.w;
}
if (DragPos.h + y > 3) {
y = 3 - DragPos.h;
}
return { x, y };
},
// 判断当前组件是否可以插入
checkCanOccupy (layout, myId, x, y, w, h) {
let A = {
x, y, w, h
}
let flag = true;
layout.forEach(B => {
if (B.i != myId) {
let bw = B.w - 1;
let bh = B.h - 1;
let aw = A.w - 1;
let ah = A.h - 1;
if (aw >= bw && ah >= bh) {
console.log(B.x + bw >= A.x, B.y + bh >= A.y, B.x <= A.x + aw, B.y <= A.y + ah)
if (B.x + bw >= A.x && B.y + bh >= A.y && B.x <= A.x + aw && B.y <= A.y + ah) {
flag = false
}
} else if (aw >= bw && ah < bh) {
// 插入项宽度大于待检测宽度,但高度小于待检测高度
if (A.x + aw >= B.x && A.x <= B.x) {
// 只能放在上面或者下面
if (A.y > B.y) {
if (A.y <= B.y + bh) {
flag = false;
}
} else if (A.y == B.y) {
flag = false;
} else {
if (A.y + ah >= B.y) {
flag = false;
}
}
}
} else if (aw < bw && ah >= bh) {
// 插入项高度大于待检测高度,但宽度小于待检测宽度
if (A.y >= B.y && A.y <= B.y + bh) {
if (A.x < B.x) {
// 在左边
if (A.x + aw >= B.x) {
flag = false;
}
} else if (A.x == B.x) {
flag = false;
} else {
if (A.x <= B.x + bw) {
// 在右边
flag = false;
}
}
}else {
if (A.y + ah >= B.y) {
if (A.y + ah == B.y) {
if (A.x + aw >= B.x) {
flag = false
}
} else {
flag = false
}
}
}
} else {
if ((B.x <= A.x + aw && B.y <= A.y + ah && B.x + bw >= A.x + aw && B.y + bh >= A.y + ah)) {
flag = false
}
}
}
});
return flag;
}
Home组件主要内容
data
// 网格数据
layout: [
],
// 当前选中项
currentSelectData: {},
gridFocus: false,
// 当前拖动的box
curBox:null,
// 当前选择项的下标
currentSelectIndex: -1,
<div
:class="['grid', gridFocus?'focus':'']"
id="content"
@dragend="handleChangeEnd($event, 'content')"
>
<div
:class="['grid-item', (item.i=='drop' || item.isSelected)?'drop':'']"
v-for="(item, index) in layout"
:key="item.i"
:style="{
top:getTop(item) + 'px',
left:getLeft(item) + 'px',
width:getWidth(item) + 'px',
height:getHeight(item) + 'px'
}"
:date-i="item.i"
draggable
@drag="handleChangeStart($event, item)"
@dragenter="handleChangeEndter($event, item)"
@dragend="handleChangeEnd($event, item)"
@click="clickGrid(item, index)"
>
<div
v-if="currentSelectIndex==index"
class="delete"
@click="deleteLayout(item)"
></div>
<div
class="text"
v-if="item.data.type == '子组件类型'"
>
// 根据需要展示不同的子组件内容
</div>
</div>
</div>
获取所在位置
getLeft (item) {
return item.x * 组件默认的宽度 + item.x * (marginLeft值 根据需求设置)
},
getTop (item) {
return item.y * 组件默认高度 + item.y * (marginTop值 根据需求设置)
},
getWidth (item) {
return item.w * 组件默认的宽度 + (item.w - 1) * (marginLeft值 根据需求设置)
},
getHeight (item) {
return item.h * 组件默认高度 + (item.h - 1) * (marginTop值 根据需求设置)
},
拖拽开始
handleChangeStart (e, item) {
this.curBox = item;
this.gridFocus = true;
let { w, h } = item;
let mouseXY = {
x: e.clientX,
y: e.clientY
}
let parentRect = document.getElementById('content').getBoundingClientRect();
let mouseInGrid = false;
if (((mouseXY.x > parentRect.left) && (mouseXY.x < parentRect.right)) && ((mouseXY.y > parentRect.top) && (mouseXY.y < parentRect.bottom))) {
mouseInGrid = true;
}
// 将临时项加入layout中
if (mouseInGrid) {
let { x, y } = this.getCurrentIndex(parentRect, mouseXY, this.curBox)
let canChange = checkCanOccupy(this.layout, item.i, x, y, item.w, item.h);
// 判断当前位置是否可以插入
if (canChange) {
item.x = x;
item.y = y;
this.layout.forEach((lay) => {
if (lay.i == item.i) {
lay.isSelected = true;
} else {
lay.isSelected = false;
}
})
}
}
// 已经加入一个临时数据
let index = this.layout.findIndex(i => item.i === i.i);
if (index != -1) {
// 实时移动位置
let { x, y } = this.getCurrentIndex(parentRect, mouseXY, this.curBox)
// 判读当前位置是否有模板
let canChange = checkCanOccupy(this.layout, item.i, x, y, w, h);
if (canChange) {
this.layout.forEach((lay) => {
if (lay.i == item.i) {
lay.isSelected = true;
} else {
lay.isSelected = false;
}
})
}
}
},
已经添加过的组件在网格中进行拖拽更改位置,并判断两个组件是否可以互换位置
// @dragend="handleChangeEnd($event, item)"
handleChangeEnd (e, item) {
this.gridFocus = false;
if (item == 'content' || this.targetBox == null || this.targetBox == this.curBox.i) {
// 移动到新的位置
let parentRect = document.getElementById('content').getBoundingClientRect();
let mouseInGrid = false;
let mouseXY = {
x: e.clientX,
y: e.clientY
}
if (((mouseXY.x > parentRect.left) && (mouseXY.x < parentRect.right)) && ((mouseXY.y > parentRect.top) && (mouseXY.y < parentRect.bottom))) {
mouseInGrid = true;
}
if (mouseInGrid) {
let { x, y } = this.getCurrentIndex(parentRect, mouseXY, this.curBox);
// 判读当前位置是否有模板
let canOccupy = checkCanOccupy(this.layout, this.curBox.i, x, y, this.curBox.w, this.curBox.h);
if (canOccupy) {
this.layout.forEach((ele) => {
if (ele.i == this.curBox.i) {
ele.x = x;
ele.y = y;
this.currentSelectData = this.deepClone(ele);
}
})
}
}
} else {
// 交换盒子位置
let targetItem = this.deepClone(this.layout.find((ele) => {
return ele.i == this.targetBox
}))
// 检查两个盒子是否可以交换
let canChangeIndex = this.checkCanChangeIndex(this.curBox, targetItem);
if (canChangeIndex) {
let curItemX = this.curBox.x;
let curItemY = this.curBox.y;
this.layout.forEach((ele) => {
if (ele.i == this.curBox.i) {
ele.x = targetItem.x;
ele.y = targetItem.y
} else if (ele.i == this.targetBox) {
ele.x = curItemX;
ele.y = curItemY;
}
})
}
this.targetBox = null
}
},