|
|
@@ -1,21 +1,7 @@
|
|
|
<template>
|
|
|
- <div>
|
|
|
- <!-- <div class="FD">
|
|
|
- <div v-if="TZshow" class="eigenvalue">
|
|
|
- <h5>特征值</h5>
|
|
|
- <p>有效值:{{ this.timeList.Xrms }}</p>
|
|
|
- <p>平均值:{{ this.timeList.mean_value }}</p>
|
|
|
- <p>最大值:{{ this.timeList.max_value }}</p>
|
|
|
- <p>最小值:{{ this.timeList.min_value }}</p>
|
|
|
- <p>峰值:{{ this.timeList.Xp }}</p>
|
|
|
- <p>峰峰值:{{ this.timeList.Xpp }}</p>
|
|
|
- <p>波形因子:{{ this.timeList.Sf }}</p>
|
|
|
- <p>脉冲指标:{{ this.timeList.If }}</p>
|
|
|
- <p>裕度指标:{{ this.timeList.Ce }}</p>
|
|
|
- <p>偏度指标:{{ this.timeList.Cw }}</p>
|
|
|
- <p>峭度指标:{{ this.timeList.Cq }}</p>
|
|
|
- </div>
|
|
|
- </div> -->
|
|
|
+ <div v-loading="loading">
|
|
|
+ <!-- ECharts 图表容器 -->
|
|
|
+ <div class="line-chart" ref="chart"></div>
|
|
|
<div class="control-panel">
|
|
|
<!-- 光标 -->
|
|
|
<div class="panel-block">
|
|
|
@@ -31,33 +17,34 @@
|
|
|
</span>
|
|
|
</div>
|
|
|
</div>
|
|
|
-
|
|
|
- <!-- 特征值 -->
|
|
|
- <div class="panel-block">
|
|
|
- <span class="label">特征值</span>
|
|
|
- <div class="btn-group">
|
|
|
- <span
|
|
|
- v-for="item in TZFeatureList"
|
|
|
- :key="item.key"
|
|
|
- :class="['btn', activeFeatures.includes(item.key) ? 'active' : '']"
|
|
|
- @click="toggleTZFeature(item.key)"
|
|
|
- >
|
|
|
- {{ item.label }}
|
|
|
- </span>
|
|
|
- </div>
|
|
|
+ <!-- 特征值(多选) -->
|
|
|
+ <div class="panel-block full-width">
|
|
|
+ <span class="label">特征 </span>
|
|
|
+ <el-cascader
|
|
|
+ v-model="selectedFeatures"
|
|
|
+ :options="cascaderOptions"
|
|
|
+ :props="cascaderProps"
|
|
|
+ collapse-tags
|
|
|
+ clearable
|
|
|
+ placeholder="请选择特征"
|
|
|
+ size="small"
|
|
|
+ @change="handleCascaderChange"
|
|
|
+ />
|
|
|
</div>
|
|
|
</div>
|
|
|
- <!-- ECharts 图表容器 -->
|
|
|
- <div class="line-chart" ref="chart" v-loading="loadingTime"></div>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
import axios from "axios";
|
|
|
import * as echarts from "echarts"; // 导入 echarts 库
|
|
|
-
|
|
|
+import cursorReferenceMixin from "./spectrogramcharts/cursorReferenceMixin";
|
|
|
+import Bdgb from "./spectrogramcharts/Bdgb";
|
|
|
+import Xdgb from "./spectrogramcharts/Xdgb";
|
|
|
+import Tjgb from "./spectrogramcharts/Tjgb";
|
|
|
export default {
|
|
|
name: "TimedomainCharts", // 组件名称
|
|
|
+ mixins: [cursorReferenceMixin, Bdgb, Xdgb, Tjgb],
|
|
|
props: {
|
|
|
currentIndex: {
|
|
|
type: Number,
|
|
|
@@ -67,7 +54,7 @@ export default {
|
|
|
type: Number,
|
|
|
default: 0,
|
|
|
},
|
|
|
- loadingTime: {
|
|
|
+ loading: {
|
|
|
type: Boolean,
|
|
|
default: false,
|
|
|
},
|
|
|
@@ -95,13 +82,44 @@ export default {
|
|
|
TZshow: false,
|
|
|
timeList: {},
|
|
|
manualMarks: [],
|
|
|
+ // 时域:多单指针
|
|
|
+ singlePointerPoints: [],
|
|
|
+ activeSinglePointer: null,
|
|
|
+ // 时域:边带
|
|
|
+ sidebandCursorVisible: false,
|
|
|
+ sidebandCursorPoints: [],
|
|
|
// 光标
|
|
|
GBcheckList: [
|
|
|
{ val: "单指针" },
|
|
|
- { val: "频带光标" },
|
|
|
- { val: "边带光标" },
|
|
|
+ // { val: "谐波光标" },
|
|
|
+ // { val: "边带光标" },
|
|
|
],
|
|
|
checkedGB: [],
|
|
|
+ featureGroups: [
|
|
|
+ {
|
|
|
+ label: "时域特征",
|
|
|
+ type: "time",
|
|
|
+ children: [
|
|
|
+ { key: "Xrms", label: "有效值" },
|
|
|
+ { key: "mean_value", label: "平均值" },
|
|
|
+ { key: "max_value", label: "最大值" },
|
|
|
+ { key: "min_value", label: "最小值" },
|
|
|
+ { key: "Xp", label: "峰值" },
|
|
|
+ { key: "Xpp", label: "峰峰值" },
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ selectedFeatures: [],
|
|
|
+ cascaderProps: {
|
|
|
+ // multiple: true, // 多选
|
|
|
+ // checkStrictly: false, // 不能选父节点(只选叶子)
|
|
|
+ // emitPath: true, // 返回路径(重要)
|
|
|
+ multiple: true,
|
|
|
+ checkStrictly: false,
|
|
|
+ emitPath: true, // 保留(你需要路径)
|
|
|
+ value: "value",
|
|
|
+ label: "label",
|
|
|
+ },
|
|
|
// 特征值按钮
|
|
|
TZFeatureList: [
|
|
|
{ key: "Xrms", label: "有效值" },
|
|
|
@@ -114,6 +132,18 @@ export default {
|
|
|
activeFeatures: [],
|
|
|
};
|
|
|
},
|
|
|
+ computed: {
|
|
|
+ cascaderOptions() {
|
|
|
+ return this.featureGroups.map((group) => ({
|
|
|
+ label: group.label,
|
|
|
+ value: group.type, // ✅ 改这里
|
|
|
+ children: group.children.map((item) => ({
|
|
|
+ label: item.label,
|
|
|
+ value: item.key,
|
|
|
+ })),
|
|
|
+ }));
|
|
|
+ },
|
|
|
+ },
|
|
|
watch: {
|
|
|
// 监听 chartData 和 chartLabels 的变化,重新绘制图表
|
|
|
chartData(newData) {
|
|
|
@@ -140,239 +170,359 @@ export default {
|
|
|
},
|
|
|
mounted() {
|
|
|
this.$nextTick(() => {
|
|
|
- setTimeout(() => {
|
|
|
- this.initializeChart(); // 延迟2秒后调用
|
|
|
- this.getTime();
|
|
|
- }, 500); // 2000毫秒,即2秒
|
|
|
+ this.initializeChart();
|
|
|
});
|
|
|
},
|
|
|
-
|
|
|
+ beforeDestroy() {
|
|
|
+ if (this.chartInstance) {
|
|
|
+ this.chartInstance.getZr().off("click", this.handleSinglePointerClick);
|
|
|
+ this.chartInstance.getZr().off("click", this.handleSidebandCursorClick);
|
|
|
+ this.chartInstance.dispose();
|
|
|
+ this.chartInstance = null;
|
|
|
+ }
|
|
|
+ },
|
|
|
methods: {
|
|
|
- initializeChart() {
|
|
|
- const chartDom = this.$refs.chart;
|
|
|
- if (chartDom && !this.chartInstance) {
|
|
|
- this.chartInstance = echarts.init(chartDom);
|
|
|
- }
|
|
|
- if (this.timeList.y && this.timeList.x) {
|
|
|
- this.updateChart(this.timeList.y, this.timeList.x);
|
|
|
+ getChartXYData() {
|
|
|
+ return {
|
|
|
+ x: this.timeList?.x || [],
|
|
|
+ y: this.timeList?.y || [],
|
|
|
+ };
|
|
|
+ },
|
|
|
+ findClosestIndex(target, xData) {
|
|
|
+ if (!Array.isArray(xData) || !xData.length) return -1;
|
|
|
+ let closestIndex = 0;
|
|
|
+ let minDiff = Math.abs(Number(xData[0]) - target);
|
|
|
+ for (let i = 1; i < xData.length; i++) {
|
|
|
+ const diff = Math.abs(Number(xData[i]) - target);
|
|
|
+ if (diff < minDiff) {
|
|
|
+ minDiff = diff;
|
|
|
+ closestIndex = i;
|
|
|
+ }
|
|
|
}
|
|
|
+ return closestIndex;
|
|
|
+ },
|
|
|
+ bindSinglePointerClick() {
|
|
|
+ if (!this.chartInstance) return;
|
|
|
+ this.chartInstance.getZr().off("click", this.handleSinglePointerClick);
|
|
|
+ this.chartInstance.getZr().on("click", this.handleSinglePointerClick);
|
|
|
+ },
|
|
|
+ unbindSinglePointerClick() {
|
|
|
+ if (!this.chartInstance) return;
|
|
|
+ this.chartInstance.getZr().off("click", this.handleSinglePointerClick);
|
|
|
+ },
|
|
|
+ enableSinglePointer() {
|
|
|
+ this.bindSinglePointerClick();
|
|
|
},
|
|
|
+ disableSinglePointer() {
|
|
|
+ this.unbindSinglePointerClick();
|
|
|
+ this.singlePointerPoints = [];
|
|
|
+ this.activeSinglePointer = null;
|
|
|
+ this.removeSinglePointerSeries();
|
|
|
+ },
|
|
|
+ handleSinglePointerClick(event) {
|
|
|
+ if (!this.checkedGB.includes("单指针")) return;
|
|
|
+ if (this.checkedGB.includes("边带光标")) return; // 边带模式冻结单指针
|
|
|
+ if (!this.chartInstance) return;
|
|
|
+
|
|
|
+ const { x, y } = this.getChartXYData();
|
|
|
+ if (!x.length || !y.length) return;
|
|
|
+
|
|
|
+ const pointInGrid = this.chartInstance.convertFromPixel(
|
|
|
+ { seriesIndex: 0 },
|
|
|
+ [event.offsetX, event.offsetY],
|
|
|
+ );
|
|
|
+ const xClick = pointInGrid?.[0];
|
|
|
+ if (xClick == null || Number.isNaN(xClick)) return;
|
|
|
+
|
|
|
+ const idx = this.findClosestIndex(xClick, x);
|
|
|
+ if (idx < 0) return;
|
|
|
+
|
|
|
+ const xAxis = Number(x[idx]);
|
|
|
+ const yValue = Number(y[idx]);
|
|
|
+ if (Number.isNaN(xAxis) || Number.isNaN(yValue)) return;
|
|
|
+
|
|
|
+ const existed = this.singlePointerPoints.some(
|
|
|
+ (p) => Number(p.xAxis) === xAxis,
|
|
|
+ );
|
|
|
+ if (!existed) {
|
|
|
+ this.singlePointerPoints.push({
|
|
|
+ xAxis,
|
|
|
+ val: yValue.toFixed(6),
|
|
|
+ });
|
|
|
+ }
|
|
|
|
|
|
- renderManualMarks(marks) {
|
|
|
+ // 默认最后一次点击的单指针作为边带中心(0线)
|
|
|
+ this.activeSinglePointer = {
|
|
|
+ xAxis,
|
|
|
+ val: yValue.toFixed(6),
|
|
|
+ };
|
|
|
+
|
|
|
+ this.applySinglePointerSeries();
|
|
|
+ },
|
|
|
+ applySinglePointerSeries() {
|
|
|
+ if (!this.chartInstance) return;
|
|
|
const option = this.chartInstance.getOption();
|
|
|
+ const otherSeries = (option.series || []).filter(
|
|
|
+ (s) => s && s.id !== "SINGLE_POINTER_LINE",
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!this.singlePointerPoints.length) {
|
|
|
+ this.chartInstance.setOption(
|
|
|
+ { series: otherSeries },
|
|
|
+ { replaceMerge: ["series"] },
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- const manualSeries = {
|
|
|
- id: "MANUAL_MARK",
|
|
|
+ const seriesConfig = {
|
|
|
+ id: "SINGLE_POINTER_LINE",
|
|
|
type: "line",
|
|
|
+ data: [],
|
|
|
+ silent: true,
|
|
|
+ z: 20,
|
|
|
markLine: {
|
|
|
+ silent: false,
|
|
|
symbol: ["none", "none"],
|
|
|
lineStyle: {
|
|
|
color: "#ff0000",
|
|
|
type: "dashed",
|
|
|
+ width: 2,
|
|
|
},
|
|
|
label: {
|
|
|
show: true,
|
|
|
+ position: "end",
|
|
|
+ formatter: (params) => params.data?.val || "",
|
|
|
},
|
|
|
- data: marks,
|
|
|
+ data: this.singlePointerPoints.map((p) => ({
|
|
|
+ xAxis: p.xAxis,
|
|
|
+ val: p.val,
|
|
|
+ })),
|
|
|
},
|
|
|
};
|
|
|
|
|
|
- this.chartInstance.setOption({
|
|
|
- series: [
|
|
|
- ...option.series.filter((s) => s.id !== "MANUAL_MARK"),
|
|
|
- manualSeries,
|
|
|
- ],
|
|
|
- });
|
|
|
+ this.chartInstance.setOption(
|
|
|
+ { series: [...otherSeries, seriesConfig] },
|
|
|
+ { replaceMerge: ["series"] },
|
|
|
+ );
|
|
|
},
|
|
|
- selectCursor(val) {
|
|
|
- if (this.checkedGB.includes(val)) {
|
|
|
- this.checkedGB = [];
|
|
|
- } else {
|
|
|
- this.checkedGB = [val];
|
|
|
- }
|
|
|
- this.chartInstance.off("click");
|
|
|
- this.applyCursor(); // 👈 核心入口
|
|
|
+ removeSinglePointerSeries() {
|
|
|
+ if (!this.chartInstance) return;
|
|
|
+ const option = this.chartInstance.getOption();
|
|
|
+ const series = (option.series || []).filter(
|
|
|
+ (s) => s && s.id !== "SINGLE_POINTER_LINE",
|
|
|
+ );
|
|
|
+ this.chartInstance.setOption({ series }, { replaceMerge: ["series"] });
|
|
|
},
|
|
|
- toggleTZFeature(key) {
|
|
|
- const i = this.activeFeatures.indexOf(key);
|
|
|
-
|
|
|
- if (i > -1) {
|
|
|
- this.activeFeatures.splice(i, 1);
|
|
|
- } else {
|
|
|
- this.activeFeatures.push(key);
|
|
|
+ enableSidebandCursor() {
|
|
|
+ this.sidebandCursorVisible = true;
|
|
|
+ if (!this.chartInstance) return;
|
|
|
+ // 边带模式:只响应边带点击,冻结单指针
|
|
|
+ this.unbindSinglePointerClick();
|
|
|
+ this.chartInstance.getZr().off("click", this.handleSidebandCursorClick);
|
|
|
+ this.chartInstance.getZr().on("click", this.handleSidebandCursorClick);
|
|
|
+ },
|
|
|
+ disableSidebandCursor() {
|
|
|
+ this.sidebandCursorVisible = false;
|
|
|
+ if (this.chartInstance) {
|
|
|
+ this.chartInstance.getZr().off("click", this.handleSidebandCursorClick);
|
|
|
+ }
|
|
|
+ this.removeSidebandCursor();
|
|
|
+ if (this.checkedGB.includes("单指针")) {
|
|
|
+ this.bindSinglePointerClick();
|
|
|
}
|
|
|
-
|
|
|
- this.renderTZFeatures();
|
|
|
},
|
|
|
- renderTZFeatures() {
|
|
|
- const marks = this.activeFeatures.map((key) => ({
|
|
|
- yAxis: this.timeList[key],
|
|
|
- label: {
|
|
|
- formatter: key,
|
|
|
- },
|
|
|
- }));
|
|
|
+ handleSidebandCursorClick(event) {
|
|
|
+ if (!this.sidebandCursorVisible || !this.chartInstance) return;
|
|
|
+ if (!this.singlePointerPoints.length) return;
|
|
|
+
|
|
|
+ const pointInGrid = this.chartInstance.convertFromPixel(
|
|
|
+ { seriesIndex: 0 },
|
|
|
+ [event.offsetX, event.offsetY],
|
|
|
+ );
|
|
|
+ const clickX = pointInGrid?.[0];
|
|
|
+ if (clickX == null || Number.isNaN(clickX)) return;
|
|
|
+
|
|
|
+ // 以离点击最近的单指针来定义边带间隔 delta
|
|
|
+ const nearestCenter = this.singlePointerPoints.reduce((prev, cur) => {
|
|
|
+ const prevDiff = Math.abs(clickX - Number(prev.xAxis));
|
|
|
+ const curDiff = Math.abs(clickX - Number(cur.xAxis));
|
|
|
+ return curDiff < prevDiff ? cur : prev;
|
|
|
+ });
|
|
|
+ const referenceCenterX = Number(nearestCenter.xAxis);
|
|
|
+ const delta = Math.abs(clickX - referenceCenterX);
|
|
|
+ if (!delta) return;
|
|
|
+
|
|
|
+ const points = [];
|
|
|
+ this.singlePointerPoints.forEach((center) => {
|
|
|
+ const centerX = Number(center.xAxis);
|
|
|
+ for (let n = -5; n <= 5; n++) {
|
|
|
+ if (n === 0) continue;
|
|
|
+ points.push({
|
|
|
+ xAxis: centerX + n * delta,
|
|
|
+ val: `${n > 0 ? "+" : ""}${n}`,
|
|
|
+ });
|
|
|
+ }
|
|
|
+ });
|
|
|
+ this.sidebandCursorPoints = points;
|
|
|
+ this.applySidebandSeries();
|
|
|
+ },
|
|
|
+ applySidebandSeries() {
|
|
|
+ if (!this.chartInstance) return;
|
|
|
+ const option = this.chartInstance.getOption();
|
|
|
+ const otherSeries = (option.series || []).filter(
|
|
|
+ (s) => s && s.id !== "SIDEBAND_CURSOR",
|
|
|
+ );
|
|
|
+
|
|
|
+ if (!this.sidebandCursorPoints.length) {
|
|
|
+ this.chartInstance.setOption(
|
|
|
+ { series: otherSeries },
|
|
|
+ { replaceMerge: ["series"] },
|
|
|
+ );
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- const featureSeries = {
|
|
|
- id: "TZ_FEATURE",
|
|
|
+ const sidebandSeries = {
|
|
|
+ id: "SIDEBAND_CURSOR",
|
|
|
type: "line",
|
|
|
+ data: [],
|
|
|
+ silent: true,
|
|
|
+ z: 21,
|
|
|
markLine: {
|
|
|
+ silent: true,
|
|
|
+ symbol: ["none", "none"],
|
|
|
lineStyle: {
|
|
|
- color: "#00aaff",
|
|
|
- type: "solid",
|
|
|
+ color: "#ff0000",
|
|
|
+ type: "dashed",
|
|
|
+ width: 1,
|
|
|
+ },
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: "start",
|
|
|
+ formatter: (params) => params.data?.val || "",
|
|
|
+ color: "#ff0000",
|
|
|
},
|
|
|
- data: marks,
|
|
|
+ data: this.sidebandCursorPoints.map((p) => ({
|
|
|
+ xAxis: p.xAxis,
|
|
|
+ val: p.val,
|
|
|
+ })),
|
|
|
},
|
|
|
};
|
|
|
|
|
|
- const option = this.chartInstance.getOption();
|
|
|
-
|
|
|
- this.chartInstance.setOption({
|
|
|
- series: [
|
|
|
- ...option.series.filter((s) => s.id !== "TZ_FEATURE"),
|
|
|
- featureSeries,
|
|
|
- ],
|
|
|
- });
|
|
|
+ this.chartInstance.setOption(
|
|
|
+ { series: [...otherSeries, sidebandSeries] },
|
|
|
+ { replaceMerge: ["series"] },
|
|
|
+ );
|
|
|
},
|
|
|
- applyCursor() {
|
|
|
+ removeSidebandCursor() {
|
|
|
+ this.sidebandCursorPoints = [];
|
|
|
if (!this.chartInstance) return;
|
|
|
-
|
|
|
const option = this.chartInstance.getOption();
|
|
|
-
|
|
|
- this.chartInstance.setOption({
|
|
|
- series: option.series.filter(
|
|
|
- (s) =>
|
|
|
- !["CURSOR_SINGLE", "CURSOR_BAND", "CURSOR_SIDEBAND"].includes(s.id),
|
|
|
- ),
|
|
|
- });
|
|
|
-
|
|
|
- const type = this.checkedGB[0];
|
|
|
-
|
|
|
- if (type === "单指针") {
|
|
|
- this.enableSingleCursor();
|
|
|
- } else if (type === "频带光标") {
|
|
|
- this.enableBandCursor();
|
|
|
- } else if (type === "边带光标") {
|
|
|
- this.enableSidebandCursor();
|
|
|
+ const series = (option.series || []).filter(
|
|
|
+ (s) => s && s.id !== "SIDEBAND_CURSOR",
|
|
|
+ );
|
|
|
+ this.chartInstance.setOption({ series }, { replaceMerge: ["series"] });
|
|
|
+ },
|
|
|
+ initializeChart() {
|
|
|
+ const chartDom = this.$refs.chart;
|
|
|
+ if (chartDom && !this.chartInstance) {
|
|
|
+ this.chartInstance = echarts.init(chartDom);
|
|
|
+ }
|
|
|
+ if (this.timeList.y && this.timeList.x) {
|
|
|
+ this.updateChart(this.timeList.y, this.timeList.x);
|
|
|
}
|
|
|
},
|
|
|
- enableSingleCursor() {
|
|
|
- this.chartInstance.off("click");
|
|
|
- console.log("单指针");
|
|
|
- this.chartInstance.on("click", (params) => {
|
|
|
- const x = params.value[0];
|
|
|
+ handleCascaderChange(val) {
|
|
|
+ if (!val || val.length === 0) {
|
|
|
+ this.activeFeatures = [];
|
|
|
+ this.renderTZFeatures();
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
- const option = this.chartInstance.getOption();
|
|
|
+ // val = [["时域特征", "Xrms"], ["时域特征", "max_value"]]
|
|
|
|
|
|
- const newSeries = {
|
|
|
- id: "CURSOR_SINGLE",
|
|
|
- type: "line",
|
|
|
- markLine: {
|
|
|
- symbol: ["none", "none"],
|
|
|
- lineStyle: { color: "#ff0000", width: 1 },
|
|
|
- label: {
|
|
|
- formatter: `X: ${x.toFixed(2)}`,
|
|
|
- },
|
|
|
- data: [{ xAxis: x }],
|
|
|
- },
|
|
|
- };
|
|
|
+ this.activeFeatures = val.map((item) => item[item.length - 1]);
|
|
|
|
|
|
- this.chartInstance.setOption({
|
|
|
- series: [
|
|
|
- ...option.series.filter((s) => s.id !== "CURSOR_SINGLE"),
|
|
|
- newSeries,
|
|
|
- ],
|
|
|
- });
|
|
|
- });
|
|
|
+ this.renderTZFeatures(); // ✅ 直接用你已有方法
|
|
|
},
|
|
|
- enableBandCursor() {
|
|
|
- console.log("频带");
|
|
|
- let points = [];
|
|
|
- this.chartInstance.off("click");
|
|
|
-
|
|
|
- this.chartInstance.on("click", (params) => {
|
|
|
- const x = params.value[0];
|
|
|
- points.push(x);
|
|
|
-
|
|
|
- if (points.length === 2) {
|
|
|
- const [x1, x2] = points.sort((a, b) => a - b);
|
|
|
+ renderTZFeatures() {
|
|
|
+ if (!this.chartInstance) return;
|
|
|
|
|
|
- const option = this.chartInstance.getOption();
|
|
|
+ const marks = this.activeFeatures
|
|
|
+ .filter((key) => this.timeList[key] !== undefined)
|
|
|
+ .map((key) => ({
|
|
|
+ yAxis: this.timeList[key],
|
|
|
+ label: {
|
|
|
+ // formatter: key,
|
|
|
+ formatter: this.timeList[key],
|
|
|
+ },
|
|
|
+ }));
|
|
|
|
|
|
- const bandSeries = {
|
|
|
- id: "CURSOR_BAND",
|
|
|
- type: "line",
|
|
|
- markArea: {
|
|
|
- itemStyle: {
|
|
|
- color: "rgba(0, 128, 255, 0.2)",
|
|
|
+ this.chartInstance.setOption({
|
|
|
+ series: [
|
|
|
+ {
|
|
|
+ id: "MAIN_SERIES",
|
|
|
+ markLine: {
|
|
|
+ silent: true,
|
|
|
+ symbol: ["none", "none"],
|
|
|
+ lineStyle: {
|
|
|
+ color: "#00aaff",
|
|
|
+ type: "dashed",
|
|
|
},
|
|
|
- data: [[{ xAxis: x1 }, { xAxis: x2 }]],
|
|
|
+ data: marks,
|
|
|
},
|
|
|
- };
|
|
|
-
|
|
|
- this.chartInstance.setOption({
|
|
|
- series: [
|
|
|
- ...option.series.filter((s) => s.id !== "CURSOR_BAND"),
|
|
|
- bandSeries,
|
|
|
- ],
|
|
|
- });
|
|
|
-
|
|
|
- points = [];
|
|
|
- }
|
|
|
+ },
|
|
|
+ ],
|
|
|
});
|
|
|
},
|
|
|
- enableSidebandCursor() {
|
|
|
- console.log("边带");
|
|
|
- this.chartInstance.off("click");
|
|
|
-
|
|
|
- this.chartInstance.on("click", (params) => {
|
|
|
- const center = params.value[0];
|
|
|
-
|
|
|
- // ✅ 正确转频(核心!!!)
|
|
|
- const rpm = this.timeList.rpm_Gen || 0;
|
|
|
- const spacing = rpm / 60 || 10;
|
|
|
-
|
|
|
- const count = 5;
|
|
|
-
|
|
|
- const lines = [];
|
|
|
-
|
|
|
- for (let i = -count; i <= count; i++) {
|
|
|
- const x = center + i * spacing;
|
|
|
-
|
|
|
- lines.push({
|
|
|
- xAxis: x,
|
|
|
- label: {
|
|
|
- formatter: `${i}x`,
|
|
|
- },
|
|
|
- });
|
|
|
+ selectCursor(val) {
|
|
|
+ const isAlreadyChecked = this.checkedGB.includes(val);
|
|
|
+ const isSinglePointerVal = val === "单指针";
|
|
|
+ const isSidebandVal = val === "边带光标";
|
|
|
+ const isHarmonicVal = val === "谐波光标";
|
|
|
+
|
|
|
+ if (isAlreadyChecked) {
|
|
|
+ if (isSinglePointerVal) {
|
|
|
+ // 单指针取消时,级联取消边带
|
|
|
+ this.checkedGB = this.checkedGB.filter(
|
|
|
+ (v) => v !== "单指针" && v !== "边带光标",
|
|
|
+ );
|
|
|
+ } else {
|
|
|
+ this.checkedGB = this.checkedGB.filter((v) => v !== val);
|
|
|
}
|
|
|
+ } else if (isSinglePointerVal || isSidebandVal) {
|
|
|
+ const next = new Set(this.checkedGB);
|
|
|
+ next.add(val);
|
|
|
+ next.delete("谐波光标");
|
|
|
+ this.checkedGB = Array.from(next);
|
|
|
+ } else if (isHarmonicVal) {
|
|
|
+ this.checkedGB = [val];
|
|
|
+ } else {
|
|
|
+ this.checkedGB = [val];
|
|
|
+ }
|
|
|
|
|
|
- const option = this.chartInstance.getOption();
|
|
|
-
|
|
|
- const sidebandSeries = {
|
|
|
- id: "CURSOR_SIDEBAND",
|
|
|
- type: "line",
|
|
|
- markLine: {
|
|
|
- symbol: ["none", "none"],
|
|
|
- lineStyle: {
|
|
|
- color: "#ffa500",
|
|
|
- type: "dashed",
|
|
|
- },
|
|
|
- data: lines,
|
|
|
- },
|
|
|
- };
|
|
|
-
|
|
|
- this.chartInstance.setOption({
|
|
|
- series: [
|
|
|
- ...option.series.filter((s) => s.id !== "CURSOR_SIDEBAND"),
|
|
|
- sidebandSeries,
|
|
|
- ],
|
|
|
- });
|
|
|
- });
|
|
|
+ const isSidebandChecked = this.checkedGB.includes("边带光标");
|
|
|
+ const isHarmonicChecked = this.checkedGB.includes("谐波光标");
|
|
|
+ const isSinglePointerChecked = this.checkedGB.includes("单指针");
|
|
|
+
|
|
|
+ isSidebandChecked
|
|
|
+ ? this.enableSidebandCursor()
|
|
|
+ : this.disableSidebandCursor();
|
|
|
+ isHarmonicChecked
|
|
|
+ ? this.enableHarmonicCursor()
|
|
|
+ : this.disableHarmonicCursor();
|
|
|
+ if (!isSinglePointerChecked) {
|
|
|
+ this.disableSinglePointer();
|
|
|
+ } else if (isSidebandChecked) {
|
|
|
+ // 保留单指针线,冻结单指针点击
|
|
|
+ this.unbindSinglePointerClick();
|
|
|
+ } else {
|
|
|
+ this.enableSinglePointer();
|
|
|
+ }
|
|
|
},
|
|
|
// 更新图表数据
|
|
|
updateChart(data, labels) {
|
|
|
if (!this.chartInstance) return; // Check if chartInstance is available
|
|
|
-
|
|
|
+ console.log(this.timeList, "updataChart 时域图");
|
|
|
const option = {
|
|
|
// title: {
|
|
|
// text: this.timeList.title,
|
|
|
@@ -382,11 +532,12 @@ export default {
|
|
|
left: 60, // 原来是100,适当缩小左右边距
|
|
|
right: 60,
|
|
|
// bottom: 90, // 给推拽条和坐标轴腾出空间
|
|
|
- top: 60,
|
|
|
+ top: 30,
|
|
|
+ bottom: 50,
|
|
|
},
|
|
|
toolbox: {
|
|
|
right: 10,
|
|
|
- top: 35, // 👈 工具栏在最上面
|
|
|
+ // top: 55, // 👈 工具栏在最上面
|
|
|
feature: {
|
|
|
dataZoom: { yAxisIndex: "none" },
|
|
|
restore: {},
|
|
|
@@ -404,7 +555,7 @@ export default {
|
|
|
name: this.timeList.xaxis,
|
|
|
nameLocation: "center",
|
|
|
nameTextStyle: {
|
|
|
- fontSize: 14,
|
|
|
+ fontSize: 12,
|
|
|
color: "#333",
|
|
|
padding: [15, 0, 0, 0], // 增加X轴标题和轴线的距离
|
|
|
},
|
|
|
@@ -423,7 +574,7 @@ export default {
|
|
|
type: "value",
|
|
|
name: this.timeList.yaxis,
|
|
|
nameTextStyle: {
|
|
|
- fontSize: 14,
|
|
|
+ fontSize: 12,
|
|
|
color: "#333",
|
|
|
padding: [0, 10, 0, 0], // 根据需要微调
|
|
|
},
|
|
|
@@ -452,24 +603,36 @@ export default {
|
|
|
dataZoom: [
|
|
|
{
|
|
|
type: "inside",
|
|
|
- start: 0,
|
|
|
- end: 10,
|
|
|
- },
|
|
|
- {
|
|
|
- type: "slider",
|
|
|
- start: 0,
|
|
|
- end: 10,
|
|
|
- handleSize: "80%",
|
|
|
- showDataShadow: false,
|
|
|
- top: 0, // 👈 放到顶部
|
|
|
- height: 20, // 👈 控制高度(可选)
|
|
|
+ xAxisIndex: 0,
|
|
|
+ filterMode: "none",
|
|
|
+ zoomOnMouseWheel: false,
|
|
|
+ moveOnMouseMove: false,
|
|
|
+ moveOnMouseWheel: false,
|
|
|
},
|
|
|
],
|
|
|
+ // dataZoom: [
|
|
|
+ // {
|
|
|
+ // type: "inside",
|
|
|
+ // start: 0,
|
|
|
+ // end: 10,
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // type: "slider",
|
|
|
+ // start: 0,
|
|
|
+ // end: 10,
|
|
|
+ // handleSize: "80%",
|
|
|
+ // showDataShadow: false,
|
|
|
+ // top: 30, // 👈 放到顶部
|
|
|
+ // height: 20, // 👈 控制高度(可选)
|
|
|
+ // },
|
|
|
+ // ],
|
|
|
series: [
|
|
|
{
|
|
|
+ id: "MAIN_SERIES", // ✅ 必须加
|
|
|
name: "数据系列",
|
|
|
type: "line",
|
|
|
data: labels.map((item, index) => [item, data[index]]),
|
|
|
+ // data: (labels || []).map((item, index) => [item, data?.[index]])
|
|
|
symbol: "none",
|
|
|
symbolSize: 8,
|
|
|
lineStyle: {
|
|
|
@@ -487,13 +650,99 @@ export default {
|
|
|
],
|
|
|
};
|
|
|
|
|
|
- this.chartInstance.setOption(option, {
|
|
|
- notMerge: false, // 👈 关键
|
|
|
+ this.chartInstance.setOption(option, true);
|
|
|
+ this.$nextTick(() => {
|
|
|
+ this.renderTZFeatures(); // ✅ 防止被覆盖
|
|
|
+ this.applySinglePointerSeries();
|
|
|
+ this.applySidebandSeries();
|
|
|
+ });
|
|
|
+ },
|
|
|
+ generateSeries(featureLines) {
|
|
|
+ const createMarkLine = (dataSource, color) => ({
|
|
|
+ type: "line",
|
|
|
+ markLine: {
|
|
|
+ silent: false,
|
|
|
+ lineStyle: { color, type: "dashed", width: 1 },
|
|
|
+ symbol: ["none", "none"],
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ position: "end",
|
|
|
+ formatter: ({ data }) => data.val,
|
|
|
+ },
|
|
|
+ emphasis: {
|
|
|
+ symbol: ["none", "none"],
|
|
|
+ lineStyle: { color: "#FF6A00", width: 2 },
|
|
|
+ label: {
|
|
|
+ show: true,
|
|
|
+ formatter: ({ value }) => `特征值: ${value}`,
|
|
|
+ color: "#000",
|
|
|
+ },
|
|
|
+ },
|
|
|
+ data: dataSource.map(({ Xaxis, val }) => ({ xAxis: Xaxis, val })),
|
|
|
+ },
|
|
|
});
|
|
|
+
|
|
|
+ const markLines = [
|
|
|
+ { data: featureLines.Fr, color: "#A633FF" },
|
|
|
+ { data: featureLines.BPFI, color: "#23357e" },
|
|
|
+ { data: featureLines.BPFO, color: "#42a0ae" },
|
|
|
+ { data: featureLines.BSF, color: "#008080" },
|
|
|
+ { data: featureLines.FTF, color: "#af254f" },
|
|
|
+ { data: featureLines.B3P, color: "#FFD700" },
|
|
|
+ ].map(({ data, color }) => createMarkLine(data, color));
|
|
|
+
|
|
|
+ return [
|
|
|
+ {
|
|
|
+ name: "数据系列",
|
|
|
+ type: "line",
|
|
|
+ data: this.spectrumList.x.map((x, i) => [x, this.spectrumList.y[i]]),
|
|
|
+ symbol: "none",
|
|
|
+ lineStyle: { color: "#162961", width: 1 },
|
|
|
+ itemStyle: { color: "#162961", borderColor: "#fff", borderWidth: 1 },
|
|
|
+ large: true,
|
|
|
+ },
|
|
|
+ ...markLines,
|
|
|
+ ];
|
|
|
},
|
|
|
+ renderFeatureSeries(featureLines) {
|
|
|
+ if (!this.chartInstance) return;
|
|
|
+
|
|
|
+ const currentOption = this.chartInstance.getOption();
|
|
|
|
|
|
+ // 保留光标
|
|
|
+ const cursorLineSeries =
|
|
|
+ currentOption.series.find((s) => s && s.id === "CURSOR_LINE_SERIES") ||
|
|
|
+ {};
|
|
|
+ const cursorPointSeries =
|
|
|
+ currentOption.series.find((s) => s && s.id === "CURSOR_POINT_SERIES") ||
|
|
|
+ {};
|
|
|
+ const cursorHighLineSeries =
|
|
|
+ currentOption.series.find((s) => s && s.id === "PEAK_REFERENCE_LINE") ||
|
|
|
+ {};
|
|
|
+
|
|
|
+ const featureSeries = this.generateSeries(featureLines);
|
|
|
+
|
|
|
+ this.chartInstance.setOption(
|
|
|
+ {
|
|
|
+ series: [
|
|
|
+ (currentOption.series || []).find(
|
|
|
+ (s) => s && (s.id === "MAIN_SERIES" || s.name === "数据系列"),
|
|
|
+ ) || (currentOption.series || []).find((s) => s),
|
|
|
+ ...featureSeries.slice(1),
|
|
|
+ cursorLineSeries,
|
|
|
+ cursorPointSeries,
|
|
|
+ cursorHighLineSeries,
|
|
|
+ ].filter((s) => s && s.type),
|
|
|
+ },
|
|
|
+ { replaceMerge: ["series"] },
|
|
|
+ );
|
|
|
+ },
|
|
|
getTime() {
|
|
|
- this.$emit("handleLoading", null, true, this.activeIndex);
|
|
|
+ this.$emit("handleLoading", {
|
|
|
+ id: this.chartId,
|
|
|
+ currentRow: this.currentRow,
|
|
|
+ loading: true,
|
|
|
+ });
|
|
|
const params = {
|
|
|
ids: this.ids,
|
|
|
analysisType: "time",
|
|
|
@@ -508,10 +757,13 @@ export default {
|
|
|
})
|
|
|
.catch((error) => {})
|
|
|
.finally(() => {
|
|
|
- this.$emit("handleLoading", this.currentRow, false, this.activeIndex);
|
|
|
+ this.$emit("handleLoading", {
|
|
|
+ id: this.chartId,
|
|
|
+ currentRow: this.currentRow,
|
|
|
+ loading: false,
|
|
|
+ });
|
|
|
});
|
|
|
},
|
|
|
-
|
|
|
Show() {
|
|
|
this.TZshow = !this.TZshow;
|
|
|
},
|
|
|
@@ -522,7 +774,7 @@ export default {
|
|
|
<style lang="scss" scoped>
|
|
|
.line-chart {
|
|
|
width: 100%;
|
|
|
- height: 320px;
|
|
|
+ height: 400px;
|
|
|
}
|
|
|
.FD {
|
|
|
width: 100%;
|