Commit ecd1a93d authored by xinzhedeai's avatar xinzhedeai

add:浸润线坡面动态绘制埋深数据显示问题排查处理。无用代码移除

parent 83aacf1b
<template>
<div class="common-page qyzz page-t1">
<div class="option page-row">
<table-filter
ref="filter"
:form="form"
:Dict="Dict"
:rules="rules"
:config="form.config"
:loadData="loadData"
/>
</div>
<div class="new-container" style="display: flex">
<div class="canvas-wrapper" style="width: 1000px">
<!-- 替换为 Canvas 元素 -->
<canvas
ref="canvas"
id="mainCanvas"
width="1000"
style="background: #f0f0f0"
></canvas>
</div>
<div class="chart-wrapper" style="padding-left: 0px">
<div
ref="chartContainer"
style="
width: 850px;
height: 550px;
margin-bottom: 20px;
margin-left: 10px;
border: 1px solid gainsboro;
"
></div>
<div class="chart-btn-group" style="margin-left: 10px;">
<button
size="small"
:type="currentChartType === 'line' ? 'primary' : ''"
@click="switchChartType('line')"
>
折线图
</button>
<button
size="small"
:type="currentChartType === 'column' ? 'primary' : ''"
@click="switchChartType('column')"
>
柱状图
</button>
</div>
</div>
</div>
<div class="panel-bottom page-row">
<div class="ctin-box">
<div class="content-within">
<div class="content-fix">
<div class="toolbar">
<div>
<el-button
v-for="(item, key) in form.config.otherBtn"
size="mini"
:type="item.type"
:icon="item.icon"
@click="item.callback.call(_self, $event)"
>{{ item.name }}</el-button
>
</div>
<div>
<el-button
v-for="(item, key) in form.config.rightBtn"
size="mini"
:type="item.type"
:icon="item.icon"
@click="item.callback.call(_self, $event)"
>{{ item.name }}</el-button
>
</div>
</div>
<!-- 表格渲染 -->
<cu-table
ref="cuTable"
:table="table"
:Dict="Dict"
:config="form.config"
:loadData="loadData"
/>
</div>
</div>
</div>
</div>
<!-- 表单渲染 -->
<cu-form
ref="cuForm"
:form="form"
:Dict="Dict"
:rules="rules"
:submit="submitForm"
:cancel="cancelForm"
:watchKeys="['paperfilename']"
/>
</div>
</template>
<script>
import Highcharts from "highcharts";
import { reqApi, Config } from "@/assets/js/httpApi.js";
import { Tools } from "@/assets/js/common.js";
import DateRangePicker from "@/components/DateRangePicker";
import TableFilter from "@/components/TableFilter";
import cuForm from "@/components/cuForm";
import cuTable from "@/components/cuTable";
import request from "@/utils/request";
export default {
name: "Dashboard",
components: {
DateRangePicker,
TableFilter,
cuForm,
cuTable
},
data() {
return {
chartDataEmpty: false,
canvasDataEmpty: false,
Dict: { selectList: [] },
table: {
page: 1,
size: 10,
total: 0,
loading: false,
dataList: []
},
rules: {},
form: {
title: "",
visible: false,
reqType: "add",
historyDialog: false,
status: { cu: 0 },
query: {},
search: {},
item: {},
file: 0,
config: {}
},
// 新增图表相关数据
chartVisible: true, // 图表弹窗可见性
deviceList: [{ id: "test001", name: "测试设备1" }], // 设备列表数据
selectedDevice: null, // 选中设备ID
dateRange: [null, null], // 日期范围选择
currentChartType: "line", // 当前图表类型(line/column)
chartLoading: false, // 图表加载状态
chartInstance: null, // Highcharts实例引用
// ...原有data属性保持不变...
canvas: null, // Canvas 实例
ctx: null, // 2D 上下文
bgImage: null, // 背景图对象
waterImage: null, // 新增:water.png 图片对象
poImage: null, // 新增:po.png 图片对象
waterValue: 0, // 新增:水位值
canvasDataReal: {
poConfigsWH: [
{
// 基础信息配置对象 用于换算具体画布xy坐标点
xieMianW: 0, // 第一个坡斜面距离第一个坡平面的水平距离。
pingMianW: 0, // 第一个坡平面距离平面宽度。
pingMianH: 0, // 第一个坡平面距离平面高度。
kongW: 0, // 目前设定打孔的位置在坡平面上距离左侧起始点的距离
kongH: 0, // 目前孔的深度
maishen: 0 // 水位到管口的垂直距离
}
],
otherConfig: {
gantanW: 0, // 干滩距离水平面的宽度(根据最后一个坡平面坐标点计算xy。)
tuduiW: 0, // 干滩距离水平面中间的土堆
shuiW: 0, // 水平面的宽度
shuiH: 0 // 水平面的高度
},
// Canvas绘制配置(支持批量扩展)
poConfigsXY: [
// 根据poConfigsWH 计算具体的xy坐标点,每个对象计算出两对坐标点。分别是斜面和平面点
],
guanConfigsXY: [
// 根据poConfigsXY和poConfigsWH的孔信息 计算具体的xy坐标点,每个对象计算出两对坐标点。分别是孔起始坐标和结束坐标
],
gantanConfigsXY: [],
jrxConfigsXY: [],
shuiweiConfigsXY: [],
tuduiConfigsXY: [],
jingjiexianConfigsXY: [],
lineConfigs: [
// { points: [], color: "red", lineWidth: 2 }, // 红色连接线配置
// { points: [], color: "yellow", lineWidth: 2 }, // 黄色连接线配置
]
}
// canvasData: [
// {
// name: "剖面名称",
// height: "剖面高度",
// width: "剖面宽度",
// code: "剖面编号",
// sdeg: "水区扇形起始角度",
// diameter: "总直径",
// jrxStepsDtoList: [
// {
// name: "台阶名称",
// code: "台阶编号",
// height: "高度(米)",
// width: "宽度(米)",
// slope: "坡度(deg)"
// }
// ],
// equipmentDataList: [
// {
// sensorid: "设备id",
// sensorname: "设备名称",
// unit: "设备单位",
// stage: "水位(传感器到水面的距离)",
// depth: "浸润线埋深(水位到管口的距离)(m)",
// smhb: "// 水面海拔",
// time: "时间",
// holeDepth: "孔口高程",
// jrxTrepanning: "浸润线开口深度",
// redAlarm: "红色预警 此预警值针对的是depth 浸润线埋深",
// orangeAlarm: "橙色预警",
// yellowAlarm: "黄色预警",
// blueAlarm: "蓝色预警"
// }
// ]
// }
// ]
};
},
created() {
// 获取基本信息
this.pageApi = Config.getModuleInfo(this);
this.Dict = this.pageApi.Dict;
this.form.config = this.pageApi.config;
this.rules = reqApi.getRules(this.Dict.baseInfo) || this.Dict.rules || {};
this.pageApi.created && this.pageApi.created();
// 新增:获取地址栏deviceId参数并设置到form.query.code
const deviceId = this.$route.query.deviceId;
if (deviceId) {
this.form.query.sensorid = deviceId; // 将deviceId赋值到查询条件的code字段
}
// this.handleCanvasData(this.canvasData);
},
mounted() {
this.$nextTick(() => {
this.loadData();
this.initChart1();
this.getCanvasData();
});
// 获取选择列表字典
var selectList = this.Dict.selectList;
Tools.asyncLoop.call(this, selectList, function(key, value, next) {
if (typeof value === "function") {
value.call(this.Dict).then(function(res) {
selectList[key] = res;
next();
});
} else {
next();
}
});
},
methods: {
// 最新
// calculateWidth(height, angleDegrees) {
// // 1. 将角度从度转换为弧度(JavaScript的Math.tan()使用弧度)
// const angleRadians = angleDegrees * Math.PI / 180;
// // 2. 计算角度的正切值
// const tanValue = Math.tan(angleRadians);
// // 3. 计算水平距离 L = H / tan(θ)
// const width = height / tanValue;
// return width;
// // 使用示例
// const height = 40; // 垂直高度
// const angle = 25; // 坡度角度(度)
// const result = calculateWidth(height, angle);
// console.log(`宽度 = ${result.toFixed(2)}`);
// // 输出:宽度 = 85.78
// },
/** 绘制文字标注 */
drawText(config) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复默认坐标系(左上角原点)
this.ctx.font = `10px Arial`; // 文字大小8px
this.ctx.fillStyle = "#000"; // 黄色
this.ctx.textAlign = "center"; // 水平居中
this.ctx.textBaseline = "top"; // 垂直顶部对齐
// 绘制文字(考虑Y轴方向转换)
const drawY = this.canvas.height - config.y; // 转换为默认坐标系Y轴
this.ctx.fillText(config.text, config.x, drawY + 5); // Y轴偏移5px避免重叠
this.ctx.restore();
},
/** 清空画布内容及配置 */
clearCanvas() {
if (this.ctx) {
// 清除画布绘制内容(基于当前坐标系)
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复默认坐标系
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // 清空整个画布
this.ctx.restore();
}
// 重置配置数据(避免残留)
this.canvasDataReal.poConfigs = [];
this.canvasDataReal.guanConfigs = [];
this.canvasDataReal.lineConfigs = [];
},
getCanvasData() {
const params = { ...this.form.query };
request({
url: "tab/jrx/dissect/visual-xinHua",
method: "get",
params
}).then(async res => {
var data = res.body;
if (!data.length) {
// 数据为空时清空画布
this.clearCanvas();
return;
}
this.waterValue = data[0].wtValue;
let data1 = [
{
code: "001",
diameter: 2,
drys: [],
equipmentDataList: [
{
blueAlarm: "6.5",
depth: "7.730",
holeDepth: "88.5",
jrxTrepanning: "8.55",
orangeAlarm: "5.5",
redAlarm: "5.0",
sensorid: "42022210030502",
sensorname: "浸润线 ZQ2",
smhb: "80.770",
stage: "0.820",
time: "2025-10-29 10:46:10",
unit: "m",
yellowAlarm: "6.0"
},
{
blueAlarm: "3.3",
depth: "4.770",
holeDepth: "84.5",
jrxTrepanning: "6.2",
orangeAlarm: "2.8",
redAlarm: "2.5",
sensorid: "42022210030501",
sensorname: "浸润线 ZQ1",
smhb: "79.730",
stage: "1.430",
time: "2025-10-29 10:45:10",
unit: "m",
yellowAlarm: "3.1"
}
],
height: null,
id: 1,
jrxStepsDtoList: [
{
code: 1,
height: null,
id: null,
name: "1",
slope: null,
width: 10
},
{
code: 2,
height: null,
id: null,
name: "2",
slope: null,
width: 10
}
],
name: "三号副坝",
pondid: "",
sdeg: 10,
type: "",
width: null,
wtValue: 84.5
},
{
code: "3",
diameter: 2,
drys: [],
equipmentDataList: [
{
blueAlarm: "3.3",
depth: "7.280",
holeDepth: "84.5",
jrxTrepanning: "7.36",
orangeAlarm: "2.8",
redAlarm: "2.5",
sensorid: "42022210030503",
sensorname: "浸润线 ZQ3",
smhb: "77.220",
stage: "0.080",
time: "2025-10-29 10:46:10",
unit: "m",
yellowAlarm: "3.1"
},
{
blueAlarm: "6.5",
depth: "8.060",
holeDepth: "88.5",
jrxTrepanning: "8.06",
orangeAlarm: "5.5",
redAlarm: "5.0",
sensorid: "42022210030504",
sensorname: "浸润线 ZQ4",
smhb: "80.440",
stage: "0.000",
time: "2025-10-29 10:46:10",
unit: "m",
yellowAlarm: "6.0"
}
],
height: null,
id: 2,
jrxStepsDtoList: [
{
code: 1,
height: null,
id: null,
name: "1",
slope: null,
width: 10
},
{
code: 2,
height: null,
id: null,
name: "2",
slope: null,
width: 10
}
],
name: "3",
pondid: "",
sdeg: 10,
type: "",
width: null,
wtValue: 84.5
},
{
code: "4",
diameter: 2,
drys: [],
equipmentDataList: [
{
blueAlarm: "6.5",
depth: "8.710",
holeDepth: "88.5",
jrxTrepanning: "9.0",
orangeAlarm: "5.5",
redAlarm: "5.0",
sensorid: "42022210030507",
sensorname: "浸润线 ZQ6",
smhb: "79.790",
stage: "0.290",
time: "2025-10-29 10:46:10",
unit: "m",
yellowAlarm: "6.0"
},
{
blueAlarm: "3.3",
depth: "6.980",
holeDepth: "84.5",
jrxTrepanning: "7.5",
orangeAlarm: "2.8",
redAlarm: "2.5",
sensorid: "42022210030505",
sensorname: "浸润线 ZQ5",
smhb: "77.520",
stage: "0.520",
time: "2025-10-29 10:45:10",
unit: "m",
yellowAlarm: "3.1"
}
],
height: null,
id: 4,
jrxStepsDtoList: [
{
code: 1,
height: null,
id: null,
name: "1",
slope: null,
width: 10
},
{
code: 2,
height: null,
id: null,
name: "2",
slope: null,
width: 10
}
],
name: "65",
pondid: "",
sdeg: 10,
type: "",
width: null,
wtValue: 84.5
},
{
code: "5",
diameter: 2,
drys: [],
equipmentDataList: [
{
blueAlarm: "6.5",
depth: "7.340",
holeDepth: "88.5",
jrxTrepanning: "8.0",
orangeAlarm: "5.5",
redAlarm: "5.0",
sensorid: "42022210030509",
sensorname: "浸润线 ZQ8",
smhb: "81.160",
stage: "0.660",
time: "2025-10-29 10:46:10",
unit: "m",
yellowAlarm: "6.0"
},
{
blueAlarm: "3.3",
depth: "6.330",
holeDepth: "84.5",
jrxTrepanning: "6.5",
orangeAlarm: "2.8",
redAlarm: "2.5",
sensorid: "42022210030508",
sensorname: "浸润线 ZQ7",
smhb: "78.170",
stage: "0.170",
time: "2025-10-29 10:46:10",
unit: "m",
yellowAlarm: "3.1"
}
],
height: null,
id: 5,
jrxStepsDtoList: [
{
code: 1,
height: null,
id: null,
name: "1",
slope: null,
width: 10
},
{
code: 2,
height: null,
id: null,
name: "2",
slope: null,
width: 10
}
],
name: "87",
pondid: "",
sdeg: 10,
type: "",
width: null,
wtValue: 84.5
},
{
code: "6",
diameter: 2,
drys: [],
equipmentDataList: [
{
blueAlarm: "3.3",
depth: "4.160",
holeDepth: "84.5",
jrxTrepanning: "6.5",
orangeAlarm: "2.8",
redAlarm: "2.5",
sensorid: "420222100305010",
sensorname: "浸润线 ZQ9",
smhb: "80.340",
stage: "2.340",
time: "2025-10-29 10:46:10",
unit: "m",
yellowAlarm: "3.1"
},
{
blueAlarm: "6.0",
depth: "6.900",
holeDepth: "88.5",
jrxTrepanning: "8.0",
orangeAlarm: "5.5",
redAlarm: "5.0",
sensorid: "420222100305011",
sensorname: "浸润线 ZQ10",
smhb: "81.600",
stage: "1.100",
time: "2025-10-29 10:46:10",
unit: "m",
yellowAlarm: "5.8"
}
],
height: null,
id: 6,
jrxStepsDtoList: [
{
code: 1,
height: null,
id: null,
name: "1",
slope: null,
width: 10
},
{
code: 2,
height: null,
id: null,
name: "2",
slope: null,
width: 10
}
],
name: "109",
pondid: "",
sdeg: 10,
type: "",
width: null,
wtValue: 84.5
},
{
code: "7",
diameter: 2,
drys: [],
equipmentDataList: [
{
blueAlarm: "6.5",
depth: "7.180",
holeDepth: "88.5",
jrxTrepanning: "8.0",
orangeAlarm: "5.5",
redAlarm: "5.0",
sensorid: "420222100305013",
sensorname: "浸润线 ZQ12",
smhb: "81.320",
stage: "0.820",
time: "2025-10-29 10:46:10",
unit: "m",
yellowAlarm: "6.0"
},
{
blueAlarm: "3.3",
depth: "4.330",
holeDepth: "84.5",
jrxTrepanning: "7.2",
orangeAlarm: "2.8",
redAlarm: "2.5",
sensorid: "420222100305012",
sensorname: "浸润线 ZQ11",
smhb: "80.170",
stage: "2.870",
time: "2025-10-29 10:46:10",
unit: "m",
yellowAlarm: "3.1"
}
],
height: null,
id: 7,
jrxStepsDtoList: [
{
code: 1,
height: null,
id: null,
name: "1",
slope: null,
width: 10
},
{
code: 2,
height: null,
id: null,
name: "2",
slope: null,
width: 10
}
],
name: "1211",
pondid: "",
sdeg: 10,
type: "",
width: null,
wtValue: 84.5
}
];
await this.handleCanvasData(data);
this.initCanvas();
});
},
handleCanvasDataNew(list = []) {
return new Promise(resolve => {
// 新增Promise封装
let canvasWidth = 1000; // 默认宽度
let canvasHeight = 600; // 默认高度
// 更新Canvas元素属性和样式
const canvasEl = this.$refs.canvas;
if (canvasEl) {
// 实际绘图区域尺寸(关键属性)
canvasEl.width = canvasWidth;
canvasEl.height = canvasHeight;
// 显示尺寸(与绘图区域保持一致)
canvasEl.style.width = `${canvasWidth}px`;
canvasEl.style.height = `${canvasHeight}px`;
}
// 数据清洗
const poumian = list[0]; // 坡面数据
const poCount = poumian.jrxStepsDtoList.length;
const lineCount = 5;
const po_pad_right = 180; // poCount > 4 ? 130 : 180; // 第一个台阶距离画布的距离
const poH = 150;
const line_color = ["red", "orange", "yellow", "blue", "white"];
//第一层台阶的开孔深度和设备图片相除的系数
const sbHeightModulus =
poumian.equipmentDataList[0].jrxTrepanning / 130;
// 存储设备数据列表(关键新增,用于后续获取depth值)
this.equipmentDataList = poumian.equipmentDataList || [];
// 坡面基本信息获取
for (let index = 0; index < poCount; index++) {
this.poConfigsWH.push({
xieMianW: 10 + index, // 第一个坡斜面距离第一个坡平面的水平距离。
pingMianW: 50 + index, // 第一个坡平面距离平面宽度。
pingMianH: 20 + index, // 第一个坡平面距离平面高度。
kongW: 10 + index, // 目前设定打孔的位置在坡平面上距离左侧起始点的距离
kongH: 20 + index, // 目前孔的深度
jrxTrepanning: 10 + index // 浸润线开口深度
});
}
// 破平面xy计算
this.poConfigsXY = [{ x: 0, y: 0 }];
this.poConfigsWH.forEach(item => {
this.poConfigsXY.push({
// 斜坡点
x: this.poConfigsXY[this.poConfigsXY.length - 1].x + item.xieMianW, // 每个斜平坡计算点都根据上一个前面已有的最后一个坐标点进行添加 第一个为x0y0
y: this.poConfigsXY[this.poConfigsXY.length - 1].y + item.pingMianH
});
this.poConfigsXY.push({
// 平坡点
x: this.poConfigsXY[this.poConfigsXY.length - 1].x + item.pingMianW,
y: this.poConfigsXY[this.poConfigsXY.length - 1].y
});
// 孔的xy计算
this.guanConfigsXY.push({
// 管的起始点 -2是获取该斜坡点的结束坐标加上孔距离位置=孔在平坡上面的位置
x: this.poConfigsXY[this.poConfigsXY.length - 2].x + item.kongW,
y: this.poConfigsXY[this.poConfigsXY.length - 2].y + 0 // 这里的0为了突出孔的仪器设备 突出坡平面距离。这里先放置0(方便后面浸润线水位的计算),等后面实际画管线的时候临时加上固定值。
});
this.guanConfigsXY.push({
// 管的结束点
x: this.poConfigsXY[this.poConfigsXY.length - 2].x + item.kongW,
y: this.poConfigsXY[this.poConfigsXY.length - 2].y - item.kongH,
maishen: item.depth // 水位到管口的垂直距离
});
});
this.gantanConfigsXY = [
// 干滩的xy坐标点
{
x: this.poConfigsXY[this.poConfigsXY.length - 1].x,
y: this.poConfigsXY[this.poConfigsXY.length - 1].y
}, // 干滩的起始点
{
x:
this.poConfigsXY[this.poConfigsXY.length - 1].x +
this.otherConfig.gantanW,
y:
this.poConfigsXY[this.poConfigsXY.length - 1].y -
this.otherConfig.shuiH
} // 干滩的结束点 (最高平坡减去水位高度)
];
this.guanConfigsXY.forEach((item, index) => {
if (index % 2 == 0) {
// 偶数为管的起始点
this.jrxConfigsXY.push(
{ x: item.x, y: item.y - item.maishen } // 浸润线的坐标点是平坡y-埋深的值(水位到平坡的垂直距离)
);
}
});
this.shuiweiConfigsXY = [
// 水位的xy坐标点(画矩形)
{ x: this.gantanConfigsXY[1].x, y: this.gantanConfigsXY[1].y }, // 水位的起始点
{
x: this.gantanConfigsXY[1].x + this.otherConfig.tuduiW,
y: this.gantanConfigsXY[1].y
} // 水位的结束点 (最高平坡减去水位高度)
];
this.tuduiConfigsXY = [
{ x: this.gantanConfigsXY[1].x, y: this.gantanConfigsXY[1].y }, // 水位的起始点
{ x: this.gantanConfigsXY[1].x + this.otherConfig.tuduiW, y: 0 } // 水位的结束点
];
this.jingjiexianConfigsXY = [
{ x: this.gantanConfigsXY[1].x, y: this.gantanConfigsXY[1].y }, // 水位的起始点
{ x: this.gantanConfigsXY[1].x + this.otherConfig.tuduiW, y: 0 } // 水位的结束点
];
// 警戒线逻辑- 初始话基本数据结构
for (let index1 = 0; index1 < lineCount; index1++) {
this.jingjiexianConfigsXY.push({
// 设置
points: [],
color: line_color[index1],
lineWidth: 2,
cha: 4 * index1 // 每条线之间的差值
});
}
// 警戒线种类下,根据管孔设备个数,设定对应的坐标点point
// this.jingjiexianConfigsXY.forEach((line, index) => {
// let lineValKey = line.color == "white" ? "depth" : line.color + "Alarm";
// // 几个管孔几个坐标点
// poumian.equipmentDataList.forEach((equip, subindex) => {
// line.points.push({
// x: this.canvasDataReal.guanConfigs[subindex].x + 1,
// y:
// this.canvasDataReal.guanConfigs[subindex].y +
// equip[lineValKey], // 管设备的y坐标+设备的警戒值*系数
// });
// });
// });
this.canvasDataReal.lineConfigs.forEach((line, index) => {
let lineValKey =
line.color == "white" ? "depth" : line.color + "Alarm";
// 几个管孔几个坐标点
poumian.equipmentDataList.forEach((equip, subindex) => {
line.points.push({
x: this.canvasDataReal.guanConfigs[subindex].x + 1,
y:
this.canvasDataReal.guanConfigs[subindex].y +
equip[lineValKey] / sbHeightModulus
});
});
});
// 4. 绘制连接线(遍历配置数组)
this.canvasDataReal.lineConfigs.forEach((config, index) => {
this.drawLine(config);
});
// 5. 绘制白色警戒线所有点的文字标注(修改部分)
console.log(this.canvasDataReal.lineConfig, "lineconfig");
this.canvasDataReal.lineConfigs.forEach(lineConfig => {
if (lineConfig.color === "white" && lineConfig.points.length > 0) {
// 添加可选链校验
lineConfig.points.forEach((point, subindex) => {
// 遍历所有坐标点
// 获取对应设备的深度值(通过索引对应)
const equipment = this.equipmentDataList[subindex];
const depth = equipment.depth || ""; // 添加空值校验
// 绘制当前点的文字
this.drawText({
x: point.x,
y: this.canvas.height - point.y,
text: `浸润线埋深:${depth}m`
});
});
}
});
resolve(); // 数据处理完成后触发resolve
});
},
handleCanvasData(list = []) {
return new Promise(resolve => {
// 新增Promise封装
let canvasWidth = 1000; // 默认宽度
let canvasHeight = 600; // 默认高度
// 更新Canvas元素属性和样式
const canvasEl = this.$refs.canvas;
if (canvasEl) {
// 实际绘图区域尺寸(关键属性)
canvasEl.width = canvasWidth;
canvasEl.height = canvasHeight;
// 显示尺寸(与绘图区域保持一致)
canvasEl.style.width = `${canvasWidth}px`;
canvasEl.style.height = `${canvasHeight}px`;
}
// 数据清洗
const poumian = list[0]; // 坡面数据
const poCount = poumian.jrxStepsDtoList.length;
const lineCount = 5;
const po_pad_right = -140; // poCount > 4 ? 130 : 180; // 第一个台阶距离画布的距离
const poH = 450;
const line_color = ["red", "orange", "yellow", "blue", "white"];
this.canvasDataReal.poConfigs = [];
this.canvasDataReal.guanConfigs = [];
this.canvasDataReal.lineConfigs = [];
//第一层台阶的开孔深度和设备图片相除的系数
const sbHeightModulus =
poumian.equipmentDataList[0].jrxTrepanning / 130;
// 存储设备数据列表(关键新增,用于后续获取depth值)
this.equipmentDataList = poumian.equipmentDataList || [];
// 坡数据数据格式处理
poumian.jrxStepsDtoList.forEach((po, poIndex) => {
this.canvasDataReal.poConfigs.push({
x: -140,
y: canvasHeight - poIndex * poH,
width: canvasWidth - poIndex * po_pad_right,
height: poH
});
});
// 管孔数据数据格式处理
poumian.equipmentDataList.forEach((equipment, equipIndex) => {
const po = this.canvasDataReal.poConfigs[equipIndex];
// 管孔数据数据格式处理
if (equipIndex == 1) {
this.canvasDataReal.guanConfigs.push({
x: po.width * 0.234 + 100 * (equipIndex + 1),
y: canvasHeight - po.height + 133,
width: 7, // 可以设置不同的宽度
height: (equipment.jrxTrepanning / sbHeightModulus) * 1.3,
image: "guanImage2" // 使用不同的图片
});
} else {
this.canvasDataReal.guanConfigs.push({
x: po.width * 0.234 + 100 * (equipIndex + 1),
y: canvasHeight - po.height + 208,
width: 7,
height: equipment.jrxTrepanning / sbHeightModulus,
image: "guanImage"
});
}
});
// 警戒线逻辑- 初始话基本数据结构
for (let index1 = 0; index1 < lineCount; index1++) {
this.canvasDataReal.lineConfigs.push({
// 设置
points: [],
color: line_color[index1],
lineWidth: 2,
cha: 4 * index1
});
}
// 警戒线种类下,根据管孔设备个数,设定对应的坐标点point
this.canvasDataReal.lineConfigs.forEach((line, index) => {
let lineValKey =
line.color == "white" ? "depth" : line.color + "Alarm";
// 几个管孔几个坐标点
poumian.equipmentDataList.forEach((equip, subindex) => {
let xOffset = 0;
let baseY = 0;
// 对于彩色线(非白色),使用统一的计算方式,确保线条是直线
if (line.color !== "white") {
// 对所有彩色线使用相同的计算逻辑,不使用不同的分母
xOffset = line.cha / 20; // 使用统一的分母值
// 直接根据设备数据计算y坐标
baseY =
this.canvasDataReal.guanConfigs[subindex].y +
equip[lineValKey] / sbHeightModulus;
} else {
baseY =
this.canvasDataReal.guanConfigs[subindex].y +
this.canvasDataReal.guanConfigs[subindex].height;
}
console.log(equip, "equip");
line.points.push({
x: this.canvasDataReal.guanConfigs[subindex].x + 1 + xOffset,
y: baseY,
depth: equip["depth"]
});
});
});
resolve(); // 数据处理完成后触发resolve
});
},
/** 初始化 Canvas 画布及坐标系 */
initCanvas() {
// 获取 Canvas 元素和上下文
this.canvas = this.$refs.canvas; // document.getElementById('mainCanvas')
if (!this.canvas) {
console.error("Canvas 元素未找到");
return;
}
this.ctx = this.canvas.getContext("2d");
// 坐标系变换:将原点移至左下角(默认原点在左上角)
this.ctx.translate(0, this.canvas.height); // 向下移动画布高度
this.ctx.scale(1, -1); // 翻转 Y 轴方向(上为正)
// // 加载背景图并绘制
// this.loadBackgroundImage();
// 加载所有图片并绘制
this.loadAllImages();
},
// 绘制water.png(独立方法)
drawWaterImage() {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.drawImage(
this.waterImage,
500,
this.canvas.height - (180 + this.waterValue * 1.2),
550,
550
);
this.ctx.restore();
},
// 绘制单个po.png(支持配置参数)
drawPoImage(config) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
// 动态计算y坐标(若未指定则默认画布底部-高度)
this.ctx.drawImage(
this.poImage,
config.x,
config.y,
config.width,
config.height
);
this.ctx.restore();
},
// 绘制单个guan.png(支持不同图片类型)
drawGuanImage(config) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
const targetImage = this[config.image]; // 根据配置获取具体图片对象(如guanImage/guanImage1)
this.ctx.drawImage(
targetImage,
config.x,
config.y,
config.width,
config.height
);
this.ctx.restore();
},
drawSecondPipeImage(config) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
// 可以使用不同的绘制逻辑
const targetImage = this[config.image] || this.guanImage;
this.ctx.drawImage(
targetImage,
config.x,
config.y,
config.width,
config.height
);
this.ctx.restore();
},
// 绘制连接线(支持多坐标点)白色
drawLine(config) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.strokeStyle = config.color;
this.ctx.lineWidth = config.lineWidth;
this.ctx.beginPath();
// 修复语法错误并改进白色线对齐逻辑
if (
config.color === "white" &&
this.canvasDataReal.guanConfigs.length > 0
) {
// 确保至少有两个点可以连接
if (config.points && config.points.length >= 2) {
config.points.forEach((point, index) => {
index === 0
? this.ctx.moveTo(point.x, point.y)
: this.ctx.lineTo(point.x, point.y);
});
}
} else {
config.points.forEach((point, index) => {
// index === 0 ? this.ctx.moveTo(point.x - 400, point.y + 220) : this.ctx.lineTo(point.x, point.y);
// index === 0 ? this.ctx.moveTo(point.x - 400, point.y + 190) : this.ctx.lineTo(point.x, point.y);
});
}
this.ctx.stroke();
this.ctx.restore();
},
// 绘制背景图(独立方法)
drawBackground() {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.drawImage(
this.bgImage,
0,
0,
this.canvas.width,
this.canvas.height
);
this.ctx.restore();
},
// 绘制文字 箭头
createCustomLineWithText(config) {
// 配置参数默认值
const defaultConfig = {
points: [],
color: "#000000",
lineWidth: 2,
showLine: true,
text: "",
textPosition: "middle",
textDirection: "horizontal",
textAngle: 0,
textOffset: 10,
textColor: "#000000",
fontSize: 10,
showArrow: false,
showArrowAtStart: false,
arrowSize: 8
};
// 合并配置
const finalConfig = { ...defaultConfig, ...config };
if (!this.ctx || !finalConfig.points || finalConfig.points.length < 1) {
console.error("Canvas上下文或坐标点不足");
return;
}
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复默认坐标系
// 只有当showLine为true且lineWidth大于0时才绘制线条
if (
finalConfig.showLine &&
finalConfig.lineWidth > 0 &&
finalConfig.points.length >= 2
) {
this.ctx.strokeStyle = finalConfig.color;
this.ctx.lineWidth = finalConfig.lineWidth;
this.ctx.beginPath();
finalConfig.points.forEach((point, index) => {
if (index == 0) {
this.ctx.moveTo(point.x, point.y);
} else {
this.ctx.lineTo(point.x, point.y);
}
});
this.ctx.stroke();
// 新增:绘制箭头
if (finalConfig.showArrow && finalConfig.points.length >= 2) {
const points = finalConfig.points;
const lastPoint = points[points.length - 1];
const prevPoint = points[points.length - 2];
// 计算箭头方向
const angle = Math.atan2(
lastPoint.y - prevPoint.y,
lastPoint.x - prevPoint.x
);
// 设置箭头样式
this.ctx.fillStyle = finalConfig.color;
// 绘制箭头三角形
this.ctx.beginPath();
this.ctx.moveTo(lastPoint.x, lastPoint.y);
this.ctx.lineTo(
lastPoint.x - finalConfig.arrowSize * Math.cos(angle - Math.PI / 6),
lastPoint.y - finalConfig.arrowSize * Math.sin(angle - Math.PI / 6)
);
this.ctx.lineTo(
lastPoint.x - finalConfig.arrowSize * Math.cos(angle + Math.PI / 6),
lastPoint.y - finalConfig.arrowSize * Math.sin(angle + Math.PI / 6)
);
this.ctx.closePath();
this.ctx.fill();
}
}
if (finalConfig.showArrowAtStart && finalConfig.points.length >= 2) {
const points = finalConfig.points;
const firstPoint = points[0];
const secondPoint = points[1];
// 计算起点箭头方向(与线段方向相反)
const angle =
Math.atan2(
secondPoint.y - firstPoint.y,
secondPoint.x - firstPoint.x
) + Math.PI;
// 设置箭头样式
this.ctx.fillStyle = finalConfig.color;
// 绘制起点箭头三角形
this.ctx.beginPath();
this.ctx.moveTo(firstPoint.x, firstPoint.y);
this.ctx.lineTo(
firstPoint.x - finalConfig.arrowSize * Math.cos(angle - Math.PI / 6),
firstPoint.y - finalConfig.arrowSize * Math.sin(angle - Math.PI / 6)
);
this.ctx.lineTo(
firstPoint.x - finalConfig.arrowSize * Math.cos(angle + Math.PI / 6),
firstPoint.y - finalConfig.arrowSize * Math.sin(angle + Math.PI / 6)
);
this.ctx.closePath();
this.ctx.fill();
}
// 绘制文字
if (finalConfig.text) {
this.ctx.font = `${finalConfig.fontSize}px Arial`;
this.ctx.fillStyle = finalConfig.textColor;
// 计算文字位置
let textX, textY;
const { points } = finalConfig;
switch (finalConfig.textPosition) {
case "start":
textX = points[0].x;
textY = points[0].y;
break;
case "end":
textX = points[points.length - 1].x;
textY = points[points.length - 1].y;
break;
default:
if (points.length >= 2) {
const midIndex = Math.floor(points.length / 2);
textX = (points[midIndex - 1].x + points[midIndex].x) / 2;
textY = (points[midIndex - 1].y + points[midIndex].y) / 2;
} else {
textX = points[0].x;
textY = points[0].y;
}
break;
}
// 根据方向调整文字
switch (finalConfig.textDirection) {
case "vertical":
this.ctx.save();
this.ctx.translate(textX, textY);
this.ctx.rotate(-Math.PI / 2); // 旋转90度
this.ctx.textAlign = "center";
this.ctx.textBaseline = "middle";
this.ctx.fillText(finalConfig.text, 0, -finalConfig.textOffset);
this.ctx.restore();
break;
case "angle":
const angleRad = (finalConfig.textAngle * Math.PI) / 180;
this.ctx.save();
this.ctx.translate(textX, textY);
this.ctx.rotate(angleRad);
this.ctx.textAlign = "center";
this.ctx.textBaseline = "middle";
this.ctx.fillText(finalConfig.text, 0, -finalConfig.textOffset);
this.ctx.restore();
break;
default:
// horizontal
this.ctx.textAlign = "center";
this.ctx.textBaseline = "bottom";
this.ctx.fillText(
finalConfig.text,
textX,
textY - finalConfig.textOffset
);
break;
}
}
this.ctx.restore();
},
// 重构后的统一绘制入口(通过配置驱动)
drawAllImages() {
// 1. 绘制背景图
this.drawBackground();
// 绘制water.png
this.drawWaterImage();
// 2. 绘制po.png(遍历配置数组)
this.canvasDataReal.poConfigs.forEach((config, index) => {
this.drawPoImage(config);
});
// 3. 绘制guan.png(遍历配置数组)
this.canvasDataReal.guanConfigs.forEach((config, index) => {
if (index == 1) {
// 执行针对第二个管子的特殊绘制方法
this.drawSecondPipeImage(config);
} else {
// 正常绘制其他管子
this.drawGuanImage(config);
}
});
// 修改 drawAllImages 方法中的蓝色阴影绘制部分
this.canvasDataReal.lineConfigs.forEach(lineConfig => {
if (lineConfig.color === "white" && lineConfig.points.length >= 2) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复默认坐标系
// 直接使用白色线的原始坐标点,确保完全对齐
const whiteLinePoints = lineConfig.points;
const canvasHeight = this.canvas.height;
// 遍历相邻点对,生成梯形阴影
for (let i = 0; i < whiteLinePoints.length - 1; i++) {
const start = whiteLinePoints[i];
const end = whiteLinePoints[i + 1];
// 构建梯形四个顶点(确保与白色线完全重合)
const trapezoid = [
{ x: start.x, y: start.y }, // 顶部左端点(与白色线起点完全对齐)
{ x: end.x, y: end.y }, // 顶部右端点(与白色线终点完全对齐)
{ x: end.x, y: canvasHeight }, // 底部右端点
{ x: start.x, y: canvasHeight } // 底部左端点
];
// 绘制蓝色阴影
this.ctx.fillStyle = "rgba(59, 175, 251, 0.3)"; // 海蓝色,透明度30%
this.ctx.beginPath();
trapezoid.forEach((point, index) => {
index === 0
? this.ctx.moveTo(point.x, point.y)
: this.ctx.lineTo(point.x, point.y);
});
this.ctx.closePath();
this.ctx.fill();
}
this.ctx.restore();
}
});
// 确保在阴影绘制之后再绘制白色线条
this.canvasDataReal.lineConfigs.forEach(config => {
this.drawLine(config);
});
const colorsToConnect = ["red", "orange", "yellow", "blue"];
// 为每种颜色定义对应的目标位置坐标
const targetPoints = {
red: { x: 550, y: 304 },
orange: { x: 560, y: 307 },
yellow: { x: 570, y: 310 },
blue: { x: 580, y: 313 }
};
// 第二条 ....
colorsToConnect.forEach(color => {
// 找到对应颜色的线配置
const lineConfig = this.canvasDataReal.lineConfigs.find(
config => config.color === color
);
if (lineConfig && lineConfig.points.length > 0) {
const lastPoint = lineConfig.points[lineConfig.points.length - 1];
// 获取该颜色对应的目标位置
const targetPoint = targetPoints[color];
// 绘制新的连接线,保持与原线相同的颜色和线宽
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.strokeStyle = color;
this.ctx.lineWidth = lineConfig.lineWidth || 2;
this.ctx.beginPath();
this.ctx.moveTo(lastPoint.x - 500, lastPoint.y + 300);
this.ctx.lineTo(targetPoint.x, targetPoint.y);
this.ctx.stroke();
this.ctx.restore();
}
});
// 5. 绘制白色警戒线所有点的文字标注(修改部分)
this.canvasDataReal.lineConfigs.forEach(lineConfig => {
if (lineConfig.color === "white" && lineConfig.points.length > 0) {
// 1. 收集所有设备信息,包括设备名称中的数字
const deviceData = [];
lineConfig.points.forEach((point, subindex) => {
const equipment = this.equipmentDataList[subindex];
if (equipment) {
// 提取设备名称中的数字
const deviceNumber = equipment.sensorname
? parseInt(equipment.sensorname.match(/\d+/)[0] || "0")
: 0;
deviceData.push({
point,
equipment,
subindex,
deviceNumber,
isOdd: deviceNumber % 2 === 1
});
}
});
// 2. 创建一个新的深度值数组,用于存放交换后的值
const newDepths = new Array(deviceData.length).fill("");
console.log(deviceData, "deviceData");
// 3. 实现相邻管子埋深值互换,根据设备名称中的数字奇偶性决定
for (let i = 0; i < deviceData.length - 1; i += 2) {
// 检查当前两个设备
const device1 = deviceData[i];
const device2 = deviceData[i + 1];
if (device1 && device2) {
// 如果第一个设备的编号是单数,则第一个管子的埋深值显示在第二个管子下面
if (device1.isOdd) {
newDepths[device1.subindex] = device1.equipment.depth || "";
newDepths[device2.subindex] = device2.equipment.depth || "";
} else if (!device2.isOdd) {
newDepths[device1.subindex] = device2.equipment.depth || "";
newDepths[device2.subindex] = device1.equipment.depth || "";
}
// else {
// // 针对第一个设备编号不是奇数,且第二个设备编号是奇数,导致前面两个判断漏掉的问题
// newDepths[i] = deviceData[i].equipment.depth || "";
// }
// newDepths[i] = deviceData[i].equipment.depth || "";
}
}
console.log(newDepths, "newDepths");
// 处理可能的奇数个设备的最后一个设备
if (deviceData.length % 2 === 1) {
const lastDevice = deviceData[deviceData.length - 1];
newDepths[lastDevice.subindex] = lastDevice.equipment.depth || "";
}
// 4. 绘制文本,使用交换后的深度值
deviceData.forEach(device => {
console.log(
"newDepths[device.subindex",
newDepths,
device.subindex,
newDepths[device.subindex]
);
this.drawText({
x: device.point.x,
y: this.canvas.height - device.point.y,
text: `浸润线埋深:${newDepths[device.subindex]}m`
});
});
}
});
// 浸润线
this.createCustomLineWithText({
points: [{ x: 350, y: 300 }, { x: 400, y: 370 }],
color: "#000",
lineWidth: 1,
text: "浸润线",
textPosition: "start", // 文字位置:start、middle、end
textDirection: "horizontal", // 文字方向:horizontal、vertical、angle
textOffset: 9,
textColor: "#000",
fontSize: 14,
showArrow: true,
arrowSize: 8
});
// 正常高水位
this.createCustomLineWithText({
points: [{ x: 585, y: 285 }, { x: 585, y: 370 }],
color: "#000",
lineWidth: 1,
text: "正常高水位",
textPosition: "end",
textDirection: "horizontal",
textOffset: -20,
textColor: "#000",
fontSize: 14
});
// 平滩
this.createCustomLineWithText({
points: [{ x: 587, y: 285 }, { x: 520, y: 285 }],
color: "#000",
lineWidth: 1,
text: "干滩",
textPosition: "middle",
textDirection: "horizontal",
textOffset: 20,
textColor: "orange",
fontSize: 15,
showArrow: true,
showArrowAtStart: true,
arrowSize: 7
});
// 大坝库
this.createCustomLineWithText({
points: [{ x: 750, y: 270 }, { x: 700, y: 355 }],
color: "#000",
lineWidth: 1,
text: "大坝库",
textPosition: "start",
textDirection: "horizontal",
textOffset: 10,
textColor: "#2c4f7c",
fontSize: 15
});
// 传感器
this.createCustomLineWithText({
points: [{ x: 300, y: 480 }, { x: 400, y: 370 }],
text: "渗压传感器",
showLine: false,
textPosition: "start",
textDirection: "horizontal",
textOffset: 15,
textColor: "#000",
fontSize: 11
});
this.createCustomLineWithText({
points: [{ x: 505, y: 400 }, { x: 400, y: 370 }],
text: "渗压传感器",
showLine: false,
textPosition: "start",
textDirection: "horizontal",
textOffset: 15,
textColor: "#000",
fontSize: 11
});
},
/** 加载所有图片并绘制 */
loadAllImages() {
// 加载背景图(原有)
this.bgImage = new Image();
this.bgImage.src = require("@/assets/images/jrx/bg.png");
// // 加载 water.png
this.waterImage = new Image();
this.waterImage.src = require("@/assets/images/jrx/shui.png");
// 加载 shan.png
this.poImage = new Image();
this.poImage.src = require("@/assets/images/jrx/shan.png");
// 加载 guan.png
this.guanImage = new Image();
this.guanImage.src = require("@/assets/images/jrx/guan0.png");
// 等待所有图片加载完成
Promise.all([
new Promise(resolve => (this.bgImage.onload = resolve)),
new Promise(resolve => (this.waterImage.onload = resolve)),
new Promise(resolve => (this.poImage.onload = resolve)),
new Promise(resolve => (this.guanImage.onload = resolve))
])
.then(() => {
// 所有图片加载完成后绘制
this.drawAllImages();
})
.catch(() => {
console.error("部分图片加载失败");
});
},
/** 初始化图表(关键修改:调整图例位置) */
initChart1() {
let params = {
sort: "date,desc"
};
if (this.form.query.daterange) {
params.daterange = this.form.query.daterange.join(",");
}
if (this.form.query.sensorid) {
params.code = this.form.query.sensorid;
}
request({
url: "data/st/imghistory",
method: "get",
params
}).then(res => {
const data = res.body;
// const data = this.getChartData().body
// console.log("data", data);
const chartData = this.seriesDataFormat(data, { datekey: "date" });
var warningLine = undefined; // this.form.config.warningLine;
var option = {
valEnabled: true,
opacity: 1,
lineWidth: 3
};
var alarmData = this.discernValidAlarmValue(
data.alarm,
data.lists,
chartData.series,
warningLine,
option
);
// const chartData = data
// console.log("chartCData", chartData);
Highcharts.setOptions({
global: {
useUTC: false
},
lang: {
resetZoom: "重置缩放比例"
}
});
this.chartInstance = new Highcharts.chart(this.$refs.chartContainer, {
chart: {
//type: '',
backgroundColor: "transparent",
//marginTop:30,
//marginBottom:30,
//marginLeft:30,
zoomType: "x" // xy
},
valEnabled: true,
opacity: 1,
lineWidth: 3,
title: {
text: ""
},
subtitle: {
text: ""
},
tooltip: {
enabled: false,
borderWidth: 10
},
xAxis: {
type: "datetime",
// 最为关键的代码:如果为空数组,则导致日期格式化失效!!
categories: chartData.categories[0] && chartData.categories,
lineWidth: 0,
//lineColor:'#ff0000',
gridLineColor: "#aaa",
dateTimeLabelFormats: {
millisecond: "%H:%M:%S.%L",
second: "%H:%M:%S",
minute: "%H:%M",
hour: "%H:%M",
day: "%m-%d",
week: "%m-%d",
month: "%Y-%m",
year: "%Y"
}
},
yAxis: {
title: {
text: ""
},
labels: {
x: -6
},
gridLineColor: "#aaa",
max: null
},
plotOptions: {
column: {
borderWidth: 0
//y:50,
//itemMarginTop:50,
},
bar: {
borderWidth: 0
}
},
tooltip: {
// {point.y:.4f} // 保留4位小数
//headerFormat: '<span style="font-size:10px">{point.key}</span><table>',
pointFormat:
'<tr><td style="color:{series.color};padding:0">{series.name}:</td>' +
'<td style="padding:0"><b>{point.y}' +
data.danwei +
"</b> </td></tr>",
footerFormat: "</table>",
shared: true,
useHTML: true,
dateTimeLabelFormats: {
millisecond: "%H:%M:%S.%L",
second: "%H:%M:%S",
minute: "%H:%M",
hour: "%H:%M",
day: "%m-%d %H时",
week: "%m-%d",
month: "%Y-%m",
year: "%Y"
}
},
legend: {
enabled: chartData.series.length > 1 ? true : false,
// 图例定位
layout: "horizontal", // 水平布局:“horizontal”, 垂直布局:“vertical”
floating: false, // 图列是否浮动
align: "right",
// 图例容器
//width:'100%', // number || String
padding: 2, // 内边距
margin: 2,
borderRadius: 5,
//borderWidth:1,
verticalAlign: "top",
// 图例项
//itemWidth:120, // 宽度
itemDistance: 10, // 间距 20
y: -10,
itemMarginTop: 2,
itemStyle: {},
itemHoverStyle: {}
},
credits: {
enabled: false
},
series: chartData.series
});
});
},
seriesDataFormat(data, opts) {
var names = data.names || [],
list = data.list || data.lists,
series = [],
categories = [],
maxVal = 0;
if (names[0] && list) {
var colors = [
"#7CB5EC",
"#90ED7D",
"#F7A35C",
"#8085E9",
"#F15C80",
"#E4D354",
"#2B908F",
"#F45B5B",
"#91E8E1",
"#0769CB",
"#00ABBD",
"#ffd886",
"#9F2E61",
"#4D670C"
];
var len = names.length;
for (var i = 0; i < len; i++) {
var item = names[i];
var serie = {
name: item.name,
data: [],
key: item.key,
type: data.chartType,
color: colors[i]
};
opts && serie.type && (serie.type = opts.type);
series.push(serie);
}
for (var item of list) {
var values = item.values;
//var datetime = item[opts.datekey || 'dateUnit'];
//var time = datetime.indexOf(' ') > 0 ? datetime.split(' ')[1] : datetime;
var timestamp = item["date"];
//categories.push(timestamp);
for (var serie of series) {
var value = values[serie.key] * 1;
Math.abs(value) > maxVal && (maxVal = Math.abs(value));
serie.data.push([timestamp, value]);
}
}
}
return { series: series, categories: categories, maxVal: maxVal };
},
discernValidAlarmValue(alarms, list, series, direction, opts) {
var xLength = (list || []).length,
maxAlarm = 0,
opts = opts || {};
if (alarms && xLength) {
var value = alarms.value;
// discern
var levelDist = [
{ color: "red", name: "红色报警线" },
{ color: "orange", name: "橙色报警线" },
{ color: "yellow", name: "黄色报警线" },
{ color: "blue", name: "蓝色报警线" }
],
alarmLine = {},
setAlarmSerie = function(value, name, color) {
// 修改蓝色色值
color === "blue" && (color = "#3BAFFB");
var sx = list[0].date,
ex = list[xLength - 1].date;
var serie = {
name: name,
type: "spline",
data: [{ x: sx, y: value }, { x: ex, y: value }],
color: color,
enableMouseTracking: false,
legend: false,
showInLegend: false,
dashStyle: "ShortDot",
lineWidth: opts.lineWidth || 1,
states: {
inactive: {
opacity: opts.opacity
}
},
dataLabels: {
enabled: opts.valEnabled || false, // 数据值, 2022/11/11(周五) 因多条报警线暂时关闭
//backgroundColor:'red',
verticalAlign: "middle",
padding: 0,
defer: false,
allowOverlap: true,
color: color,
style: {
textOutline: "none"
}
},
tooltip: {
//footerFormat:'',
//pointFormat:'',
headerFormat: ""
//nullFormat:'',
},
marker: {
enabled: false
},
zIndex: -10
};
series.push(serie);
};
/* 智能报警线
var level = (~~alarms.alarmLevel || levelDist.length+1) - 1;
var nearLevel = level ? level - 1 : level;
var alarm = levelDist[level];
var nearAlarm = levelDist[nearLevel];
nearAlarm.value = value[nearAlarm.color];
alarmLine[nearAlarm.color] = nearAlarm;
if(alarm){
alarm.value = value[alarm.color];
alarmLine[alarm.color] = alarm;
}; */
// 多条报警线
for (var item of levelDist) {
item.value = value[item.color];
alarmLine[item.color] = item;
}
// add
for (var key in alarmLine) {
var line = alarmLine[key];
line.value > maxAlarm && (maxAlarm = line.value);
setAlarmSerie(line.value, line.name, line.color);
direction && setAlarmSerie(0 - line.value, line.name, line.color);
}
}
return { series, maxAlarm };
},
/** 切换图表类型 */
switchChartType(type) {
this.currentChartType = type;
if (this.chartInstance) {
this.chartInstance.series.forEach(series => {
series.update({ type: this.currentChartType });
});
}
},
loadData: reqApi.common.getRequst,
cancelForm() {
this.form.visible = false;
},
submitForm(form, item) {
reqApi.common.submitForm.call(this, form, item);
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scope>
.qyzz {
}
</style>
<template>
<div class="common-page qyzz page-t1">
<div class="option page-row">
<table-filter
ref="filter"
:form="form"
:Dict="Dict"
:rules="rules"
:config="form.config"
:loadData="loadData"
/>
</div>
<div class="new-container" style="display: flex">
<div class="canvas-wrapper" style="width: 1000px">
<!-- 替换为 Canvas 元素 -->
<canvas
ref="canvas"
id="mainCanvas"
width="1000"
style="background: #f0f0f0"
></canvas>
</div>
<div class="chart-wrapper" style="padding-left: 0px">
<div
ref="chartContainer"
style="
width: 850px;
height: 550px;
margin-bottom: 20px;
margin-left: 10px;
border: 1px solid gainsboro;
"
></div>
<div class="chart-btn-group" style="margin-left: 10px;">
<button
size="small"
:type="currentChartType === 'line' ? 'primary' : ''"
@click="switchChartType('line')"
>
折线图
</button>
<button
size="small"
:type="currentChartType === 'column' ? 'primary' : ''"
@click="switchChartType('column')"
>
柱状图
</button>
</div>
</div>
</div>
<div class="panel-bottom page-row">
<div class="ctin-box">
<div class="content-within">
<div class="content-fix">
<div class="toolbar">
<div>
<el-button
v-for="(item, key) in form.config.otherBtn"
size="mini"
:type="item.type"
:icon="item.icon"
@click="item.callback.call(_self, $event)"
>{{ item.name }}</el-button
>
</div>
<div>
<el-button
v-for="(item, key) in form.config.rightBtn"
size="mini"
:type="item.type"
:icon="item.icon"
@click="item.callback.call(_self, $event)"
>{{ item.name }}</el-button
>
</div>
</div>
<!-- 表格渲染 -->
<cu-table
ref="cuTable"
:table="table"
:Dict="Dict"
:config="form.config"
:loadData="loadData"
/>
</div>
</div>
</div>
</div>
<!-- 表单渲染 -->
<cu-form
ref="cuForm"
:form="form"
:Dict="Dict"
:rules="rules"
:submit="submitForm"
:cancel="cancelForm"
:watchKeys="['paperfilename']"
/>
</div>
</template>
<script>
import Highcharts from "highcharts";
import { reqApi, Config } from "@/assets/js/httpApi.js";
import { Tools } from "@/assets/js/common.js";
import DateRangePicker from "@/components/DateRangePicker";
import TableFilter from "@/components/TableFilter";
import cuForm from "@/components/cuForm";
import cuTable from "@/components/cuTable";
import request from "@/utils/request";
export default {
name: "Dashboard",
components: {
DateRangePicker,
TableFilter,
cuForm,
cuTable
},
data() {
return {
chartDataEmpty: false,
canvasDataEmpty: false,
Dict: { selectList: [] },
table: {
page: 1,
size: 10,
total: 0,
loading: false,
dataList: []
},
rules: {},
form: {
title: "",
visible: false,
reqType: "add",
historyDialog: false,
status: { cu: 0 },
query: {},
search: {},
item: {},
file: 0,
config: {}
},
// 新增图表相关数据
chartVisible: true, // 图表弹窗可见性
deviceList: [{ id: "test001", name: "测试设备1" }], // 设备列表数据
selectedDevice: null, // 选中设备ID
dateRange: [null, null], // 日期范围选择
currentChartType: "line", // 当前图表类型(line/column)
chartLoading: false, // 图表加载状态
chartInstance: null, // Highcharts实例引用
// ...原有data属性保持不变...
canvas: null, // Canvas 实例
ctx: null, // 2D 上下文
bgImage: null, // 背景图对象
waterImage: null, // 新增:water.png 图片对象
poImage: null, // 新增:po.png 图片对象
waterValue: 0, // 新增:水位值
canvasDataReal: {
poConfigsWH: [
{
// 基础信息配置对象 用于换算具体画布xy坐标点
xieMianW: 0, // 第一个坡斜面距离第一个坡平面的水平距离。
pingMianW: 0, // 第一个坡平面距离平面宽度。
pingMianH: 0, // 第一个坡平面距离平面高度。
kongW: 0, // 目前设定打孔的位置在坡平面上距离左侧起始点的距离
kongH: 0, // 目前孔的深度
maishen: 0 // 水位到管口的垂直距离
}
],
otherConfig: {
gantanW: 0, // 干滩距离水平面的宽度(根据最后一个坡平面坐标点计算xy。)
tuduiW: 0, // 干滩距离水平面中间的土堆
shuiW: 0, // 水平面的宽度
shuiH: 0 // 水平面的高度
},
// Canvas绘制配置(支持批量扩展)
poConfigsXY: [
// 根据poConfigsWH 计算具体的xy坐标点,每个对象计算出两对坐标点。分别是斜面和平面点
],
guanConfigsXY: [
// 根据poConfigsXY和poConfigsWH的孔信息 计算具体的xy坐标点,每个对象计算出两对坐标点。分别是孔起始坐标和结束坐标
],
gantanConfigsXY: [],
jrxConfigsXY: [],
shuiweiConfigsXY: [],
tuduiConfigsXY: [],
jingjiexianConfigsXY: [],
lineConfigs: [
// { points: [], color: "red", lineWidth: 2 }, // 红色连接线配置
// { points: [], color: "yellow", lineWidth: 2 }, // 黄色连接线配置
]
}
// canvasData: [
// {
// name: "剖面名称",
// height: "剖面高度",
// width: "剖面宽度",
// code: "剖面编号",
// sdeg: "水区扇形起始角度",
// diameter: "总直径",
// jrxStepsDtoList: [
// {
// name: "台阶名称",
// code: "台阶编号",
// height: "高度(米)",
// width: "宽度(米)",
// slope: "坡度(deg)"
// }
// ],
// equipmentDataList: [
// {
// sensorid: "设备id",
// sensorname: "设备名称",
// unit: "设备单位",
// stage: "水位(传感器到水面的距离)",
// depth: "浸润线埋深(水位到管口的距离)(m)",
// smhb: "// 水面海拔",
// time: "时间",
// holeDepth: "孔口高程",
// jrxTrepanning: "浸润线开口深度",
// redAlarm: "红色预警 此预警值针对的是depth 浸润线埋深",
// orangeAlarm: "橙色预警",
// yellowAlarm: "黄色预警",
// blueAlarm: "蓝色预警"
// }
// ]
// }
// ]
};
},
created() {
// 获取基本信息
this.pageApi = Config.getModuleInfo(this);
this.Dict = this.pageApi.Dict;
this.form.config = this.pageApi.config;
this.rules = reqApi.getRules(this.Dict.baseInfo) || this.Dict.rules || {};
this.pageApi.created && this.pageApi.created();
// 新增:获取地址栏deviceId参数并设置到form.query.code
const deviceId = this.$route.query.deviceId;
if (deviceId) {
this.form.query.sensorid = deviceId; // 将deviceId赋值到查询条件的code字段
}
// this.handleCanvasData(this.canvasData);
},
mounted() {
this.$nextTick(() => {
this.loadData();
this.initChart1();
this.getCanvasData();
});
// 获取选择列表字典
var selectList = this.Dict.selectList;
Tools.asyncLoop.call(this, selectList, function(key, value, next) {
if (typeof value === "function") {
value.call(this.Dict).then(function(res) {
selectList[key] = res;
next();
});
} else {
next();
}
});
},
methods: {
// 最新
// calculateWidth(height, angleDegrees) {
// // 1. 将角度从度转换为弧度(JavaScript的Math.tan()使用弧度)
// const angleRadians = angleDegrees * Math.PI / 180;
// // 2. 计算角度的正切值
// const tanValue = Math.tan(angleRadians);
// // 3. 计算水平距离 L = H / tan(θ)
// const width = height / tanValue;
// return width;
// // 使用示例
// const height = 40; // 垂直高度
// const angle = 25; // 坡度角度(度)
// const result = calculateWidth(height, angle);
// console.log(`宽度 = ${result.toFixed(2)}`);
// // 输出:宽度 = 85.78
// },
/** 绘制文字标注 */
drawText(config) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复默认坐标系(左上角原点)
this.ctx.font = `10px Arial`; // 文字大小8px
this.ctx.fillStyle = "#000"; // 黄色
this.ctx.textAlign = "center"; // 水平居中
this.ctx.textBaseline = "top"; // 垂直顶部对齐
// 绘制文字(考虑Y轴方向转换)
const drawY = this.canvas.height - config.y; // 转换为默认坐标系Y轴
this.ctx.fillText(config.text, config.x, drawY + 5); // Y轴偏移5px避免重叠
this.ctx.restore();
},
/** 清空画布内容及配置 */
clearCanvas() {
if (this.ctx) {
// 清除画布绘制内容(基于当前坐标系)
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复默认坐标系
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height); // 清空整个画布
this.ctx.restore();
}
// 重置配置数据(避免残留)
this.canvasDataReal.poConfigs = [];
this.canvasDataReal.guanConfigs = [];
this.canvasDataReal.lineConfigs = [];
},
getCanvasData() {
const params = { ...this.form.query };
request({
url: "tab/jrx/dissect/visual-xinHua",
method: "get",
params
}).then(async res => {
var data = res.body;
if (!data.length) {
// 数据为空时清空画布
this.clearCanvas();
return;
}
this.waterValue = data[0].wtValue;
await this.handleCanvasData(data);
this.initCanvas();
});
},
handleCanvasData(list = []) {
return new Promise(resolve => {
// 新增Promise封装
let canvasWidth = 1000; // 默认宽度
let canvasHeight = 600; // 默认高度
// 更新Canvas元素属性和样式
const canvasEl = this.$refs.canvas;
if (canvasEl) {
// 实际绘图区域尺寸(关键属性)
canvasEl.width = canvasWidth;
canvasEl.height = canvasHeight;
// 显示尺寸(与绘图区域保持一致)
canvasEl.style.width = `${canvasWidth}px`;
canvasEl.style.height = `${canvasHeight}px`;
}
// 数据清洗
const poumian = list[0]; // 坡面数据
const poCount = poumian.jrxStepsDtoList.length;
const lineCount = 5;
const po_pad_right = -140; // poCount > 4 ? 130 : 180; // 第一个台阶距离画布的距离
const poH = 450;
const line_color = ["red", "orange", "yellow", "blue", "white"];
this.canvasDataReal.poConfigs = [];
this.canvasDataReal.guanConfigs = [];
this.canvasDataReal.lineConfigs = [];
//第一层台阶的开孔深度和设备图片相除的系数
const sbHeightModulus =
poumian.equipmentDataList[0].jrxTrepanning / 130;
// 存储设备数据列表(关键新增,用于后续获取depth值)
this.equipmentDataList = poumian.equipmentDataList || [];
// 坡数据数据格式处理
poumian.jrxStepsDtoList.forEach((po, poIndex) => {
this.canvasDataReal.poConfigs.push({
x: -140,
y: canvasHeight - poIndex * poH,
width: canvasWidth - poIndex * po_pad_right,
height: poH
});
});
// 管孔数据数据格式处理
poumian.equipmentDataList.forEach((equipment, equipIndex) => {
const po = this.canvasDataReal.poConfigs[equipIndex];
// 管孔数据数据格式处理
if (equipIndex == 1) {
this.canvasDataReal.guanConfigs.push({
x: po.width * 0.234 + 100 * (equipIndex + 1),
y: canvasHeight - po.height + 133,
width: 7, // 可以设置不同的宽度
height: (equipment.jrxTrepanning / sbHeightModulus) * 1.3,
image: "guanImage2" // 使用不同的图片
});
} else {
this.canvasDataReal.guanConfigs.push({
x: po.width * 0.234 + 100 * (equipIndex + 1),
y: canvasHeight - po.height + 208,
width: 7,
height: equipment.jrxTrepanning / sbHeightModulus,
image: "guanImage"
});
}
});
// 警戒线逻辑- 初始话基本数据结构
for (let index1 = 0; index1 < lineCount; index1++) {
this.canvasDataReal.lineConfigs.push({
// 设置
points: [],
color: line_color[index1],
lineWidth: 2,
cha: 4 * index1
});
}
// 警戒线种类下,根据管孔设备个数,设定对应的坐标点point
this.canvasDataReal.lineConfigs.forEach((line, index) => {
let lineValKey =
line.color == "white" ? "depth" : line.color + "Alarm";
// 几个管孔几个坐标点
poumian.equipmentDataList.forEach((equip, subindex) => {
let xOffset = 0;
let baseY = 0;
// 对于彩色线(非白色),使用统一的计算方式,确保线条是直线
if (line.color !== "white") {
// 对所有彩色线使用相同的计算逻辑,不使用不同的分母
xOffset = line.cha / 20; // 使用统一的分母值
// 直接根据设备数据计算y坐标
baseY =
this.canvasDataReal.guanConfigs[subindex].y +
equip[lineValKey] / sbHeightModulus;
} else {
baseY =
this.canvasDataReal.guanConfigs[subindex].y +
this.canvasDataReal.guanConfigs[subindex].height;
}
console.log(equip, "equip");
line.points.push({
x: this.canvasDataReal.guanConfigs[subindex].x + 1 + xOffset,
y: baseY,
depth: equip["depth"]
});
});
});
resolve(); // 数据处理完成后触发resolve
});
},
/** 初始化 Canvas 画布及坐标系 */
initCanvas() {
// 获取 Canvas 元素和上下文
this.canvas = this.$refs.canvas; // document.getElementById('mainCanvas')
if (!this.canvas) {
console.error("Canvas 元素未找到");
return;
}
this.ctx = this.canvas.getContext("2d");
// 坐标系变换:将原点移至左下角(默认原点在左上角)
this.ctx.translate(0, this.canvas.height); // 向下移动画布高度
this.ctx.scale(1, -1); // 翻转 Y 轴方向(上为正)
// // 加载背景图并绘制
// this.loadBackgroundImage();
// 加载所有图片并绘制
this.loadAllImages();
},
// 绘制water.png(独立方法)
drawWaterImage() {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.drawImage(
this.waterImage,
500,
this.canvas.height - (180 + this.waterValue * 1.2),
550,
550
);
this.ctx.restore();
},
// 绘制单个po.png(支持配置参数)
drawPoImage(config) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
// 动态计算y坐标(若未指定则默认画布底部-高度)
this.ctx.drawImage(
this.poImage,
config.x,
config.y,
config.width,
config.height
);
this.ctx.restore();
},
// 绘制单个guan.png(支持不同图片类型)
drawGuanImage(config) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
const targetImage = this[config.image]; // 根据配置获取具体图片对象(如guanImage/guanImage1)
this.ctx.drawImage(
targetImage,
config.x,
config.y,
config.width,
config.height
);
this.ctx.restore();
},
drawSecondPipeImage(config) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
// 可以使用不同的绘制逻辑
const targetImage = this[config.image] || this.guanImage;
this.ctx.drawImage(
targetImage,
config.x,
config.y,
config.width,
config.height
);
this.ctx.restore();
},
// 绘制连接线(支持多坐标点)白色
drawLine(config) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.strokeStyle = config.color;
this.ctx.lineWidth = config.lineWidth;
this.ctx.beginPath();
// 修复语法错误并改进白色线对齐逻辑
if (
config.color === "white" &&
this.canvasDataReal.guanConfigs.length > 0
) {
// 确保至少有两个点可以连接
if (config.points && config.points.length >= 2) {
config.points.forEach((point, index) => {
index === 0
? this.ctx.moveTo(point.x, point.y)
: this.ctx.lineTo(point.x, point.y);
});
}
} else {
config.points.forEach((point, index) => {
// index === 0 ? this.ctx.moveTo(point.x - 400, point.y + 220) : this.ctx.lineTo(point.x, point.y);
// index === 0 ? this.ctx.moveTo(point.x - 400, point.y + 190) : this.ctx.lineTo(point.x, point.y);
});
}
this.ctx.stroke();
this.ctx.restore();
},
// 绘制背景图(独立方法)
drawBackground() {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.drawImage(
this.bgImage,
0,
0,
this.canvas.width,
this.canvas.height
);
this.ctx.restore();
},
// 绘制文字 箭头
createCustomLineWithText(config) {
// 配置参数默认值
const defaultConfig = {
points: [],
color: "#000000",
lineWidth: 2,
showLine: true,
text: "",
textPosition: "middle",
textDirection: "horizontal",
textAngle: 0,
textOffset: 10,
textColor: "#000000",
fontSize: 10,
showArrow: false,
showArrowAtStart: false,
arrowSize: 8
};
// 合并配置
const finalConfig = { ...defaultConfig, ...config };
if (!this.ctx || !finalConfig.points || finalConfig.points.length < 1) {
console.error("Canvas上下文或坐标点不足");
return;
}
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复默认坐标系
// 只有当showLine为true且lineWidth大于0时才绘制线条
if (
finalConfig.showLine &&
finalConfig.lineWidth > 0 &&
finalConfig.points.length >= 2
) {
this.ctx.strokeStyle = finalConfig.color;
this.ctx.lineWidth = finalConfig.lineWidth;
this.ctx.beginPath();
finalConfig.points.forEach((point, index) => {
if (index == 0) {
this.ctx.moveTo(point.x, point.y);
} else {
this.ctx.lineTo(point.x, point.y);
}
});
this.ctx.stroke();
// 新增:绘制箭头
if (finalConfig.showArrow && finalConfig.points.length >= 2) {
const points = finalConfig.points;
const lastPoint = points[points.length - 1];
const prevPoint = points[points.length - 2];
// 计算箭头方向
const angle = Math.atan2(
lastPoint.y - prevPoint.y,
lastPoint.x - prevPoint.x
);
// 设置箭头样式
this.ctx.fillStyle = finalConfig.color;
// 绘制箭头三角形
this.ctx.beginPath();
this.ctx.moveTo(lastPoint.x, lastPoint.y);
this.ctx.lineTo(
lastPoint.x - finalConfig.arrowSize * Math.cos(angle - Math.PI / 6),
lastPoint.y - finalConfig.arrowSize * Math.sin(angle - Math.PI / 6)
);
this.ctx.lineTo(
lastPoint.x - finalConfig.arrowSize * Math.cos(angle + Math.PI / 6),
lastPoint.y - finalConfig.arrowSize * Math.sin(angle + Math.PI / 6)
);
this.ctx.closePath();
this.ctx.fill();
}
}
if (finalConfig.showArrowAtStart && finalConfig.points.length >= 2) {
const points = finalConfig.points;
const firstPoint = points[0];
const secondPoint = points[1];
// 计算起点箭头方向(与线段方向相反)
const angle =
Math.atan2(
secondPoint.y - firstPoint.y,
secondPoint.x - firstPoint.x
) + Math.PI;
// 设置箭头样式
this.ctx.fillStyle = finalConfig.color;
// 绘制起点箭头三角形
this.ctx.beginPath();
this.ctx.moveTo(firstPoint.x, firstPoint.y);
this.ctx.lineTo(
firstPoint.x - finalConfig.arrowSize * Math.cos(angle - Math.PI / 6),
firstPoint.y - finalConfig.arrowSize * Math.sin(angle - Math.PI / 6)
);
this.ctx.lineTo(
firstPoint.x - finalConfig.arrowSize * Math.cos(angle + Math.PI / 6),
firstPoint.y - finalConfig.arrowSize * Math.sin(angle + Math.PI / 6)
);
this.ctx.closePath();
this.ctx.fill();
}
// 绘制文字
if (finalConfig.text) {
this.ctx.font = `${finalConfig.fontSize}px Arial`;
this.ctx.fillStyle = finalConfig.textColor;
// 计算文字位置
let textX, textY;
const { points } = finalConfig;
switch (finalConfig.textPosition) {
case "start":
textX = points[0].x;
textY = points[0].y;
break;
case "end":
textX = points[points.length - 1].x;
textY = points[points.length - 1].y;
break;
default:
if (points.length >= 2) {
const midIndex = Math.floor(points.length / 2);
textX = (points[midIndex - 1].x + points[midIndex].x) / 2;
textY = (points[midIndex - 1].y + points[midIndex].y) / 2;
} else {
textX = points[0].x;
textY = points[0].y;
}
break;
}
// 根据方向调整文字
switch (finalConfig.textDirection) {
case "vertical":
this.ctx.save();
this.ctx.translate(textX, textY);
this.ctx.rotate(-Math.PI / 2); // 旋转90度
this.ctx.textAlign = "center";
this.ctx.textBaseline = "middle";
this.ctx.fillText(finalConfig.text, 0, -finalConfig.textOffset);
this.ctx.restore();
break;
case "angle":
const angleRad = (finalConfig.textAngle * Math.PI) / 180;
this.ctx.save();
this.ctx.translate(textX, textY);
this.ctx.rotate(angleRad);
this.ctx.textAlign = "center";
this.ctx.textBaseline = "middle";
this.ctx.fillText(finalConfig.text, 0, -finalConfig.textOffset);
this.ctx.restore();
break;
default:
// horizontal
this.ctx.textAlign = "center";
this.ctx.textBaseline = "bottom";
this.ctx.fillText(
finalConfig.text,
textX,
textY - finalConfig.textOffset
);
break;
}
}
this.ctx.restore();
},
// 重构后的统一绘制入口(通过配置驱动)
drawAllImages() {
// 1. 绘制背景图
this.drawBackground();
// 绘制water.png
this.drawWaterImage();
// 2. 绘制po.png(遍历配置数组)
this.canvasDataReal.poConfigs.forEach((config, index) => {
this.drawPoImage(config);
});
// 3. 绘制guan.png(遍历配置数组)
this.canvasDataReal.guanConfigs.forEach((config, index) => {
if (index == 1) {
// 执行针对第二个管子的特殊绘制方法
this.drawSecondPipeImage(config);
} else {
// 正常绘制其他管子
this.drawGuanImage(config);
}
});
// 修改 drawAllImages 方法中的蓝色阴影绘制部分
this.canvasDataReal.lineConfigs.forEach(lineConfig => {
if (lineConfig.color === "white" && lineConfig.points.length >= 2) {
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复默认坐标系
// 直接使用白色线的原始坐标点,确保完全对齐
const whiteLinePoints = lineConfig.points;
const canvasHeight = this.canvas.height;
// 遍历相邻点对,生成梯形阴影
for (let i = 0; i < whiteLinePoints.length - 1; i++) {
const start = whiteLinePoints[i];
const end = whiteLinePoints[i + 1];
// 构建梯形四个顶点(确保与白色线完全重合)
const trapezoid = [
{ x: start.x, y: start.y }, // 顶部左端点(与白色线起点完全对齐)
{ x: end.x, y: end.y }, // 顶部右端点(与白色线终点完全对齐)
{ x: end.x, y: canvasHeight }, // 底部右端点
{ x: start.x, y: canvasHeight } // 底部左端点
];
// 绘制蓝色阴影
this.ctx.fillStyle = "rgba(59, 175, 251, 0.3)"; // 海蓝色,透明度30%
this.ctx.beginPath();
trapezoid.forEach((point, index) => {
index === 0
? this.ctx.moveTo(point.x, point.y)
: this.ctx.lineTo(point.x, point.y);
});
this.ctx.closePath();
this.ctx.fill();
}
this.ctx.restore();
}
});
// 确保在阴影绘制之后再绘制白色线条
this.canvasDataReal.lineConfigs.forEach(config => {
this.drawLine(config);
});
const colorsToConnect = ["red", "orange", "yellow", "blue"];
// 为每种颜色定义对应的目标位置坐标
const targetPoints = {
red: { x: 550, y: 304 },
orange: { x: 560, y: 307 },
yellow: { x: 570, y: 310 },
blue: { x: 580, y: 313 }
};
// 第二条 ....
colorsToConnect.forEach(color => {
// 找到对应颜色的线配置
const lineConfig = this.canvasDataReal.lineConfigs.find(
config => config.color === color
);
if (lineConfig && lineConfig.points.length > 0) {
const lastPoint = lineConfig.points[lineConfig.points.length - 1];
// 获取该颜色对应的目标位置
const targetPoint = targetPoints[color];
// 绘制新的连接线,保持与原线相同的颜色和线宽
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.strokeStyle = color;
this.ctx.lineWidth = lineConfig.lineWidth || 2;
this.ctx.beginPath();
this.ctx.moveTo(lastPoint.x - 500, lastPoint.y + 300);
this.ctx.lineTo(targetPoint.x, targetPoint.y);
this.ctx.stroke();
this.ctx.restore();
}
});
// 5. 绘制白色警戒线所有点的文字标注(修改部分)
this.canvasDataReal.lineConfigs.forEach(lineConfig => {
if (lineConfig.color === "white" && lineConfig.points.length > 0) {
// 1. 收集所有设备信息,包括设备名称中的数字
const deviceData = [];
lineConfig.points.forEach((point, subindex) => {
const equipment = this.equipmentDataList[subindex];
if (equipment) {
deviceData.push({
point,
equipment,
subindex,
});
}
});
// 4. 绘制文本,使用交换后的深度值
deviceData.forEach(device => {
this.drawText({
x: device.point.x,
y: this.canvas.height - device.point.y,
text: `浸润线埋深:${device.equipment.depth || ""}m`
});
});
}
});
// 浸润线
this.createCustomLineWithText({
points: [{ x: 350, y: 300 }, { x: 400, y: 370 }],
color: "#000",
lineWidth: 1,
text: "浸润线",
textPosition: "start", // 文字位置:start、middle、end
textDirection: "horizontal", // 文字方向:horizontal、vertical、angle
textOffset: 9,
textColor: "#000",
fontSize: 14,
showArrow: true,
arrowSize: 8
});
// 正常高水位
this.createCustomLineWithText({
points: [{ x: 585, y: 285 }, { x: 585, y: 370 }],
color: "#000",
lineWidth: 1,
text: "正常高水位",
textPosition: "end",
textDirection: "horizontal",
textOffset: -20,
textColor: "#000",
fontSize: 14
});
// 平滩
this.createCustomLineWithText({
points: [{ x: 587, y: 285 }, { x: 520, y: 285 }],
color: "#000",
lineWidth: 1,
text: "干滩",
textPosition: "middle",
textDirection: "horizontal",
textOffset: 20,
textColor: "orange",
fontSize: 15,
showArrow: true,
showArrowAtStart: true,
arrowSize: 7
});
// 大坝库
this.createCustomLineWithText({
points: [{ x: 750, y: 270 }, { x: 700, y: 355 }],
color: "#000",
lineWidth: 1,
text: "大坝库",
textPosition: "start",
textDirection: "horizontal",
textOffset: 10,
textColor: "#2c4f7c",
fontSize: 15
});
// 传感器
this.createCustomLineWithText({
points: [{ x: 300, y: 480 }, { x: 400, y: 370 }],
text: "渗压传感器",
showLine: false,
textPosition: "start",
textDirection: "horizontal",
textOffset: 15,
textColor: "#000",
fontSize: 11
});
this.createCustomLineWithText({
points: [{ x: 505, y: 400 }, { x: 400, y: 370 }],
text: "渗压传感器",
showLine: false,
textPosition: "start",
textDirection: "horizontal",
textOffset: 15,
textColor: "#000",
fontSize: 11
});
},
/** 加载所有图片并绘制 */
loadAllImages() {
// 加载背景图(原有)
this.bgImage = new Image();
this.bgImage.src = require("@/assets/images/jrx/bg.png");
// // 加载 water.png
this.waterImage = new Image();
this.waterImage.src = require("@/assets/images/jrx/shui.png");
// 加载 shan.png
this.poImage = new Image();
this.poImage.src = require("@/assets/images/jrx/shan.png");
// 加载 guan.png
this.guanImage = new Image();
this.guanImage.src = require("@/assets/images/jrx/guan0.png");
// 等待所有图片加载完成
Promise.all([
new Promise(resolve => (this.bgImage.onload = resolve)),
new Promise(resolve => (this.waterImage.onload = resolve)),
new Promise(resolve => (this.poImage.onload = resolve)),
new Promise(resolve => (this.guanImage.onload = resolve))
])
.then(() => {
// 所有图片加载完成后绘制
this.drawAllImages();
})
.catch(() => {
console.error("部分图片加载失败");
});
},
/** 初始化图表(关键修改:调整图例位置) */
initChart1() {
let params = {
sort: "date,desc"
};
if (this.form.query.daterange) {
params.daterange = this.form.query.daterange.join(",");
}
if (this.form.query.sensorid) {
params.code = this.form.query.sensorid;
}
request({
url: "data/st/imghistory",
method: "get",
params
}).then(res => {
const data = res.body;
// const data = this.getChartData().body
// console.log("data", data);
const chartData = this.seriesDataFormat(data, { datekey: "date" });
var warningLine = undefined; // this.form.config.warningLine;
var option = {
valEnabled: true,
opacity: 1,
lineWidth: 3
};
var alarmData = this.discernValidAlarmValue(
data.alarm,
data.lists,
chartData.series,
warningLine,
option
);
// const chartData = data
// console.log("chartCData", chartData);
Highcharts.setOptions({
global: {
useUTC: false
},
lang: {
resetZoom: "重置缩放比例"
}
});
this.chartInstance = new Highcharts.chart(this.$refs.chartContainer, {
chart: {
//type: '',
backgroundColor: "transparent",
//marginTop:30,
//marginBottom:30,
//marginLeft:30,
zoomType: "x" // xy
},
valEnabled: true,
opacity: 1,
lineWidth: 3,
title: {
text: ""
},
subtitle: {
text: ""
},
tooltip: {
enabled: false,
borderWidth: 10
},
xAxis: {
type: "datetime",
// 最为关键的代码:如果为空数组,则导致日期格式化失效!!
categories: chartData.categories[0] && chartData.categories,
lineWidth: 0,
//lineColor:'#ff0000',
gridLineColor: "#aaa",
dateTimeLabelFormats: {
millisecond: "%H:%M:%S.%L",
second: "%H:%M:%S",
minute: "%H:%M",
hour: "%H:%M",
day: "%m-%d",
week: "%m-%d",
month: "%Y-%m",
year: "%Y"
}
},
yAxis: {
title: {
text: ""
},
labels: {
x: -6
},
gridLineColor: "#aaa",
max: null
},
plotOptions: {
column: {
borderWidth: 0
//y:50,
//itemMarginTop:50,
},
bar: {
borderWidth: 0
}
},
tooltip: {
// {point.y:.4f} // 保留4位小数
//headerFormat: '<span style="font-size:10px">{point.key}</span><table>',
pointFormat:
'<tr><td style="color:{series.color};padding:0">{series.name}:</td>' +
'<td style="padding:0"><b>{point.y}' +
data.danwei +
"</b> </td></tr>",
footerFormat: "</table>",
shared: true,
useHTML: true,
dateTimeLabelFormats: {
millisecond: "%H:%M:%S.%L",
second: "%H:%M:%S",
minute: "%H:%M",
hour: "%H:%M",
day: "%m-%d %H时",
week: "%m-%d",
month: "%Y-%m",
year: "%Y"
}
},
legend: {
enabled: chartData.series.length > 1 ? true : false,
// 图例定位
layout: "horizontal", // 水平布局:“horizontal”, 垂直布局:“vertical”
floating: false, // 图列是否浮动
align: "right",
// 图例容器
//width:'100%', // number || String
padding: 2, // 内边距
margin: 2,
borderRadius: 5,
//borderWidth:1,
verticalAlign: "top",
// 图例项
//itemWidth:120, // 宽度
itemDistance: 10, // 间距 20
y: -10,
itemMarginTop: 2,
itemStyle: {},
itemHoverStyle: {}
},
credits: {
enabled: false
},
series: chartData.series
});
});
},
seriesDataFormat(data, opts) {
var names = data.names || [],
list = data.list || data.lists,
series = [],
categories = [],
maxVal = 0;
if (names[0] && list) {
var colors = [
"#7CB5EC",
"#90ED7D",
"#F7A35C",
"#8085E9",
"#F15C80",
"#E4D354",
"#2B908F",
"#F45B5B",
"#91E8E1",
"#0769CB",
"#00ABBD",
"#ffd886",
"#9F2E61",
"#4D670C"
];
var len = names.length;
for (var i = 0; i < len; i++) {
var item = names[i];
var serie = {
name: item.name,
data: [],
key: item.key,
type: data.chartType,
color: colors[i]
};
opts && serie.type && (serie.type = opts.type);
series.push(serie);
}
for (var item of list) {
var values = item.values;
//var datetime = item[opts.datekey || 'dateUnit'];
//var time = datetime.indexOf(' ') > 0 ? datetime.split(' ')[1] : datetime;
var timestamp = item["date"];
//categories.push(timestamp);
for (var serie of series) {
var value = values[serie.key] * 1;
Math.abs(value) > maxVal && (maxVal = Math.abs(value));
serie.data.push([timestamp, value]);
}
}
}
return { series: series, categories: categories, maxVal: maxVal };
},
discernValidAlarmValue(alarms, list, series, direction, opts) {
var xLength = (list || []).length,
maxAlarm = 0,
opts = opts || {};
if (alarms && xLength) {
var value = alarms.value;
// discern
var levelDist = [
{ color: "red", name: "红色报警线" },
{ color: "orange", name: "橙色报警线" },
{ color: "yellow", name: "黄色报警线" },
{ color: "blue", name: "蓝色报警线" }
],
alarmLine = {},
setAlarmSerie = function(value, name, color) {
// 修改蓝色色值
color === "blue" && (color = "#3BAFFB");
var sx = list[0].date,
ex = list[xLength - 1].date;
var serie = {
name: name,
type: "spline",
data: [{ x: sx, y: value }, { x: ex, y: value }],
color: color,
enableMouseTracking: false,
legend: false,
showInLegend: false,
dashStyle: "ShortDot",
lineWidth: opts.lineWidth || 1,
states: {
inactive: {
opacity: opts.opacity
}
},
dataLabels: {
enabled: opts.valEnabled || false, // 数据值, 2022/11/11(周五) 因多条报警线暂时关闭
//backgroundColor:'red',
verticalAlign: "middle",
padding: 0,
defer: false,
allowOverlap: true,
color: color,
style: {
textOutline: "none"
}
},
tooltip: {
//footerFormat:'',
//pointFormat:'',
headerFormat: ""
//nullFormat:'',
},
marker: {
enabled: false
},
zIndex: -10
};
series.push(serie);
};
/* 智能报警线
var level = (~~alarms.alarmLevel || levelDist.length+1) - 1;
var nearLevel = level ? level - 1 : level;
var alarm = levelDist[level];
var nearAlarm = levelDist[nearLevel];
nearAlarm.value = value[nearAlarm.color];
alarmLine[nearAlarm.color] = nearAlarm;
if(alarm){
alarm.value = value[alarm.color];
alarmLine[alarm.color] = alarm;
}; */
// 多条报警线
for (var item of levelDist) {
item.value = value[item.color];
alarmLine[item.color] = item;
}
// add
for (var key in alarmLine) {
var line = alarmLine[key];
line.value > maxAlarm && (maxAlarm = line.value);
setAlarmSerie(line.value, line.name, line.color);
direction && setAlarmSerie(0 - line.value, line.name, line.color);
}
}
return { series, maxAlarm };
},
/** 切换图表类型 */
switchChartType(type) {
this.currentChartType = type;
if (this.chartInstance) {
this.chartInstance.series.forEach(series => {
series.update({ type: this.currentChartType });
});
}
},
loadData: reqApi.common.getRequst,
cancelForm() {
this.form.visible = false;
},
submitForm(form, item) {
reqApi.common.submitForm.call(this, form, item);
}
}
};
</script>
<style rel="stylesheet/scss" lang="scss" scope>
.qyzz {
}
</style>
...@@ -294,167 +294,6 @@ export default { ...@@ -294,167 +294,6 @@ export default {
this.initCanvas(); this.initCanvas();
}); });
}, },
handleCanvasDataNew(list = []) {
return new Promise((resolve) => {
// 新增Promise封装
let canvasWidth = 1000; // 默认宽度
let canvasHeight = 600; // 默认高度
// 更新Canvas元素属性和样式
const canvasEl = this.$refs.canvas;
if (canvasEl) {
// 实际绘图区域尺寸(关键属性)
canvasEl.width = canvasWidth;
canvasEl.height = canvasHeight;
// 显示尺寸(与绘图区域保持一致)
canvasEl.style.width = `${canvasWidth}px`;
canvasEl.style.height = `${canvasHeight}px`;
}
// 数据清洗
const poumian = list[0]; // 坡面数据
const poCount = poumian.jrxStepsDtoList.length;
const lineCount = 5;
const po_pad_right = 180; // poCount > 4 ? 130 : 180; // 第一个台阶距离画布的距离
const poH = 150;
const line_color = ["red", "orange", "yellow", "blue", "white"];
//第一层台阶的开孔深度和设备图片相除的系数
const sbHeightModulus =
poumian.equipmentDataList[0].jrxTrepanning / 130;
// 存储设备数据列表(关键新增,用于后续获取depth值)
this.equipmentDataList = poumian.equipmentDataList || [];
// 坡面基本信息获取
for (let index = 0; index < poCount; index++) {
this.poConfigsWH.push({
xieMianW: 10 + index, // 第一个坡斜面距离第一个坡平面的水平距离。
pingMianW: 50 + index, // 第一个坡平面距离平面宽度。
pingMianH: 20 + index, // 第一个坡平面距离平面高度。
kongW: 10 + index, // 目前设定打孔的位置在坡平面上距离左侧起始点的距离
kongH: 20 + index, // 目前孔的深度
jrxTrepanning: 10 + index, // 浸润线开口深度
})
}
// 破平面xy计算
this.poConfigsXY = [{ x: 0, y: 0 }]
this.poConfigsWH.forEach((item) => {
this.poConfigsXY.push({ // 斜坡点
x: this.poConfigsXY[this.poConfigsXY.length - 1].x + item.xieMianW, // 每个斜平坡计算点都根据上一个前面已有的最后一个坐标点进行添加 第一个为x0y0
y: this.poConfigsXY[this.poConfigsXY.length - 1].y + item.pingMianH,
})
this.poConfigsXY.push({ // 平坡点
x: this.poConfigsXY[this.poConfigsXY.length - 1].x + item.pingMianW,
y: this.poConfigsXY[this.poConfigsXY.length - 1].y,
})
// 孔的xy计算
this.guanConfigsXY.push({ // 管的起始点 -2是获取该斜坡点的结束坐标加上孔距离位置=孔在平坡上面的位置
x: this.poConfigsXY[this.poConfigsXY.length - 2].x + item.kongW,
y: this.poConfigsXY[this.poConfigsXY.length - 2].y + 0, // 这里的0为了突出孔的仪器设备 突出坡平面距离。这里先放置0(方便后面浸润线水位的计算),等后面实际画管线的时候临时加上固定值。
})
this.guanConfigsXY.push({ // 管的结束点
x: this.poConfigsXY[this.poConfigsXY.length - 2].x + item.kongW,
y: this.poConfigsXY[this.poConfigsXY.length - 2].y - item.kongH,
maishen: item.depth, // 水位到管口的垂直距离
})
})
this.gantanConfigsXY = [ // 干滩的xy坐标点
{ x: this.poConfigsXY[this.poConfigsXY.length - 1].x, y: this.poConfigsXY[this.poConfigsXY.length - 1].y }, // 干滩的起始点
{
x: this.poConfigsXY[this.poConfigsXY.length - 1].x + this.otherConfig.gantanW,
y: this.poConfigsXY[this.poConfigsXY.length - 1].y - this.otherConfig.shuiH
}, // 干滩的结束点 (最高平坡减去水位高度)
]
this.guanConfigsXY.forEach((item, index) => {
if (index % 2 == 0) { // 偶数为管的起始点
this.jrxConfigsXY.push(
{ x: item.x, y: item.y - item.maishen }, // 浸润线的坐标点是平坡y-埋深的值(水位到平坡的垂直距离)
)
}
})
this.shuiweiConfigsXY = [ // 水位的xy坐标点(画矩形)
{ x: this.gantanConfigsXY[1].x, y: this.gantanConfigsXY[1].y }, // 水位的起始点
{ x: this.gantanConfigsXY[1].x + this.otherConfig.tuduiW, y: this.gantanConfigsXY[1].y }, // 水位的结束点 (最高平坡减去水位高度)
]
this.tuduiConfigsXY = [
{ x: this.gantanConfigsXY[1].x, y: this.gantanConfigsXY[1].y }, // 水位的起始点
{ x: this.gantanConfigsXY[1].x + this.otherConfig.tuduiW, y: 0 }, // 水位的结束点
]
this.jingjiexianConfigsXY = [
{ x: this.gantanConfigsXY[1].x, y: this.gantanConfigsXY[1].y }, // 水位的起始点
{ x: this.gantanConfigsXY[1].x + this.otherConfig.tuduiW, y: 0 }, // 水位的结束点
]
// 警戒线逻辑- 初始话基本数据结构
for (let index1 = 0; index1 < lineCount; index1++) {
this.jingjiexianConfigsXY.push({
// 设置
points: [],
color: line_color[index1],
lineWidth: 2,
cha: 4 * index1, // 每条线之间的差值
});
}
// 警戒线种类下,根据管孔设备个数,设定对应的坐标点point
// this.jingjiexianConfigsXY.forEach((line, index) => {
// let lineValKey = line.color == "white" ? "depth" : line.color + "Alarm";
// // 几个管孔几个坐标点
// poumian.equipmentDataList.forEach((equip, subindex) => {
// line.points.push({
// x: this.canvasDataReal.guanConfigs[subindex].x + 1,
// y:
// this.canvasDataReal.guanConfigs[subindex].y +
// equip[lineValKey], // 管设备的y坐标+设备的警戒值*系数
// });
// });
// });
this.canvasDataReal.lineConfigs.forEach((line, index) => {
let lineValKey = line.color == "white" ? "depth" : line.color + "Alarm";
// 几个管孔几个坐标点
poumian.equipmentDataList.forEach((equip, subindex) => {
line.points.push({
x: this.canvasDataReal.guanConfigs[subindex].x + 1,
y: this.canvasDataReal.guanConfigs[subindex].y + equip[lineValKey] / sbHeightModulus,
});
});
});
// 4. 绘制连接线(遍历配置数组)
this.canvasDataReal.lineConfigs.forEach((config, index) => {
this.drawLine(config);
});
// 5. 绘制白色警戒线所有点的文字标注(修改部分)
this.canvasDataReal.lineConfigs.forEach((lineConfig) => {
if (lineConfig.color === "white" && lineConfig.points.length > 0) {
// 添加可选链校验
lineConfig.points.forEach((point, subindex) => {
// 遍历所有坐标点
// 获取对应设备的深度值(通过索引对应)
const equipment = this.equipmentDataList[subindex];
const depth = equipment.depth || ""; // 添加空值校验
// 绘制当前点的文字
this.drawText({
x: point.x,
y: this.canvas.height - point.y,
text: `浸润线埋深:${depth}m`,
});
});
}
});
resolve(); // 数据处理完成后触发resolve
});
},
handleCanvasData(list = []) { handleCanvasData(list = []) {
return new Promise((resolve) => { return new Promise((resolve) => {
// 新增Promise封装 // 新增Promise封装
...@@ -945,50 +784,20 @@ export default { ...@@ -945,50 +784,20 @@ export default {
lineConfig.points.forEach((point, subindex) => { lineConfig.points.forEach((point, subindex) => {
const equipment = this.equipmentDataList[subindex]; const equipment = this.equipmentDataList[subindex];
if (equipment) { if (equipment) {
// 提取设备名称中的数字
const deviceNumber = equipment.sensorname ? parseInt(equipment.sensorname.match(/\d+/)[0] || '0') : 0;
deviceData.push({ deviceData.push({
point, point,
equipment, equipment,
subindex, subindex,
deviceNumber,
isOdd: deviceNumber % 2 === 1
}); });
} }
}); });
// 2. 创建一个新的深度值数组,用于存放交换后的值
const newDepths = new Array(deviceData.length).fill('');
// 3. 实现相邻管子埋深值互换,根据设备名称中的数字奇偶性决定
for (let i = 0; i < deviceData.length - 1; i += 2) {
// 检查当前两个设备
const device1 = deviceData[i];
const device2 = deviceData[i + 1];
if (device1 && device2) {
// 如果第一个设备的编号是单数,则第一个管子的埋深值显示在第二个管子下面
if (device1.isOdd) {
newDepths[device1.subindex] = device1.equipment.depth || '';
newDepths[device2.subindex] = device2.equipment.depth || '';
} else if (!device2.isOdd) {
newDepths[device1.subindex] = device2.equipment.depth || '';
newDepths[device2.subindex] = device1.equipment.depth || '';
}
}
}
// 处理可能的奇数个设备的最后一个设备
if (deviceData.length % 2 === 1) {
const lastDevice = deviceData[deviceData.length - 1];
newDepths[lastDevice.subindex] = lastDevice.equipment.depth || '';
}
// 4. 绘制文本,使用交换后的深度值 // 4. 绘制文本,使用交换后的深度值
deviceData.forEach(device => { deviceData.forEach(device => {
this.drawText({ this.drawText({
x: device.point.x, x: device.point.x,
y: this.canvas.height - device.point.y, y: this.canvas.height - device.point.y,
text: `浸润线埋深:${newDepths[device.subindex]}m`, text: `浸润线埋深:${device.equipment.depth}m`,
}); });
}); });
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment