chart.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. <template>
  2. <div class="totalCurveChartBox">
  3. <div class="l">
  4. <el-tree
  5. v-if="currentNodeKey"
  6. accordion
  7. highlight-current
  8. node-key="id"
  9. :data="treeData"
  10. :props="{
  11. children: 'children',
  12. label: 'label',
  13. }"
  14. @node-click="nodeClick"
  15. :default-expanded-keys="defaultTreeData"
  16. :default-checked-keys="defaultTreeData"
  17. :current-node-key="currentNodeKey"
  18. />
  19. </div>
  20. <div class="r">
  21. <CurrentScatterChart
  22. width="100%"
  23. height="90%"
  24. :xAxisData="xAxisData"
  25. :yAxisData="{ splitLine: { show: false } }"
  26. :seriesData="seriesData"
  27. :showLegend="true"
  28. :brushSelected="true"
  29. @getSelected="getSelected"
  30. />
  31. </div>
  32. <el-dialog
  33. top="100px"
  34. :title="wtId + (tableData.length ? ' (' + tableData.length + '条)' : '')"
  35. custom-class="modal"
  36. v-model="showDialog"
  37. width="80%"
  38. @closed="
  39. (res) => {
  40. showDialog = false;
  41. tableData = [];
  42. lassoTable = [];
  43. gzlx = '';
  44. gzItem = null;
  45. }
  46. "
  47. >
  48. <div class="chartDialogBox">
  49. <div class="l">
  50. <el-table
  51. :data="tableData"
  52. style="width: 100%; height: 600px; overflow-y: scroll"
  53. height="250"
  54. border
  55. >
  56. <el-table-column
  57. prop="sj"
  58. label="时间"
  59. width="200px"
  60. align="center"
  61. ></el-table-column>
  62. <el-table-column
  63. prop="fs"
  64. label="风速(m/s)"
  65. sortable
  66. align="center"
  67. ></el-table-column>
  68. <el-table-column
  69. prop="gl"
  70. label="功率(kw)"
  71. sortable
  72. align="center"
  73. ></el-table-column>
  74. </el-table>
  75. </div>
  76. <div class="r">
  77. <el-table
  78. :data="lassoTable"
  79. style="width: 100%; height: 600px; overflow-y: scroll"
  80. height="250"
  81. border
  82. row-key="value"
  83. >
  84. <el-table-column prop="label" label="故障分类" width="130" />
  85. <el-table-column prop="warntime" label="故障时间" align="center" />
  86. <el-table-column prop="warndes" label="故障描述" align="center" />
  87. <el-table-column
  88. prop="model"
  89. label="机型"
  90. align="center"
  91. width="100"
  92. />
  93. </el-table>
  94. <el-table
  95. :data="lassoTable"
  96. style="width: 100%; height: 600px; overflow-y: scroll"
  97. height="250"
  98. border
  99. v-if="false"
  100. >
  101. <el-table-column type="expand">
  102. <template #default="props">
  103. <el-table
  104. :data="props.row.children"
  105. style="width: 100%"
  106. height="250"
  107. border
  108. >
  109. <el-table-column
  110. prop="warntime"
  111. label="故障时间"
  112. align="center"
  113. />
  114. <el-table-column
  115. prop="warndes"
  116. label="故障描述"
  117. align="center"
  118. />
  119. <el-table-column prop="model" label="机型" align="center" />
  120. </el-table>
  121. </template>
  122. </el-table-column>
  123. <el-table-column prop="label" label="故障类型" />
  124. </el-table>
  125. </div>
  126. </div>
  127. <template #footer>
  128. <span class="dialog-footer">
  129. <span class="gzlxTitle" :class="!gzlx ? 'twinkle' : ''"
  130. >故障类型:</span
  131. >
  132. <el-select
  133. style="width: 150px"
  134. v-model="gzlx"
  135. clearable
  136. placeholder="请选择"
  137. popper-class="select"
  138. @change="gzlxChange"
  139. >
  140. <el-option
  141. v-for="item in gzlxArray"
  142. :key="item.id"
  143. :value="item._index"
  144. :label="item.name"
  145. >
  146. </el-option>
  147. </el-select>
  148. <el-button
  149. class="btn green"
  150. type="success"
  151. style="margin-left: 12px"
  152. @click="save"
  153. :disabled="!gzlx && !gzItem"
  154. >
  155. 保存
  156. </el-button>
  157. </span>
  158. </template>
  159. </el-dialog>
  160. </div>
  161. </template>
  162. <script>
  163. import CurrentScatterChart from "../../components/chart/scatter/current-scatter-chart.vue";
  164. import axios from "axios";
  165. export default {
  166. components: {
  167. CurrentScatterChart,
  168. },
  169. data() {
  170. return {
  171. treeData: [],
  172. defaultTreeData: [], //默认选中数据
  173. xAxisData: [],
  174. seriesData: [],
  175. station: "",
  176. wtId: "",
  177. time: "",
  178. showDialog: false,
  179. tableData: [],
  180. gzlx: "",
  181. gzlxArray: [],
  182. gzItem: null,
  183. currentNodeKey: "",
  184. lassoTimeArray: [],
  185. lassoTable: [],
  186. };
  187. },
  188. created() {
  189. this.getTree();
  190. this.getGzlx();
  191. },
  192. methods: {
  193. getLassoData() {
  194. const that = this;
  195. that.API.requestData({
  196. method: "POST",
  197. baseURL: "http://192.168.10.5:9002/",
  198. subUrl: "powercurve/lasso",
  199. data: {
  200. station: "NSS_FDC",
  201. wtid: "NG01_70",
  202. time: that.lassoTimeArray.toString(),
  203. },
  204. success(res) {
  205. let lassoTable = res.data.data;
  206. lassoTable.forEach((pEle) => {
  207. if (pEle.children?.length) {
  208. pEle.sortNum = pEle.children.length;
  209. pEle.label += `(${pEle.children.length}条)`;
  210. } else {
  211. pEle.sortNum = 0;
  212. pEle.label += "(0条)";
  213. }
  214. pEle.children.forEach((cEle) => {
  215. cEle.label = "---";
  216. });
  217. });
  218. lassoTable.sort((a, b) => {
  219. return b.sortNum - a.sortNum;
  220. });
  221. that.lassoTable = lassoTable;
  222. },
  223. });
  224. },
  225. // 选择故障类型
  226. gzlxChange(idx) {
  227. let gzItem = this.gzlxArray.find((ele) => {
  228. return ele._index === idx;
  229. });
  230. this.gzItem = gzItem;
  231. },
  232. // 获取树形数据
  233. getTree() {
  234. const that = this;
  235. that.API.requestData({
  236. method: "GET",
  237. baseURL: "http://192.168.1.18:9002/",
  238. subUrl: "powercurve/tree",
  239. success(res) {
  240. that.treeData = that.addLabelToTreeData(res.data);
  241. // 初始化第一条
  242. that.defaultTreeData.push(res.data[0].id);
  243. that.defaultTreeData.push(res.data[0].children[0].id);
  244. that.defaultTreeData.push(res.data[0].children[0].children[0].id);
  245. that.defaultTreeData.push(
  246. res.data[0].children[0].children[0].children[0].id
  247. );
  248. let getFirstTreeData = that.getFirstTreeData(res.data[0]);
  249. that.currentNodeKey = getFirstTreeData.id;
  250. that.station = getFirstTreeData.stationen;
  251. that.wtId = getFirstTreeData.windturbineid;
  252. that.time = getFirstTreeData.time;
  253. that.getChartData();
  254. },
  255. });
  256. },
  257. // 获取树形数据
  258. getGzlx() {
  259. const that = this;
  260. that.API.requestData({
  261. method: "GET",
  262. baseURL: "http://192.168.1.18:9002/",
  263. subUrl: "know/fault/type/all",
  264. success(res) {
  265. res.data.forEach((ele, index) => {
  266. ele._index = index;
  267. });
  268. that.gzlxArray = res.data;
  269. },
  270. });
  271. },
  272. // 树形结构点击处理
  273. nodeClick(res) {
  274. if (!res.children) {
  275. this.station = res.stationen;
  276. this.wtId = res.windturbineid;
  277. this.time = res.time;
  278. this.getChartData();
  279. }
  280. },
  281. // 获取树状结构内第一个节点内的第一条数据
  282. getFirstTreeData(treeData) {
  283. if (treeData?.children) {
  284. return this.getFirstTreeData(treeData.children[0]);
  285. } else {
  286. return treeData;
  287. }
  288. },
  289. // 给树状结构最后一层添加label字段
  290. addLabelToTreeData(treeData) {
  291. for (let i = 0; i < treeData.length; i++) {
  292. const item = treeData[i];
  293. if (Array.isArray(item?.children)) {
  294. this.addLabelToTreeData(item.children);
  295. } else {
  296. treeData.forEach((ele) => {
  297. ele.label = ele.windturbineid;
  298. });
  299. }
  300. }
  301. return treeData;
  302. },
  303. // 获取图表数据
  304. getChartData() {
  305. const that = this;
  306. that.API.requestData({
  307. method: "POST",
  308. baseURL: "http://192.168.1.18:9002/",
  309. subUrl: "scatter/list",
  310. showLoading: true,
  311. data: {
  312. station: that.station,
  313. wtId: that.wtId,
  314. time: that.time,
  315. },
  316. success(res) {
  317. if (
  318. !res.data.lineactual?.length &&
  319. !res.data.lineoptimal?.length &&
  320. !res.data.scatter?.length
  321. ) {
  322. that.BASE.showMsg({
  323. type: "success",
  324. msg: "所选风机暂无图表数据,请更换风机后尝试",
  325. });
  326. }
  327. let sjgl = [];
  328. let zygl = [];
  329. let fsgl = [];
  330. let xAxisData = [];
  331. res.data.lineactual.forEach((ele, index) => {
  332. sjgl.push(ele[1]);
  333. xAxisData.push(index);
  334. });
  335. res.data.lineoptimal.forEach((ele) => {
  336. zygl.push(ele[1]);
  337. });
  338. res.data.scatter.forEach((ele) => {
  339. if (ele[1] >= 0) fsgl.push(ele);
  340. });
  341. that.seriesData = [
  342. {
  343. name: "风速功率",
  344. type: "effectScatter",
  345. showEffectOn: "emphasis",
  346. symbolSize: 5,
  347. data: fsgl,
  348. xAxisIndex: 1,
  349. },
  350. {
  351. name: "实际功率",
  352. type: "line",
  353. symbol: "circle", //设定为实心点
  354. symbolSize: 0, //设定实心点的大小
  355. smooth: true, //这个是把线变成曲线
  356. data: sjgl,
  357. itemStyle: {
  358. normal: {
  359. color: "#05bb4c",
  360. lineStyle: {
  361. color: "#05bb4c",
  362. },
  363. },
  364. },
  365. xAxisIndex: 0,
  366. },
  367. {
  368. name: "最优功率",
  369. type: "line",
  370. symbol: "circle", //设定为实心点
  371. symbolSize: 0, //设定实心点的大小
  372. smooth: true, //这个是把线变成曲线
  373. data: zygl,
  374. itemStyle: {
  375. normal: {
  376. color: "#f8de5b",
  377. lineStyle: {
  378. color: "#f8de5b",
  379. },
  380. },
  381. },
  382. xAxisIndex: 0,
  383. },
  384. ];
  385. that.xAxisData = xAxisData;
  386. },
  387. });
  388. },
  389. // 获取图表框选结果
  390. getSelected(res) {
  391. const seriesIndex = res[0]?.selected[0]?.seriesIndex;
  392. const selected = res[0]?.selected[0]?.dataIndex;
  393. let lassoTimeArray = [];
  394. if (selected?.length) {
  395. let tableData = [];
  396. selected?.forEach((seleIndex) => {
  397. const item = this.seriesData[seriesIndex].data[seleIndex];
  398. tableData.push({
  399. fs: item[0],
  400. gl: item[1],
  401. sj: item[2],
  402. });
  403. lassoTimeArray.push(item[2]);
  404. });
  405. this.lassoTimeArray = lassoTimeArray;
  406. this.getLassoData();
  407. this.tableData = tableData;
  408. this.showDialog = true;
  409. }
  410. },
  411. // 保存
  412. save() {
  413. const that = this;
  414. let data = [];
  415. that.tableData.forEach((ele) => {
  416. data.push({
  417. tag: "0",
  418. windturbineid: that.wtId,
  419. faulttype: that.gzItem.name,
  420. faultcode: that.gzItem.code,
  421. starttime: ele.sj,
  422. stationen: that.station,
  423. category: "1",
  424. });
  425. });
  426. this.BASE.showLoading();
  427. axios({
  428. method: "post",
  429. url: "http://192.168.1.18:9002/case/fault/insert",
  430. data,
  431. header: {
  432. "Content-Type": "application/json",
  433. },
  434. }).then((res) => {
  435. if (res.data.code === 200) {
  436. let lassoData = [];
  437. this.lassoTable.forEach((pEle) => {
  438. pEle.children.forEach((cEle) => {
  439. lassoData.push(cEle);
  440. });
  441. });
  442. axios({
  443. method: "post",
  444. url: "http://192.168.10.5:9002/case/warning/insert",
  445. data: lassoData,
  446. header: {
  447. "Content-Type": "application/json",
  448. },
  449. }).then((res) => {
  450. if (res.data.code === 200) {
  451. that.showDialog = false;
  452. that.BASE.showMsg({
  453. type: "success",
  454. msg: "保存成功",
  455. });
  456. that.BASE.closeLoading();
  457. }
  458. });
  459. }
  460. });
  461. },
  462. },
  463. };
  464. </script>
  465. <style lang="less" scoped>
  466. .totalCurveChartBox {
  467. display: flex;
  468. justify-content: space-between;
  469. align-items: flex-start;
  470. width: 100%;
  471. height: 100%;
  472. .l {
  473. width: 15%;
  474. height: 100%;
  475. .el-tree {
  476. height: 100%;
  477. }
  478. }
  479. .r {
  480. width: 85%;
  481. height: 100%;
  482. margin-left: 20px;
  483. }
  484. .gzlxTitle {
  485. color: rgb(179, 189, 192);
  486. transition: 0.2s;
  487. }
  488. .gzlxTitle.twinkle {
  489. animation: twinkle 0.75s infinite;
  490. }
  491. @keyframes twinkle {
  492. 0% {
  493. color: rgb(179, 189, 192);
  494. }
  495. 50% {
  496. color: #f25656;
  497. }
  498. 100% {
  499. color: rgb(179, 189, 192);
  500. }
  501. }
  502. .chartDialogBox {
  503. width: 100%;
  504. display: flex;
  505. justify-content: start;
  506. align-items: center;
  507. .l,
  508. .r {
  509. width: 50%;
  510. }
  511. }
  512. }
  513. </style>
  514. <style lang="less">
  515. .totalCurveChartBox {
  516. .el-dialog__body {
  517. max-height: 620px;
  518. overflow: hidden;
  519. }
  520. .el-table__expand-icon {
  521. color: #b3bdc0;
  522. }
  523. }
  524. </style>