前端时间有个需求,需要对3×3(不一定,也可能多行)的卡片布局,进行拖拽,拖拽过程中自动排序,以下代码是基于vue2,可直接运行,报错可评论滴我
部分代码优化来自于GPT4o和Claude:官方直连GPT/Claude
代码如下:
<template>
<div style="width: 600px; height: 2000px;margin-top:20px">
<hr />
<br />
<button @click="addItem">添加元素</button>
<div class="container" style="width: 100%; margin-top: 10px; height: 100%">
<grid-layout
:layout="layout"
:col-num="colNum"
:row-height="30"
:vertical-compact="false"
:use-css-transforms="true"
@layout-updated="layoutUpdatedEvent"
>
<grid-item
v-for="item in layout"
:key="item.i"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:i="item.i"
>
<span class="text">{{ item.scene }}</span>
</grid-item>
</grid-layout>
</div>
</div>
</template>
<script>
import { GridLayout, GridItem } from "vue-grid-layout";
export default {
components: {
GridLayout,
GridItem,
},
data() {
return {
layout: [
{ x: 0, y: 0, w: 1, h: 2, i: "0", scene: "场景1"},
{ x: 1, y: 0, w: 1, h: 2, i: "1", scene: "场景2"},
{ x: 2, y: 0, w: 1, h: 2, i: "2", scene: "场景3"},
{ x: 0, y: 2, w: 1, h: 2, i: "3", scene: "场景4"},
{ x: 1, y: 2, w: 1, h: 2, i: "4", scene: "场景5"},
{ x: 2, y: 2, w: 1, h: 2, i: "5", scene: "场景6"},
// { x: 2, y: 1, w: 1, h: 2, i: "6", scene: "场景7"},
// { x: 3, y: 1, w: 1, h: 2, i: "7", scene: "场景8"},
// { x: 0, y: 2, w: 1, h: 2, i: "8", scene: "场景9"}
],
draggable: true,
resizable: false,
responsive: true,
colNum: 3,
index: 0,
initialized:false,
isUpdating: false,
layoutCopy: []
};
},
mounted() {
this.index = this.layout.length;
},
methods: {
layoutUpdatedEvent(newLayout) {
if (!this.isUpdating) {
this.isUpdating = true;
this.rearrangeLayout(newLayout);
this.$nextTick(() => {
this.isUpdating = false;
});
}
},
rearrangeLayout(layout) {
// 创建 layout 的深拷贝,防止修改原始数据
let newLayout = layout;
// 按 y 和 x 排序
newLayout.sort((a, b) => a.y - b.y || a.x - b.x);
// 重新排列布局
for (let i = 0; i < newLayout.length; i++) {
newLayout[i].x = (i % 3) * 1;
newLayout[i].y = Math.floor(i / 3)*newLayout[i].h;
}
this.layout= newLayout;
},
addItem(){
// Add a new item. It must have a unique key!
this.layout.push({
x: (this.layout.length * 1) % (this.colNum || 12),// q:为什么* 2 a:因为每个元素的宽度是2
y: this.layout.length + (this.colNum || 12), // puts it at the bottom
w: 1,
h: 2,
i: this.index,
scene: `场景${this.layout.length + 1}`
});
console.log(this.layout,"this.layout");
// Increment the counter to ensure key is always unique.
this.index++;
},
removeItem(){
const index = this.layout.map(item => item.i).indexOf(val);
this.layout.splice(index, 1);
},
}
};
</script>
<style>
.container .vue-grid-item.vue-grid-placeholder {
background: none;
border: #00893d dashed 2px;
}
.vue-grid-layout {
background: #eee;
}
.vue-grid-item:not(.vue-grid-placeholder) {
background: #00893d;
border: 1px solid #00893d;
}
.vue-grid-item .resizing {
opacity: 0.9;
}
.vue-grid-item .static {
background: #cce;
}
.vue-grid-item .text {
font-size: 24px;
text-align: center;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
margin: auto;
height: 100%;
width: 100%;
color: #FFF;
}
.vue-grid-item .no-drag {
height: 100%;
width: 100%;
}
.vue-grid-item .minMax {
font-size: 12px;
}
.vue-grid-item .add {
cursor: pointer;
}
.vue-draggable-handle {
position: absolute;
width: 20px;
height: 20px;
top: 0;
left: 0;
background-position: bottom right;
padding: 0 8px 8px 0;
background-repeat: no-repeat;
background-origin: content-box;
box-sizing: border-box;
cursor: pointer;
}
.layoutJSON {
background: #ddd;
border: 1px solid #00893d;
margin-top: 10px;
padding: 10px;
}
.columns {
-moz-columns: 120px;
-webkit-columns: 120px;
columns: 120px;
}
</style>