timedomainchartsNew.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839
  1. <template>
  2. <div v-loading="loading">
  3. <!-- ECharts 图表容器 -->
  4. <div class="line-chart" ref="chart"></div>
  5. <div class="control-panel">
  6. <!-- 光标 -->
  7. <div class="panel-block">
  8. <span class="label">光标</span>
  9. <div class="btn-group">
  10. <span
  11. v-for="item in GBcheckList"
  12. :key="item.val"
  13. :class="['btn', checkedGB.includes(item.val) ? 'active' : '']"
  14. @click="selectCursor(item.val)"
  15. >
  16. {{ item.val }}
  17. </span>
  18. </div>
  19. </div>
  20. <!-- 特征值(多选) -->
  21. <div class="panel-block full-width">
  22. <span class="label">特征 </span>
  23. <el-cascader
  24. v-model="selectedFeatures"
  25. :options="cascaderOptions"
  26. :props="cascaderProps"
  27. collapse-tags
  28. clearable
  29. placeholder="请选择特征"
  30. size="small"
  31. @change="handleCascaderChange"
  32. />
  33. </div>
  34. </div>
  35. </div>
  36. </template>
  37. <script>
  38. import axios from "axios";
  39. import * as echarts from "echarts"; // 导入 echarts 库
  40. import cursorReferenceMixin from "./spectrogramcharts/cursorReferenceMixin";
  41. import Bdgb from "./spectrogramcharts/Bdgb";
  42. import Xdgb from "./spectrogramcharts/Xdgb";
  43. import Tjgb from "./spectrogramcharts/Tjgb";
  44. export default {
  45. name: "TimedomainCharts", // 组件名称
  46. mixins: [cursorReferenceMixin, Bdgb, Xdgb, Tjgb],
  47. props: {
  48. currentIndex: {
  49. type: Number,
  50. default: 0,
  51. },
  52. activeIndex: {
  53. type: Number,
  54. default: 0,
  55. },
  56. loading: {
  57. type: Boolean,
  58. default: false,
  59. },
  60. ids: {
  61. type: Array,
  62. default: () => [],
  63. },
  64. timeListTwo: {
  65. type: Object,
  66. default: () => ({}),
  67. },
  68. currentRow: {
  69. type: Object,
  70. default: () => ({}),
  71. },
  72. windCode: {
  73. type: String,
  74. default: "",
  75. },
  76. },
  77. data() {
  78. return {
  79. chartInstance: null,
  80. option: null,
  81. TZshow: false,
  82. timeList: {},
  83. manualMarks: [],
  84. // 时域:多单指针
  85. singlePointerPoints: [],
  86. activeSinglePointer: null,
  87. // 时域:边带
  88. sidebandCursorVisible: false,
  89. sidebandCursorPoints: [],
  90. // 光标
  91. GBcheckList: [
  92. { val: "单指针" },
  93. // { val: "谐波光标" },
  94. // { val: "边带光标" },
  95. ],
  96. checkedGB: [],
  97. featureGroups: [
  98. {
  99. label: "时域特征",
  100. type: "time",
  101. children: [
  102. { key: "Xrms", label: "有效值" },
  103. { key: "mean_value", label: "平均值" },
  104. { key: "max_value", label: "最大值" },
  105. { key: "min_value", label: "最小值" },
  106. { key: "Xp", label: "峰值" },
  107. { key: "Xpp", label: "峰峰值" },
  108. ],
  109. },
  110. ],
  111. selectedFeatures: [],
  112. cascaderProps: {
  113. // multiple: true, // 多选
  114. // checkStrictly: false, // 不能选父节点(只选叶子)
  115. // emitPath: true, // 返回路径(重要)
  116. multiple: true,
  117. checkStrictly: false,
  118. emitPath: true, // 保留(你需要路径)
  119. value: "value",
  120. label: "label",
  121. },
  122. // 特征值按钮
  123. TZFeatureList: [
  124. { key: "Xrms", label: "有效值" },
  125. { key: "mean_value", label: "平均值" },
  126. { key: "max_value", label: "最大值" },
  127. { key: "min_value", label: "最小值" },
  128. { key: "Xp", label: "峰值" },
  129. { key: "Xpp", label: "峰峰值" },
  130. ],
  131. activeFeatures: [],
  132. };
  133. },
  134. computed: {
  135. cascaderOptions() {
  136. return this.featureGroups.map((group) => ({
  137. label: group.label,
  138. value: group.type, // ✅ 改这里
  139. children: group.children.map((item) => ({
  140. label: item.label,
  141. value: item.key,
  142. })),
  143. }));
  144. },
  145. },
  146. watch: {
  147. // 监听 chartData 和 chartLabels 的变化,重新绘制图表
  148. chartData(newData) {
  149. this.updateChart(newData, this.chartLabels);
  150. },
  151. chartLabels(newLabels) {
  152. this.updateChart(this.chartData, newLabels);
  153. },
  154. timeListTwo(newValue) {
  155. this.timeList = newValue; // 将 timeListTwo 的新值赋给 timeList
  156. if (this.chartInstance) {
  157. this.updateChart(this.timeList.y, this.timeList.x); // 更新图表
  158. }
  159. },
  160. // 监听 timeList 的变化
  161. timeList: {
  162. handler(newValue) {
  163. if (this.chartInstance) {
  164. this.updateChart(newValue.y, newValue.x); // 只在 chartInstance 初始化后更新图表
  165. }
  166. },
  167. deep: true, // 深度监听
  168. },
  169. },
  170. mounted() {
  171. this.$nextTick(() => {
  172. this.initializeChart();
  173. });
  174. },
  175. beforeDestroy() {
  176. if (this.chartInstance) {
  177. this.chartInstance.getZr().off("click", this.handleSinglePointerClick);
  178. this.chartInstance.getZr().off("click", this.handleSidebandCursorClick);
  179. this.chartInstance.dispose();
  180. this.chartInstance = null;
  181. }
  182. },
  183. methods: {
  184. getChartXYData() {
  185. return {
  186. x: this.timeList?.x || [],
  187. y: this.timeList?.y || [],
  188. };
  189. },
  190. findClosestIndex(target, xData) {
  191. if (!Array.isArray(xData) || !xData.length) return -1;
  192. let closestIndex = 0;
  193. let minDiff = Math.abs(Number(xData[0]) - target);
  194. for (let i = 1; i < xData.length; i++) {
  195. const diff = Math.abs(Number(xData[i]) - target);
  196. if (diff < minDiff) {
  197. minDiff = diff;
  198. closestIndex = i;
  199. }
  200. }
  201. return closestIndex;
  202. },
  203. bindSinglePointerClick() {
  204. if (!this.chartInstance) return;
  205. this.chartInstance.getZr().off("click", this.handleSinglePointerClick);
  206. this.chartInstance.getZr().on("click", this.handleSinglePointerClick);
  207. },
  208. unbindSinglePointerClick() {
  209. if (!this.chartInstance) return;
  210. this.chartInstance.getZr().off("click", this.handleSinglePointerClick);
  211. },
  212. enableSinglePointer() {
  213. this.bindSinglePointerClick();
  214. },
  215. disableSinglePointer() {
  216. this.unbindSinglePointerClick();
  217. this.singlePointerPoints = [];
  218. this.activeSinglePointer = null;
  219. this.removeSinglePointerSeries();
  220. },
  221. handleSinglePointerClick(event) {
  222. if (!this.checkedGB.includes("单指针")) return;
  223. if (this.checkedGB.includes("边带光标")) return; // 边带模式冻结单指针
  224. if (!this.chartInstance) return;
  225. const { x, y } = this.getChartXYData();
  226. if (!x.length || !y.length) return;
  227. const pointInGrid = this.chartInstance.convertFromPixel(
  228. { seriesIndex: 0 },
  229. [event.offsetX, event.offsetY],
  230. );
  231. const xClick = pointInGrid?.[0];
  232. if (xClick == null || Number.isNaN(xClick)) return;
  233. const idx = this.findClosestIndex(xClick, x);
  234. if (idx < 0) return;
  235. const xAxis = Number(x[idx]);
  236. const yValue = Number(y[idx]);
  237. if (Number.isNaN(xAxis) || Number.isNaN(yValue)) return;
  238. const existed = this.singlePointerPoints.some(
  239. (p) => Number(p.xAxis) === xAxis,
  240. );
  241. if (!existed) {
  242. this.singlePointerPoints.push({
  243. xAxis,
  244. val: yValue.toFixed(6),
  245. });
  246. }
  247. // 默认最后一次点击的单指针作为边带中心(0线)
  248. this.activeSinglePointer = {
  249. xAxis,
  250. val: yValue.toFixed(6),
  251. };
  252. this.applySinglePointerSeries();
  253. },
  254. applySinglePointerSeries() {
  255. if (!this.chartInstance) return;
  256. const option = this.chartInstance.getOption();
  257. const otherSeries = (option.series || []).filter(
  258. (s) => s && s.id !== "SINGLE_POINTER_LINE",
  259. );
  260. if (!this.singlePointerPoints.length) {
  261. this.chartInstance.setOption(
  262. { series: otherSeries },
  263. { replaceMerge: ["series"] },
  264. );
  265. return;
  266. }
  267. const seriesConfig = {
  268. id: "SINGLE_POINTER_LINE",
  269. type: "line",
  270. data: [],
  271. silent: true,
  272. z: 20,
  273. markLine: {
  274. silent: false,
  275. symbol: ["none", "none"],
  276. lineStyle: {
  277. color: "#ff0000",
  278. type: "dashed",
  279. width: 2,
  280. },
  281. label: {
  282. show: true,
  283. position: "end",
  284. formatter: (params) => params.data?.val || "",
  285. },
  286. data: this.singlePointerPoints.map((p) => ({
  287. xAxis: p.xAxis,
  288. val: p.val,
  289. })),
  290. },
  291. };
  292. this.chartInstance.setOption(
  293. { series: [...otherSeries, seriesConfig] },
  294. { replaceMerge: ["series"] },
  295. );
  296. },
  297. removeSinglePointerSeries() {
  298. if (!this.chartInstance) return;
  299. const option = this.chartInstance.getOption();
  300. const series = (option.series || []).filter(
  301. (s) => s && s.id !== "SINGLE_POINTER_LINE",
  302. );
  303. this.chartInstance.setOption({ series }, { replaceMerge: ["series"] });
  304. },
  305. enableSidebandCursor() {
  306. this.sidebandCursorVisible = true;
  307. if (!this.chartInstance) return;
  308. // 边带模式:只响应边带点击,冻结单指针
  309. this.unbindSinglePointerClick();
  310. this.chartInstance.getZr().off("click", this.handleSidebandCursorClick);
  311. this.chartInstance.getZr().on("click", this.handleSidebandCursorClick);
  312. },
  313. disableSidebandCursor() {
  314. this.sidebandCursorVisible = false;
  315. if (this.chartInstance) {
  316. this.chartInstance.getZr().off("click", this.handleSidebandCursorClick);
  317. }
  318. this.removeSidebandCursor();
  319. if (this.checkedGB.includes("单指针")) {
  320. this.bindSinglePointerClick();
  321. }
  322. },
  323. handleSidebandCursorClick(event) {
  324. if (!this.sidebandCursorVisible || !this.chartInstance) return;
  325. if (!this.singlePointerPoints.length) return;
  326. const pointInGrid = this.chartInstance.convertFromPixel(
  327. { seriesIndex: 0 },
  328. [event.offsetX, event.offsetY],
  329. );
  330. const clickX = pointInGrid?.[0];
  331. if (clickX == null || Number.isNaN(clickX)) return;
  332. // 以离点击最近的单指针来定义边带间隔 delta
  333. const nearestCenter = this.singlePointerPoints.reduce((prev, cur) => {
  334. const prevDiff = Math.abs(clickX - Number(prev.xAxis));
  335. const curDiff = Math.abs(clickX - Number(cur.xAxis));
  336. return curDiff < prevDiff ? cur : prev;
  337. });
  338. const referenceCenterX = Number(nearestCenter.xAxis);
  339. const delta = Math.abs(clickX - referenceCenterX);
  340. if (!delta) return;
  341. const points = [];
  342. this.singlePointerPoints.forEach((center) => {
  343. const centerX = Number(center.xAxis);
  344. for (let n = -5; n <= 5; n++) {
  345. if (n === 0) continue;
  346. points.push({
  347. xAxis: centerX + n * delta,
  348. val: `${n > 0 ? "+" : ""}${n}`,
  349. });
  350. }
  351. });
  352. this.sidebandCursorPoints = points;
  353. this.applySidebandSeries();
  354. },
  355. applySidebandSeries() {
  356. if (!this.chartInstance) return;
  357. const option = this.chartInstance.getOption();
  358. const otherSeries = (option.series || []).filter(
  359. (s) => s && s.id !== "SIDEBAND_CURSOR",
  360. );
  361. if (!this.sidebandCursorPoints.length) {
  362. this.chartInstance.setOption(
  363. { series: otherSeries },
  364. { replaceMerge: ["series"] },
  365. );
  366. return;
  367. }
  368. const sidebandSeries = {
  369. id: "SIDEBAND_CURSOR",
  370. type: "line",
  371. data: [],
  372. silent: true,
  373. z: 21,
  374. markLine: {
  375. silent: true,
  376. symbol: ["none", "none"],
  377. lineStyle: {
  378. color: "#ff0000",
  379. type: "dashed",
  380. width: 1,
  381. },
  382. label: {
  383. show: true,
  384. position: "start",
  385. formatter: (params) => params.data?.val || "",
  386. color: "#ff0000",
  387. },
  388. data: this.sidebandCursorPoints.map((p) => ({
  389. xAxis: p.xAxis,
  390. val: p.val,
  391. })),
  392. },
  393. };
  394. this.chartInstance.setOption(
  395. { series: [...otherSeries, sidebandSeries] },
  396. { replaceMerge: ["series"] },
  397. );
  398. },
  399. removeSidebandCursor() {
  400. this.sidebandCursorPoints = [];
  401. if (!this.chartInstance) return;
  402. const option = this.chartInstance.getOption();
  403. const series = (option.series || []).filter(
  404. (s) => s && s.id !== "SIDEBAND_CURSOR",
  405. );
  406. this.chartInstance.setOption({ series }, { replaceMerge: ["series"] });
  407. },
  408. initializeChart() {
  409. const chartDom = this.$refs.chart;
  410. if (chartDom && !this.chartInstance) {
  411. this.chartInstance = echarts.init(chartDom);
  412. }
  413. if (this.timeList.y && this.timeList.x) {
  414. this.updateChart(this.timeList.y, this.timeList.x);
  415. }
  416. },
  417. handleCascaderChange(val) {
  418. if (!val || val.length === 0) {
  419. this.activeFeatures = [];
  420. this.renderTZFeatures();
  421. return;
  422. }
  423. // val = [["时域特征", "Xrms"], ["时域特征", "max_value"]]
  424. this.activeFeatures = val.map((item) => item[item.length - 1]);
  425. this.renderTZFeatures(); // ✅ 直接用你已有方法
  426. },
  427. renderTZFeatures() {
  428. if (!this.chartInstance) return;
  429. const marks = this.activeFeatures
  430. .filter((key) => this.timeList[key] !== undefined)
  431. .map((key) => ({
  432. yAxis: this.timeList[key],
  433. label: {
  434. // formatter: key,
  435. formatter: this.timeList[key],
  436. },
  437. }));
  438. this.chartInstance.setOption({
  439. series: [
  440. {
  441. id: "MAIN_SERIES",
  442. markLine: {
  443. silent: true,
  444. symbol: ["none", "none"],
  445. lineStyle: {
  446. color: "#00aaff",
  447. type: "dashed",
  448. },
  449. data: marks,
  450. },
  451. },
  452. ],
  453. });
  454. },
  455. selectCursor(val) {
  456. const isAlreadyChecked = this.checkedGB.includes(val);
  457. const isSinglePointerVal = val === "单指针";
  458. const isSidebandVal = val === "边带光标";
  459. const isHarmonicVal = val === "谐波光标";
  460. if (isAlreadyChecked) {
  461. if (isSinglePointerVal) {
  462. // 单指针取消时,级联取消边带
  463. this.checkedGB = this.checkedGB.filter(
  464. (v) => v !== "单指针" && v !== "边带光标",
  465. );
  466. } else {
  467. this.checkedGB = this.checkedGB.filter((v) => v !== val);
  468. }
  469. } else if (isSinglePointerVal || isSidebandVal) {
  470. const next = new Set(this.checkedGB);
  471. next.add(val);
  472. next.delete("谐波光标");
  473. this.checkedGB = Array.from(next);
  474. } else if (isHarmonicVal) {
  475. this.checkedGB = [val];
  476. } else {
  477. this.checkedGB = [val];
  478. }
  479. const isSidebandChecked = this.checkedGB.includes("边带光标");
  480. const isHarmonicChecked = this.checkedGB.includes("谐波光标");
  481. const isSinglePointerChecked = this.checkedGB.includes("单指针");
  482. isSidebandChecked
  483. ? this.enableSidebandCursor()
  484. : this.disableSidebandCursor();
  485. isHarmonicChecked
  486. ? this.enableHarmonicCursor()
  487. : this.disableHarmonicCursor();
  488. if (!isSinglePointerChecked) {
  489. this.disableSinglePointer();
  490. } else if (isSidebandChecked) {
  491. // 保留单指针线,冻结单指针点击
  492. this.unbindSinglePointerClick();
  493. } else {
  494. this.enableSinglePointer();
  495. }
  496. },
  497. // 更新图表数据
  498. updateChart(data, labels) {
  499. if (!this.chartInstance) return; // Check if chartInstance is available
  500. console.log(this.timeList, "updataChart 时域图");
  501. const option = {
  502. // title: {
  503. // text: this.timeList.title,
  504. // left: "center",
  505. // },
  506. grid: {
  507. left: 60, // 原来是100,适当缩小左右边距
  508. right: 60,
  509. // bottom: 90, // 给推拽条和坐标轴腾出空间
  510. top: 30,
  511. bottom: 50,
  512. },
  513. toolbox: {
  514. right: 10,
  515. // top: 55, // 👈 工具栏在最上面
  516. feature: {
  517. dataZoom: { yAxisIndex: "none" },
  518. restore: {},
  519. saveAsImage: {},
  520. // myCustomTool3: {
  521. // show: true,
  522. // title: "特征值",
  523. // icon: `image://${require("@/assets/analyse/10.png")}`,
  524. // onclick: () => this.Show(),
  525. // },
  526. },
  527. },
  528. xAxis: {
  529. type: "value",
  530. name: this.timeList.xaxis,
  531. nameLocation: "center",
  532. nameTextStyle: {
  533. fontSize: 12,
  534. color: "#333",
  535. padding: [15, 0, 0, 0], // 增加X轴标题和轴线的距离
  536. },
  537. axisLabel: {
  538. margin: 1, // 增加数值标签和轴线的间距
  539. formatter: (value) => value,
  540. },
  541. axisTick: {
  542. show: true,
  543. },
  544. axisLine: {
  545. show: true,
  546. },
  547. },
  548. yAxis: {
  549. type: "value",
  550. name: this.timeList.yaxis,
  551. nameTextStyle: {
  552. fontSize: 12,
  553. color: "#333",
  554. padding: [0, 10, 0, 0], // 根据需要微调
  555. },
  556. axisLabel: {
  557. margin: 10,
  558. formatter: (value) => value,
  559. },
  560. axisTick: {
  561. show: true,
  562. },
  563. axisLine: {
  564. show: true,
  565. },
  566. },
  567. tooltip: {
  568. trigger: "axis",
  569. formatter: (params) => {
  570. const xValue = params[0].value[0];
  571. const yValue = params[0].value[1];
  572. return `X: ${xValue}<br/>Y: ${yValue}`;
  573. },
  574. axisPointer: {
  575. type: "line",
  576. },
  577. },
  578. dataZoom: [
  579. {
  580. type: "inside",
  581. xAxisIndex: 0,
  582. filterMode: "none",
  583. zoomOnMouseWheel: false,
  584. moveOnMouseMove: false,
  585. moveOnMouseWheel: false,
  586. },
  587. ],
  588. // dataZoom: [
  589. // {
  590. // type: "inside",
  591. // start: 0,
  592. // end: 10,
  593. // },
  594. // {
  595. // type: "slider",
  596. // start: 0,
  597. // end: 10,
  598. // handleSize: "80%",
  599. // showDataShadow: false,
  600. // top: 30, // 👈 放到顶部
  601. // height: 20, // 👈 控制高度(可选)
  602. // },
  603. // ],
  604. series: [
  605. {
  606. id: "MAIN_SERIES", // ✅ 必须加
  607. name: "数据系列",
  608. type: "line",
  609. data: labels.map((item, index) => [item, data[index]]),
  610. // data: (labels || []).map((item, index) => [item, data?.[index]])
  611. symbol: "none",
  612. symbolSize: 8,
  613. lineStyle: {
  614. color: "#162961",
  615. width: 1,
  616. },
  617. itemStyle: {
  618. color: "#162961",
  619. borderColor: "#fff",
  620. borderWidth: 1,
  621. },
  622. large: true,
  623. progressive: 2000,
  624. },
  625. ],
  626. };
  627. this.chartInstance.setOption(option, true);
  628. this.$nextTick(() => {
  629. this.renderTZFeatures(); // ✅ 防止被覆盖
  630. this.applySinglePointerSeries();
  631. this.applySidebandSeries();
  632. });
  633. },
  634. generateSeries(featureLines) {
  635. const createMarkLine = (dataSource, color) => ({
  636. type: "line",
  637. markLine: {
  638. silent: false,
  639. lineStyle: { color, type: "dashed", width: 1 },
  640. symbol: ["none", "none"],
  641. label: {
  642. show: true,
  643. position: "end",
  644. formatter: ({ data }) => data.val,
  645. },
  646. emphasis: {
  647. symbol: ["none", "none"],
  648. lineStyle: { color: "#FF6A00", width: 2 },
  649. label: {
  650. show: true,
  651. formatter: ({ value }) => `特征值: ${value}`,
  652. color: "#000",
  653. },
  654. },
  655. data: dataSource.map(({ Xaxis, val }) => ({ xAxis: Xaxis, val })),
  656. },
  657. });
  658. const markLines = [
  659. { data: featureLines.Fr, color: "#A633FF" },
  660. { data: featureLines.BPFI, color: "#23357e" },
  661. { data: featureLines.BPFO, color: "#42a0ae" },
  662. { data: featureLines.BSF, color: "#008080" },
  663. { data: featureLines.FTF, color: "#af254f" },
  664. { data: featureLines.B3P, color: "#FFD700" },
  665. ].map(({ data, color }) => createMarkLine(data, color));
  666. return [
  667. {
  668. name: "数据系列",
  669. type: "line",
  670. data: this.spectrumList.x.map((x, i) => [x, this.spectrumList.y[i]]),
  671. symbol: "none",
  672. lineStyle: { color: "#162961", width: 1 },
  673. itemStyle: { color: "#162961", borderColor: "#fff", borderWidth: 1 },
  674. large: true,
  675. },
  676. ...markLines,
  677. ];
  678. },
  679. renderFeatureSeries(featureLines) {
  680. if (!this.chartInstance) return;
  681. const currentOption = this.chartInstance.getOption();
  682. // 保留光标
  683. const cursorLineSeries =
  684. currentOption.series.find((s) => s && s.id === "CURSOR_LINE_SERIES") ||
  685. {};
  686. const cursorPointSeries =
  687. currentOption.series.find((s) => s && s.id === "CURSOR_POINT_SERIES") ||
  688. {};
  689. const cursorHighLineSeries =
  690. currentOption.series.find((s) => s && s.id === "PEAK_REFERENCE_LINE") ||
  691. {};
  692. const featureSeries = this.generateSeries(featureLines);
  693. this.chartInstance.setOption(
  694. {
  695. series: [
  696. (currentOption.series || []).find(
  697. (s) => s && (s.id === "MAIN_SERIES" || s.name === "数据系列"),
  698. ) || (currentOption.series || []).find((s) => s),
  699. ...featureSeries.slice(1),
  700. cursorLineSeries,
  701. cursorPointSeries,
  702. cursorHighLineSeries,
  703. ].filter((s) => s && s.type),
  704. },
  705. { replaceMerge: ["series"] },
  706. );
  707. },
  708. getTime() {
  709. this.$emit("handleLoading", {
  710. id: this.chartId,
  711. currentRow: this.currentRow,
  712. loading: true,
  713. });
  714. const params = {
  715. ids: this.ids,
  716. analysisType: "time",
  717. windCode: this.windCode,
  718. };
  719. axios
  720. .post("/AnalysisMulti/analysis/time", params)
  721. .then((res) => {
  722. this.timeList = JSON.parse(res.data);
  723. console.log(this.timeList, "timeList");
  724. })
  725. .catch((error) => {})
  726. .finally(() => {
  727. this.$emit("handleLoading", {
  728. id: this.chartId,
  729. currentRow: this.currentRow,
  730. loading: false,
  731. });
  732. });
  733. },
  734. Show() {
  735. this.TZshow = !this.TZshow;
  736. },
  737. },
  738. };
  739. </script>
  740. <style lang="scss" scoped>
  741. .line-chart {
  742. width: 100%;
  743. height: 400px;
  744. }
  745. .FD {
  746. width: 100%;
  747. height: 1px;
  748. position: relative;
  749. }
  750. .eigenvalue {
  751. position: absolute;
  752. top: 30px;
  753. right: 0;
  754. font-size: 10px;
  755. width: 100px;
  756. border: 1px solid black;
  757. padding: 5px;
  758. background: #fff;
  759. z-index: 99;
  760. h5 {
  761. line-height: 16px;
  762. height: 16px;
  763. }
  764. }
  765. .control-panel {
  766. display: flex;
  767. flex-wrap: wrap;
  768. gap: 12px;
  769. padding: 8px 12px;
  770. background: #f5f7fa;
  771. border: 1px solid #ddd;
  772. border-radius: 6px;
  773. margin-bottom: 10px;
  774. }
  775. .panel-block {
  776. display: flex;
  777. align-items: center;
  778. gap: 6px;
  779. }
  780. .label {
  781. font-size: 12px;
  782. color: #666;
  783. }
  784. .btn-group {
  785. display: flex;
  786. gap: 6px;
  787. }
  788. .btn {
  789. padding: 2px 8px;
  790. font-size: 12px;
  791. border: 1px solid #ccc;
  792. border-radius: 3px;
  793. cursor: pointer;
  794. }
  795. .btn.active {
  796. background: #409eff;
  797. color: #fff;
  798. }
  799. </style>