| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848 |
- <template>
- <div>
- <!-- ECharts 图表容器 -->
- <div class="line-chart" ref="chart"></div>
- <div class="control-panel">
- <!-- 频率范围 -->
- <div class="box full-row">
- <div class="panel-block">
- <span class="label1">频率</span>
- <el-input v-model="freqMin" size="mini" placeholder="下限" />
- <span>~</span>
- <el-input v-model="freqMax" size="mini" placeholder="上限" />
- <el-button size="mini" type="primary" @click="handleFreqRange">
- 应用
- </el-button>
- </div>
- </div>
- </div>
- <div class="control-panel">
- <!-- 手动标注 -->
- <div class="panel-block">
- <span class="label1">标注</span>
- <el-input v-model="manualFreq" size="mini" placeholder="频率" />
- <el-input v-model="multiple" size="mini" placeholder="倍频" />
- <el-button size="mini" type="success" @click="handleMark"
- >标注</el-button
- >
- </div>
- <!-- 光标(单选) -->
- <div class="panel-block">
- <span class="label">光标</span>
- <div class="btn-group">
- <span
- v-for="item in GBcheckList"
- :key="item.val"
- :class="['btn', checkedGB.includes(item.val) ? 'active' : '']"
- @click="selectCursor(item.val)"
- >
- {{ item.val }}
- </span>
- </div>
- </div>
- <!-- 特征选择(四分类) -->
- <div class="feature-grid">
- <div
- class="feature-item"
- v-for="group in featureGroups"
- :key="group.type"
- >
- <div class="label">{{ group.label }}</div>
- <el-select
- v-model="selectedMap[group.type]"
- multiple
- collapse-tags
- size="small"
- placeholder="请选择"
- :disabled="!group.children.length"
- @change="handleFeatureChange"
- >
- <el-option
- v-for="item in group.children"
- :key="item"
- :label="item"
- :value="item"
- />
- </el-select>
- </div>
- </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>
- </div>
- </template>
- <script>
- import axios from "axios";
- import * as echarts from "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,
- default: 0,
- },
- activeIndex: {
- type: Number,
- default: 0,
- },
- ids: {
- type: Array,
- default: () => [],
- },
- spectrumListTwo: {
- type: Object,
- default: () => ({}),
- },
- currentRow: {
- type: Object,
- default: () => ({}),
- },
- windCode: {
- type: String,
- default: "",
- },
- },
- data() {
- return {
- freqMin: "",
- freqMax: "",
- manualFreq: "",
- multiple: 1,
- manualMarks: [],
- chartInstance: null,
- option: null,
- // TZshow: false,
- BGshow: false,
- PXshow: false,
- spectrumList: {},
- GBcheckList: [
- { val: "添加光标", checked: false, disabled: false },
- { val: "谐波光标", checked: false, disabled: false },
- { val: "边带光标", checked: false, disabled: false },
- { val: "移动峰值", checked: false, disabled: false },
- ],
- PXcheckList: [
- { val: "转速频率", checked: false },
- { val: "内圈故障频率", checked: false },
- { val: "外圈故障频率", checked: false },
- { val: "滚动体故障频率", checked: false },
- { val: "保持架频率", checked: false },
- { val: "叶片频率", checked: false },
- ],
- featureGroups: [
- {
- label: "轴承故障",
- type: "bearing",
- children: ["GEN-DE BPFO", "GEN-DE BPFI", "GEN-DE BSF", "GEN-DE FTF"],
- },
- {
- label: "转动基频",
- type: "rotation",
- children: ["转速频率"],
- },
- {
- label: "结构频率",
- type: "structure",
- children: ["叶片频率"],
- },
- {
- label: "齿轮特征",
- type: "gear",
- children: [],
- },
- ],
- featureKeyMap: {
- "GEN-DE BPFO": "BPFO",
- "GEN-DE BPFI": "BPFI",
- "GEN-DE BSF": "BSF",
- "GEN-DE FTF": "FTF",
- 转速频率: "Fr",
- 叶片频率: "B3P",
- },
- // 每个分类单独存
- selectedMap: {
- bearing: [],
- rotation: [],
- structure: [],
- gear: [],
- },
- // 最终统一给图表用
- checkedFeatures: [],
- selectedFeatures: [],
- cascaderProps: {
- multiple: true, // 多选
- checkStrictly: false, // 不能选父节点(只选叶子)
- emitPath: true, // 返回路径(重要)
- },
- Fr: [],
- BPFI: [],
- BPFO: [],
- BSF: [],
- FTF: [],
- B3P: [],
- checkedGB: [],
- checkedValues: [],
- };
- },
- computed: {
- cascaderOptions() {
- return this.featureGroups.map((group) => ({
- label: group.label,
- value: group.label,
- children: group.children.map((item) => ({
- label: item,
- value: item,
- })),
- }));
- },
- },
- watch: {
- spectrumListTwo(newValue) {
- this.spectrumList = newValue;
- if (this.chartInstance) {
- this.updateChart(this.spectrumList.y, this.spectrumList.x);
- }
- },
- spectrumList: {
- handler(newValue) {
- if (!newValue) return;
- if (this.chartInstance) {
- this.updateChart(newValue.y, newValue.x);
- }
- },
- deep: true,
- },
- },
- beforeDestroy() {
- if (this.chartInstance) {
- this.chartInstance.getZr().off("dblclick", this.handleDoubleClick);
- this.chartInstance
- .getZr()
- .off("mousemove", this.handleSidebandCursorMove);
- this.chartInstance.dispose();
- }
- },
- mounted() {
- this.$nextTick(() => {
- setTimeout(() => {
- this.initializeChart();
- this.getTime();
- }, 500);
- });
- },
- methods: {
- handleFeatureChange() {
- // 1️⃣ 汇总所有选中的特征
- const allSelected = Object.values(this.selectedMap).flat();
- this.checkedFeatures = allSelected;
- // 2️⃣ 更新图表
- this.updateFeatureLinesByGroup();
- },
- // handleCascaderChange(val) {
- // // val 示例:
- // // [
- // // ["轴承故障", "GEN-DE BPFO"],
- // // ["转动基频", "转速频率"]
- // // ]
- // // 👉 只取最后一层(真正的特征)
- // this.checkedFeatures = val.map((item) => item[item.length - 1]);
- // this.updateFeatureLines();
- // },
- selectCursor(val) {
- // 单选逻辑
- if (this.checkedGB.includes(val)) {
- this.checkedGB = [];
- } else {
- this.checkedGB = [val];
- }
- const isMoveChecked = this.checkedGB.includes("移动峰值");
- const isSidebandChecked = this.checkedGB.includes("边带光标");
- const isHarmonicChecked = this.checkedGB.includes("谐波光标");
- isMoveChecked ? this.handleMoveCursor() : this.removeCursor();
- isSidebandChecked
- ? this.enableSidebandCursor()
- : this.disableSidebandCursor();
- isHarmonicChecked
- ? this.enableHarmonicCursor()
- : this.disableHarmonicCursor();
- },
- initializeChart() {
- const chartDom = this.$refs.chart;
- if (chartDom && !this.chartInstance) {
- this.chartInstance = echarts.init(chartDom);
- this.chartInstance.getZr().on("dblclick", this.handleDoubleClick);
- }
- this.$nextTick(() => {
- if (this.chartInstance && this.spectrumList.y && this.spectrumList.x) {
- this.updateChart(this.spectrumList.y, this.spectrumList.x);
- }
- });
- },
- toggleFeature(val) {
- const index = this.checkedValues.indexOf(val);
- if (index > -1) {
- this.checkedValues.splice(index, 1);
- } else {
- this.checkedValues.push(val);
- }
- this.handleCheckChange(); // 复用你原逻辑
- },
- handleFreqRange() {
- const min = Number(this.freqMin);
- const max = Number(this.freqMax);
- if (isNaN(min) || isNaN(max)) return;
- this.chartInstance.setOption({
- xAxis: { min, max },
- });
- },
- handleMark() {
- const freq = Number(this.manualFreq);
- const multiple = Number(this.multiple) || 1;
- if (isNaN(freq)) return;
- const marks = [];
- for (let i = 1; i <= multiple; i++) {
- marks.push({
- xAxis: freq * i,
- val: `${i}x`,
- });
- }
- this.manualMarks = marks;
- this.renderManualMarks();
- },
- // 特征值
- handleCheckChange() {
- this.PXcheckList.forEach((item) => {
- item.checked = this.checkedValues.includes(item.val);
- });
- this.updateFeatureLines();
- },
- renderManualMarks() {
- const option = this.chartInstance.getOption();
- const manualSeries = {
- id: "MANUAL_MARK",
- type: "line",
- markLine: {
- symbol: ["none", "none"],
- lineStyle: {
- color: "#ff0000",
- width: 2,
- type: "dashed",
- },
- label: {
- formatter: ({ data }) => data.val,
- },
- data: this.manualMarks,
- },
- };
- this.chartInstance.setOption({
- series: [
- ...option.series.filter((s) => s.id !== "MANUAL_MARK"),
- manualSeries,
- ],
- });
- },
- updateFeatureLinesByGroup() {
- if (!this.spectrumList) return;
- const featureLines = {
- Fr: [],
- BPFI: [],
- BPFO: [],
- BSF: [],
- FTF: [],
- B3P: [],
- };
- this.checkedFeatures.forEach((featureName) => {
- const key = this.featureKeyMap[featureName];
- if (!key) return;
- switch (key) {
- case "Fr":
- featureLines.Fr = this.spectrumList.fn_Gen || [];
- break;
- case "BPFI":
- featureLines.BPFI = this.spectrumList.BPFI || [];
- break;
- case "BPFO":
- featureLines.BPFO = this.spectrumList.BPFO || [];
- break;
- case "BSF":
- featureLines.BSF = this.spectrumList.BSF || [];
- break;
- case "FTF":
- featureLines.FTF = this.spectrumList.FTF || [];
- break;
- case "B3P":
- featureLines.B3P = Array.isArray(this.spectrumList.B3P)
- ? this.spectrumList.B3P
- : this.spectrumList.B3P
- ? [{ Xaxis: this.spectrumList.B3P, val: "3P" }]
- : [];
- break;
- }
- });
- this.renderFeatureSeries(featureLines);
- },
- updateFeatureLines() {
- const newFeatureLines = {
- Fr: this.checkedValues.includes("转速频率")
- ? this.spectrumList.fn_Gen
- : [],
- BPFI: this.checkedValues.includes("内圈故障频率")
- ? this.spectrumList.BPFI
- : [],
- BPFO: this.checkedValues.includes("外圈故障频率")
- ? this.spectrumList.BPFO
- : [],
- BSF: this.checkedValues.includes("滚动体故障频率")
- ? this.spectrumList.BSF
- : [],
- FTF: this.checkedValues.includes("保持架频率")
- ? this.spectrumList.FTF
- : [],
- B3P: this.checkedValues.includes("叶片频率")
- ? Array.isArray(this.spectrumList.B3P)
- ? this.spectrumList.B3P
- : [{ Xaxis: this.spectrumList.B3P, val: "3P" }]
- : [],
- };
- if (this.chartInstance) {
- const currentOption = this.chartInstance.getOption();
- // 获取现有的光标系列
- const cursorLineSeries =
- currentOption.series.find((s) => s.id === "CURSOR_LINE_SERIES") || {};
- const cursorPointSeries =
- currentOption.series.find((s) => s.id === "CURSOR_POINT_SERIES") ||
- {};
- const cursorHighLineSeries =
- currentOption.series.find((s) => s.id === "PEAK_REFERENCE_LINE") ||
- {};
- // 生成新的特征值系列
- const featureSeries = this.generateSeries(newFeatureLines);
- this.chartInstance.setOption(
- {
- series: [
- currentOption.series[0], // 主数据系列
- ...featureSeries.slice(1), // 新的特征值系列
- cursorLineSeries, // 保留光标线系列
- cursorPointSeries, // 保留光标点系列
- cursorHighLineSeries, // 保留峰值参考线
- ],
- },
- { replaceMerge: ["series"] },
- );
- }
- },
- 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: {
- 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,
- ];
- },
- updateChart(data, labels) {
- if (
- !this.chartInstance ||
- !Array.isArray(labels) ||
- !Array.isArray(data) ||
- labels.length !== data.length
- ) {
- console.error("Invalid data or labels");
- return;
- }
- const createMarkLine = (dataSource, color) => ({
- type: "line",
- markLine: {
- silent: false,
- lineStyle: { color, type: "solid", width: 2 },
- symbol: ["none", "none"],
- label: {
- show: true,
- position: "end",
- formatter: ({ data }) => data.val,
- },
- emphasis: {
- lineStyle: { color: "#FF6A00", width: 4 },
- label: {
- show: true,
- formatter: ({ value }) => `特征值: ${value}`,
- color: "#000",
- backgroundColor: "#FFF",
- padding: [2, 4],
- borderRadius: 3,
- fontSize: 12,
- },
- },
- data: dataSource.map(({ Xaxis, val }) => ({ xAxis: Xaxis, val })),
- },
- });
- const markLines = [
- { data: this.Fr, color: "#A633FF" },
- { data: this.BPFI, color: "#23357e" },
- { data: this.BPFO, color: "#42a0ae" },
- { data: this.BSF, color: "#008080" },
- { data: this.FTF, color: "#af254f" },
- { data: this.B3P, color: "#FFD700" },
- ].map(({ data, color }) => createMarkLine(data, color));
- const option = {
- grid: {
- left: 60, // 原来是100,适当缩小左右边距
- right: 20,
- top: 80, // 给推拽条和坐标轴腾出空间
- // top: 60,
- },
- // title: { text: this.spectrumList.title, left: "center" },
- toolbox: {
- right: 10,
- top: 40, // 👈 工具栏在最上面
- feature: {
- dataZoom: { yAxisIndex: "none" },
- restore: {},
- saveAsImage: {},
- },
- },
- xAxis: {
- type: "value",
- name: this.spectrumList.xaxis,
- nameLocation: "center",
- nameTextStyle: {
- fontSize: 14,
- color: "#333",
- padding: [15, 0, 0, 0], // 增加X轴标题和轴线的距离
- },
- axisLabel: {
- margin: 1, // 增加数值标签和轴线的间距
- formatter: (value) => value,
- },
- },
- yAxis: {
- type: "value",
- name: this.spectrumList.yaxis,
- nameTextStyle: {
- fontSize: 14,
- color: "#333",
- padding: [10, 0, 0, 0],
- },
- },
- tooltip: {
- trigger: "axis",
- formatter: ([
- {
- value: [x, y],
- },
- ]) => `X: ${x}<br/>Y: ${y}`,
- axisPointer: { type: "line" },
- },
- dataZoom: [
- { type: "inside", start: 0, end: 10 },
- {
- type: "slider",
- start: 0,
- end: 10,
- handleSize: "80%",
- showDataShadow: false,
- top: 10, // 👈 放到顶部
- height: 20, // 👈 控制高度(可选)
- },
- ],
- series: [
- {
- name: "数据系列",
- type: "line",
- data: labels.map((x, i) => [x, data[i]]),
- symbol: "none",
- lineStyle: { color: "#162961", width: 1 },
- itemStyle: {
- color: "#162961",
- borderColor: "#fff",
- borderWidth: 1,
- },
- large: true,
- progressive: 2000,
- },
- ...markLines,
- ],
- };
- this.chartInstance.setOption(option);
- },
- renderFeatureSeries(featureLines) {
- if (!this.chartInstance) return;
- const currentOption = this.chartInstance.getOption();
- // 保留光标
- const cursorLineSeries =
- currentOption.series.find((s) => s.id === "CURSOR_LINE_SERIES") || {};
- const cursorPointSeries =
- currentOption.series.find((s) => s.id === "CURSOR_POINT_SERIES") || {};
- const cursorHighLineSeries =
- currentOption.series.find((s) => s.id === "PEAK_REFERENCE_LINE") || {};
- const featureSeries = this.generateSeries(featureLines);
- this.chartInstance.setOption(
- {
- series: [
- currentOption.series[0], // 主线
- ...featureSeries.slice(1),
- cursorLineSeries,
- cursorPointSeries,
- cursorHighLineSeries,
- ],
- },
- { replaceMerge: ["series"] },
- );
- },
- // 获取数据
- getTime() {
- this.$emit("handleLoading", null, true, this.activeIndex);
- const params = {
- ids: this.ids,
- windCode: this.windCode,
- analysisType: "frequency",
- };
- axios
- .post("/AnalysisMulti/analysis/frequency", params)
- .then((res) => {
- this.spectrumList = { ...JSON.parse(res.data) };
- console.log(this.spectrumList, "频谱图数据1");
- const XrmsValue = this.spectrumList?.Xrms;
- this.$emit("updateXrms", XrmsValue);
- // 拉完数据后,如果已有选中项,自动渲染
- this.$nextTick(() => {
- this.updateFeatureLinesByGroup();
- });
- })
- .catch((error) => {
- console.error(error);
- })
- .finally(() => {
- this.$emit("handleLoading", this.currentRow, false, this.activeIndex);
- });
- },
- Show(value) {
- const stateMap = {
- 1: { TZshow: true, BGshow: false, PXshow: false },
- 2: { TZshow: false, BGshow: true, PXshow: false },
- 3: { TZshow: false, BGshow: false, PXshow: true },
- };
- if (stateMap[value]) {
- this.TZshow = value === "1" ? !this.TZshow : false;
- this.BGshow = value === "2" ? !this.BGshow : false;
- this.PXshow = value === "3" ? !this.PXshow : false;
- }
- },
- },
- };
- </script>
- <style lang="scss" scoped>
- .line-chart {
- width: 100%;
- height: 320px;
- }
- .FD {
- width: 100%;
- height: 1px;
- position: relative;
- }
- .eigenvalue {
- position: absolute;
- top: 60px;
- right: 0;
- font-size: 10px;
- width: 146px;
- border: 1px solid rgb(182, 182, 182);
- padding: 5px;
- background: #fff;
- z-index: 99;
- border-radius: 5px;
- h5 {
- line-height: 16px;
- height: 16px;
- }
- }
- .eigenvalue--first {
- width: 100px;
- }
- .control-panel {
- display: flex;
- flex-wrap: wrap;
- justify-content: space-between;
- gap: 12px;
- padding: 8px 12px;
- background: #f5f7fa;
- border: 1px solid #ddd;
- border-radius: 6px;
- margin-bottom: 10px;
- }
- /* 🌟 关键:独占一行 */
- .full-row {
- width: 100%;
- .panel-block {
- width: 45%;
- }
- }
- .panel-block {
- display: flex;
- align-items: center;
- gap: 6px;
- }
- .label {
- font-size: 12px;
- color: #666;
- }
- .label1 {
- font-size: 12px;
- color: #666;
- display: inline-block;
- width: 75px;
- }
- .btn-group {
- display: flex;
- gap: 6px;
- }
- .btn {
- padding: 2px 8px;
- font-size: 12px;
- border: 1px solid #ccc;
- border-radius: 3px;
- cursor: pointer;
- }
- .btn.active {
- background: #409eff;
- color: #fff;
- }
- .full-width {
- width: 100%;
- }
- .el-cascader {
- font-size: 12px;
- }
- .el-cascader__tags {
- max-width: 240px;
- overflow: hidden;
- }
- .feature-grid {
- display: grid;
- grid-template-columns: repeat(4, 1fr); // 两列布局
- gap: 10px;
- width: 100%;
- }
- .feature-item {
- display: flex;
- align-items: center;
- gap: 6px;
- }
- .el-select {
- flex: 1;
- }
- </style>
|