index.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725
  1. <template>
  2. <div
  3. class="dataAnalysisAngleAna"
  4. :class="!theme ? 'themeDark' : 'themeLight'"
  5. >
  6. <div class="dataAnalysisAngleAnaMain">
  7. <div class="main_top">
  8. <p class="topPsty">浆距角分析</p>
  9. </div>
  10. <div class="main">
  11. <div class="treeDataMain">
  12. <tree-cop
  13. :data="treeData"
  14. :height="treeHeight"
  15. :currentNodeKey="currentNodeKey"
  16. @currentChange="funCurrentChange"
  17. @refresh="funGetTree"
  18. >
  19. </tree-cop>
  20. </div>
  21. <div class="excelDataMain">
  22. <excel-cop
  23. :data="excelList"
  24. :height="excelHeight"
  25. :theme="theme"
  26. @excelChange="funExcelChange"
  27. >
  28. </excel-cop>
  29. </div>
  30. <div class="tableDataMain">
  31. <el-tabs v-model="activeTab">
  32. <el-tab-pane label="图表展示" name="1"> </el-tab-pane>
  33. <el-tab-pane label="桨距角风速曲线" name="2" v-if="false">
  34. </el-tab-pane>
  35. <el-tab-pane label="表格数据" name="3"> </el-tab-pane>
  36. <el-tab-pane
  37. :label="`异常测点 (${abnormalPoint.length}个)`"
  38. name="4"
  39. >
  40. </el-tab-pane>
  41. <div v-if="activeTab === '3'" :style="{ height: tableHeight }">
  42. <table-cop
  43. class=""
  44. :data="tableData"
  45. :column="tableColumn"
  46. :theme="theme"
  47. :height="tableHeight"
  48. :tableId="tableShowId"
  49. :tableName="tableName"
  50. :rowstyle="true"
  51. ></table-cop>
  52. </div>
  53. <div v-show="activeTab === '1'" :style="{ height: tableHeight }">
  54. <!-- :height="`calc( ${tableHeight})`" -->
  55. <p :style="!theme ? 'color: #fff' : 'color: #000'" style="width: 100%;text-align: center">变桨角度偏差率:{{angleData*100}}%</p>
  56. <CurrentScatterChart
  57. ref="chartRef"
  58. width="100%"
  59. height="75vh"
  60. :chartTitle="''"
  61. style="margin-top: 3vh"
  62. :xAxisData="xAxisData"
  63. :yAxisData="{ splitLine: { show: false } }"
  64. :seriesData="seriesData"
  65. :seriesAllData="seriesAllData"
  66. :showLegend="true"
  67. :brushSelected="false"
  68. :dataSet="dataSet"
  69. :theme="theme"
  70. :echartsTheme="echartsTheme"
  71. @getSelected="funChartSelect"
  72. />
  73. </div>
  74. <div
  75. v-if="false && activeTab === '2'"
  76. :style="{ height: tableHeight }"
  77. >
  78. <!-- <div id="speedLine" style="width:100%;height:100%"></div> -->
  79. <bar-chart-cop
  80. width="100%"
  81. height="100%"
  82. :theme="theme"
  83. :echartsTheme="echartsTheme"
  84. :xAxis="linexAxis"
  85. :yAxis="lineyAxis"
  86. :series="lineSeries"
  87. ></bar-chart-cop>
  88. </div>
  89. <div v-if="activeTab === '4'" :style="{ height: tableHeight }">
  90. <el-table
  91. :data="abnormalPoint"
  92. style="width: 100%"
  93. :max-height="tableHeight"
  94. >
  95. <el-table-column
  96. prop="date"
  97. label="异常时间 (点击定位至图表位置)"
  98. width="400"
  99. align="center"
  100. >
  101. <template #default="scoped">
  102. <p style="cursor: pointer" @click="createMark(scoped.row)">
  103. <span>{{ scoped.row.date }}</span>
  104. </p>
  105. </template>
  106. </el-table-column>
  107. <el-table-column
  108. prop="yp1"
  109. label="叶片1"
  110. width="180"
  111. align="center"
  112. />
  113. <el-table-column
  114. prop="yp2"
  115. label="叶片2"
  116. width="180"
  117. align="center"
  118. />
  119. <el-table-column
  120. prop="yp3"
  121. label="叶片3"
  122. width="180"
  123. align="center"
  124. />
  125. </el-table>
  126. </div>
  127. </el-tabs>
  128. </div>
  129. </div>
  130. </div>
  131. </div>
  132. </template>
  133. <script setup name="prepare">
  134. import excelCop from "@/components/generatingCapacityComponent/excel.vue";
  135. import treeCop from "@/components/generatingCapacityComponent/tree.vue";
  136. import tableCop from "@/components/generatingCapacityComponent/table.vue";
  137. import barChartCop from "../spaceAnalysis/components/barChart.vue";
  138. import * as echarts from "echarts";
  139. import { ElMessage } from "element-plus";
  140. import { onMounted, ref, reactive, onActivated, watch } from "vue";
  141. import { useStore } from "vuex";
  142. import CurrentScatterChart from "./components/current-scatter-chart.vue";
  143. import httpRequest from "@/utils/request.js";
  144. /**配置参数 */
  145. const tableHeight = ref(window.innerHeight - 170 + "px");
  146. const treeHeight = ref(window.innerHeight - 120 + "px"); //tree高度
  147. const excelHeight = ref(window.innerHeight - 120 + "px"); //excel高度
  148. /**excel 开始 */
  149. const excelList = ref([]);
  150. const speedParams = ref({});
  151. const currentNodeKey = ref("");
  152. import jsonData from "./components/data.json";
  153. const funExcelChange = async (obj) => {
  154. chartRef.value.setMarkItem({});
  155. //点击excel项时
  156. funSubmit({
  157. ids: obj.id,
  158. });
  159. tableShowId.value = obj.id
  160. tableName.value = obj.name.substring(0, obj.name.indexOf('_'));
  161. getSpeedLine({
  162. ids: obj.id,
  163. });
  164. speedParams.value = {
  165. ids: obj.id,
  166. };
  167. };
  168. const createMark = (markInfo) => {
  169. activeTab.value = "1";
  170. chartRef.value.initChart(markInfo);
  171. };
  172. /**tree 开始 */
  173. const treeData = ref([]);
  174. const actTreeNode = ref(null); //当前激活的treeNode
  175. const funRepeatMap = (arr, type) => {
  176. return arr.map((o) => {
  177. if (o.children) {
  178. const findIndex = o.children.findIndex((p) => !!p.type);
  179. if (findIndex !== -1) {
  180. o.childs = o.children;
  181. o.children = [];
  182. if (!actTreeNode.value && type === "prepare") {
  183. //判断当且仅有process获取tree时 赋值
  184. actTreeNode.value = o;
  185. }
  186. }
  187. }
  188. return {
  189. ...o,
  190. children: o.children.length ? funRepeatMap(o.children, type) : [],
  191. };
  192. });
  193. };
  194. const funGetTree = async () => {
  195. // const res = await httpRequest.get("/power/prepare/treepitch");
  196. const res = await httpRequest.get("/power/process/tree");
  197. treeData.value = funRepeatMap(res.data, "process");
  198. excelList.value = [];
  199. if (actTreeNode.value) {
  200. funCurrentChange({
  201. current: actTreeNode.value,
  202. currentNode: null,
  203. });
  204. const child = actTreeNode.value.childs[0];
  205. const obj = {
  206. id: child.id,
  207. interval: child.interval,
  208. path: child.path,
  209. prepareid: child.prepareid,
  210. station: child.station,
  211. time: child.time,
  212. type: child.type,
  213. windturbine: child.windturbine,
  214. name: child.path.substring(
  215. child.path.indexOf(child.station + "_") + (child.station + "_").length
  216. ),
  217. };
  218. currentNodeKey.value = actTreeNode.value?.id || "";
  219. funExcelChange(obj);
  220. }
  221. };
  222. const funCurrentChange = ({ current, currentNode }) => {
  223. if (current.childs) {
  224. excelList.value = current.childs.map((o) => {
  225. return {
  226. id: o.id,
  227. interval: o.interval,
  228. path: o.path,
  229. prepareid: o.prepareid,
  230. station: o.station,
  231. time: o.time,
  232. type: o.type,
  233. windturbine: o.windturbine,
  234. name: o.path.substring(
  235. o.path.indexOf(o.station + "_") + (o.station + "_").length
  236. ),
  237. };
  238. });
  239. if (excelList.value.length > 0) {
  240. funExcelChange(excelList.value[0]);
  241. }
  242. } else {
  243. excelList.value = [];
  244. }
  245. };
  246. /**table 开始 */
  247. const tableShowId = ref("");
  248. const tableName = ref("");
  249. const tableColumn = ref([
  250. {
  251. prop: "time",
  252. label: "时间",
  253. },
  254. {
  255. prop: "yp1",
  256. label: "叶片一",
  257. },
  258. {
  259. prop: "yp2",
  260. label: "叶片二",
  261. },
  262. {
  263. prop: "yp3",
  264. label: "叶片三",
  265. },
  266. ]);
  267. const tableLoading = ref(false);
  268. const tableData = ref([]);
  269. /**tab */
  270. const activeTab = ref("1");
  271. /**chart Data */
  272. const xAxisData = ref([]);
  273. const chartRef = ref(); //chart 的ref
  274. const seriesData = ref([]);
  275. const seriesAllData = ref([]);
  276. const dataSet = ref("");
  277. const angleData = ref("");
  278. const funChartSelect = async (batch) => {
  279. return false;
  280. };
  281. /**submit */
  282. const funSubmit = async (params) => {
  283. const res = await httpRequest.get("/blade/angle", {
  284. params: params,
  285. });
  286. if (res.code !== 200) {
  287. return false;
  288. }
  289. angleData.value = res.data.angle
  290. let exTime = [];
  291. let yp1 = [],
  292. yp2 = [],
  293. yp3 = [],
  294. speed = [];
  295. res.data.bw.forEach((it) => {
  296. exTime.push(it.time);
  297. yp1.push(it.yp1);
  298. yp2.push(it.yp2);
  299. yp3.push(it.yp3);
  300. speed.push(it.speed);
  301. });
  302. tableData.value = res.data.bw;
  303. seriesAllData.value = res.data.bw;
  304. // res.data.bw.forEach(it => {
  305. // yp2.push(it.yp2)
  306. // })
  307. xAxisData.value = exTime;
  308. seriesData.value = [
  309. // {
  310. // name: "并网(绿)/停机(红)",
  311. // type: "line",
  312. // symbol: "line", //设定为实心点
  313. // symbolSize: 0, //设定实心点的大小
  314. // smooth: false, //这个是把线变成曲线
  315. // data: yp1,
  316. // xAxisIndex: 0,
  317. // },
  318. {
  319. name: "叶片1",
  320. type: "line",
  321. symbol: "line", //设定为实心点
  322. symbolSize: 0, //设定实心点的大小
  323. smooth: false, //这个是把线变成曲线
  324. data: yp1,
  325. xAxisIndex: 0,
  326. },
  327. {
  328. name: "叶片2",
  329. type: "line",
  330. symbol: "line", //设定为实心点
  331. symbolSize: 0, //设定实心点的大小
  332. smooth: false, //这个是把线变成曲线
  333. data: yp2,
  334. xAxisIndex: 0,
  335. },
  336. {
  337. name: "叶片3",
  338. type: "line",
  339. symbol: "line", //设定为实心点
  340. symbolSize: 0, //设定实心点的大小
  341. smooth: false, //这个是把线变成曲线
  342. data: yp3,
  343. xAxisIndex: 0,
  344. },
  345. {
  346. name: "风速",
  347. type: "line",
  348. yAxisIndex: 1,
  349. symbol: "line", //设定为实心点
  350. symbolSize: 0, //设定实心点的大小
  351. smooth: false, //这个是把线变成曲线
  352. data: speed,
  353. xAxisIndex: 0,
  354. },
  355. ];
  356. const exPoint = [];
  357. seriesData.value?.[0]?.data?.forEach((ele, index) => {
  358. const numEqual = [
  359. ele,
  360. seriesData.value[1].data[index],
  361. seriesData.value[2].data[index],
  362. ];
  363. if (areNumbersDistinct(numEqual)) {
  364. exPoint.push({
  365. date: exTime[index],
  366. yp1: numEqual[0],
  367. yp2: numEqual[1],
  368. yp3: numEqual[2],
  369. dataIndex: index,
  370. });
  371. }
  372. });
  373. abnormalPoint.value = exPoint;
  374. };
  375. const linexAxis = reactive({
  376. type: "category",
  377. name: "风速",
  378. data: [],
  379. });
  380. const lineyAxis = reactive({
  381. type: "value",
  382. name: "度",
  383. splitLine: {
  384. show: false,
  385. },
  386. axisTick: {
  387. show: true,
  388. },
  389. axisLine: {
  390. onZero: false,
  391. },
  392. });
  393. const lineSeries = reactive([
  394. {
  395. name: "桨距角风速曲线",
  396. type: "line",
  397. data: [],
  398. symbol: "line", //设定为实心点
  399. symbolSize: 0, //设定实心点的大小
  400. },
  401. ]);
  402. const getSpeedLine = async (params) => {
  403. const speedRes = await httpRequest.get("/blade/curve", {
  404. params: params,
  405. });
  406. if (speedRes.code === 200) {
  407. if (speedRes.data) {
  408. let xAsis = [];
  409. let series = [];
  410. for (const item in speedRes.data) {
  411. xAsis.push(item);
  412. series.push(speedRes.data[item]);
  413. }
  414. linexAxis.data = xAsis;
  415. lineSeries[0].data = series;
  416. }
  417. }
  418. };
  419. /**created */
  420. // funGetTree()
  421. const theme = ref(null);
  422. const echartsTheme = ref("");
  423. const store = useStore();
  424. watch(
  425. () => store.state.theme,
  426. (newVal, oldVal) => {
  427. theme.value = newVal;
  428. echartsTheme.value = !newVal ? "dark" : "";
  429. funGetTree();
  430. },
  431. {
  432. deep: true,
  433. }
  434. );
  435. const abnormalPoint = ref([]);
  436. const initPageData = () => {
  437. treeData.value = funRepeatMap(
  438. JSON.parse(JSON.stringify(jsonData.treeData)),
  439. "prepare"
  440. );
  441. excelList.value = [];
  442. if (actTreeNode.value) {
  443. if (actTreeNode.value.childs) {
  444. excelList.value = actTreeNode.value.childs.map((o) => {
  445. return {
  446. id: o.id,
  447. interval: o.interval,
  448. path: o.path,
  449. prepareid: o.prepareid,
  450. station: o.station,
  451. time: o.time,
  452. type: o.type,
  453. windturbine: o.windturbine,
  454. isCheck: false,
  455. name: o.path.substring(
  456. o.path.indexOf(o.station + "_") + (o.station + "_").length
  457. ),
  458. };
  459. });
  460. } else {
  461. excelList.value = [];
  462. }
  463. const child = actTreeNode.value.childs[0];
  464. const obj = {
  465. id: child.id,
  466. interval: child.interval,
  467. path: child.path,
  468. prepareid: child.prepareid,
  469. station: child.station,
  470. time: child.time,
  471. type: child.type,
  472. windturbine: child.windturbine,
  473. name: child.path.substring(
  474. child.path.indexOf(child.station + "_") + (child.station + "_").length
  475. ),
  476. };
  477. currentNodeKey.value = actTreeNode.value?.id || "";
  478. let exTime = [];
  479. let yp1 = [],
  480. yp2 = [],
  481. yp3 = [],
  482. speed = [];
  483. jsonData.angelData.bw.forEach((it) => {
  484. exTime.push(it.time);
  485. yp1.push(it.yp1);
  486. yp2.push(it.yp2);
  487. yp3.push(it.yp3);
  488. speed.push(it.speed);
  489. });
  490. tableData.value = jsonData.angelData.bw;
  491. seriesAllData.value = jsonData.angelData.bw;
  492. xAxisData.value = exTime;
  493. seriesData.value = [
  494. {
  495. name: "叶片1",
  496. type: "line",
  497. symbol: "line", //设定为实心点
  498. symbolSize: 0, //设定实心点的大小
  499. smooth: false, //这个是把线变成曲线
  500. data: yp1,
  501. xAxisIndex: 0,
  502. },
  503. {
  504. name: "叶片2",
  505. type: "line",
  506. symbol: "line", //设定为实心点
  507. symbolSize: 0, //设定实心点的大小
  508. smooth: false, //这个是把线变成曲线
  509. data: yp2,
  510. xAxisIndex: 0,
  511. },
  512. {
  513. name: "叶片3",
  514. type: "line",
  515. symbol: "line", //设定为实心点
  516. symbolSize: 0, //设定实心点的大小
  517. smooth: false, //这个是把线变成曲线
  518. data: yp3,
  519. xAxisIndex: 0,
  520. },
  521. {
  522. name: "风速",
  523. type: "line",
  524. yAxisIndex: 1,
  525. symbol: "line", //设定为实心点
  526. symbolSize: 0, //设定实心点的大小
  527. smooth: false, //这个是把线变成曲线
  528. data: speed,
  529. xAxisIndex: 0,
  530. },
  531. ];
  532. tableShowId.value = obj.id
  533. tableName.value = obj.name.substring(0, obj.name.indexOf('_'));
  534. let xAsis = [];
  535. let series = [];
  536. for (const item in jsonData.curveData) {
  537. xAsis.push(item);
  538. series.push(jsonData.curveData[item]);
  539. }
  540. linexAxis.data = xAsis;
  541. lineSeries[0].data = series;
  542. speedParams.value = {
  543. ids: obj.id,
  544. };
  545. const exPoint = [];
  546. seriesData.value?.[0]?.data?.forEach((ele, index) => {
  547. const numEqual = [
  548. ele,
  549. seriesData.value[1].data[index],
  550. seriesData.value[2].data[index],
  551. ];
  552. if (areNumbersDistinct(numEqual)) {
  553. exPoint.push({
  554. date: exTime[index],
  555. yp1: numEqual[0],
  556. yp2: numEqual[1],
  557. yp3: numEqual[2],
  558. dataIndex: index,
  559. });
  560. }
  561. });
  562. abnormalPoint.value = exPoint;
  563. }
  564. };
  565. const areNumbersDistinct = (arr) => {
  566. return arr[0] !== arr[1] || arr[0] !== arr[2] || arr[1] !== arr[2];
  567. };
  568. /**mounted */
  569. onMounted(() => {
  570. initPageData();
  571. funGetTree();
  572. theme.value = store.state.theme;
  573. echartsTheme.value = !theme.value ? "dark" : "";
  574. tableHeight.value = window.innerHeight - 170 + "px";
  575. excelHeight.value = window.innerHeight - 120 + "px";
  576. treeHeight.value = window.innerHeight - 120 + "px";
  577. window.addEventListener("resize", () => {
  578. tableHeight.value = window.innerHeight - 170 + "px";
  579. excelHeight.value = window.innerHeight - 120 + "px";
  580. treeHeight.value = window.innerHeight - 120 + "px";
  581. });
  582. });
  583. /**activated */
  584. onActivated(() => {
  585. // funGetTree()
  586. // funSubmit()
  587. });
  588. </script>
  589. <style lang="less" scoped>
  590. .dataAnalysisAngleAna {
  591. height: 100%;
  592. .dataAnalysisAngleAnaMain {
  593. height: 100%;
  594. .main_top {
  595. height: 40px;
  596. display: flex;
  597. align-items: center;
  598. .topPsty {
  599. position: relative;
  600. top: 5px;
  601. padding: 7px 20px;
  602. font-size: 12px;
  603. font-weight: 600;
  604. margin-left: 10px;
  605. border-radius: 3px;
  606. }
  607. }
  608. .main {
  609. display: flex;
  610. width: 100%;
  611. .treeDataMain,
  612. .excelDataMain,
  613. .tableDataMain {
  614. border-radius: 10px;
  615. }
  616. .treeDataMain {
  617. margin-right: 10px;
  618. padding: 10px 0 10px 10px;
  619. width: calc(19% - 20px);
  620. }
  621. .excelDataMain {
  622. margin-right: 10px;
  623. padding: 10px 0 10px 10px;
  624. width: calc(15% - 20px);
  625. }
  626. .tableDataMain {
  627. padding: 10px;
  628. width: calc(66% - 20px);
  629. position: relative;
  630. .butten_com {
  631. position: absolute;
  632. right: 20px;
  633. z-index: 111111;
  634. }
  635. }
  636. }
  637. }
  638. }
  639. .themeDark {
  640. .dataAnalysisAngleAnaMain {
  641. .main_top {
  642. .topPsty {
  643. color: #1c99ff;
  644. background: #1e2126;
  645. }
  646. }
  647. .main {
  648. background: #13171e;
  649. .treeDataMain {
  650. background: transparent;
  651. }
  652. .excelDataMain {
  653. background: #313233;
  654. }
  655. .tableDataMain {
  656. margin-top: 5px;
  657. background: #212223;
  658. }
  659. }
  660. }
  661. }
  662. .themeLight {
  663. padding: 0;
  664. .dataAnalysisAngleAnaMain {
  665. .main_top {
  666. .topPsty {
  667. color: #2778ff;
  668. background: #ffffff;
  669. }
  670. }
  671. .main {
  672. background: #e6e8f2;
  673. .treeDataMain {
  674. background: transparent;
  675. }
  676. .excelDataMain {
  677. background: #f4f6fb;
  678. }
  679. .tableDataMain {
  680. background: #fff;
  681. margin-top: 5px;
  682. }
  683. }
  684. }
  685. }
  686. </style>