assetssMag.vue 40 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352
  1. <template>
  2. <div class="global-variable">
  3. <div class="condition">
  4. <!-- <el-alert
  5. title="分析前请核对数据是否导入,点击查看是否导入即可查看"
  6. type="warning"
  7. show-icon
  8. >
  9. </el-alert> -->
  10. <el-form
  11. :inline="true"
  12. ref="ruleForm"
  13. :model="formInline"
  14. class="demo-form-inline"
  15. :rules="rules"
  16. >
  17. <el-form-item label="分析主题:" prop="analysisName">
  18. <el-input
  19. size="small"
  20. v-model="formInline.analysisName"
  21. placeholder="请输入分析主题"
  22. ></el-input>
  23. </el-form-item>
  24. <el-form-item label="风场名称:" prop="fieldName">
  25. <el-input
  26. size="small"
  27. v-model="formInline.fieldName"
  28. placeholder="请输入风场名称"
  29. ></el-input>
  30. </el-form-item>
  31. <el-form-item label="分析状态:" prop="analysisState">
  32. <el-select
  33. size="small"
  34. v-model="formInline.analysisState"
  35. placeholder="请选择分析状态"
  36. >
  37. <el-option
  38. v-for="item in options"
  39. :key="item.value"
  40. :label="item.label"
  41. :value="item.value"
  42. >
  43. </el-option>
  44. </el-select>
  45. </el-form-item>
  46. <el-form-item>
  47. <el-button type="primary" @click="() => fetchData()" size="small"
  48. >查询</el-button
  49. >
  50. <el-button @click="reset('ruleForm')" size="small">重置</el-button>
  51. </el-form-item>
  52. <el-form-item class="right-align">
  53. <el-button @click="Newanalyse" size="small">创建分析</el-button>
  54. <el-button
  55. @click="examine"
  56. size="small"
  57. v-hasPermi="['home:offlinedata']"
  58. >查看导入数据</el-button
  59. >
  60. <el-button
  61. @click="handleAutoAsstessList"
  62. size="small"
  63. v-hasPermi="['home:performance:autoAssetss']"
  64. >查看自动分析列表</el-button
  65. >
  66. <!-- <el-button @click="handleDownLoadChart" size="small">下载</el-button> -->
  67. </el-form-item>
  68. </el-form>
  69. <!-- <div class="progress" v-if="downloadDisabled">
  70. <div class="progressText">文档生成进度:</div>
  71. <el-progress
  72. :percentage="progressPercent"
  73. :color="colors"
  74. ></el-progress>
  75. </div> -->
  76. </div>
  77. <div class="list-page">
  78. <el-table
  79. class="center-align-table"
  80. :data="tableData"
  81. border
  82. :cell-style="rowStyle"
  83. >
  84. <el-table-column
  85. align="center"
  86. label="分析主题"
  87. prop="analysisName"
  88. min-width="200"
  89. fixed
  90. >
  91. <template slot-scope="scope">
  92. <el-tooltip
  93. class="item"
  94. effect="dark"
  95. :content="scope.row.sketch || '暂无分析简述'"
  96. placement="top"
  97. >
  98. <span>{{ scope.row.analysisName }}</span>
  99. </el-tooltip>
  100. </template>
  101. </el-table-column>
  102. <el-table-column
  103. align="center"
  104. prop="fieldName"
  105. label="风场名称"
  106. min-width="200"
  107. >
  108. </el-table-column>
  109. <el-table-column
  110. prop="loginName"
  111. align="center"
  112. label="分析状态"
  113. min-width="150"
  114. >
  115. <template slot-scope="scope">
  116. <span v-if="scope.row.analysisState == -1">未关联</span>
  117. <span
  118. v-else-if="
  119. scope.row.analysisState == 20 && scope.row.errState == 0
  120. "
  121. style="color: #f90"
  122. >分析中</span
  123. >
  124. <span
  125. v-else-if="
  126. scope.row.errState == 1 && scope.row.analysisState == 30
  127. "
  128. style="color: #f00"
  129. >分析异常</span
  130. >
  131. <span
  132. v-else-if="scope.row.analysisState == 30"
  133. style="color: #4caf50"
  134. >分析完成</span
  135. >
  136. <span
  137. v-else-if="scope.row.analysisState == 10"
  138. style="color: #4caf50"
  139. >排队中</span
  140. >
  141. <span v-else>/</span>
  142. </template>
  143. </el-table-column>
  144. <el-table-column
  145. prop="loginName"
  146. align="center"
  147. label="进度"
  148. min-width="150"
  149. >
  150. <template slot-scope="scope">
  151. <el-progress
  152. v-if="scope.row.analysisState == 20"
  153. :text-inside="true"
  154. :stroke-width="20"
  155. :percentage="scope.row.analysisProgress"
  156. :class="{
  157. 'indeterminate-progress': scope.row.analysisProgress < 100,
  158. 'animated-progress': true,
  159. }"
  160. ></el-progress>
  161. <span v-else>/</span>
  162. </template>
  163. </el-table-column>
  164. <el-table-column
  165. align="center"
  166. label="排队序号"
  167. prop="orderNum"
  168. min-width="200"
  169. >
  170. <template slot-scope="scope">
  171. <span> {{ scope.row.orderNum }}</span>
  172. </template>
  173. </el-table-column>
  174. <el-table-column
  175. align="center"
  176. label="数据类型名称"
  177. prop="dataTypeName"
  178. min-width="200"
  179. >
  180. </el-table-column>
  181. <el-table-column
  182. v-hasPermi="['home:performance:autoAssetss']"
  183. prop="onOffCall"
  184. align="center"
  185. label="自动分析状态"
  186. min-width="140"
  187. >
  188. <template slot-scope="scope">
  189. <span>
  190. {{
  191. scope.row.onOffCall == 0
  192. ? "暂停"
  193. : scope.row.onOffCall === 1
  194. ? "开启"
  195. : "/"
  196. }}</span
  197. >
  198. </template>
  199. </el-table-column>
  200. <el-table-column
  201. prop="roleName"
  202. align="center"
  203. label="异常信息"
  204. min-width="120"
  205. >
  206. <template slot-scope="scope">
  207. <el-button
  208. v-if="scope.row.errState == 1"
  209. @click="abnormalDialog(scope.row, '异常详情')"
  210. type="text"
  211. size="small"
  212. >异常详情</el-button
  213. >
  214. <span v-else>/</span>
  215. </template>
  216. </el-table-column>
  217. <el-table-column
  218. prop="roleName"
  219. align="center"
  220. label="分析记录"
  221. min-width="120"
  222. >
  223. <template slot-scope="scope">
  224. <!-- 分析完成 -->
  225. <el-button
  226. v-if="scope.row.analysisState == 30 && scope.row.errState !== 1"
  227. @click="handleAssetssDetail(scope.row, '1')"
  228. type="text"
  229. size="small"
  230. >分析详情</el-button
  231. >
  232. <!-- 分析中 -->
  233. <el-button
  234. v-else-if="
  235. scope.row.analysisState == 20 && scope.row.errState == 0
  236. "
  237. @click="handleAssetssDetail(scope.row, '0')"
  238. type="text"
  239. size="small"
  240. >分析详情</el-button
  241. >
  242. <span v-else>/</span>
  243. </template>
  244. </el-table-column>
  245. <el-table-column
  246. prop="reportVos"
  247. align="center"
  248. label="报告"
  249. min-width="120"
  250. >
  251. <template slot-scope="scope">
  252. <el-dropdown v-if="scope.row.reportVos.length > 0" trigger="click">
  253. <el-button type="text" size="small" class="el-dropdown-link">
  254. 查看报告
  255. </el-button>
  256. <el-dropdown-menu slot="dropdown">
  257. <el-dropdown-item
  258. v-for="val in scope.row.reportVos"
  259. class="reportItemVal"
  260. >
  261. <div class="reportLeft">
  262. <span>{{ val.reportName }}</span>
  263. </div>
  264. <div class="reportRight">
  265. <el-button
  266. type="text"
  267. size="small"
  268. @click="detailReportAssetss(val)"
  269. >
  270. 查看
  271. </el-button>
  272. <el-button
  273. type="text"
  274. size="small"
  275. @click="downLoadeAssetss(val)"
  276. >
  277. 下载
  278. </el-button>
  279. </div>
  280. </el-dropdown-item>
  281. </el-dropdown-menu>
  282. </el-dropdown>
  283. <span v-else>/</span>
  284. </template>
  285. </el-table-column>
  286. <el-table-column
  287. prop="dataStartTime"
  288. align="center"
  289. label="开始时间"
  290. min-width="230"
  291. >
  292. </el-table-column>
  293. <el-table-column
  294. prop="dataEndTime"
  295. align="center"
  296. label="结束时间"
  297. min-width="230"
  298. >
  299. </el-table-column>
  300. <el-table-column
  301. prop="transition"
  302. align="center"
  303. fixed="right"
  304. label="操作"
  305. min-width="300"
  306. >
  307. <template slot-scope="scope">
  308. <el-button
  309. @click="handleDownLoadChart(scope.row)"
  310. type="text"
  311. size="small"
  312. :disabled="downloadDisabled"
  313. v-if="
  314. (scope.row.errState == 1 && scope.row.analysisState == 30) ||
  315. scope.row.analysisState == 30
  316. "
  317. >
  318. 下载报告
  319. </el-button>
  320. <el-button
  321. @click="abnormalDialog(scope.row, '上传报告')"
  322. type="text"
  323. size="small"
  324. >上传报告</el-button
  325. >
  326. <el-button
  327. @click="handleAssetss(scope.row)"
  328. type="text"
  329. :disabled="
  330. (scope.row.analysisState == 20 && scope.row.errState == 0) ||
  331. scope.row.analysisState == 10
  332. "
  333. size="small"
  334. >分析</el-button
  335. >
  336. <el-button
  337. @click="abnormalDialog(scope.row, '机组异常记录')"
  338. type="text"
  339. size="small"
  340. >机组异常记录</el-button
  341. >
  342. <el-button
  343. v-if="scope.row.analysisState == 10"
  344. @click="insertQueue(scope.row, 1)"
  345. type="text"
  346. size="small"
  347. >插队</el-button
  348. >
  349. <el-button
  350. v-if="scope.row.analysisState == 10"
  351. @click="insertQueue(scope.row, 0)"
  352. type="text"
  353. size="small"
  354. >取消插队</el-button
  355. >
  356. </template>
  357. </el-table-column>
  358. </el-table>
  359. <div class="pagination-container">
  360. <el-pagination
  361. @current-change="handleCurrentChange"
  362. :current-page.sync="formInline.pageNum"
  363. layout="total, prev, pager, next"
  364. :page-size="formInline.pageSize"
  365. :total="formInline.totalSize"
  366. >
  367. </el-pagination>
  368. </div>
  369. </div>
  370. <!-- 弹出层 -->
  371. <!-- 异常信息 /机组异常记录 /上传报告-->
  372. <my-dialog
  373. :visible="dialogVisible"
  374. :title="title"
  375. :rowInfo="rowInfo"
  376. @confirm="handleConfirm"
  377. :errorInfo="errorInfo"
  378. @getTableList="fetchData"
  379. >
  380. <div slot="tableEl">
  381. <el-empty description="暂无数据"></el-empty>
  382. </div>
  383. </my-dialog>
  384. <!-- 查看报告详情 -->
  385. <el-dialog
  386. title="PDF 预览"
  387. :visible.sync="dialogReportVisible"
  388. :width="dialogWidth"
  389. class="pdfDialog"
  390. :before-close="handleCloses"
  391. >
  392. <span>
  393. <div
  394. style="
  395. float: right;
  396. color: #fff;
  397. cursor: pointer;
  398. position: absolute;
  399. left: 90%;
  400. top: 21px;
  401. "
  402. >
  403. <span
  404. v-if="dialogWidth === '80%'"
  405. @click="() => (dialogWidth = '100%')"
  406. >全屏查看</span
  407. >
  408. <span
  409. v-if="dialogWidth === '100%'"
  410. @click="() => (dialogWidth = '80%')"
  411. >还原</span
  412. >
  413. <!-- {{ isFullscreen ? "还原" : "全屏" }} -->
  414. </div>
  415. </span>
  416. <div class="pdf-container" ref="viewer">
  417. <iframe :src="pdfUrl" width="100%" height="100%"></iframe>
  418. </div>
  419. <div></div>
  420. </el-dialog>
  421. <!-- 创建分析弹出框 -->
  422. <el-dialog
  423. title="创建分析"
  424. :visible.sync="addDialogVisible"
  425. width="30%"
  426. :before-close="AddHandleCloses"
  427. >
  428. <el-form
  429. :model="addRuleForm"
  430. :rules="addRules"
  431. ref="addRuleForm"
  432. label-width="100px"
  433. class="add-ruleForm"
  434. >
  435. <el-form-item label="关联风场" prop="fieldCode">
  436. <el-select
  437. v-model="addRuleForm.fieldCode"
  438. placeholder="请选择关联风场"
  439. >
  440. <el-option
  441. :label="item.fieldName"
  442. v-for="item in fieldCodeList"
  443. :value="item.codeNumber"
  444. ></el-option>
  445. </el-select>
  446. </el-form-item>
  447. <el-form-item label="" prop="analysisName">
  448. <b style="color: #f00; font-size: 12px"
  449. >不填写分析主题,自动采用系统生成的主题!</b
  450. >
  451. </el-form-item>
  452. <el-form-item label="分析主题" prop="analysisName">
  453. <el-input v-model="addRuleForm.analysisName"></el-input>
  454. </el-form-item>
  455. <el-form-item label="分析简述" prop="sketch">
  456. <el-input type="textarea" v-model="addRuleForm.sketch"></el-input>
  457. </el-form-item>
  458. <el-form-item>
  459. <el-button type="primary" @click="addRuleFormSubmit">提交</el-button>
  460. <el-button @click="AddHandleCloses">取消</el-button>
  461. </el-form-item>
  462. </el-form>
  463. </el-dialog>
  464. </div>
  465. </template>
  466. <script>
  467. import MyDialog from "./components/dialogCom.vue";
  468. import { downloadPDF } from "@/utils/common";
  469. import {
  470. analysisResultList,
  471. addAnalysisResult,
  472. queryCodeNum,
  473. editPriority,
  474. analysisDetail,
  475. } from "@/api/performance";
  476. import { downloadDocx } from "@/utils/common";
  477. import { getFieldInfo } from "@/api/overview";
  478. import axios from "axios";
  479. import pLimit from "p-limit";
  480. import { allAnalysisType } from "./js/allAnalysisType";
  481. import { mapMutations, mapState } from "vuex";
  482. import {
  483. windEngineMillPage,
  484. getWindEngineGroupByFieldCode,
  485. } from "@/api/ledger.js";
  486. export default {
  487. components: {
  488. MyDialog,
  489. },
  490. data() {
  491. return {
  492. progress: {
  493. current: 0,
  494. total: 0,
  495. },
  496. colors: [
  497. { color: "#f56c6c", percentage: 20 },
  498. { color: "#e6a23c", percentage: 40 },
  499. { color: "#5cb87a", percentage: 60 },
  500. { color: "#1989fa", percentage: 80 },
  501. { color: "#6f7ad3", percentage: 100 },
  502. ],
  503. fileDataList: {},
  504. fieldInfo: {}, //风场信息
  505. firstLoad: false, // 是否是第一次加载
  506. fieldCodeList: [],
  507. addDialogVisible: false,
  508. dialogWidth: "80%",
  509. intervalId: null,
  510. startTime: null,
  511. maxPollingTime: 3 * 60 * 1000, //轮询最大时间
  512. dialogVisible: false,
  513. loadingView: false,
  514. loading: false, //数据加载中
  515. errorInfo: "",
  516. options: [
  517. {
  518. value: "-1",
  519. label: "未关联",
  520. },
  521. {
  522. value: "10",
  523. label: "排队中",
  524. },
  525. {
  526. value: "20",
  527. label: "分析中",
  528. },
  529. {
  530. value: "30",
  531. label: "已分析",
  532. },
  533. ],
  534. addRules: {
  535. fieldCode: [
  536. { required: true, message: "请选择关联风场", trigger: "change" },
  537. ],
  538. },
  539. addRuleForm: {
  540. fieldCode: "",
  541. analysisName: "",
  542. sketch: "",
  543. },
  544. rules: {
  545. fieldName: { trigger: "blur" },
  546. },
  547. roleList: [],
  548. formInline: {
  549. fieldName: undefined,
  550. pageNum: 1,
  551. pageSize: 10,
  552. totalSize: 0,
  553. analysisName: undefined,
  554. analysisState: undefined,
  555. },
  556. allAnalysis: [],
  557. tableData: [],
  558. rowInfo: {},
  559. title: "",
  560. viewerInstance: null, // PDF 文件 URL
  561. dialogReportVisible: false,
  562. pdfUrl: "",
  563. isPolling: false, // 轮询状态标识
  564. firstLoad: true, // 是否是第一次加载
  565. };
  566. },
  567. created() {
  568. this.firstLoad = true;
  569. window.addEventListener("message", this.handleMessage); //江
  570. this.fetchData();
  571. },
  572. computed: {
  573. ...mapState("settings", {
  574. downloadDisabled: "downloadDisabled",
  575. }),
  576. progressPercent() {
  577. if (this.progress.total === 0) return 0;
  578. return Math.round((this.progress.current / this.progress.total) * 100);
  579. },
  580. },
  581. methods: {
  582. ...mapMutations("settings", ["setDownloadDisabled"]),
  583. async postChartData(
  584. urlType,
  585. row,
  586. itemAnalysis,
  587. itemField,
  588. filterAnalysis,
  589. typeChart
  590. ) {
  591. try {
  592. const objectname =
  593. itemAnalysis.analysisTypeCode === "temperature_large_components"
  594. ? filterAnalysis.filterFileAddr +
  595. "/" +
  596. itemField.fieldEngineName +
  597. ".jpg"
  598. : urlType + itemField.fieldEngineName + ".jpg" ||
  599. urlType + itemField.engineTypeCode + ".jpg";
  600. const res = await axios.post(
  601. `/downLoadChart/chartServer/charts/${urlType}`,
  602. {
  603. fieldEngineCode: itemField.fieldEngineCode,
  604. bucketName: "bucket-zhzn",
  605. engineTypeCode: itemField.machineTypeCode,
  606. objectName:
  607. `charts/${row.fieldCode}/${row.batchCode}/${itemAnalysis.analysisTypeCode}/` +
  608. objectname,
  609. fieldInfo: this.fieldInfo,
  610. fileAddr: itemField.fileAddr,
  611. chartType:
  612. filterAnalysis.typeDocxName === "production_indicator_unit"
  613. ? "radar"
  614. : urlType,
  615. }
  616. );
  617. // 每完成 10% 提示一次(可调)
  618. const percent = Math.floor(
  619. (this.progress.current / this.progress.total) * 100
  620. );
  621. if (percent % 5 === 0 && !this._shownPercents?.includes(percent)) {
  622. this._shownPercents = this._shownPercents || [];
  623. this._shownPercents.push(percent);
  624. this.$notify.info(
  625. `📈 已完成 ${percent}%/${this.progress.current}张,共 ${this.progress.total} 张图表`
  626. );
  627. }
  628. let key = `zn-techcn-replace-tags-${filterAnalysis.typeDocxName}-${typeChart}`;
  629. if (urlType === "yawErrorBarSumChart") {
  630. key = `zn-techcn-replace-tags-${filterAnalysis.typeDocxName}-generalFiles2`;
  631. if (!this.fileDataList["yawErrorRows"]) {
  632. this.$set(this.fileDataList, "yawErrorRows", []); // Vue 2 中响应式设置对象属性
  633. }
  634. this.fileDataList["yawErrorRows"] = res?.data?.data?.data;
  635. } else if (urlType === "yawErrorChart") {
  636. key = `zn-techcn-replace-tags-${filterAnalysis.typeDocxName}-generalFiles1`;
  637. }
  638. if (filterAnalysis.typeDocxName === "production_indicator_all") {
  639. if (!this.fileDataList["rows"]) {
  640. this.$set(this.fileDataList, "rows", []); // Vue 2 中响应式设置对象属性
  641. }
  642. this.fileDataList["rows"] = res?.data?.data?.data;
  643. }
  644. if (itemField.engineTypeCode === "total_fault_result") {
  645. if (!this.fileDataList["faultRows"]) {
  646. this.$set(this.fileDataList, "faultRows", []); // Vue 2 中响应式设置对象属性
  647. }
  648. this.fileDataList["faultRows"] = res?.data?.data?.data;
  649. }
  650. if (itemField.engineTypeCode === "turbine_fault_result") {
  651. if (!this.fileDataList["windTurbineRows"]) {
  652. this.$set(this.fileDataList, "windTurbineRows", []); // Vue 2 中响应式设置对象属性
  653. }
  654. this.fileDataList["windTurbineRows"] = res?.data?.data?.data;
  655. }
  656. if (filterAnalysis.typeDocxName === "production_indicator_unit") {
  657. if (!this.fileDataList[key]) {
  658. this.$set(this.fileDataList, key, []); // Vue 2 中响应式设置对象属性
  659. }
  660. res?.data?.data?.imageUrls.map((imgval) => {
  661. this.fileDataList[key].push(imgval);
  662. });
  663. } else {
  664. if (!this.fileDataList[key]) {
  665. this.$set(this.fileDataList, key, []); // Vue 2 中响应式设置对象属性
  666. }
  667. this.fileDataList[key].push(res?.data?.data?.imageUrl);
  668. }
  669. } catch (err) {
  670. console.error("生成失败:", err);
  671. }
  672. },
  673. async handleDownLoadChart(row) {
  674. this.fileDataList = {};
  675. this.setDownloadDisabled(true);
  676. this.$notify.warning("开始生成 Word 文档...");
  677. try {
  678. await this.getAllAnalysis(row.batchCode);
  679. await this.getFieldDetail(row.batchCode);
  680. this.$message.info("开始生成word文档");
  681. const limit = pLimit(5); // 限制同时并发的请求数量为 5
  682. const tasks = [];
  683. for (const itemAnalysis of this.allAnalysis) {
  684. const filterAnalysis = allAnalysisType.filter(
  685. (itemType) => itemType.typeName === itemAnalysis.analysisTypeName
  686. )[0];
  687. if (itemAnalysis.generalFiles) {
  688. //这里过滤的是发电机温度的类型 在url 中是否能找到filterFileAddr
  689. for (const itemField of filterAnalysis.filterFileAddr
  690. ? itemAnalysis.generalFiles
  691. .filter((item) => item.fileAddr.endsWith(".json"))
  692. .filter((item) =>
  693. item.fileAddr.includes(filterAnalysis.filterFileAddr)
  694. ) || []
  695. : itemAnalysis.generalFiles) {
  696. //静态偏航误差 --通过allAnalysisType.js文件配置实现需求
  697. if (Array.isArray(filterAnalysis.generalFiles.urlType)) {
  698. filterAnalysis.generalFiles.urlType.map(
  699. (itemUrlType, indUrlType) => {
  700. tasks.push(
  701. limit(async () => {
  702. await this.postChartData(
  703. itemUrlType,
  704. row,
  705. itemAnalysis,
  706. itemField,
  707. filterAnalysis,
  708. "generalFiles"
  709. );
  710. this.progress.current++;
  711. })
  712. );
  713. }
  714. );
  715. } else if (filterAnalysis.typeCode === "fault") {
  716. if (itemField.fileAddr.includes("turbine_fault_result")) {
  717. if (itemField.engineTypeCode === "turbine_fault_result") {
  718. tasks.push(
  719. limit(async () => {
  720. await this.postChartData(
  721. "faultUnitChart",
  722. row,
  723. itemAnalysis,
  724. itemField,
  725. filterAnalysis,
  726. "generalFiles"
  727. );
  728. this.progress.current++;
  729. })
  730. );
  731. }
  732. } else {
  733. if (itemField.engineTypeCode === "total_fault_result") {
  734. tasks.push(
  735. limit(async () => {
  736. await this.postChartData(
  737. "faultAllChart",
  738. row,
  739. itemAnalysis,
  740. itemField,
  741. filterAnalysis,
  742. "generalFiles"
  743. );
  744. this.progress.current++;
  745. })
  746. );
  747. }
  748. }
  749. } else if (filterAnalysis.typeCode === "production_indicator") {
  750. //总图全场
  751. if (
  752. itemField.fileAddr.includes(
  753. filterAnalysis.generalFiles.FileTypeFromUrl
  754. )
  755. ) {
  756. tasks.push(
  757. limit(async () => {
  758. await this.postChartData(
  759. filterAnalysis.generalFiles.urlType,
  760. row,
  761. itemAnalysis,
  762. itemField,
  763. filterAnalysis,
  764. "generalFiles"
  765. );
  766. this.progress.current++;
  767. // 每完成 10% 提示一次(可调)
  768. })
  769. );
  770. } else {
  771. tasks.push(
  772. limit(async () => {
  773. await this.postChartData(
  774. "radarChart",
  775. row,
  776. itemAnalysis,
  777. itemField,
  778. filterAnalysis,
  779. "generalFiles"
  780. );
  781. this.progress.current++;
  782. })
  783. );
  784. }
  785. } else {
  786. tasks.push(
  787. limit(async () => {
  788. await this.postChartData(
  789. filterAnalysis.generalFiles.urlType,
  790. row,
  791. itemAnalysis,
  792. itemField,
  793. filterAnalysis,
  794. "generalFiles"
  795. );
  796. this.progress.current++;
  797. })
  798. );
  799. }
  800. }
  801. }
  802. if (itemAnalysis.diagramRelations) {
  803. for (const itemField of filterAnalysis.filterFileAddr
  804. ? itemAnalysis.diagramRelations
  805. .filter((item) => item.fileAddr.endsWith(".json"))
  806. .filter((item) =>
  807. item.fileAddr.includes(filterAnalysis.filterFileAddr)
  808. ) || []
  809. : itemAnalysis.diagramRelations) {
  810. const urlType = Array.isArray(
  811. filterAnalysis.diagramRelations.urlType
  812. )
  813. ? this.getFileTypeFromUrl(
  814. itemField.fileAddr,
  815. filterAnalysis.diagramRelations.FileTypeFromUrl
  816. ) === filterAnalysis.diagramRelations.FileTypeFromUrl
  817. ? filterAnalysis.diagramRelations.urlType[0]
  818. : filterAnalysis.diagramRelations.urlType[1]
  819. : filterAnalysis.diagramRelations.urlType;
  820. tasks.push(
  821. limit(async () => {
  822. await this.postChartData(
  823. urlType,
  824. row,
  825. itemAnalysis,
  826. itemField,
  827. filterAnalysis,
  828. "diagramRelations"
  829. );
  830. this.progress.current++;
  831. })
  832. );
  833. }
  834. }
  835. }
  836. this.progress.total = tasks.length;
  837. await Promise.all(tasks);
  838. const engineTypeList = await this.getEngineTypeList(
  839. this.fieldInfo.engineMillTypes,
  840. row.batchCode
  841. );
  842. this.$notify.success("✅ 图表全部生成完成,开始生成 Word 文档...");
  843. const wordFilePath = await axios.post(
  844. "/downLoadChart/chartServer/charts/CopyFileCsv",
  845. {
  846. fieldInfo: this.fieldInfo,
  847. dataTime: `${this.analysisInfo.dataStartTime}-${this.analysisInfo.dataEndTime}`,
  848. bucketName: "bucket-zhzn",
  849. objectName: `charts/${row.fieldCode}/${row.batchCode}`,
  850. engineTypeList: engineTypeList,
  851. ...this.fileDataList,
  852. }
  853. );
  854. // //下载minio 文件
  855. downloadDocx(
  856. wordFilePath.data.data.url,
  857. wordFilePath.data.data.fileName
  858. );
  859. this.$notify.success("🎉 Word 文档生成并已下载!");
  860. this.setDownloadDisabled(false);
  861. } catch (error) {
  862. this.$notify.error("🎉 Word 文档生成并下载失败!");
  863. this.setDownloadDisabled(false);
  864. }
  865. },
  866. async getEngineTypeList(typeList, batchCode) {
  867. return await Promise.all(
  868. typeList.map((item) => this.getEngineMillList(item, batchCode))
  869. );
  870. },
  871. // 查询
  872. async getEngineMillList(machineTypeCode, batchCode) {
  873. let dataArr = {
  874. fieldCode: this.fieldInfo.fieldCode,
  875. batchCode: batchCode,
  876. };
  877. const fieldRes = await getWindEngineGroupByFieldCode(dataArr);
  878. const map = new Map();
  879. const uniqueList = [];
  880. for (const item of fieldRes.data.windEngineGroupVoList) {
  881. if (!map.has(item.millTypeCode)) {
  882. map.set(item.millTypeCode, true);
  883. uniqueList.push(item);
  884. }
  885. }
  886. let paramsData = {
  887. machineTypeCode: machineTypeCode || undefined,
  888. pageNum: 1,
  889. pageSize: 10,
  890. };
  891. this.loading = true;
  892. const res = await windEngineMillPage(paramsData);
  893. const filterMill = uniqueList.filter(
  894. (item) => item.millTypeCode === res.data.list[0].millTypeCode
  895. );
  896. return { ...res.data.list[0], ratedPower: filterMill[0].ratedCapacity };
  897. },
  898. getFileTypeFromUrl(url, keyword) {
  899. return url.includes(keyword) ? keyword : "Unknown";
  900. },
  901. async getFieldDetail(batchCode) {
  902. const res = await getFieldInfo({ batchCode });
  903. this.fieldInfo = res.data.fieldInfo; //风场信息
  904. this.analysisInfo = res.data.analysisInfo;
  905. },
  906. //获取所有分析类型,分析结果接口
  907. async getAllAnalysis(batchCode) {
  908. const res = await analysisDetail({ batchCode });
  909. // console.log(res.data, "分析全部结果");
  910. this.allAnalysis = res.data;
  911. },
  912. //自动分析列表页面跳转
  913. handleAutoAsstessList() {
  914. window.open(
  915. this.$router.resolve({ path: "/home/performance/autoAssetss" }).href,
  916. "_blank"
  917. );
  918. },
  919. // 创建分析时请求
  920. async addRuleFormSubmit() {
  921. this.$refs.addRuleForm.validate(async (valid) => {
  922. if (valid) {
  923. try {
  924. const res = await addAnalysisResult(this.addRuleForm);
  925. if (res.code === 200) {
  926. this.$message({
  927. type: "success",
  928. message: "创建成功",
  929. });
  930. this.addDialogVisible = false;
  931. this.loading = false;
  932. this.isPolling = false;
  933. await this.fetchData();
  934. let obj = this.fieldCodeList.filter((itemS) => {
  935. if (itemS.codeNumber === this.addRuleForm.fieldCode) {
  936. return itemS;
  937. }
  938. });
  939. this.$router.push({
  940. path: "/home/performance/editAssets",
  941. query: {
  942. batchCode: this.tableData[0].batchCode,
  943. analysisTypeCode: this.tableData[0].analysisTypeCode,
  944. fieldEngineCode: this.tableData[0].fieldCode,
  945. fieldName: obj[0].fieldName,
  946. analysisName: this.addRuleForm.analysisName,
  947. },
  948. });
  949. }
  950. } catch (err) {
  951. console.error(err);
  952. }
  953. }
  954. });
  955. },
  956. AddHandleCloses(done) {
  957. this.$confirm("确认关闭?")
  958. .then((_) => {
  959. this.addDialogVisible = false;
  960. done();
  961. })
  962. .catch((_) => {});
  963. },
  964. //获取风场列表
  965. async getQueryCodeNumList() {
  966. this.loading = true;
  967. try {
  968. const result = await queryCodeNum();
  969. this.fieldCodeList = result.data.fieldCodeList;
  970. this.loading = false;
  971. } catch (error) {
  972. console.error(error);
  973. this.loading = false;
  974. }
  975. },
  976. handleCloses(done) {
  977. this.$confirm("确认关闭?")
  978. .then((_) => {
  979. done();
  980. })
  981. .catch((_) => {});
  982. },
  983. //查看pdf 文件
  984. detailReportAssetss(row) {
  985. this.dialogReportVisible = true;
  986. this.pdfUrl = row.reportAddr;
  987. },
  988. //下载pdf 文件
  989. downLoadeAssetss(row) {
  990. downloadPDF(row.reportAddr, row.reportName);
  991. },
  992. //插队接口
  993. async insertQueue(row, index) {
  994. this.$confirm(`确认插队?`, "提示", {
  995. confirmButtonText: "确定",
  996. cancelButtonText: "取消",
  997. type: "warning",
  998. })
  999. .then(async () => {
  1000. const formData = new FormData();
  1001. formData.append("batchCode", row.batchCode);
  1002. formData.append("priority", index);
  1003. const result = await editPriority(formData);
  1004. if (result.code === 200) {
  1005. this.$message({
  1006. type: "success",
  1007. message: "插队成功",
  1008. });
  1009. await this.fetchData();
  1010. }
  1011. })
  1012. .catch(() => {});
  1013. },
  1014. //分析
  1015. handleAssetss(row) {
  1016. this.$router.push({
  1017. path: "/home/performance/editAssets",
  1018. query: {
  1019. batchCode: row.batchCode,
  1020. analysisTypeCode: row.analysisTypeCode,
  1021. fieldEngineCode: row.fieldCode,
  1022. fieldName: row.fieldName,
  1023. analysisName: row.analysisName,
  1024. },
  1025. });
  1026. },
  1027. //分析详情
  1028. handleAssetssDetail(row, state) {
  1029. const navigateToDetails = () => {
  1030. this.$router.push({
  1031. path: "/home/performance/overview",
  1032. query: {
  1033. batchCode: row.batchCode,
  1034. // analysisTypeCode: row.analysisTypeCode,
  1035. fieldCode: row.fieldCode,
  1036. },
  1037. });
  1038. };
  1039. if (state === "0") {
  1040. // 分析状态为分析中
  1041. this.$confirm(
  1042. "当前查看的分析记录为历史分析结果,最新分析记录还未分析完成不展示!"
  1043. )
  1044. .then(() => {
  1045. navigateToDetails();
  1046. })
  1047. .catch(() => {});
  1048. } else {
  1049. navigateToDetails();
  1050. }
  1051. },
  1052. abnormalDialog(row, title) {
  1053. this.dialogVisible = true;
  1054. if (title === "异常详情") {
  1055. this.errorInfo = row.errInfo;
  1056. this.rowInfo = {};
  1057. } else if (title === "机组异常记录") {
  1058. this.errorInfo = "";
  1059. this.rowInfo = { ...row };
  1060. } else if (title === "上传报告") {
  1061. this.errorInfo = "";
  1062. this.rowInfo = {};
  1063. this.rowInfo.batchCode = row.batchCode;
  1064. }
  1065. this.title = title;
  1066. },
  1067. handleConfirm() {
  1068. this.dialogVisible = false;
  1069. },
  1070. // 页码变化时才触发查询
  1071. handleCurrentChange(val) {
  1072. this.formInline.pageNum = val;
  1073. this.fetchData();
  1074. },
  1075. // 修改 getTableList 方法,避免重复请求
  1076. async getTableList() {
  1077. // 如果正在请求中,跳过此次调用
  1078. if (this.loading || this.isPolling) return;
  1079. this.loading = true;
  1080. try {
  1081. const params = { ...this.formInline, totalSize: undefined };
  1082. // 传递条件参数
  1083. if (this.formInline.analysisState !== undefined) {
  1084. params.analysisState = this.formInline.analysisState;
  1085. }
  1086. if (this.formInline.errState !== undefined) {
  1087. params.errState = this.formInline.errState;
  1088. }
  1089. const result = await analysisResultList(params);
  1090. this.tableData = result.data.list;
  1091. this.formInline.totalSize = result.data.totalSize;
  1092. } catch (error) {
  1093. this.$message({
  1094. type: "error",
  1095. message: "请检查是否连接网络",
  1096. });
  1097. } finally {
  1098. this.loading = false;
  1099. }
  1100. },
  1101. // 改进 handleMessage,避免频繁请求
  1102. handleMessage(event) {
  1103. // 确保消息来自当前域
  1104. if (event.origin !== window.location.origin) {
  1105. return;
  1106. }
  1107. const { fieldName, analysisState, errState } = event.data;
  1108. // 更新表单字段
  1109. if (fieldName !== undefined) {
  1110. this.formInline.fieldName = fieldName;
  1111. }
  1112. if (analysisState !== undefined) {
  1113. this.formInline.analysisState = analysisState;
  1114. }
  1115. if (errState !== undefined) {
  1116. this.formInline.errState = errState;
  1117. }
  1118. if (!this.loading) {
  1119. if (this.firstLoad && (analysisState || errState || fieldName)) {
  1120. // this.getTableList();
  1121. this.startPolling();
  1122. this.fetchData();
  1123. }
  1124. //
  1125. }
  1126. },
  1127. rowStyle() {
  1128. return "text-align:center";
  1129. },
  1130. // 重置
  1131. reset(formName) {
  1132. this.$refs[formName].resetFields();
  1133. this.formInline.fieldName = "";
  1134. this.formInline.analysisName = "";
  1135. this.formInline.analysisState = "";
  1136. this.formInline.errState = "";
  1137. this.fetchData();
  1138. },
  1139. // fetchData 方法在轮询中调用
  1140. async fetchData() {
  1141. if (
  1142. this.formInline.fieldName ||
  1143. this.formInline.analysisState ||
  1144. this.formInline.errState
  1145. ) {
  1146. this.firstLoad = false;
  1147. }
  1148. try {
  1149. const result = await analysisResultList({
  1150. ...this.formInline,
  1151. totalSize: undefined,
  1152. });
  1153. this.tableData = result.data.list;
  1154. this.formInline.totalSize = result.data.totalSize;
  1155. } catch (error) {
  1156. this.$message({
  1157. type: "error",
  1158. message: "请检查是否连接网络",
  1159. });
  1160. }
  1161. },
  1162. stopPolling() {
  1163. // 停止轮询
  1164. if (this.intervalId) {
  1165. clearInterval(this.intervalId);
  1166. this.intervalId = null;
  1167. }
  1168. this.isPolling = false;
  1169. },
  1170. // 启动轮询时,避免重复请求
  1171. startPolling() {
  1172. this.startTime = new Date().getTime();
  1173. this.isPolling = true;
  1174. this.intervalId = setInterval(() => {
  1175. const currentTime = new Date().getTime();
  1176. if (currentTime - this.startTime >= this.maxPollingTime) {
  1177. this.stopPolling();
  1178. } else {
  1179. // 轮询期间不重复请求,检查是否可以调用 fetchData
  1180. if (!this.loading) {
  1181. this.fetchData();
  1182. }
  1183. }
  1184. }, 10000); // 每10秒检查一次
  1185. },
  1186. //创建分析
  1187. Newanalyse() {
  1188. this.addDialogVisible = true;
  1189. this.getQueryCodeNumList();
  1190. },
  1191. examine() {
  1192. const targetUrl = this.$router.resolve({ name: "transition" }).href;
  1193. window.open(targetUrl, "_blank");
  1194. },
  1195. },
  1196. mounted() {
  1197. this.startPolling();
  1198. },
  1199. beforeDestroy() {
  1200. this.stopPolling();
  1201. // 销毁消息监听器
  1202. window.removeEventListener("message", this.handleMessage); //江
  1203. },
  1204. };
  1205. </script>
  1206. <style lang="scss" scoped>
  1207. @keyframes striped-flow {
  1208. from {
  1209. background-position: 0 100px;
  1210. }
  1211. to {
  1212. background-position: 1.25em 1.25em;
  1213. }
  1214. }
  1215. .general {
  1216. display: flex;
  1217. flex-wrap: wrap;
  1218. .condition {
  1219. width: 50%;
  1220. display: flex;
  1221. p {
  1222. width: 100px;
  1223. text-align: right;
  1224. line-height: 40px;
  1225. }
  1226. span {
  1227. line-height: 40px;
  1228. padding-left: 20px;
  1229. }
  1230. .el-select {
  1231. width: 100%;
  1232. margin-bottom: 20px;
  1233. }
  1234. .el-input {
  1235. margin-bottom: 20px;
  1236. }
  1237. }
  1238. }
  1239. .progress {
  1240. display: flex;
  1241. align-items: center;
  1242. position: fixed;
  1243. top: 25px;
  1244. right: 300px;
  1245. .progressText {
  1246. font-size: 14px;
  1247. color: #606266;
  1248. }
  1249. .el-progress {
  1250. width: 300px;
  1251. }
  1252. }
  1253. .attachment {
  1254. display: flex;
  1255. padding-top: 10px;
  1256. p {
  1257. margin-right: 20px;
  1258. color: #409eff;
  1259. }
  1260. }
  1261. .add-ruleForm {
  1262. .el-select {
  1263. width: 100%;
  1264. }
  1265. }
  1266. .addition {
  1267. display: flex;
  1268. justify-content: flex-end;
  1269. margin-bottom: 10px;
  1270. }
  1271. .demo-ruleForm {
  1272. .el-form-item {
  1273. margin-bottom: 25px;
  1274. }
  1275. }
  1276. ::v-deep .animated-progress .el-progress-bar__outer {
  1277. height: 15px; /* Adjust height as needed */
  1278. background-color: rgb(235, 238, 245);
  1279. background-image: linear-gradient(
  1280. 45deg,
  1281. rgba(0, 0, 0, 0.1) 25%,
  1282. transparent 25%,
  1283. transparent 50%,
  1284. rgba(0, 0, 0, 0.1) 50%,
  1285. rgba(0, 0, 0, 0.1) 75%,
  1286. transparent 75%,
  1287. transparent
  1288. );
  1289. background-size: 1.25em 1.25em;
  1290. animation: striped-flow 3s linear infinite;
  1291. }
  1292. .reportItemVal {
  1293. display: flex;
  1294. justify-content: space-between;
  1295. align-items: center;
  1296. .reportLeft {
  1297. margin-right: 50px;
  1298. }
  1299. }
  1300. ::v-deep .pdfDialog .el-dialog {
  1301. height: 100vh;
  1302. display: flex;
  1303. flex-direction: column;
  1304. margin: 0 auto !important;
  1305. .el-dialog__body {
  1306. // height: 100% !important;
  1307. flex: 1;
  1308. background: #fff;
  1309. .pdf-container {
  1310. display: flex;
  1311. justify-content: center;
  1312. align-items: center;
  1313. height: 100% !important;
  1314. }
  1315. }
  1316. }
  1317. canvas {
  1318. border: 1px solid #dcdfe6;
  1319. }
  1320. .right-align {
  1321. white-space: nowrap;
  1322. }
  1323. </style>