You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

399 lines
15 KiB
Vue

1 month ago
<template>
<div class="draggable-chart" :style="{ top: `${position.top}px`, left: `${position.left}px` }"
@mousedown="startDrag">
<el-card class="box-card">
<div id="chart" :style="{ width: `${dimensions.width}px`, height: `${dimensions.height}px` }"></div>
<div class="resize-handle" @mousedown.stop="startResize"></div> <!-- 添加调整大小控制柄 -->
</el-card>
</div>
</template>
<script>
import * as echarts from 'echarts';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([CanvasRenderer]);
export default {
props: {
data: Array,
title: String,
titleSize: Number,
titlePosition: String,
showAxis: Boolean,
showYAxis: Boolean,
barWidth: Number,
yXAxisMaxValue: Number,
chartType: String,
chartNum: Number,
wellParamData: {
type: Object // 提供一个默认值为一个空对象
},
reverseScatter: Boolean
},
data() {
// 初始化拖动和调整大小状态和偏移量
return {
position: { top: 150, left: 400 },
dragging: false,
offset: { x: 0, y: 0 },
dimensions: { width: 1000, height: 600 }, // 初始宽度和高度
resizing: false,
resizeStart: { x: 0, y: 0 }
};
},
mounted() {
this.initChart();
},
watch: {
data: {
handler: 'initChart',
deep: true,
},
title: 'initChart',
titleSize: 'initChart',
titlePosition: 'initChart',
showAxis: 'initChart',
showYAxis: 'initChart',
barWidth: 'initChart',
yXAxisMaxValue: 'initChart',
chartType: 'initChart',
wellParamData: {
handler: 'initChart',
deep: true
},
reverseScatter: {
handler: 'initChart',
deep: true // 启用深度监听
}
},
methods: {
initChart() {
const chartDom = document.getElementById('chart');
if (!this.chart) {
this.chart = echarts.init(chartDom);
}
if (this.chartNum === 5) {
// 动态确定横坐标和纵坐标参数名称
const allParams = Object.values(this.wellParamData).flat(); // 展平所有数据
const paramNames = [...new Set(allParams.map(item => item.ParamName))]; // 获取唯一参数名称
if (paramNames.length !== 2) {
console.error("数据中的参数名称数量不是2个无法绘制散点图");
return;
}
const shapes = ['circle', 'rect', 'triangle', 'diamond', 'pin', 'arrow'];
let shapeIndex = 0;
const [xParamName, yParamName] = this.reverseScatter ? paramNames : paramNames.reverse(); // 动态获取两个参数名称
let minXValue = 0;
let minYValue = 0;
const series = Object.entries(this.wellParamData).map(([key, paramList]) => {
const xValues = paramList
.filter(item => item.ParamName === xParamName)
.map(item => item.Point.Z); // 横坐标值 (垂直地应力)
if (xValues.length > 0) {
let thisMinXValue = Math.min(...xValues);
if (minXValue == 0 || minXValue > thisMinXValue) {
minXValue = thisMinXValue;
}
}
const yValues = paramList
.filter(item => item.ParamName === yParamName)
.map(item => item.Point.Z); // 纵坐标值 (加砂强度)
if (yValues.length > 0) {
let thisMinYValue = Math.min(...yValues);
if (minYValue == 0 || minYValue > thisMinYValue) {
minYValue = thisMinYValue;
}
}
// 匹配阶段号
const stageNos = paramList
.filter(item => item.ParamName === xParamName)
.map(item => item.StageNo);
// 将每个阶段的横纵坐标配对
const scatterData = stageNos.map((stage, index) => [
xValues[index], // X 值
yValues[index], // Y 值
stage, // 阶段号,用于提示
]);
const symbol = shapes[shapeIndex % shapes.length];
shapeIndex++;
return {
name: key, // 当前 series 的名称 (井号)
type: "scatter",
data: scatterData,
itemStyle: {
color: this.getRandomColor(), // 设置每个 series 的随机颜色
},
symbol,
// 设置不同的形状
symbolSize: 10, // 控制形状大小
};
});
const scatterOption = {
title: {
text: this.title,
left: this.titlePosition,
textStyle: {
fontSize: this.titleSize,
},
top: 'bottom',
},
tooltip: {
trigger: "item",
formatter: params => {
const stage = params.value[2];
return `${params.seriesName}<br>阶段号: ${stage}<br>${xParamName}: ${params.value[0]}<br>${yParamName}: ${params.value[1]}`;
},
},
legend: {
show: true,
type: "scroll",
orient: "horizontal",
top: 10,
},
xAxis: {
name: xParamName,
type: "value",
min: Math.floor(minXValue * 10) / 10
},
yAxis: {
name: yParamName,
type: "value",
min: Math.floor(minYValue * 10) / 10
},
series, // 将生成的 series 数据放入配置
};
this.chart.clear();
this.chart.setOption(scatterOption);
}
else if (this.chartNum == 4) {
let xValueName = '';
let minXValue = 0;
let minYValue = 0
let yValueName = '';
const series = Object.entries(this.wellParamData).map(([JH, params]) => {
let xValue = 0;
let yValue = 0
if (this.reverseScatter) {
xValue = params[0]?.Point.Z;
xValueName = params[0]?.ParamName;
yValue = params[1]?.Point.Z;
yValueName = params[1]?.ParamName;
} else {
xValue = params[1]?.Point.Z;
xValueName = params[1]?.ParamName;
yValue = params[0]?.Point.Z;
yValueName = params[0]?.ParamName;
}
if (minXValue == 0 || minXValue > xValue) {
minXValue = Math.floor(xValue * 10) / 10;
}
if (minYValue == 0 || minYValue > yValue) {
minYValue = Math.floor(yValue * 10) / 10;
}
return {
name: JH,
type: "scatter",
data: [[xValue, yValue]],
itemStyle: {
color: this.getRandomColor()
}
};
});
const scatterOption = {
title: {
text: this.title,
left: this.titlePosition,
textStyle: {
fontSize: this.titleSize,
},
top: 'bottom',
},
tooltip: {
trigger: "item",
formatter: params => {
return `${params.seriesName}<br>${xValueName}: ${params.value[0]}<br>${yValueName}: ${params.value[1]}`;
}
},
legend: {
show: true,
type: "scroll",
orient: "horizontal",
top: 10
},
xAxis: {
name: xValueName,
type: "value",
min: minXValue
},
yAxis: {
name: yValueName,
type: "value",
min: minYValue
},
series
}
this.chart.clear();
if (scatterOption.series.length > 0) {
this.chart.setOption(scatterOption);
}
}
else {
const wells = Object.keys(this.wellParamData);
const maxLength = Math.max(...Object.values(this.wellParamData).map(arr => arr.length));
const xAxisData = Array.from({ length: maxLength }, (_, i) => `${i + 1}`);
const seriesData = wells.map(well => {
const wellData = this.wellParamData[well]; // 获取某个井的数据
const colorItem = this.data.find(item => item.ParamName == wellData[0].ParamName); // 查找对应的 color 项
const color = colorItem ? colorItem.Color : 'gray';
const zValues = wellData.map(item => item.Point.Z);
return { color, zValues }; // 返回包含颜色和 z 值的对象
});
const firstSeriesData = seriesData.map(item => [item.color, item.zValues[0]]);
const option = {
title: {
text: this.title,
left: this.titlePosition,
textStyle: {
fontSize: this.titleSize,
},
top: 'bottom',
},
tooltip: {},
legend: {
data: wells
},
xAxis: this.chartType === 'pie' ? { show: false } : {
data: (this.chartNum == 0 || this.chartNum == 1 || this.chartNum == 4) ? xAxisData : wells,
show: this.showAxis,
axisLabel: {
rotate: 45 // 将标签旋转45度
}
},
yAxis: this.chartType === 'pie' ? { show: false } : {
axisLine: {
show: true,
},
max: this.yXAxisMaxValue,
show: this.showYAxis,
},
series: (this.chartNum == 0 || this.chartNum == 1) ? wells.map((well, index) => ({
name: well,
type: this.chartType,
data: seriesData[index].zValues,
color: this.chartNum == 0 ? this.getRandomColor() : seriesData[index].color,
barWidth: this.barWidth // 动态设置柱宽
})) : [
{
type: this.chartType,
data: firstSeriesData.map(item => ({
value: item[1],
itemStyle: {
color: item[0],
}
})),
barWidth: this.barWidth
}
],
};
this.chart.clear();
if (option.series.length > 0) {
this.chart.setOption(option);
}
}
},
getRandomColor() {
const letters = '0123456789ABCDEF';
let color = '#';
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
},
startResize(event) {
this.resizing = true;
this.resizeStart = { x: event.clientX, y: event.clientY };
document.addEventListener('mousemove', this.onResize);
document.addEventListener('mouseup', this.stopResize);
},
onResize(event) {
if (this.resizing) {
const deltaX = event.clientX - this.resizeStart.x;
const deltaY = event.clientY - this.resizeStart.y;
this.dimensions.width += deltaX;
this.dimensions.height += deltaY;
this.resizeStart = { x: event.clientX, y: event.clientY };
this.chart.resize(); // 更新图表大小
}
},
stopResize() {
this.resizing = false;
document.removeEventListener('mousemove', this.onResize);
document.removeEventListener('mouseup', this.stopResize);
},
startDrag(event) {
this.dragging = true;
this.offset.x = event.clientX - this.position.left;
this.offset.y = event.clientY - this.position.top;
document.addEventListener('mousemove', this.onDrag);
document.addEventListener('mouseup', this.stopDrag);
},
onDrag(event) {
if (this.dragging) {
this.position.left = event.clientX - this.offset.x;
this.position.top = event.clientY - this.offset.y;
}
},
stopDrag() {
this.dragging = false;
document.removeEventListener('mousemove', this.onDrag);
document.removeEventListener('mouseup', this.stopDrag);
},
},
};
</script>
<style scoped>
.box-card {
position: relative;
}
.draggable-chart {
position: absolute;
cursor: move;
z-index: 1000;
}
.resize-handle {
position: absolute;
width: 0;
height: 0;
right: 0;
bottom: 0;
border-left: 10px solid transparent;
border-top: 10px solid transparent;
border-right: 10px solid #37e40b;
/* 控制柄颜色 */
cursor: se-resize;
z-index: 10;
}
</style>