在使用draggable的时候遇到了一些问题 好在最终解决了 记录下希望能帮助到有缘人
页面功能 可实现模块的放大缩小以及位置的更换 同时echarts会跟着父元素大小而变化,有兴趣者自己研究吧 代码放在下面了 直接cv就好 不难 但是有些细节要注意 比如draggable需要绑定唯一的key值否则改变元素样式时会出问题 以及echarts跟随父盒子自适应需要在echarts中配置grid属性并结合window的监听事件去实现
<template>
<div class="itxst">
<div class="group">
<draggable
v-model="group"
ghost-class="ghost"
handle=".move"
filter=".forbid"
:force-fallback="true"
chosen-class="chosenClass"
animation="300"
@start="onStart"
@end="onEnd"
group="group1"
:fallback-class="true"
:fallback-on-body="true"
:touch-start-threshold="50"
:fallback-tolerance="50"
:move="onMove"
class="draggablesty"
item-key="id"
>
<template #item="{ element }">
<div
:class="element.disabledMove ? 'forbid item' : 'item'"
:key="element.id"
>
<label class="move">{{ element.name }}</label>
<div class="echarts" :ref="setItemRef"></div>
<span class="icon" @mousedown="mouseScale"></span>
</div>
</template>
</draggable>
</div>
</div>
</template>
<script setup>
import { reactive, toRefs, onMounted } from "vue";
import * as echarts from "echarts";
//导入draggable组件
import draggable from "vuedraggable";
const state = reactive({
/*工作台的数据结构
disabledMove:禁止移动
disabledPark:禁止停靠
*/
group: [
{
name: "消息",
id: 1,
disabledMove: false,
disabledPark: false,
refVal: "ref1",
},
{
name: "库存",
id: 2,
disabledMove: false,
disabledPark: false,
refVal: "ref2",
},
{
name: "销量",
id: 3,
disabledMove: false,
disabledPark: false,
refVal: "ref3",
},
{
name: "日志",
id: 4,
disabledMove: false,
disabledPark: false,
refVal: "ref4",
},
],
});
const { group } = toRefs(state);
let itemRefs = [];
const setItemRef = (el) => {
if (el) {
itemRefs.push(el);
}
};
//拖拽开始的事件
const onStart = (e) => {
console.log("开始拖拽");
};
//拖拽结束的事件
const onEnd = (e) => {
console.log("结束拖拽");
};
const mouseScale = (ev) => {
// 子节点
let target = ev.target;
// 父节点
let parent = ev.target.parentNode;
let myEvent = ev;
var distanceX = myEvent.clientX - parent.offsetWidth;
var distanceY = myEvent.clientY - parent.offsetHeight;
document.onmousemove = function (ev) {
var myEvent = ev;
parent.style.width =
myEvent.clientX - distanceX + target.offsetWidth + "px";
parent.style.height =
myEvent.clientY - distanceY + target.offsetHeight + "px";
};
document.onmouseup = function () {
document.onmousemove = null;
document.onmouseup = null;
};
};
const myECharts = () => {
let myChart = echarts.init(itemRefs[0]);
// 基于准备好的dom,初始化echarts实例
// 绘制图表
myChart.setOption({
title: {
text: "ECharts 入门示例",
x: "center",
},
tooltip: {},
xAxis: {
data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"],
},
yAxis: {},
grid: {
top: 35,
left: 55,
right: 45,
bottom: 20,
width: "auto",
height: "auto",
},
series: [
{
name: "销量",
type: "bar",
data: [5, 20, 36, 10, 10, 20],
},
],
});
window.addEventListener("resize", function () {
myChart.resize();
});
window.addEventListener("mousemove", function () {
myChart.resize();
});
};
const onMove = (e, originalEvent) => {
//不允许停靠
if (e.relatedContext.element.disabledPark == true) return false;
return true;
};
onMounted(() => {
myECharts();
});
</script>
<style>
body {
padding: 0px;
margin: 0px;
background-color: #f1f1f1;
}
.itxst {
background-color: #f1f1f1;
display: flex;
padding: 20px;
width: 100%;
}
.group {
width: 100%;
height: 100%;
}
.draggablesty {
display: flex;
flex-wrap: wrap;
width: 100%;
height: 100%;
}
.item {
border: solid 1px #ddd;
padding: 0px;
text-align: left;
background-color: #fff;
margin-bottom: 10px;
display: flex;
flex-direction: column;
user-select: none;
width: 48%;
height: 250px;
margin: 1%;
position: relative;
}
.echarts {
width: 100%;
height: 80%;
}
.icon {
position: absolute;
right: 0;
bottom: 0;
border-right: 5px solid aquamarine;
border-bottom: 5px solid aquamarine;
border-top: 5px solid transparent;
border-left: 5px solid transparent;
cursor: nw-resize;
}
.item > label {
border-bottom: solid 1px #ddd;
padding: 6px 10px;
color: #333;
}
.item > label:hover {
cursor: move;
}
.item > p {
padding: 6px 10px;
color: #666;
}
.ghost {
border: solid 1px rgb(19, 41, 239) !important;
}
.chosenClass {
opacity: 1;
border: solid 1px red;
}
.fallbackClass {
background-color: aquamarine;
}
</style>