envelopecharts.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. <template>
  2. <div>
  3. <!-- ECharts 图表容器 -->
  4. <div class="section">
  5. <el-input
  6. size="small"
  7. v-model="xiaoval"
  8. placeholder="请输入查询范围"
  9. @input="handleInput"
  10. ></el-input>
  11. <span>—</span>
  12. <el-input
  13. size="small"
  14. v-model="daval"
  15. placeholder="请输入查询范围"
  16. @input="handleInput"
  17. ></el-input>
  18. <el-button size="small" @click="chaxun">确定</el-button>
  19. <div v-if="PXshow" class="eigenvalue">
  20. <el-checkbox-group v-model="checkedValues" @change="handleCheckChange">
  21. <el-checkbox
  22. v-for="(item, index) in PXcheckList"
  23. :key="index"
  24. :label="item.val"
  25. >
  26. {{ item.val }}
  27. </el-checkbox>
  28. </el-checkbox-group>
  29. </div>
  30. <!-- 光标 -->
  31. <!-- <div v-if="BGshow" class="eigenvalue">
  32. <el-checkbox-group v-model="checkedGB" @change="handlecursor">
  33. <el-checkbox v-for="(item, index) in GBcheckList" :key="index" :label="item.val" :disabled="item.disabled">
  34. {{ item.val }}
  35. </el-checkbox>
  36. </el-checkbox-group>
  37. </div> -->
  38. </div>
  39. <div class="line-chart" ref="chart"></div>
  40. </div>
  41. </template>
  42. <script>
  43. import * as echarts from "echarts"; // 导入 echarts 库
  44. import axios from "axios";
  45. import Bdgb from "./envelopecharts/Bdgb";
  46. import Xdgb from "./envelopecharts/Xdgb";
  47. import Tjgb from "./envelopecharts/Tjgb";
  48. import cursorReferenceMixin from "./envelopecharts/cursorReferenceMixin";
  49. export default {
  50. name: "TimedomainCharts", // 组件名称
  51. mixins: [cursorReferenceMixin, Bdgb, Xdgb, Tjgb],
  52. props: {
  53. // 可以通过 props 接收外部传入的数据
  54. currentIndex: {
  55. type: Number,
  56. default: 0,
  57. },
  58. ids: {
  59. type: Array,
  60. default: () => [],
  61. },
  62. activeIndex: {
  63. type: Number,
  64. default: 0,
  65. },
  66. envelopeListTwo: {
  67. type: Object,
  68. default: () => ({}),
  69. },
  70. currentRow: {
  71. type: Object,
  72. default: () => ({}),
  73. },
  74. windCode: {
  75. type: String,
  76. default: "",
  77. },
  78. },
  79. data() {
  80. return {
  81. chartInstance: null,
  82. option: null,
  83. xiaoval: "",
  84. daval: "",
  85. envelopeList: {},
  86. TZshow: false,
  87. BGshow: false,
  88. PXshow: false,
  89. GBcheckList: [
  90. { val: "添加光标", checked: false, disabled: false },
  91. { val: "谐波光标", checked: false, disabled: false },
  92. { val: "边带光标", checked: false, disabled: false },
  93. { val: "移动峰值", checked: false, disabled: false },
  94. ],
  95. PXcheckList: [
  96. { val: "Fr", checked: false },
  97. { val: "BPFI", checked: false },
  98. { val: "BPFO", checked: false },
  99. { val: "BSF", checked: false },
  100. { val: "FTF", checked: false },
  101. { val: "3P", checked: false },
  102. ],
  103. Fr: [],
  104. BPFI: [],
  105. BPFO: [],
  106. BSF: [],
  107. FTF: [],
  108. B3P: [],
  109. checkedGB: [],
  110. checkedValues: [],
  111. // 添加光标
  112. cursorPoints: [], // 存储参考点数据
  113. };
  114. },
  115. watch: {
  116. // 移除 envelopeList 的监听
  117. envelopeListTwo: {
  118. handler(newValue) {
  119. this.updateChart(newValue.y, newValue.x); // 更新图表
  120. },
  121. deep: true, // 确保监听对象的深层次变化
  122. },
  123. },
  124. mounted() {
  125. this.initializeChart();
  126. this.$nextTick(() => {
  127. if (this.chartInstance) {
  128. this.chartInstance.getZr().on("dblclick", this.handleDoubleClick);
  129. }
  130. });
  131. },
  132. beforeDestroy() {
  133. if (this.chartInstance) {
  134. this.chartInstance.getZr().off("dblclick", this.handleDoubleClick);
  135. this.chartInstance
  136. .getZr()
  137. .off("mousemove", this.handleSidebandCursorMove);
  138. this.chartInstance.dispose();
  139. }
  140. },
  141. methods: {
  142. handlecursor() {
  143. // 特殊光标类型数组
  144. const specialCursors = ["添加光标", "移动峰值", "边带光标", "谐波光标"];
  145. // 检查是否有多个特殊光标被选中
  146. const selectedSpecials = specialCursors.filter((type) =>
  147. this.checkedGB.includes(type)
  148. );
  149. // 如果多于1个,则只保留第一个
  150. if (selectedSpecials.length > 1) {
  151. this.checkedGB = [
  152. ...this.checkedGB.filter((val) => !specialCursors.includes(val)),
  153. selectedSpecials[0], // 保留第一个选中的
  154. ];
  155. }
  156. // 其余逻辑保持不变...
  157. const isMoveChecked = this.checkedGB.includes("移动峰值");
  158. const isSidebandChecked = this.checkedGB.includes("边带光标");
  159. const isHarmonicChecked = this.checkedGB.includes("谐波光标");
  160. isMoveChecked ? this.handleMoveCursor() : this.removeCursor();
  161. isSidebandChecked
  162. ? this.enableSidebandCursor()
  163. : this.disableSidebandCursor();
  164. isHarmonicChecked
  165. ? this.enableHarmonicCursor()
  166. : this.disableHarmonicCursor();
  167. // 设置互斥disabled状态
  168. this.GBcheckList = this.GBcheckList.map((item) => ({
  169. ...item,
  170. disabled:
  171. specialCursors.includes(item.val) &&
  172. this.checkedGB.some(
  173. (val) => val !== item.val && specialCursors.includes(val)
  174. ),
  175. }));
  176. },
  177. // 。。。。。。。。。。。。。。。
  178. handleInput() {
  179. this.$emit("update:modelValue", {
  180. xiaoval: this.xiaoval,
  181. daval: this.daval,
  182. });
  183. },
  184. chaxun() {
  185. this.$emit("handleLoading", null, true, this.activeIndex);
  186. const params = {
  187. ids: this.ids,
  188. windCode: this.windCode,
  189. analysisType: "envelope",
  190. fmin: this.xiaoval,
  191. fmax: this.daval,
  192. };
  193. axios
  194. .post("/WJapi/analysis/envelope", params)
  195. .then((res) => {
  196. this.envelopeList = JSON.parse(res.data);
  197. const XrmsValue = this.envelopeList?.Xrms;
  198. this.$emit("updateXrms", XrmsValue);
  199. this.PXcheckList.forEach((item) => {
  200. if (item.checked) {
  201. switch (item.val) {
  202. case "Fr":
  203. this.Fr = this.envelopeList.fn_Gen;
  204. break;
  205. case "BPFI":
  206. this.BPFI = this.envelopeList.BPFI;
  207. break;
  208. case "BPFO":
  209. this.BPFO = this.envelopeList.BPFO;
  210. break;
  211. case "BSF":
  212. this.BSF = this.envelopeList.BSF;
  213. break;
  214. case "FTF":
  215. this.FTF = this.envelopeList.FTF;
  216. break;
  217. case "3P":
  218. this.B3P = Array.isArray(this.envelopeList.B3P)
  219. ? this.envelopeList.B3P
  220. : [{ Xaxis: this.envelopeList.B3P, val: "3P" }];
  221. break;
  222. default:
  223. break;
  224. }
  225. }
  226. });
  227. const defaultData =
  228. this.envelopeList.y || this.envelopeListTwo.y || [];
  229. const defaultLabels =
  230. this.envelopeList.x || this.envelopeListTwo.x || [];
  231. // 这里是数据采样,减少数据量
  232. const sampleData = defaultData.filter((_, index) => index % 50 === 0); // 每隔50个数据点采样一次
  233. const sampleLabels = defaultLabels.filter(
  234. (_, index) => index % 50 === 0
  235. ); // 同样对 X 数据进行采样
  236. // 直接调用 updateChart 方法
  237. this.updateChart(sampleData, sampleLabels);
  238. })
  239. .catch((error) => {
  240. console.error(error);
  241. })
  242. .finally(() => {
  243. this.$emit("handleLoading", this.currentRow, false, this.activeIndex);
  244. });
  245. },
  246. handleCheckChange() {
  247. this.PXcheckList.forEach((item) => {
  248. item.checked = this.checkedValues.includes(item.val);
  249. });
  250. // 构建新的特征频率数据
  251. const newFeatureLines = {
  252. Fr: this.checkedValues.includes("Fr") ? this.envelopeList.fn_Gen : [],
  253. BPFI: this.checkedValues.includes("BPFI") ? this.envelopeList.BPFI : [],
  254. BPFO: this.checkedValues.includes("BPFO") ? this.envelopeList.BPFO : [],
  255. BSF: this.checkedValues.includes("BSF") ? this.envelopeList.BSF : [],
  256. FTF: this.checkedValues.includes("FTF") ? this.envelopeList.FTF : [],
  257. B3P: this.checkedValues.includes("3P")
  258. ? Array.isArray(this.envelopeList.B3P)
  259. ? this.envelopeList.B3P
  260. : [{ Xaxis: this.envelopeList.B3P, val: "3P" }]
  261. : [],
  262. };
  263. // 仅更新 `series`,避免重新渲染整个 ECharts 组件
  264. if (this.chartInstance) {
  265. this.chartInstance.setOption(
  266. {
  267. series: this.generateSeries(newFeatureLines),
  268. },
  269. { replaceMerge: ["series"] }
  270. );
  271. }
  272. },
  273. generateSeries(featureLines) {
  274. const createMarkLine = (dataSource, color) => ({
  275. type: "line",
  276. markLine: {
  277. silent: false,
  278. lineStyle: { color, type: "dashed", width: 1 },
  279. symbol: ["arrow", "none"],
  280. label: {
  281. show: true,
  282. position: "end",
  283. formatter: ({ data }) => data.val,
  284. },
  285. emphasis: {
  286. lineStyle: { color: "#FF6A00", width: 2 },
  287. label: {
  288. show: true,
  289. formatter: ({ value }) => `特征值: ${value}`,
  290. color: "#000",
  291. },
  292. },
  293. data: dataSource.map(({ Xaxis, val }) => ({ xAxis: Xaxis, val })),
  294. },
  295. });
  296. const markLines = [
  297. { data: featureLines.Fr, color: "#A633FF" },
  298. { data: featureLines.BPFI, color: "#23357e" },
  299. { data: featureLines.BPFO, color: "#42a0ae" },
  300. { data: featureLines.BSF, color: "#008080" },
  301. { data: featureLines.FTF, color: "#af254f" },
  302. { data: featureLines.B3P, color: "#FFD700" },
  303. ].map(({ data, color }) => createMarkLine(data, color));
  304. return [
  305. {
  306. name: "数据系列",
  307. type: "line",
  308. data: this.envelopeList.x.map((x, i) => [x, this.envelopeList.y[i]]),
  309. symbol: "none",
  310. lineStyle: { color: "#162961", width: 1 },
  311. itemStyle: { color: "#162961", borderColor: "#fff", borderWidth: 1 },
  312. large: true,
  313. },
  314. ...markLines,
  315. ];
  316. },
  317. initializeChart() {
  318. // 获取图表容器
  319. const chartDom = this.$refs.chart;
  320. // 初始化图表实例
  321. this.chartInstance = echarts.init(chartDom);
  322. // 初始化时使用空数据
  323. const defaultData = [];
  324. const defaultLabels = [];
  325. this.updateChart(defaultData, defaultLabels);
  326. },
  327. updateChart(data, labels) {
  328. // 获取 x 数组中的最大值
  329. const maxX = Math.max(...labels);
  330. // 计算最大值的下一个 5 的倍数
  331. const maxAxisValue = Math.ceil(maxX / 5) * 5;
  332. // 生成从 0 到 maxAxisValue 的刻度数组,间隔为 5
  333. const ticks = [];
  334. for (let i = 0; i <= maxAxisValue; i += 5) {
  335. ticks.push(i);
  336. }
  337. const option = {
  338. title: {
  339. text: this.envelopeList.title || this.envelopeListTwo.title,
  340. left: "center",
  341. },
  342. toolbox: {
  343. feature: {
  344. dataZoom: { yAxisIndex: "none" },
  345. restore: {},
  346. saveAsImage: {},
  347. myCustomTool: {
  348. show: true,
  349. title: "上一条",
  350. icon: `image://${require("@/assets/analyse/08.png")}`,
  351. onclick: () => this.previousRow(),
  352. },
  353. myCustomTool2: {
  354. show: true,
  355. title: "下一条",
  356. icon: `image://${require("@/assets/analyse/09.png")}`,
  357. onclick: () => this.nextRow(),
  358. },
  359. // myCustomTool4: {
  360. // show: true,
  361. // title: "光标",
  362. // icon: `image://${require("@/assets/analyse/12.png")}`,
  363. // onclick: () => this.Show("2"),
  364. // },
  365. myCustomTool3: {
  366. show: true,
  367. title: "特征频率",
  368. icon: `image://${require("@/assets/analyse/13.png")}`,
  369. onclick: () => this.Show("3"),
  370. },
  371. },
  372. },
  373. dataZoom: [
  374. {
  375. type: "slider", // 使用滑动条
  376. start: 0, // 起始比例
  377. end: 10, // 结束比例
  378. },
  379. {
  380. type: "inside", // 鼠标滚轮缩放
  381. },
  382. ],
  383. xAxis: {
  384. type: "value", // 使用数值型 x 轴,确保每个 x 值都有展示
  385. name: this.envelopeList.xaxis || this.envelopeListTwo.xaxis, // 设置 X 轴标题
  386. nameLocation: "center", // 标题位置:可选 "start" | "center" | "end"
  387. nameTextStyle: {
  388. fontSize: 14,
  389. color: "#333", // 标题颜色
  390. padding: [10, 0, 0, 0], // 上、右、下、左的间距
  391. },
  392. axisLabel: {
  393. formatter: (value) => {
  394. // 格式化显示刻度为 5 的倍数
  395. return value;
  396. },
  397. },
  398. axisTick: {
  399. show: true, // 显示刻度线
  400. },
  401. axisLine: {
  402. show: true, // 显示轴线
  403. },
  404. data: ticks, // 设置刻度
  405. },
  406. yAxis: {
  407. type: "value",
  408. name: this.envelopeList.yaxis || this.envelopeListTwo.yaxis,
  409. },
  410. tooltip: {
  411. trigger: "axis",
  412. formatter: (params) => {
  413. // 获取原始的 x 数值和对应的 y 值
  414. const xValue = params[0].value[0]; // 获取原始的 x 数值
  415. const yValue = params[0].value[1]; // 获取对应的 y 数值
  416. return `X: ${xValue}<br/>Y: ${yValue}`; // 显示原始的 x 和 y 值
  417. },
  418. axisPointer: {
  419. type: "line", // 显示十字线
  420. },
  421. },
  422. series: [
  423. {
  424. name: "数据系列",
  425. type: "line", // 折线图
  426. data: labels.map((item, index) => [item, data[index]]), // 修改为 [x, y] 数组
  427. symbol: "none", // 数据点的样式
  428. symbolSize: 8, // 数据点的大小
  429. lineStyle: {
  430. color: "#162961",
  431. width: 1, // 线条宽度
  432. },
  433. itemStyle: {
  434. color: "#162961",
  435. borderColor: "#fff",
  436. borderWidth: 1,
  437. },
  438. },
  439. ],
  440. };
  441. // 使用更新的配置更新图表
  442. this.chartInstance.setOption(option);
  443. },
  444. previousRow() {
  445. this.$emit("update-previous-row", 3, this.activeIndex);
  446. },
  447. // 触发下一条
  448. nextRow() {
  449. this.$emit("update-next-row", 3, this.activeIndex);
  450. },
  451. Show(value) {
  452. const stateMap = {
  453. 1: { TZshow: true, BGshow: false, PXshow: false },
  454. 2: { TZshow: false, BGshow: true, PXshow: false },
  455. 3: { TZshow: false, BGshow: false, PXshow: true },
  456. };
  457. if (stateMap[value]) {
  458. this.TZshow = value === "1" ? !this.TZshow : false;
  459. this.BGshow = value === "2" ? !this.BGshow : false;
  460. this.PXshow = value === "3" ? !this.PXshow : false;
  461. }
  462. // this.TZshow = !this.TZshow;
  463. },
  464. },
  465. };
  466. </script>
  467. <style lang="scss" scoped>
  468. .line-chart {
  469. width: 100%;
  470. height: 260px;
  471. }
  472. .eigenvalue {
  473. position: absolute;
  474. top: 60px;
  475. right: 0;
  476. font-size: 10px;
  477. width: 100px;
  478. border: 1px solid black;
  479. padding: 5px;
  480. background: #fff;
  481. z-index: 99;
  482. h5 {
  483. line-height: 16px;
  484. height: 16px;
  485. }
  486. }
  487. .section {
  488. position: relative;
  489. display: flex;
  490. // line-height: 32px;
  491. .el-input {
  492. width: 150px;
  493. }
  494. .el-button {
  495. margin-left: 10px;
  496. }
  497. }
  498. </style>