父组件
javascript
<template>
<div class="app-container">
<div class="content">
<el-form
:model="echartsqueryParams"
ref="echartsqueryForm"
:inline="true"
>
<el-form-item label="号" prop="furnaceNumber">
<el-select
v-model="echartsqueryParams.furnaceNumber"
placeholder="请选择"
style="width: 150px"
>
<el-option
v-for="item in deviceOptions"
:key="item"
:label="item"
:value="item"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="日期">
<el-date-picker
v-model="timeData"
popper-class="noClear"
size="small"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
range-separator="-"
start-placeholder="开始日期"
end-placeholder="结束日期"
:clearable="false"
@change="handleChange"
></el-date-picker>
</el-form-item>
<el-form-item label="真空">
<el-input
style="width: 150px"
v-model="csValue"
placeholder="请输入"
type="number"
@blur="inputChange"
/>
</el-form-item>
<el-form-item label="时间间隔" prop="interval">
<el-select
v-model="echartsqueryParams.interval"
placeholder="请选择"
style="width: 150px"
>
<el-option
v-for="item in options"
:key="item.value"
:label="item.label"
:value="item.value"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item>
<el-button
icon="el-icon-printer"
type="primary"
@click="ceshi = !ceshi"
size="mini"
>打印</el-button
>
<el-button
type="primary"
icon="el-icon-search"
size="mini"
@click="handleQuery_echarts"
>搜索</el-button
>
<el-button
icon="el-icon-refresh"
size="mini"
@click="resetQuery_echarts"
>重置</el-button
>
</el-form-item>
</el-form>
<div>
<el-checkbox-group
v-model="checkedCities"
@change="handleCheckedCitiesChange"
>
<el-checkbox v-for="city in cities" :label="city" :key="city">{{
city
}}</el-checkbox>
</el-checkbox-group>
</div>
<div style="margin-bottom: 15px"></div>
<div
v-loading="echartsLoading"
style="width: 100%; height: calc(100% - 40px - 60px)"
>
<moreyaxis
v-if="axiosData.length > 0"
:legend="legend"
:axiosData="axiosData"
:yaxiosData="yaxios"
:serios="serios"
:colors="colors"
:gridRIght="gridRIght"
></moreyaxis>
<el-empty description="暂无数据" v-else></el-empty>
</div>
</div>
<!-- 打印内容 -->
<el-dialog
class="dialogPrint"
title="请确认打印内容"
:visible.sync="ceshi"
style="height: 90vh"
>
<div id="printMe" style="width: 100%">
<div style="width: 1px; height: 1px"></div>
<div v-if="echartsList.nodeTimes.length > 0">
<div style="display: flex">
<p style="margin: 0; margin-right: 20px">
号:{{ echartsqueryParams.furnaceNumber }}
</p>
<p style="margin: 0; margin-right: 20px">
开始时间:{{ echartsList.nodeTimes[0] }}
</p>
<p style="margin: 0">
结束时间:{{
echartsList.nodeTimes[Number(echartsList.nodeTimes.length) - 1]
}}
</p>
</div>
<ul
style="
padding: 0;
display: grid;
grid-template-columns: repeat(4, 1fr);
grid-column-gap: 10px;
grid-row-gap: 10px;
"
>
<li v-for="(item, index) in echartsList.series" :key="index">
<p
style="
width: 100%;
border: 1px solid #f9f9f9;
padding: 10px;
margin: 0;
"
>
<span
style="border-left: 3px solid #4068e0; padding-left: 5px"
>{{ item.name }}</span
>
</p>
<div style="display: flex">
<p
style="
font-size: 12px;
margin: 0;
width: 50%;
border: 1px solid #f9f9f9;
border-top: none;
padding: 10px;
"
>
开始{{ item.name.substring(item.name.length - 2) }}:{{
item.values[0]
}}{{ item.unit }}
</p>
<p
style="
font-size: 12px;
margin: 0;
width: 50%;
border: 1px solid #f9f9f9;
border-top: none;
padding: 10px;
"
>
结束{{ item.name.substring(item.name.length - 2) }}:{{
item.values[Number(item.values.length) - 1]
}}{{ item.unit }}
</p>
</div>
</li>
</ul>
</div>
<div style="width: 100%; height: 500px" v-if="axiosData.length > 0">
<moreyaxis
style="width: 100%; height: 500px"
:legend="legend"
:axiosData="axiosData"
:yaxiosData="yaxios"
:serios="serios"
:colors="colors"
:gridRIght="gridRIght"
></moreyaxis>
</div>
</div>
<div slot="footer">
<el-button @click="ceshi = false">取 消</el-button>
<el-button type="primary" v-print="printObj">确 定</el-button>
</div>
</el-dialog>
</div>
</template>
<script>
import moreyaxis from "@/views/echarts/moreyaxis.vue";
import { echartsData } from "@/api/collocation/records";
export default {
components: { moreyaxis },
data() {
return {
deviceOptions: [28030002, 13020101, 28030003, 13020100],
ceshi: false,
// 图表参数
start: true,
echartsList: {
nodeTimes: [],
series: [],
},
echartsLoading: true,
colors: ["#6488FE", "#FF6F6F", "#FEE177", "#2AF0CF", "#08BE87"],
legend: [],
axiosData: [],
serios: [],
yaxios: [],
// 查询参数
echartsqueryParams: {
startTime: null,
endTime: null,
interval: 30,
id: null,
// limit: 200,
furnaceNumber: null,
},
timeData: [],
options: [
{
label: "十秒",
value: 10,
},
{
label: "三十秒",
value: 30,
},
{
label: "一分钟",
value: 60,
},
{
label: "五分钟",
value: 300,
},
{
label: "十分钟",
value: 600,
},
{
label: "十五分钟",
value: 900,
},
{
label: "半小时",
value: 1800,
},
{
label: "一小时",
value: 3600,
},
],
printObj: {
id: "printMe",
popTitle: "记录",
closeCallback: this.closeCallback,
},
detailsTable: [],
gridRIght: 0,
csValue: null,
cities: [],
cityOptions: [],
checkAll: false,
isIndeterminate: true,
checkedCities: [],
data: {},
};
},
methods: {
handleChange(value) {
if (value) {
const start = new Date(value[0]);
const end = new Date(value[1]);
if (end - start > 24 * 60 * 60 * 1000) {
this.$message.error("结束时间不能大于开始时间一天");
this.timeData = null; // 清空选择
}
}
},
// 设备列表
deviceData() {
listDevice().then((res) => [console.log(res)]);
},
handleCheckedCitiesChange(value) {
this.echartsSerios(this.data.series);
},
// 导出
handleExport_echarts() {},
// 图表搜索
handleQuery_echarts() {
this.echartsLoading = true;
this.echartsDataList();
},
// 图表重置
resetQuery_echarts() {
this.updateCurrentTime();
this.echartsLoading = true;
this.echartsqueryParams.interval = null;
this.echartsDataList();
},
// 图表数据解析
echartsDataList(row) {
this.echartsqueryParams.startTime = this.timeData[0];
this.echartsqueryParams.endTime = this.timeData[1];
echartsData(this.echartsqueryParams).then((res) => {
this.data = res.data;
this.checkedCities = this.data.series.map((i) => i.name);
this.cityOptions = this.data.series.map((i) => i.name);
this.cities = this.data.series.map((i) => i.name);
this.axiosData = this.data.nodeTimes;
this.legend = this.data.series.map((i) => i.name);
this.echartsLoading = false;
this.echartsList = JSON.parse(JSON.stringify(this.data));
this.echartsList.series = this.echartsList.series.filter(
(item) => item.name !== "真空"
);
this.echartsSerios(this.data.series);
});
},
echartsSerios(data) {
this.serios = data.map((i, index) => {
const name = {
name: i.name,
type: "line",
yAxisIndex: index,
data: i.values,
unit: i.unit,
z: 5,
zLevel: 5,
symbolSize: 10,
label: {
show: true,
color: "#999999", // 标签文字颜色
fontSize: 12, // 标签文字大小
fontWeight: 400,
avoidLabelOverlap: true,
overlap: false,
formatter: function (params) {
if (params.dataIndex % 4 === 0) {
return params.value;
} else {
return ""; // 不显示标签以避免重叠
}
},
},
};
if (i.name === "真空" && this.csValue != 0) {
name.markLine = {
data: [{ type: "average", name: i.name, yAxis: this.csValue }],
};
}
return name;
});
let currentOffset = 0; // 总体的偏移量
let nameLengthOffset = 0;
this.yaxios = data.map((i, index) => {
nameLengthOffset = i.name.length * 20; // 名称长度偏移量
const name = {
type: "value",
name: i.name,
position: "right",
alignTicks: true,
offset: currentOffset,
axisLine: {
show: true,
lineStyle: {
color: this.colors[index % this.colors.length],
},
},
axisLabel: {
formatter: "{value}" + i.unit,
},
axisTick: {
show: true,
},
};
// 判断哪个轴不显示
if (this.checkedCities.indexOf(i.name) > -1) {
name.show = true;
} else {
name.show = false;
nameLengthOffset = nameLengthOffset - i.name.length * 20;
}
if (i.yAxisMin != null) {
name.min = i.yAxisMin;
}
if (i.yAxisMax != null) {
name.max = i.yAxisMax;
}
currentOffset += nameLengthOffset; // 更新偏移量
return name;
});
this.gridRIght = currentOffset - nameLengthOffset / 2; // 更新网格右侧偏移量
},
updateCurrentTime() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
const hours = String(now.getHours()).padStart(2, "0");
const minutes = String(now.getMinutes()).padStart(2, "0");
const seconds = String(now.getSeconds()).padStart(2, "0");
// 获取前一个小时的时间
const previousHour = new Date(now.getTime() - 3600000); // 3600000 毫秒 = 1 小时
const previousYear = previousHour.getFullYear();
const previousMonth = String(previousHour.getMonth() + 1).padStart(
2,
"0"
);
const previousDay = String(previousHour.getDate()).padStart(2, "0");
const previousHours = String(previousHour.getHours()).padStart(2, "0");
const previousMinutes = String(previousHour.getMinutes()).padStart(
2,
"0"
);
const previousSeconds = String(previousHour.getSeconds()).padStart(
2,
"0"
);
this.$set(this, "timeData", [
`${previousYear}-${previousMonth}-${previousDay} ${previousHours}:${previousMinutes}:${previousSeconds}`,
`${year}-${month}-${day} ${hours}:${minutes}:${seconds}`,
]);
// this.echartsqueryParams.furnaceNumber = 13020100;
// this.timeData = ["2025-09-08 00:00:00", "2025-09-08 00:03:00"];
},
inputChange(value) {
this.echartsDataList();
},
},
created() {
this.echartsqueryParams.furnaceNumber = this.deviceOptions[0];
this.updateCurrentTime();
},
mounted() {
this.echartsDataList();
},
};
</script>
<style lang="scss" scoped>
ul li {
list-style: none;
}
.app-container {
width: 100%;
height: calc(100vh - 100px);
.content {
width: 100%;
height: 100%;
overflow: auto;
}
}
::v-deep .el-dialog {
width: 80vw !important;
border-radius: 8px;
margin-bottom: 0;
margin-top: 5vh !important;
display: flex;
flex-direction: column;
max-height: calc(90vh - 100px);
overflow: hidden;
box-sizing: border-box;
.el-dialog__header {
padding-top: 14px;
}
.el-dialog__body {
margin: 0 20px 20px 20px;
padding: 0;
overflow: auto;
}
}
@media print {
@page {
size: auto;
// margin: 3mm;
}
}
</style>
子组件
javascript
<template>
<div ref="chartRef" style="width: 100%; height: 100%"></div>
</template>
<script>
import * as echarts from "echarts";
export default {
name: "moreyaxis",
props: {
legend: {
type: Array,
default: () => [],
},
axiosData: {
type: Array,
default: () => [],
},
yaxiosData: {
type: Array,
default: () => [],
},
serios: {
type: Array,
default: () => [],
},
colors: {
type: Array,
default: () => [],
},
gridRIght: {
type: Number,
default: () => 0,
},
},
data() {
return {
chartInstance: null,
selectedPoints: [], // 用于存储选中的节点
};
},
watch: {
yaxiosData: {
deep: true,
handler() {
this.selectedPoints = [];
this.destroyChart();
this.initChart();
},
},
},
mounted() {
if (this.serios.length > 0) {
this.initChart();
}
this.setupResizeObserver();
},
beforeDestroy() {
if (this.chartInstance) {
this.chartInstance.dispose();
}
},
methods: {
initChart() {
if (!this.chartInstance) {
this.chartInstance = echarts.init(this.$refs.chartRef);
}
let currentData = null; //
const option = {
color: this.colors,
tooltip: {
trigger: "axis",
axisPointer: {
type: "cross",
},
textStyle: {
fontSize: 10,
},
// triggerOn: "click",
alwaysShowContent: false, // 关键:永久显示
enterable: false,
show: true, // 默认不显示
},
dataZoom: [
{
type: "inside",
},
],
grid: {
top: "60px",
left: "40px",
bottom: "40px",
right: this.gridRIght + "px",
},
legend: {
data: this.legend,
top: 0,
},
xAxis: [
{
boundaryGap: false,
type: "category",
axisTick: {
alignWithLabel: true,
},
data: this.axiosData,
axisLabel: {
formatter: function (params) {
var newParamsName = "";
var paramsNameNumber = params.length;
var provideNumber = 10;
var rowNumber = Math.ceil(paramsNameNumber / provideNumber);
for (let row = 0; row < rowNumber; row++) {
newParamsName +=
params.substring(
row * provideNumber,
(row + 1) * provideNumber
) + "\n";
}
return newParamsName;
},
color: "#999",
},
},
],
yAxis: this.yaxiosData,
series: this.serios,
graphic: {
elements: this.selectedPoints.map((point) => ({
type: "group",
children: [
{
z: 10,
zLevel: 10,
type: "rect",
shape: { width: 140, height: 90 },
style: {
fill: "#FFF",
// stroke: "#666", // 边框颜色
// lineWidth: 1, // 边框宽度
shadowBlur: 5, // 阴影模糊度
shadowColor: "#666", // 阴影颜色
shadowOffsetX: 2, // 阴影水平偏移
shadowOffsetY: 2, // 阴影垂直偏移
padding: ["10px", "5px"],
},
},
{
type: "text",
z: 10,
zLevel: 10,
style: {
text: this.formatTooltipContent(point),
fill: "#999",
fontSize: 12,
textBaseline: "middle",
padding: [10, 0, 0, 0],
},
position: [10, 0],
},
],
position: [point.x, point.y],
draggable: true,
})),
},
};
this.chartInstance.setOption(option);
// 监听点击事件
this.chartInstance.on("click", (params) => {
if (params.componentType === "series") {
const existingPoint = this.selectedPoints.find(
(point) =>
point.seriesName === params.seriesName &&
point.dataIndex === params.dataIndex
);
if (existingPoint) {
this.selectedPoints = this.selectedPoints.filter(
(point) =>
point.seriesName !== params.seriesName ||
point.dataIndex !== params.dataIndex
);
} else {
this.selectedPoints.push({
seriesIndex: params.seriesIndex,
name: params.name,
seriesName: params.seriesName,
value: params.value,
x: params.event.offsetX,
y: params.event.offsetY,
dataIndex: params.dataIndex,
});
}
// this.selectedPoints.forEach((item) => {
// this.chartInstance.dispatchAction({
// type: "showTip",
// // 系列的 index,在 tooltip 的 trigger 为 axis 的时候可选。
// seriesIndex: item.seriesIndex,
// // 数据项的 index,如果不指定也可以通过 name 属性根据名称指定数据项
// dataIndex: item.dataIndex,
// });
// });
this.destroyChart();
this.initChart();
}
});
},
formatTooltipContent(point) {
// 格式化 tooltip 内容,显示多个轴的数据
let content = `${this.axiosData[point.dataIndex]}\n`;
this.serios.forEach((series) => {
if (series.data[point.dataIndex] !== undefined) {
content += `${series.name}: ${series.data[point.dataIndex]}${
series.unit
}\n`;
}
});
return content;
},
destroyChart() {
if (this.chartInstance) {
this.chartInstance.dispose();
this.chartInstance = null;
}
},
setupResizeObserver() {
const resizeObserver = new ResizeObserver((entries) => {
for (let entry of entries) {
if (entry.target === this.$refs.chartRef) {
if (this.chartInstance) {
this.chartInstance.resize();
}
}
}
});
resizeObserver.observe(this.$refs.chartRef);
},
},
};
</script>
<style scoped>
/* 添加一些样式 */
</style>
源代码记录