spectrogramcharts copy.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556
  1. <template>
  2. <div>
  3. <div class="FD">
  4. <!-- 光标 -->
  5. <div v-if="BGshow" class="eigenvalue">
  6. <el-checkbox-group v-model="checkedGB" @change="handlecursor">
  7. <el-checkbox v-for="(item, index) in GBcheckList" :key="index" :label="item.val">
  8. {{ item.val }}
  9. </el-checkbox>
  10. </el-checkbox-group>
  11. </div>
  12. <!-- 特征值 -->
  13. <div v-if="PXshow" class="eigenvalue">
  14. <el-checkbox-group v-model="checkedValues" @change="handleCheckChange">
  15. <el-checkbox v-for="(item, index) in PXcheckList" :key="index" :label="item.val">
  16. {{ item.val }}
  17. </el-checkbox>
  18. </el-checkbox-group>
  19. </div>
  20. </div>
  21. <!-- ECharts 图表容器 -->
  22. <div class="line-chart" ref="chart"></div>
  23. </div>
  24. </template>
  25. <script>
  26. import axios from "axios";
  27. import * as echarts from "echarts"; // 导入 echarts 库
  28. export default {
  29. name: "TimedomainCharts", // 组件名称
  30. props: {
  31. currentIndex: {
  32. type: Number,
  33. default: 0,
  34. },
  35. activeIndex: {
  36. type: Number,
  37. default: 0,
  38. },
  39. ids: {
  40. type: Array,
  41. default: () => [],
  42. },
  43. spectrumListTwo: {
  44. type: Object,
  45. default: () => ({}),
  46. },
  47. currentRow: {
  48. type: Object,
  49. default: () => ({}),
  50. },
  51. windCode: {
  52. type: String,
  53. default: "",
  54. },
  55. },
  56. data() {
  57. return {
  58. chartInstance: null,
  59. option: null,
  60. TZshow: false,
  61. BGshow: false,
  62. PXshow: false,
  63. spectrumList: {},
  64. GBcheckList: [
  65. { val: "添加光标" , checked: false},
  66. { val: "谐波光标" , checked: false},
  67. { val: "边带光标" , checked: false},
  68. { val: "移动峰值" , checked: false},
  69. ],
  70. PXcheckList: [
  71. { val: "Fr", checked: false },
  72. { val: "BPFI", checked: false },
  73. { val: "BPFO", checked: false },
  74. { val: "BSF", checked: false },
  75. { val: "FTF", checked: false },
  76. { val: "3P", checked: false },
  77. ],
  78. Fr: [],
  79. BPFI: [],
  80. BPFO: [],
  81. BSF: [],
  82. FTF: [],
  83. B3P: [],
  84. checkedGB: [],
  85. checkedValues: [],
  86. cursorLines: [], // 存储添加的光标线
  87. cursorPoints: [], // 用于存储参考点(浮动点)数据
  88. };
  89. },
  90. watch: {
  91. // 监听 chartData 和 chartLabels 的变化,重新绘制图表
  92. chartData(newData) {
  93. this.updateChart(newData, this.chartLabels);
  94. },
  95. chartLabels(newLabels) {
  96. this.updateChart(this.chartData, newLabels);
  97. },
  98. spectrumListTwo(newValue) {
  99. this.spectrumList = newValue; // 将 spectrumListTwo 的新值赋给 spectrumList
  100. if (this.chartInstance) {
  101. this.updateChart(this.spectrumList.y, this.spectrumList.x); // 更新图表
  102. }
  103. },
  104. spectrumList: {
  105. handler(newValue) {
  106. if (!newValue) return;
  107. console.log(newValue, "newValue");
  108. if (this.chartInstance) {
  109. this.updateChart(newValue.y, newValue.x); // 只在 chartInstance 初始化后更新图表
  110. }
  111. },
  112. deep: true, // 深度监听
  113. },
  114. },
  115. destroyed() {
  116. if (this.chartInstance) {
  117. this.chartInstance.getZr().off('dblclick', this.handleDoubleClick)
  118. }
  119. },
  120. mounted() {
  121. this.$nextTick(() => {
  122. setTimeout(() => {
  123. this.initializeChart(); // 延迟2秒后调用
  124. this.getTime();
  125. }, 500); // 500毫秒,即0.5秒
  126. });
  127. },
  128. methods: {
  129. initializeChart() {
  130. const chartDom = this.$refs.chart;
  131. if (chartDom && !this.chartInstance) {
  132. this.chartInstance = echarts.init(chartDom); // 仅初始化一次
  133. }
  134. // 使用 $nextTick 确保数据更新后再渲染图表
  135. this.$nextTick(() => {
  136. if (this.chartInstance && this.spectrumList.y && this.spectrumList.x) {
  137. this.updateChart(this.spectrumList.y, this.spectrumList.x); // 更新图表
  138. }
  139. });
  140. // 绑定双击事件
  141. this.chartInstance.getZr().on('dblclick', this.handleDoubleClick)
  142. },
  143. handleDoubleClick(event) {
  144. // 只有勾选了“添加光标”时才响应
  145. if (!this.checkedGB.includes('添加光标')) return
  146. // 转换鼠标位置为图表的 x 轴数值
  147. const pointInGrid = this.chartInstance.convertFromPixel({ seriesIndex: 0 }, [event.offsetX, event.offsetY])
  148. const xValue = pointInGrid[0]
  149. // 添加参考线(使用“光标”命名)
  150. this.cursorLines.push({ xAxis: xValue, val: '光标' })
  151. // 更新图表:添加参考线
  152. this.updateCursorLines()
  153. // 自动取消勾选
  154. this.checkedGB = this.checkedGB.filter(val => val !== '添加光标')
  155. this.handlecursor() // 同步 checked 状态
  156. },
  157. updateCursorLines() {
  158. const cursorLine = {
  159. type: 'line',
  160. markLine: {
  161. silent: true,
  162. lineStyle: { color: '#FF0000', type: 'dotted', width: 2 },
  163. symbol: ['none', 'none'],
  164. label: {
  165. show: true,
  166. formatter: ({ data }) => `光标`,
  167. position: 'end',
  168. },
  169. data: this.cursorLines.map(({ xAxis, val }) => ({ xAxis, val })),
  170. },
  171. }
  172. // 更新图表,仅更新 series
  173. if (this.chartInstance) {
  174. this.chartInstance.setOption(
  175. {
  176. series: [
  177. {
  178. name: '数据系列',
  179. type: 'line',
  180. data: this.spectrumList.x.map((x, i) => [x, this.spectrumList.y[i]]),
  181. symbol: 'none',
  182. lineStyle: { color: '#162961', width: 1 },
  183. itemStyle: { color: '#162961', borderColor: '#fff', borderWidth: 1 },
  184. large: true,
  185. },
  186. ...this.generateSeries({
  187. Fr: this.Fr,
  188. BPFI: this.BPFI,
  189. BPFO: this.BPFO,
  190. BSF: this.BSF,
  191. FTF: this.FTF,
  192. B3P: this.B3P,
  193. }),
  194. cursorLine,
  195. ],
  196. },
  197. { replaceMerge: ['series'] }
  198. )
  199. }
  200. },
  201. // 更新图表数据
  202. updateChart(data, labels) {
  203. if (
  204. !this.chartInstance ||
  205. !Array.isArray(labels) ||
  206. !Array.isArray(data) ||
  207. labels.length !== data.length
  208. ) {
  209. console.error("Invalid data or labels");
  210. return;
  211. }
  212. const createMarkLine = (dataSource, color) => ({
  213. type: "line",
  214. markLine: {
  215. silent: false,
  216. lineStyle: { color, type: "solid", width: 2 },
  217. symbol: ["arrow", "none"],
  218. label: {
  219. show: true,
  220. position: "end",
  221. formatter: ({ data }) => data.val,
  222. },
  223. emphasis: {
  224. lineStyle: { color: "#FF6A00", width: 4 },
  225. label: {
  226. show: true,
  227. formatter: ({ value }) => `特征值: ${value}`,
  228. color: "#000",
  229. backgroundColor: "#FFF",
  230. padding: [2, 4],
  231. borderRadius: 3,
  232. fontSize: 12,
  233. },
  234. },
  235. data: dataSource.map(({ Xaxis, val }) => ({ xAxis: Xaxis, val })),
  236. },
  237. });
  238. const markLines = [
  239. { data: this.Fr, color: "#A633FF" },
  240. { data: this.BPFI, color: "#23357e" },
  241. { data: this.BPFO, color: "#42a0ae" },
  242. { data: this.BSF, color: "#008080" },
  243. { data: this.FTF, color: "#af254f" },
  244. { data: this.B3P, color: "#FFD700" },
  245. ].map(({ data, color }) => createMarkLine(data, color));
  246. const option = {
  247. title: { text: this.spectrumList.title, left: "center" },
  248. toolbox: {
  249. feature: {
  250. dataZoom: { yAxisIndex: "none" },
  251. restore: {},
  252. saveAsImage: {},
  253. myCustomTool: {
  254. show: true,
  255. title: "上一条",
  256. icon: `image://${require("@/assets/analyse/08.png")}`,
  257. onclick: () => this.previousRow(),
  258. },
  259. myCustomTool2: {
  260. show: true,
  261. title: "下一条",
  262. icon: `image://${require("@/assets/analyse/09.png")}`,
  263. onclick: () => this.nextRow(),
  264. },
  265. myCustomTool4: {
  266. show: true,
  267. title: "光标",
  268. icon: `image://${require("@/assets/analyse/12.png")}`,
  269. onclick: () => this.Show("2"),
  270. },
  271. myCustomTool5: {
  272. show: true,
  273. title: "特征频率",
  274. icon: `image://${require("@/assets/analyse/13.png")}`,
  275. onclick: () => this.Show("3"),
  276. },
  277. },
  278. },
  279. xAxis: {
  280. type: "value",
  281. name: this.spectrumList.xaxis,
  282. nameLocation: "center",
  283. nameTextStyle: {
  284. fontSize: 14,
  285. color: "#333",
  286. padding: [10, 0, 0, 0],
  287. },
  288. },
  289. yAxis: {
  290. type: "value",
  291. name: this.spectrumList.yaxis,
  292. nameTextStyle: {
  293. fontSize: 14,
  294. color: "#333",
  295. padding: [10, 0, 0, 0],
  296. },
  297. },
  298. tooltip: {
  299. trigger: "axis",
  300. formatter: ([
  301. {
  302. value: [x, y],
  303. },
  304. ]) => `X: ${x}<br/>Y: ${y}`,
  305. axisPointer: { type: "line" },
  306. },
  307. dataZoom: [
  308. { type: "inside", start: 0, end: 10 },
  309. {
  310. type: "slider",
  311. start: 0,
  312. end: 10,
  313. handleSize: "80%",
  314. showDataShadow: false,
  315. },
  316. ],
  317. series: [
  318. {
  319. name: "数据系列",
  320. type: "line",
  321. data: labels.map((x, i) => [x, data[i]]),
  322. symbol: "none",
  323. lineStyle: { color: "#162961", width: 1 },
  324. itemStyle: {
  325. color: "#162961",
  326. borderColor: "#fff",
  327. borderWidth: 1,
  328. },
  329. large: true,
  330. progressive: 2000,
  331. },
  332. ...markLines,
  333. ],
  334. };
  335. this.chartInstance.setOption(option);
  336. },
  337. // 处理复选框变化 特征值
  338. handleCheckChange() {
  339. this.PXcheckList.forEach((item) => {
  340. item.checked = this.checkedValues.includes(item.val);
  341. });
  342. // 构建新的特征频率数据
  343. const newFeatureLines = {
  344. Fr: this.checkedValues.includes("Fr") ? this.spectrumList.fn_Gen : [],
  345. BPFI: this.checkedValues.includes("BPFI") ? this.spectrumList.BPFI : [],
  346. BPFO: this.checkedValues.includes("BPFO") ? this.spectrumList.BPFO : [],
  347. BSF: this.checkedValues.includes("BSF") ? this.spectrumList.BSF : [],
  348. FTF: this.checkedValues.includes("FTF") ? this.spectrumList.FTF : [],
  349. B3P: this.checkedValues.includes("3P")
  350. ? Array.isArray(this.spectrumList.B3P)
  351. ? this.spectrumList.B3P
  352. : [{ Xaxis: this.spectrumList.B3P, val: "3P" }]
  353. : [],
  354. };
  355. // 仅更新 `series`,避免重新渲染整个 ECharts 组件
  356. if (this.chartInstance) {
  357. this.chartInstance.setOption(
  358. {
  359. series: this.generateSeries(newFeatureLines),
  360. },
  361. { replaceMerge: ["series"] }
  362. );
  363. }
  364. },
  365. generateSeries(featureLines) {
  366. const createMarkLine = (dataSource, color) => ({
  367. type: "line",
  368. markLine: {
  369. silent: false,
  370. lineStyle: { color, type: "dashed", width:1 },
  371. symbol: ["arrow", "none"],
  372. label: {
  373. show: true,
  374. position: "end",
  375. formatter: ({ data }) => data.val,
  376. },
  377. emphasis: {
  378. lineStyle: { color: "#FF6A00", width: 2 },
  379. label: {
  380. show: true,
  381. formatter: ({ value }) => `特征值: ${value}`,
  382. color: "#000",
  383. },
  384. },
  385. data: dataSource.map(({ Xaxis, val }) => ({ xAxis: Xaxis, val })),
  386. },
  387. });
  388. const markLines = [
  389. { data: featureLines.Fr, color: "#A633FF" },
  390. { data: featureLines.BPFI, color: "#23357e" },
  391. { data: featureLines.BPFO, color: "#42a0ae" },
  392. { data: featureLines.BSF, color: "#008080" },
  393. { data: featureLines.FTF, color: "#af254f" },
  394. { data: featureLines.B3P, color: "#FFD700" },
  395. ].map(({ data, color }) => createMarkLine(data, color));
  396. return [
  397. {
  398. name: "数据系列",
  399. type: "line",
  400. data: this.spectrumList.x.map((x, i) => [x, this.spectrumList.y[i]]),
  401. symbol: "none",
  402. lineStyle: { color: "#162961", width: 1 },
  403. itemStyle: { color: "#162961", borderColor: "#fff", borderWidth: 1 },
  404. large: true,
  405. },
  406. ...markLines,
  407. ];
  408. },
  409. // 处理复选框变化 光标
  410. handlecursor(){
  411. this.GBcheckList.forEach((item) => {
  412. item.checked = this.checkedGB.includes(item.val);
  413. console.log( item.checked ,"item.checked");
  414. });
  415. },
  416. getTime() {
  417. this.$emit("handleLoading", null, true, this.activeIndex);
  418. const params = {
  419. ids: this.ids,
  420. windCode: this.windCode,
  421. analysisType: "frequency",
  422. };
  423. axios
  424. .post("/WJapi/analysis/frequency", params)
  425. .then((res) => {
  426. console.log(res, "频谱图数据");
  427. this.spectrumList = { ...JSON.parse(res.data) };
  428. console.log(this.spectrumList, "频谱图数据1");
  429. const XrmsValue = this.spectrumList?.Xrms;
  430. // 通过 $emit 传递 XrmsValue 给父组件
  431. this.$emit("updateXrms", XrmsValue);
  432. this.PXcheckList.forEach((item) => {
  433. if (item.checked) {
  434. switch (item.val) {
  435. case "Fr":
  436. this.Fr = this.spectrumList.fn_Gen;
  437. break;
  438. case "BPFI":
  439. this.BPFI = this.spectrumList.BPFI;
  440. break;
  441. case "BPFO":
  442. this.BPFO = this.spectrumList.BPFO;
  443. break;
  444. case "BSF":
  445. this.BSF = this.spectrumList.BSF;
  446. break;
  447. case "FTF":
  448. this.FTF = this.spectrumList.FTF;
  449. break;
  450. case "3P":
  451. this.B3P = Array.isArray(this.spectrumList.B3P)
  452. ? this.spectrumList.B3P
  453. : [{ Xaxis: this.spectrumList.B3P, val: "3P" }];
  454. break;
  455. default:
  456. break;
  457. }
  458. }
  459. });
  460. })
  461. .catch((error) => {
  462. console.error(error);
  463. })
  464. .finally(() => {
  465. this.$emit("handleLoading", this.currentRow, false, this.activeIndex);
  466. });
  467. },
  468. previousRow() {
  469. this.$emit("update-previous-row", 2, this.activeIndex);
  470. },
  471. nextRow() {
  472. this.$emit("update-next-row", 2, this.activeIndex);
  473. },
  474. Show(value) {
  475. const stateMap = {
  476. 1: { TZshow: true, BGshow: false, PXshow: false },
  477. 2: { TZshow: false, BGshow: true, PXshow: false },
  478. 3: { TZshow: false, BGshow: false, PXshow: true },
  479. };
  480. if (stateMap[value]) {
  481. // Toggle the state for the given value
  482. this.TZshow = value === "1" ? !this.TZshow : false;
  483. this.BGshow = value === "2" ? !this.BGshow : false;
  484. this.PXshow = value === "3" ? !this.PXshow : false;
  485. }
  486. },
  487. },
  488. };
  489. </script>
  490. <style lang="scss" scoped>
  491. .line-chart {
  492. width: 100%;
  493. height: 280px;
  494. }
  495. .FD {
  496. width: 100%;
  497. height: 1px;
  498. position: relative;
  499. }
  500. .eigenvalue {
  501. position: absolute;
  502. top: 30px;
  503. right: 0;
  504. font-size: 10px;
  505. width: 100px;
  506. border: 1px solid black;
  507. padding: 5px;
  508. background: #fff;
  509. z-index: 99;
  510. h5 {
  511. line-height: 16px;
  512. height: 16px;
  513. }
  514. }
  515. </style>