Commit bb304d10 authored by xinzhedeai's avatar xinzhedeai

add: 图表展示-x轴日期动态变换fix

parent 51ed3a8d
<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:1200px;height: 600px;">
<!-- 替换为 Canvas 元素 -->
<canvas
ref="canvas"
id="mainCanvas"
width="1200"
height="600"
style="background: #f0f0f0;"
></canvas>
</div>
<div class="chart-wrapper">
<!-- 图表容器 -->
<div ref="chartContainer" style="width:0px; height: 400px; margin-bottom: 20px;"></div>
<!-- 图表操作区域(调整到最下方左侧) -->
<div class="chart-btn-group">
<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>
<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: 1200px; height: 600px">
<!-- 替换为 Canvas 元素 -->
<canvas
ref="canvas"
id="mainCanvas"
width="1200"
height="600"
style="background: #f0f0f0"
></canvas>
</div>
<div class="chart-wrapper">
<!-- 图表容器780 -->
<div
ref="chartContainer"
style="width: 780px; height: 600px; margin-bottom: 20px"
></div>
<!-- 图表操作区域(调整到最下方左侧) -->
<div class="chart-btn-group">
<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 Highcharts from "highcharts";
import Highcharts from "highcharts";
import HTreemap from "highcharts/modules/treemap.js";
import Highcharts3D from "highcharts/highcharts-3d.js";
//import oldie from 'highcharts/oldie.js';
//import cylinder from 'highcharts/cylinder.js';
HTreemap(Highcharts); // treemap 类型
Highcharts3D(Highcharts); // 3D 类型
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 {
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: false, // 图表弹窗可见性
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 图片对象
}
},
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();
},
mounted() {
this.$nextTick(() => {
this.loadData();
});
// 获取选择列表字典
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()};
});
this.chartVisible = true;
// 弹窗显示后初始化图表
this.$nextTick(() => {
this.initChart()
// 新增:在图表初始化的 $nextTick 后初始化 Canvas(确保 DOM 已渲染)
this.$nextTick(() => {
// 初始化 Canvas
setTimeout(()=>{
this.initCanvas();
}, 1000)
});
});
},
methods: {
/** 初始化 Canvas 画布及坐标系 */
initCanvas() {
// 获取 Canvas 元素和上下文
this.canvas = this.$refs.canvas; // document.getElementById('mainCanvas')
if (!this.canvas) {
console.error('Canvas 元素未找到');
return;
}
console.log(this.canvas, 'canvas')
this.ctx = this.canvas.getContext('2d');
// 坐标系变换:将原点移至左下角(默认原点在左上角)
this.ctx.translate(0, this.canvas.height); // 向下移动画布高度
this.ctx.scale(1, -1); // 翻转 Y 轴方向(上为正)
// 加载背景图并绘制
this.loadBackgroundImage();
// 加载所有图片并绘制
this.loadAllImages();
},
/** 加载所有图片并绘制 */
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/water.png');
// 加载 po.png
this.poImage = new Image();
this.poImage.src = require('@/assets/images/jrx/po.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('部分图片加载失败');
});
},
/** 绘制所有图片(背景图 + 新增图片) */
drawAllImages() {
// 1. 绘制背景图(原有逻辑)
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(); // 恢复之前的变换
// 3. 绘制 po.png(同样临时恢复默认坐标系)
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.drawImage(
this.poImage,
0, this.canvas.height - 150, // Y坐标调整为画布高度 - 图片高度
879, 150 // 绘制尺寸(与原尺寸一致)
);
this.ctx.restore();
// 3. 绘制 guan0.png(po.png 右侧中间,上层)
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
// 计算位置:
// po.png 右侧 x 坐标 = po.png x + po.png 宽度 = 0 + 879 = 879
// po.png 垂直中间 y 坐标 = po.png y + po.png 高度/2 = (canvas.height - 150) + 75 = canvas.height - 75
// guan0.png 垂直居中需要 y = po.png中间y - guan0.png高度/2 = (canvas.height - 75) - (121/2) = canvas.height - 135.5
const guanX = 879-150;
const guanY = this.canvas.height - 135.5;
this.ctx.drawImage(
this.guanImage,
guanX, guanY, // 目标位置(右侧中间)
6, 121 // 目标尺寸(宽6px,高121px)
);
this.ctx.restore();
// 2. 绘制 water.png(临时恢复默认坐标系,避免翻转)
this.ctx.save(); // 保存当前变换状态
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复默认变换(不翻转)
this.ctx.drawImage(
this.waterImage,
0, this.canvas.height - 255, // Y坐标调整为画布高度 - 图片高度(原点在左下角)
485, 255 // 绘制尺寸(与原尺寸一致)
);
this.ctx.restore(); // 恢复之前的变换
},
/** 加载背景图并绘制 */
loadBackgroundImage() {
this.bgImage = new Image();
this.bgImage.src = require('@/assets/images/jrx/bg.png'); // 路径根据项目实际结构调整
// 图片加载完成后绘制
this.bgImage.onload = () => {
// 绘制背景图(注意:坐标系已变换,Y轴向上)
// 原点在左下角,直接从 (0,0) 开始绘制,高度方向需适配翻转后的坐标系
this.ctx.drawImage(
this.bgImage,
0, 0, // 图片在画布上的起始坐标(左下角原点)
this.canvas.width, // 绘制宽度
this.canvas.height // 绘制高度
);
};
// 处理图片加载错误
this.bgImage.onerror = () => {
console.error('背景图加载失败');
this.ctx.fillStyle = '#f0f0f0';
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
};
},
/** 初始化图表(关键修改:调整图例位置) */
initChart() {
const mockData = this.generateMockData();
console.log('shuju', mockData)
this.chartInstance = Highcharts.chart(this.$refs.chartContainer, {
title: { text: '监测数据趋势图' },
xAxis: { type: 'datetime' },
yAxis: { title: { text: '监测值' } },
legend: { // 新增图例配置
align: 'right', // 水平右对齐
verticalAlign: 'top', // 垂直顶部对齐
layout: 'vertical', // 垂直排列
x: -20, // 微调水平位置
y: 10 // 微调垂直位置
},
series: [{ name: '水位', data: [...mockData.waterLevel] }, { name: '浸润线埋深', data: [...mockData.infiltration] }]
});
},
/** 加载图表数据 */
async loadChartData() {
if (!this.selectedDevice || !this.dateRange[0] || !this.dateRange[1]) {
this.$message.warning('请选择完整查询条件');
return;
}
this.chartLoading = true;
try {
const params = {
deviceId: this.selectedDevice,
startDate: this.dateRange[0],
endDate: this.dateRange[1]
};
// const res = await reqApi.getMonitorData(params); // 替换为实际数据接口
// const mockData = this.generateMockData();
// this.updateChartData(mockData);
} catch (error) {
this.$message.error('加载图表数据失败');
} finally {
this.chartLoading = false;
}
},
/** 生成测试数据 */
generateMockData() {
const [startDate, endDate] = this.dateRange;
const start = new Date(startDate).getTime();
const end = new Date(endDate).getTime();
const days = Math.ceil((end - start) / (1000 * 3600 * 24)); // 计算天数差
const mockData = { waterLevel: [], infiltration: [] };
// 生成每天的模拟数据(时间戳+随机值)
for (let i = 0; i < 30; i++) {
const timestamp = start + i * 1000 * 3600 * 24;
// 水位:10-20之间的随机数(保留2位小数)
mockData.waterLevel.push([timestamp, Number((10 + Math.random() * 10).toFixed(2))]);
// 浸润线埋深:5-15之间的随机数(保留2位小数)
mockData.infiltration.push([timestamp, Number((5 + Math.random() * 10).toFixed(2))]);
}
return mockData;
},
/** 更新图表数据 */
updateChartData(data) {
if (!this.chartInstance) return;
// 假设data结构:{ waterLevel: [], infiltration: [] }
this.chartInstance.series[0].setData(data.waterLevel);
this.chartInstance.series[1].setData(data.infiltration);
},
/** 切换图表类型 */
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);
},
}
}
name: "Dashboard",
components: {
DateRangePicker,
TableFilter,
cuForm,
cuTable,
},
data() {
return {
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: false, // 图表弹窗可见性
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 图片对象
};
},
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();
},
mounted() {
this.$nextTick(() => {
this.loadData();
});
// 获取选择列表字典
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();
}
});
this.chartVisible = true;
// 弹窗显示后初始化图表
this.$nextTick(() => {
this.initChart();
this.initChart1();
// 新增:在图表初始化的 $nextTick 后初始化 Canvas(确保 DOM 已渲染)
this.$nextTick(() => {
// 初始化 Canvas
setTimeout(() => {
this.initCanvas();
}, 1000);
});
});
},
methods: {
/** 初始化 Canvas 画布及坐标系 */
initCanvas() {
// 获取 Canvas 元素和上下文
this.canvas = this.$refs.canvas; // document.getElementById('mainCanvas')
if (!this.canvas) {
console.error("Canvas 元素未找到");
return;
}
console.log(this.canvas, "canvas");
this.ctx = this.canvas.getContext("2d");
// 坐标系变换:将原点移至左下角(默认原点在左上角)
this.ctx.translate(0, this.canvas.height); // 向下移动画布高度
this.ctx.scale(1, -1); // 翻转 Y 轴方向(上为正)
// 加载背景图并绘制
this.loadBackgroundImage();
// 加载所有图片并绘制
this.loadAllImages();
},
/** 加载所有图片并绘制 */
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/water.png");
// 加载 po.png
this.poImage = new Image();
this.poImage.src = require("@/assets/images/jrx/po.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("部分图片加载失败");
});
},
/** 绘制所有图片(背景图 + 新增图片) */
drawAllImages() {
// 1. 绘制背景图(原有逻辑)
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(); // 恢复之前的变换
// 3. 绘制 po.png(同样临时恢复默认坐标系)
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
this.ctx.drawImage(
this.poImage,
0,
this.canvas.height - 150, // Y坐标调整为画布高度 - 图片高度
879,
150 // 绘制尺寸(与原尺寸一致)
);
this.ctx.restore();
// 3. 绘制 guan0.png(po.png 右侧中间,上层)
this.ctx.save();
this.ctx.setTransform(1, 0, 0, 1, 0, 0);
// 计算位置:
// po.png 右侧 x 坐标 = po.png x + po.png 宽度 = 0 + 879 = 879
// po.png 垂直中间 y 坐标 = po.png y + po.png 高度/2 = (canvas.height - 150) + 75 = canvas.height - 75
// guan0.png 垂直居中需要 y = po.png中间y - guan0.png高度/2 = (canvas.height - 75) - (121/2) = canvas.height - 135.5
const guanX = 879 - 150;
const guanY = this.canvas.height - 135.5;
this.ctx.drawImage(
this.guanImage,
guanX,
guanY, // 目标位置(右侧中间)
6,
121 // 目标尺寸(宽6px,高121px)
);
this.ctx.restore();
// 2. 绘制 water.png(临时恢复默认坐标系,避免翻转)
this.ctx.save(); // 保存当前变换状态
this.ctx.setTransform(1, 0, 0, 1, 0, 0); // 恢复默认变换(不翻转)
this.ctx.drawImage(
this.waterImage,
0,
this.canvas.height - 255, // Y坐标调整为画布高度 - 图片高度(原点在左下角)
485,
255 // 绘制尺寸(与原尺寸一致)
);
this.ctx.restore(); // 恢复之前的变换
},
/** 加载背景图并绘制 */
loadBackgroundImage() {
this.bgImage = new Image();
this.bgImage.src = require("@/assets/images/jrx/bg.png"); // 路径根据项目实际结构调整
// 图片加载完成后绘制
this.bgImage.onload = () => {
// 绘制背景图(注意:坐标系已变换,Y轴向上)
// 原点在左下角,直接从 (0,0) 开始绘制,高度方向需适配翻转后的坐标系
this.ctx.drawImage(
this.bgImage,
0,
0, // 图片在画布上的起始坐标(左下角原点)
this.canvas.width, // 绘制宽度
this.canvas.height // 绘制高度
);
};
// 处理图片加载错误
this.bgImage.onerror = () => {
console.error("背景图加载失败");
this.ctx.fillStyle = "#f0f0f0";
this.ctx.fillRect(0, 0, this.canvas.width, this.canvas.height);
};
},
/** 初始化图表(关键修改:调整图例位置) */
initChart() {
return;
const mockData = this.generateMockData();
console.log("shuju", mockData);
this.chartInstance = Highcharts.chart(this.$refs.chartContainer, {
title: { text: "监测数据趋势图" },
xAxis: { type: "datetime" },
yAxis: { title: { text: "监测值" } },
legend: {
// 新增图例配置
align: "right", // 水平右对齐
verticalAlign: "top", // 垂直顶部对齐
layout: "vertical", // 垂直排列
x: -20, // 微调水平位置
y: 10, // 微调垂直位置
},
series: [
{ name: "水位", data: [...mockData.waterLevel] },
{ name: "浸润线埋深", data: [...mockData.infiltration] },
],
});
},
initChart1() {
request({
url: "data/st/imghistory",
method: "get",
params: {
daterange: "2020-06-02,2026-07-11",
sort: "date,desc",
code: "202206201438352828",
},
}).then((res) => {
// const data = res.body;
const data = this.getChartData().body
console.log("data", data);
const chartData = this.seriesDataFormat(data, { datekey: "date" });
// 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,
});
});
},
/**
* 将时间戳格式化为 MM-DD 月份日期格式
* @param {number} timestamp - 毫秒级时间戳 (如 1655707213000)
* @returns {string} 格式化的日期字符串 (如 "05-16")
*/
formatToMonthDay(timestamp) {
// 创建Date对象并验证输入
if (typeof timestamp !== "number" || isNaN(timestamp)) {
console.warn("Invalid timestamp input:", timestamp);
return "--"; // 返回占位符
}
// 使用单个Date对象避免重复创建
const date = new Date(timestamp);
// 获取月份和日期(月份需+1)
const month = date.getUTCMonth() + 1; // UTC月份 (0-11)
const day = date.getUTCDate(); // UTC日期 (1-31)
// 使用模板字符串和padStart格式化
return `${month.toString().padStart(2, "0")}-${day
.toString()
.padStart(2, "0")}`;
},
/**
* series数据格式化
* @param: {Array} list
* @param: {Object} opts [standOut(突出最大值)]
* @example1: qf.UI.showFloatMenu({top:y,left:x, eventOutClose:}, html);
* @return:
* @author: Kimber
* @updatetime: 2022/12/28
* @createtime: 2022/12/28
*/
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 };
},
/**
* 递归继承, 新对象在先, 原对象在后, 节省运行效率
* @param: {Object} inherit // 继承者
* @param: {Object} give // 传承者
* @param: {Function} call // 方法回调, 于在在特殊情况自定义
* @example1: var options = reversExtends(option, opts);
* @return:
* @author: Kimber
* @updatetime: 2022/1/12
* @createtime: 2022/1/12
*/
reversExtends(inherit, give, fn) {
return (function run(main, assist) {
var keys = Object.keys(assist),
i = 0;
return (function loop() {
var key = keys[i];
i++;
if (key) {
fn && fn(key, main, assist);
return (
typeof main[key] === "object"
? run(main[key], assist[key])
: (main[key] = assist[key]),
loop()
);
} else {
return inherit;
}
})();
})(inherit, give);
},
/**
* 根据报警级别识别近两条报警线
* @param: {Number} level
* @param: {Object} value
* @example1: series = discernValidAlarmValue(data.alarm, data.lists, series);
* @return:
* @author: Kimber
* @updatetime: 2022/4/18(周一)
* @createtime: 2022/4/18(周一)
*/
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 };
},
getChartData() {
return {
body: {
alarm: {
alarmLevel: 0,
value: {
red: 2.5,
orange: 2.8,
blue: 3.3,
yellow: 3.1,
},
},
danwei: "m",
lists: [
{
date: 1750694360000,
dateUnit: "",
values: {
depth: "3.620",
stage: "0.960",
},
},
{
date: 1750690760000,
dateUnit: "",
values: {
depth: "3.620",
stage: "0.960",
},
},
{
date: 1750687160000,
dateUnit: "",
values: {
depth: "3.610",
stage: "0.970",
},
},
{
date: 1750683561000,
dateUnit: "",
values: {
depth: "3.610",
stage: "0.970",
},
},
{
date: 1750679961000,
dateUnit: "",
values: {
depth: "3.620",
stage: "0.960",
},
},
{
date: 1750676361000,
dateUnit: "",
values: {
depth: "3.640",
stage: "0.940",
},
},
{
date: 1750672761000,
dateUnit: "",
values: {
depth: "3.650",
stage: "0.930",
},
},
{
date: 1750669161000,
dateUnit: "",
values: {
depth: "3.680",
stage: "0.900",
},
},
{
date: 1750665561000,
dateUnit: "",
values: {
depth: "3.720",
stage: "0.860",
},
},
{
date: 1750661961000,
dateUnit: "",
values: {
depth: "3.760",
stage: "0.820",
},
},
{
date: 1750658361000,
dateUnit: "",
values: {
depth: "3.780",
stage: "0.800",
},
},
{
date: 1750654761000,
dateUnit: "",
values: {
depth: "3.800",
stage: "0.780",
},
},
{
date: 1750651161000,
dateUnit: "",
values: {
depth: "3.810",
stage: "0.770",
},
},
{
date: 1750647561000,
dateUnit: "",
values: {
depth: "3.800",
stage: "0.780",
},
},
{
date: 1750643961000,
dateUnit: "",
values: {
depth: "3.800",
stage: "0.780",
},
},
{
date: 1750640361000,
dateUnit: "",
values: {
depth: "3.790",
stage: "0.790",
},
},
{
date: 1750636760000,
dateUnit: "",
values: {
depth: "3.780",
stage: "0.800",
},
},
{
date: 1750633160000,
dateUnit: "",
values: {
depth: "3.770",
stage: "0.810",
},
},
{
date: 1750629560000,
dateUnit: "",
values: {
depth: "3.760",
stage: "0.820",
},
},
{
date: 1750625960000,
dateUnit: "",
values: {
depth: "3.740",
stage: "0.840",
},
},
{
date: 1750622360000,
dateUnit: "",
values: {
depth: "3.730",
stage: "0.850",
},
},
{
date: 1750618760000,
dateUnit: "",
values: {
depth: "3.730",
stage: "0.850",
},
},
{
date: 1750615160000,
dateUnit: "",
values: {
depth: "3.710",
stage: "0.870",
},
},
{
date: 1750611560000,
dateUnit: "",
values: {
depth: "3.710",
stage: "0.870",
},
},
],
names: [
{
name: "水位1222",
key: "stage",
},
{
name: "浸润线埋深",
key: "depth",
},
],
range: "",
sensorname: "浸润线 ZQ1",
title: "",
},
code: 200,
msg: "",
};
},
/** 加载图表数据 */
async loadChartData() {
if (!this.selectedDevice || !this.dateRange[0] || !this.dateRange[1]) {
this.$message.warning("请选择完整查询条件");
return;
}
this.chartLoading = true;
try {
const params = {
deviceId: this.selectedDevice,
startDate: this.dateRange[0],
endDate: this.dateRange[1],
};
// const res = await reqApi.getMonitorData(params); // 替换为实际数据接口
// const mockData = this.generateMockData();
// this.updateChartData(mockData);
} catch (error) {
this.$message.error("加载图表数据失败");
} finally {
this.chartLoading = false;
}
},
/** 生成测试数据 */
generateMockData() {
const [startDate, endDate] = this.dateRange;
const start = new Date(startDate).getTime();
const end = new Date(endDate).getTime();
const days = Math.ceil((end - start) / (1000 * 3600 * 24)); // 计算天数差
const mockData = { waterLevel: [], infiltration: [] };
// 生成每天的模拟数据(时间戳+随机值)
for (let i = 0; i < 30; i++) {
const timestamp = start + i * 1000 * 3600 * 24;
// 水位:10-20之间的随机数(保留2位小数)
mockData.waterLevel.push([
timestamp,
Number((10 + Math.random() * 10).toFixed(2)),
]);
// 浸润线埋深:5-15之间的随机数(保留2位小数)
mockData.infiltration.push([
timestamp,
Number((5 + Math.random() * 10).toFixed(2)),
]);
}
return mockData;
},
/** 更新图表数据 */
updateChartData(data) {
if (!this.chartInstance) return;
// 假设data结构:{ waterLevel: [], infiltration: [] }
this.chartInstance.series[0].setData(data.waterLevel);
this.chartInstance.series[1].setData(data.infiltration);
},
/** 切换图表类型 */
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{
}
.qyzz {
}
</style>
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