|
|
<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>
|