index.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  1. <!--
  2. * @Author: your name
  3. * @Date: 2025-01-13 13:56:55
  4. * @LastEditTime: 2025-07-24 10:49:46
  5. * @LastEditors: bogon
  6. * @Description: In User Settings Edit
  7. * @FilePath: /performance-test/src/views/overview/components/fault_all/index.vue
  8. -->
  9. <template>
  10. <div class="type-variable">
  11. <!-- 全场故障统计 -->
  12. <div class="left scroller">
  13. <FilterChart
  14. :setUpimg="[]"
  15. :isShowEngList="false"
  16. :windList="windEngineGroupList"
  17. @getEnfineList="getEnfineList"
  18. @handlePrevious="handlePrevious"
  19. @handleNext="handleNext"
  20. ></FilterChart>
  21. <el-alert type="info" :closable="false">
  22. <template v-slot:title>
  23. <div style="display: flex; align-items: center">
  24. <i
  25. class="el-icon-info"
  26. style="font-size: 20px; margin-right: 5px"
  27. ></i>
  28. <h3>分析说明:</h3>
  29. </div>
  30. </template>
  31. <div style="font-size: 12px; margin-top: 10px">
  32. 对全场机组各类故障的次数和时长进行统计
  33. </div>
  34. </el-alert>
  35. <div class="chartsBox" v-if="zongFaultCsvData.length > 0">
  36. <FaultAll
  37. :faultTypes="
  38. zongFaultCsvData &&
  39. zongFaultCsvData[0] &&
  40. zongFaultCsvData[0].data.map((item) => item.fault_detail)
  41. "
  42. :faultCounts="
  43. zongFaultCsvData &&
  44. zongFaultCsvData[0] &&
  45. zongFaultCsvData[0].data.map((item) => item.count)
  46. "
  47. :faultDurations="
  48. zongFaultCsvData &&
  49. zongFaultCsvData[0] &&
  50. zongFaultCsvData[0].data.map((item) => item.fault_time_sum)
  51. "
  52. :zongFaultCsvData="zongFaultCsvData"
  53. ></FaultAll>
  54. <Time3DBarChart
  55. :setUpImgData="setUpImgData"
  56. :key="'Time3DChart'"
  57. :index="`${new Date().getTime()}` + 'fen'"
  58. :ref="'Time3DChart'"
  59. :fileAddr="item.fileAddr"
  60. >
  61. </Time3DBarChart>
  62. <template v-for="(itemCsv, indCsv) in zongFaultCsvData">
  63. <el-table
  64. max-height="500"
  65. :key="indCsv + 'indCsv'"
  66. :default-sort="{ prop: 'fault_time_sum', order: 'descending' }"
  67. :data="filteredData(itemCsv)"
  68. border
  69. style="width: 100%"
  70. align="center"
  71. >
  72. <el-table-column prop="fault_detail" label="故障类型">
  73. </el-table-column>
  74. <el-table-column prop="count" sortable label="故障次数(次)">
  75. </el-table-column>
  76. <el-table-column
  77. prop="fault_time_sum"
  78. sortable
  79. label="故障时长(小时)"
  80. >
  81. <template slot-scope="scope">
  82. {{ Number(scope.row.fault_time_sum).toFixed(2) }}
  83. </template>
  84. </el-table-column>
  85. <el-table-column align="right">
  86. <template slot="header" slot-scope="scope">
  87. <el-input
  88. v-model="search"
  89. size="mini"
  90. placeholder="输入故障类型关键字搜索"
  91. />
  92. </template>
  93. </el-table-column>
  94. </el-table>
  95. </template>
  96. </div>
  97. <el-empty description="暂无分析记录" v-else></el-empty>
  98. <el-dialog
  99. v-if="isShowDescription"
  100. title="添加评论"
  101. :visible="isShow"
  102. width="30%"
  103. v-dialogDrag
  104. :modal="false"
  105. :lock-scroll="false"
  106. :modal-append-to-body="false"
  107. @close="handleClose"
  108. >
  109. <el-tabs value="first">
  110. <el-tab-pane label="意见描述" name="first">
  111. <TinymceEditor
  112. ref="editor"
  113. v-model="comment"
  114. @input="handleEditorInput($event)"
  115. @onClick="onClick"
  116. >
  117. </TinymceEditor>
  118. </el-tab-pane>
  119. </el-tabs>
  120. <el-row
  121. type="flex"
  122. class="row-bg"
  123. justify="end"
  124. style="margin: 20px 60px 0 0"
  125. >
  126. <el-col :span="2">
  127. <el-button type="primary" size="small" @click="handleComment"
  128. >提交评论</el-button
  129. >
  130. </el-col>
  131. </el-row>
  132. </el-dialog>
  133. </div>
  134. <div class="right" v-if="isShowTinymceEditorCom">
  135. <DicCard
  136. :batchCode="initBatchCode"
  137. :analysisTypeCode="'fault'"
  138. :commentDescriptionVos="commentDescriptionVos"
  139. >
  140. </DicCard>
  141. </div>
  142. </div>
  143. </template>
  144. <script>
  145. import DicCard from "@/views/overview/components/dicCard/index.vue";
  146. import FilterChart from "@/views/overview/components/filterChart/index.vue";
  147. import FaultAll from "@/views/performance/components/chartsCom/FaultAll.vue";
  148. import TinymceEditor from "@/components/Tinymce.vue";
  149. import Time3DBarChart from "@/views/performance/components/chartsCom/Time3DBarChart.vue";
  150. import {
  151. analysisDetail,
  152. queryAnalysisedEngine,
  153. analysisCommentEdit,
  154. } from "@/api/performance";
  155. import Papa from "papaparse";
  156. import axios from "axios";
  157. export default {
  158. name: "fault_all",
  159. components: {
  160. DicCard,
  161. FilterChart,
  162. FaultAll,
  163. TinymceEditor,
  164. },
  165. props: {
  166. initBatchCode: {
  167. default: "",
  168. type: String,
  169. },
  170. isShowTinymceEditorCom: {
  171. default: true,
  172. type: Boolean,
  173. },
  174. isShow: {
  175. default: false,
  176. type: Boolean,
  177. },
  178. analysisTypeCode: {
  179. default: "",
  180. type: String,
  181. },
  182. batchCodeList: {
  183. default: "",
  184. type: Array,
  185. },
  186. },
  187. data() {
  188. return {
  189. search: "",
  190. form: {
  191. value2: "",
  192. },
  193. setUpImgData: [],
  194. windEngineGroupList: [], //批次风机列表
  195. fieldEngineCodes: [], //选中风机
  196. comment: "",
  197. options: [],
  198. zongFaultCsvHeader: [],
  199. zongFaultCsvData: [],
  200. fenFaultCsvHeader: [],
  201. fenFaultCsvData: [],
  202. commentDescriptionVos: [], //评论列表
  203. editableTabs: [],
  204. isShowDescription: false,
  205. };
  206. },
  207. computed: {
  208. // 根据搜索关键字过滤数据
  209. filteredData() {
  210. return (itemCsv) => {
  211. // 如果有搜索关键词,则过滤数据
  212. if (this.search) {
  213. return itemCsv.data.filter((item) => {
  214. return item.fault_detail
  215. .toLowerCase()
  216. .includes(this.search.toLowerCase());
  217. });
  218. }
  219. // 没有搜索关键词时返回所有数据
  220. return itemCsv.data;
  221. };
  222. },
  223. filteredFenData() {
  224. return (itemCsv) => {
  225. // 如果有搜索关键词,则过滤数据
  226. if (this.searchFen) {
  227. return itemCsv.data.filter((item) => {
  228. return item.wind_turbine_name
  229. .toLowerCase()
  230. .includes(this.searchFen.toLowerCase());
  231. });
  232. }
  233. // 没有搜索关键词时返回所有数据
  234. return itemCsv.data;
  235. };
  236. },
  237. },
  238. watch: {
  239. initBatchCode(newVal) {
  240. if (newVal) {
  241. this.fetchData(); // 调用合并后的函数
  242. }
  243. },
  244. analysisTypeCode(newVal) {
  245. if (newVal) {
  246. this.fetchData(); // 调用合并后的函数
  247. }
  248. },
  249. isShow() {
  250. if (this.isShow) {
  251. if (!this.isShowDescription) {
  252. this.$message({
  253. message: "当前分析模型暂无分析,不能进行评论操作",
  254. type: "warning",
  255. });
  256. this.$emit("setIsShow");
  257. }
  258. }
  259. },
  260. },
  261. mounted() {
  262. if (this.initBatchCode && this.analysisTypeCode) {
  263. this.fetchData(); // 调用合并后的函数
  264. }
  265. },
  266. methods: {
  267. handleClose() {
  268. //关闭评论弹框
  269. this.$emit("setIsShow");
  270. },
  271. async handleComment() {
  272. try {
  273. await analysisCommentEdit({
  274. batchCode: this.initBatchCode,
  275. analysisTypeCode: this.analysisTypeCode,
  276. commentList: this.editableTabs.map((item) => {
  277. return {
  278. commentTypeCode: item.commentTypeCode,
  279. comment: item.commentTypeName === "分析评论" ? this.comment : "",
  280. };
  281. }),
  282. });
  283. this.$message({
  284. type: "success",
  285. message: "保存成功",
  286. });
  287. this.comment = "";
  288. this.getAnalysisDetail();
  289. this.$emit("setIsShow");
  290. } catch (e) {
  291. console.error(e);
  292. this.loading = false;
  293. }
  294. },
  295. onSubmit() {
  296. console.log("submit!");
  297. },
  298. // 封装的获取 CSV 数据方法
  299. fetchCsvData(analysisType, url) {
  300. axios
  301. .get(url, { responseType: "blob" }) // 确保数据以 blob 格式返回
  302. .then((response) => {
  303. const reader = new FileReader();
  304. reader.onload = (e) => {
  305. const csvText = e.target.result;
  306. Papa.parse(csvText, {
  307. header: true, // 使用 CSV 第一行作为键
  308. complete: (result) => {
  309. // 根据分析模型设置不同的数据
  310. if (analysisType === "fault") {
  311. if (Object.keys(result.data[0]).includes("fault_detail")) {
  312. //总图故障统计展示
  313. this.zongFaultCsvHeader.push(Object.keys(result.data[0]));
  314. this.zongFaultCsvData.push({
  315. data: result.data
  316. .filter((row) => Object.keys(row).length)
  317. .slice(0, result.data.length - 1)
  318. .map((item) => ({
  319. ...item,
  320. count: Number(item.count),
  321. fault_time_sum: Number(item.fault_time_sum) / 3600,
  322. })),
  323. });
  324. }
  325. }
  326. },
  327. error: (error) => {
  328. console.error("CSV 解析错误:", error);
  329. },
  330. });
  331. };
  332. reader.readAsText(response.data); // 读取 blob 数据
  333. })
  334. .catch((error) => {
  335. console.error("无法获取 CSV 文件:", error);
  336. });
  337. },
  338. // 合并后的函数,处理数据请求
  339. async fetchData() {
  340. try {
  341. // 获取分析详情
  342. await this.getAnalysisDetail();
  343. // 获取风机列表
  344. await this.getWindEnfineList(this.initBatchCode, this.analysisTypeCode);
  345. } catch (err) {
  346. console.error("Failed to fetch data:", err);
  347. }
  348. },
  349. // 获取分析详情接口
  350. async getAnalysisDetail() {
  351. try {
  352. const result = await analysisDetail({
  353. batchCode: this.initBatchCode,
  354. analysisTypeCode: "fault",
  355. fieldEngineCodes:
  356. this.fieldEngineCodes.length === 0
  357. ? undefined
  358. : this.fieldEngineCodes.join(","),
  359. });
  360. if (result.data.length > 0) {
  361. this.isShowDescription = true;
  362. }
  363. if (
  364. result.data &&
  365. result.data[0] &&
  366. result.data[0].commentTypeRelations
  367. ) {
  368. this.editableTabs = result.data[0].commentTypeRelations;
  369. }
  370. //当前评论展示获取
  371. if (
  372. result.data &&
  373. result.data[0] &&
  374. result.data[0].commentDescriptionVos
  375. ) {
  376. this.commentDescriptionVos = result.data[0].commentDescriptionVos;
  377. }
  378. if (result.data && result.data[0] && result.data[0].generalFiles) {
  379. result.data[0].generalFiles.map((item) => {
  380. if (item.fileAddr) {
  381. this.zongFaultCsvHeader = [];
  382. this.zongFaultCsvData = [];
  383. this.fetchCsvData("fault", item.fileAddr);
  384. }
  385. });
  386. } else {
  387. this.zongFaultCsvHeader = [];
  388. this.zongFaultCsvData = [];
  389. }
  390. } catch (err) {
  391. console.error("Failed to fetch analysis details:", err);
  392. }
  393. },
  394. // 请求风机列表
  395. async getWindEnfineList(batchCode, analysisTypeCode) {
  396. // console.log("请求风机列表 分钟级");
  397. const resEngineList = await queryAnalysisedEngine({
  398. batchCode: batchCode,
  399. analysisTypeCode,
  400. });
  401. this.windEngineGroupList = resEngineList.data;
  402. },
  403. handleEditorInput(index, newVal) {
  404. // 更新对应的 comment 值
  405. // 如果该功能没有实现,可以删除这个方法
  406. },
  407. //获取选中风机list
  408. getEnfineList(data, setUpImg) {
  409. this.fieldEngineCodes = data;
  410. this.setUpImgData = [...setUpImg];
  411. this.getAnalysisDetail();
  412. },
  413. //下一条
  414. handleNext() {
  415. const index = this.batchCodeList.findIndex(
  416. (item) => item === this.initBatchCode
  417. );
  418. if (index === this.batchCodeList.length - 1) {
  419. this.$message.warning("已经是最后一个分析结果了");
  420. return;
  421. }
  422. this.$emit("setInitBathCode", this.batchCodeList[index + 1]);
  423. },
  424. //上一条
  425. handlePrevious() {
  426. const index = this.batchCodeList.findIndex(
  427. (item) => item === this.initBatchCode
  428. );
  429. if (index === 0) {
  430. this.$message.warning("没有上一条了");
  431. return;
  432. }
  433. this.$emit("setInitBathCode", this.batchCodeList[index - 1]);
  434. },
  435. onClick() {},
  436. },
  437. };
  438. </script>
  439. <style scoped lang="scss">
  440. .type-variable {
  441. display: flex;
  442. height: 90%;
  443. overflow: hidden;
  444. .left {
  445. width: 30%;
  446. height: 100%;
  447. overflow: auto;
  448. padding: 20px;
  449. flex: 1;
  450. .chartsBox {
  451. height: 100%;
  452. overflow-y: scroll;
  453. }
  454. /* 滚动条整体样式 */
  455. &::-webkit-scrollbar {
  456. width: 6px; /* 滚动条宽度 */
  457. }
  458. /* 滚动条轨道 */
  459. &::-webkit-scrollbar-track {
  460. background: #f5f7fa;
  461. border-radius: 3px;
  462. }
  463. /* 滚动条滑块 */
  464. &::-webkit-scrollbar-thumb {
  465. background: #c0c4cc;
  466. border-radius: 3px;
  467. }
  468. /* 滚动条滑块悬停时 */
  469. &::-webkit-scrollbar-thumb:hover {
  470. background: #909399;
  471. }
  472. }
  473. .right {
  474. width: 0px;
  475. height: 100%;
  476. overflow: hidden;
  477. }
  478. }
  479. .el-dialog__wrapper {
  480. position: relative !important;
  481. }
  482. ::v-deep .el-dialog {
  483. position: fixed !important;
  484. z-index: 999 !important;
  485. top: 50%;
  486. left: 50%;
  487. transform: translate(0, -50%);
  488. }
  489. </style>