index.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545
  1. <template>
  2. <div class="view-table-content">
  3. <div class="view-dataSpecies-left">
  4. <MenuTree :currentNodeKey="currentNodeKey" nodeKey="id" :treedata="menuTreeData" @TreeNodeclick="treeNodeClick" v-bind="treeObj"> </MenuTree>
  5. </div>
  6. <div class="view-dataSpecies-right" v-loading="loading">
  7. <div class="view-dataType-title">
  8. <div class="view-dataType-title-btn"></div>
  9. <div class="view-dataType-title-search">
  10. <el-input placeholder="请输入架次号" v-model="keyWordData" class="input1">
  11. <el-button slot="append" icon="el-icon-search" @click="searchClick"></el-button>
  12. </el-input>
  13. </div>
  14. </div>
  15. <div class="view-dataType-table">
  16. <LTable ref="table" @selection-change="selection" :defaultFetch="false" :fetch="fetch" :columns="columns" :dataSource="tableData" :options="options" :pagination="tableRequset"></LTable>
  17. </div>
  18. <el-dialog title="诊断结果" :visible.sync="resultVisible" width="1200px">
  19. <LTable ref="resultTable" :showColumnSetting="false" :defaultFetch="false" :columns="resultColumns" :dataSource="resultTableData" :options="resultOptions" :pagination="resultTableRequset"></LTable>
  20. </el-dialog>
  21. <el-dialog title="执行进度" :visible.sync="progressVisible" width="800px">
  22. <el-progress :text-inside="true" :stroke-width="24" :percentage="percentage" status="success"></el-progress>
  23. </el-dialog>
  24. <el-dialog title="结果展示" :visible.sync="resultShowVisible" :before-close="resultShowDialogClose" v-loading="tagLoading" width="800px">
  25. <el-descriptions border :column="1">
  26. <el-descriptions-item label="评分">
  27. <div style="display: flex; align-items: center">
  28. <el-rate v-model="changeScore" disabled text-color="#ff9900" score-template="{value}"> </el-rate>
  29. <span style="margin-left: 3px; color: #f7ba2a; font-size: 1.2rem">{{ diagnosisResult.score }}</span>
  30. </div>
  31. </el-descriptions-item>
  32. <el-descriptions-item label="飞行能力影响">
  33. <el-tag class="fault" style="margin-right: 20px" type="danger" v-for="(item, index) in diagnosisResult.degradation" :key="index" @click="handelQA(item)">{{ item }}</el-tag>
  34. </el-descriptions-item>
  35. <el-descriptions-item label="故障结果">
  36. <el-tag class="fault" style="margin-right: 20px" type="danger" v-for="(item, index) in diagnosisResult.fault" :key="index" @click="handelQA(item)">{{ item }}</el-tag>
  37. </el-descriptions-item>
  38. </el-descriptions>
  39. <span slot="footer" class="dialog-footer">
  40. <el-button type="primary" @click="resultShowDialogClose">确 定</el-button>
  41. </span>
  42. </el-dialog>
  43. <el-dialog title="问答结果" :visible.sync="QAVisible" width="1000px" :before-close="QADialogClose" append-to-body>
  44. <div style="color: white; font-size: 20px; margin-bottom: 20px">
  45. {{ QAData.graphAnswer.answer }}
  46. </div>
  47. <graphECharts :width="950" :height="400" v-if="QAData.graphAnswer.graph" :graphData="QAData.graphAnswer.graph"></graphECharts>
  48. <span slot="footer" class="dialog-footer">
  49. <el-button type="primary" @click="QAVisible = false">确 定</el-button>
  50. </span>
  51. </el-dialog>
  52. </div>
  53. </div>
  54. </template>
  55. <script>
  56. import store from '@/store'
  57. import { getDataImport } from '@/api/als/dataImport'
  58. import { getFaultDiagnosisResult } from '@/api/als/faultDiagnosisResult'
  59. import { getAircaftCatalogTree } from '@/api/als/sideTree'
  60. import { getAircaftCatalogAll } from '@/api/als/aircraft'
  61. import { deepClone, debounce } from '@/utils/index'
  62. import { handleTree, flattenTree } from '../utils/common'
  63. import { getAirConfiguration } from '@/api/als/airConfiguration'
  64. import { executeFaultIntell } from '@/api/als/algorithm'
  65. import { getListByIdsApi } from '@/api/als/oss'
  66. import { handlerQAndA } from '@/api/als/intelligentQA'
  67. import graphECharts from '@/views/als/components/Charts/graph.vue'
  68. export default {
  69. name: 'FaultDiagnosis',
  70. components: { graphECharts },
  71. data() {
  72. // 这里存放数据
  73. return {
  74. loading: false,
  75. resultVisible: false,
  76. progressVisible: false,
  77. resultShowVisible: false,
  78. QAVisible: false,
  79. keyWordData: '',
  80. aircaftModelIdList: [],
  81. currentNodeKey: '',
  82. currentNode: {},
  83. menuTreeData: [],
  84. treeObj: {
  85. title: '所属机种',
  86. activityheight: '275px',
  87. searchIcon: false,
  88. configure: {
  89. children: 'children',
  90. label: 'label'
  91. }
  92. },
  93. typeTree: {
  94. children: 'children',
  95. label: 'label'
  96. },
  97. searchValue: '',
  98. columns: [
  99. {
  100. prop: 'aircraftId',
  101. label: '编目',
  102. render: (h, params) => {
  103. const matchedItem = this.aircaftCatalogAll.find((item) => params.row.aircraftId.trim() === item.aircaftCatalogId.trim())
  104. if (matchedItem) {
  105. return h('span', matchedItem.aircaftCatalogCode)
  106. } else {
  107. return h('span', {}, '')
  108. }
  109. }
  110. },
  111. {
  112. prop: 'sortieNo',
  113. label: '架次号'
  114. },
  115. {
  116. prop: 'flightDate',
  117. label: '飞行时间'
  118. },
  119. {
  120. prop: 'source',
  121. label: '数据源',
  122. render: (h, params) => {
  123. if (params.row.source == 1) {
  124. return h('span', '飞参数据')
  125. } else {
  126. return h('span', 'MDC数据')
  127. }
  128. }
  129. },
  130. {
  131. prop: 'status',
  132. label: '状态',
  133. // 1 未处理,2 已处理
  134. render: (h, params) => {
  135. if (params.row.status == '1') {
  136. return h('span', '未处理')
  137. } else if (params.row.status == '2') {
  138. return h('span', { class: 'success-state' }, '已处理')
  139. } else {
  140. return h('span', { class: 'warning-state' }, '异常')
  141. }
  142. }
  143. },
  144. {
  145. button: true,
  146. label: '操作',
  147. width: '240px',
  148. group: [
  149. {
  150. name: '故障诊断',
  151. type: 'text',
  152. round: false,
  153. plain: false,
  154. onClick: (row, index, scope) => {
  155. this.faultExecute(row)
  156. }
  157. },
  158. {
  159. name: '查看',
  160. type: 'text',
  161. statusKey: 'status',
  162. unDisableKey: '2',
  163. round: false,
  164. plain: false,
  165. onClick: (row, index, scope) => {
  166. this.checkRelustList(row)
  167. }
  168. }
  169. ]
  170. }
  171. ],
  172. options: {
  173. stripe: false, // 斑马纹
  174. mutiSelect: true, // 多选框
  175. index: false, // 显示序号, 多选则 mutiSelect
  176. loading: false, // 表格动画
  177. initTable: false, // 是否一挂载就加载数据
  178. border: true,
  179. height: 'calc(100vh - 300px)'
  180. },
  181. tableCheckItems: [],
  182. tableData: [],
  183. tableRequset: {
  184. total: 0,
  185. pageIndex: 1,
  186. pageSize: 20,
  187. searchValue: ''
  188. },
  189. resultColumns: [
  190. {
  191. prop: 'modelType',
  192. label: '算法名称'
  193. },
  194. {
  195. prop: 'createTime',
  196. label: '执行时间',
  197. render: (h, params) => {
  198. return h('span', params.row.createTime.split(' ')[0])
  199. }
  200. },
  201. {
  202. prop: 'status',
  203. label: '状态',
  204. render: (h, params) => {
  205. return h('span', { class: 'success-state' }, '成功')
  206. }
  207. },
  208. {
  209. button: true,
  210. label: '结果',
  211. width: '240px',
  212. group: [
  213. {
  214. name: '查看结果',
  215. type: 'text',
  216. round: false,
  217. plain: false,
  218. onClick: (row, index, scope) => {
  219. this.checkRelustShow(row)
  220. }
  221. }
  222. ]
  223. }
  224. ],
  225. resultOptions: {
  226. stripe: false, // 斑马纹
  227. mutiSelect: true, // 多选框
  228. index: false, // 显示序号, 多选则 mutiSelect
  229. loading: false, // 表格动画
  230. initTable: false, // 是否一挂载就加载数据
  231. border: true,
  232. height: 'calc(100vh - 600px)'
  233. },
  234. resultTableCheckItems: [],
  235. resultTableData: [],
  236. resultTableRequset: {
  237. total: 0,
  238. pageIndex: 1,
  239. pageSize: 10,
  240. searchValue: ''
  241. },
  242. debounceFn: debounce(this.fetch, 500),
  243. aircaftCatalogAll: [],
  244. form: {
  245. id: '',
  246. sortieNo: '',
  247. ossId: '',
  248. aircraftId: '',
  249. aircraftType: '',
  250. flightDate: '',
  251. status: '',
  252. result: '',
  253. partId: null
  254. },
  255. currentSortieNo: '',
  256. partsData: [],
  257. allAirConfig: [],
  258. percentage: 0,
  259. resultShowData: {
  260. url: '',
  261. result: ''
  262. },
  263. diagnosisResult: {
  264. degradation: [],
  265. fault: [],
  266. score: null
  267. },
  268. changeScore: null,
  269. QAData: {
  270. userId: '',
  271. graphAnswer: {}
  272. },
  273. tagLoading: false
  274. }
  275. },
  276. watch: {
  277. keyWord() {
  278. this.tableRequset.pageIndex = 1
  279. this.debounceFn()
  280. }
  281. },
  282. computed: {},
  283. mounted() {
  284. this.getAircaftCatalogTreeAPI()
  285. },
  286. methods: {
  287. async getAircaftCatalogTreeAPI(params) {
  288. try {
  289. const { data } = await getAircaftCatalogTree(params)
  290. this.menuTreeData = data
  291. const getAircaftCatalogAllParams = {
  292. keyWord: '',
  293. aircaftModelIdList: []
  294. }
  295. const { data: data2 } = await getAircaftCatalogAll(getAircaftCatalogAllParams)
  296. this.aircaftCatalogAll = data2
  297. const { data: airConfigData } = await getAirConfiguration()
  298. this.allAirConfig = airConfigData
  299. if (data.length) {
  300. this.currentNodeKey = data[0].id
  301. this.currentNode = data[0]
  302. this.aircaftModelIdList = this.getTreeLeafData(data[0]?.children)
  303. .map((e) => e.id)
  304. .toString()
  305. this.getDataImportAPI({ aircraftId: this.aircaftModelIdList })
  306. }
  307. } catch (error) {}
  308. },
  309. getTreeLeafData(list) {
  310. const newArr = []
  311. function getLeaf(data, arr) {
  312. data.forEach((e) => {
  313. if (e.type === 2) {
  314. arr.push(e)
  315. }
  316. if (e.children.length) {
  317. getLeaf(e.children, arr)
  318. }
  319. })
  320. }
  321. getLeaf(list, newArr)
  322. return newArr
  323. },
  324. async getFaultDiagnosisResultAPI(params) {
  325. if (this.$refs.resultTable) this.$refs.resultTable.clearSelection()
  326. const { pageSize, pageIndex } = this.resultTableRequset
  327. const {
  328. data: { list, total }
  329. } = await getFaultDiagnosisResult({ pageSize, pageNum: pageIndex, ...params })
  330. this.resultTableData = list
  331. this.resultTableRequset.total = total
  332. },
  333. async getDataImportAPI(params) {
  334. if (this.$refs.table) this.$refs.table.clearSelection()
  335. const { keyWord } = this
  336. const { pageSize, pageIndex } = this.tableRequset
  337. const {
  338. data: { list, total }
  339. } = await getDataImport({ pageSize, pageNum: pageIndex, ...params, source: 1 })
  340. this.tableData = list
  341. this.tableRequset.total = total
  342. },
  343. fetch() {
  344. this.getDataImportAPI({ aircraftId: this.aircaftModelIdList })
  345. },
  346. async searchClick() {
  347. this.getDataImportAPI({ aircraftId: this.aircaftModelIdList, sortieNo: this.keyWordData })
  348. },
  349. treeNodeClick(data) {
  350. this.$refs.table.clearSelection()
  351. this.currentNodeKey = data.id
  352. this.currentNode = data
  353. this.aircaftModelIdList = this.getTreeLeafData(data.children.length ? data.children : [data])
  354. .map((e) => e.id)
  355. .toString()
  356. if (!this.aircaftModelIdList) {
  357. this.aircaftModelIdList = '#'
  358. }
  359. this.getDataImportAPI({ aircraftId: this.aircaftModelIdList })
  360. },
  361. async getAirConfigurationAPI(params) {
  362. try {
  363. const { data } = await getAirConfiguration(params)
  364. const treeData = handleTree(data, 'id')
  365. this.partsData = treeData
  366. } catch (error) {}
  367. },
  368. faultExecute(row) {
  369. this.form = deepClone(row)
  370. const allAir = flattenTree(this.menuTreeData)
  371. const item = allAir.find((item) => item.id === this.form.aircraftId)
  372. this.form.aircraftType = item.parentId
  373. this.currentSortieNo = row.sortieNo
  374. this.beginExecute(row)
  375. },
  376. partIdSelect(node) {
  377. this.form.partId = node.id
  378. },
  379. normalizer(node) {
  380. if (node.children && !node.children.length) {
  381. delete node.children
  382. }
  383. return {
  384. id: node.id,
  385. label: node.name,
  386. children: node.children
  387. }
  388. },
  389. handleClose() {
  390. this.currentSortieNo = ''
  391. this.form = {
  392. id: '',
  393. sortieNo: '',
  394. ossId: '',
  395. aircraftId: '',
  396. aircraftType: '',
  397. flightDate: '',
  398. status: '',
  399. result: '',
  400. partId: null
  401. }
  402. },
  403. resultShowDialogClose() {
  404. this.resultShowVisible = false
  405. this.diagnosisResult = {
  406. degradation: [],
  407. fault: [],
  408. score: null
  409. }
  410. this.changeScore = null
  411. },
  412. submit() {
  413. this.beginExecute(row)
  414. },
  415. selection(val) {
  416. this.tableCheckItems = val
  417. },
  418. async checkRelustList(row) {
  419. this.getFaultDiagnosisResultAPI({ diagnosisId: row.id })
  420. this.resultVisible = true
  421. },
  422. checkRelustShow(row) {
  423. const resData = JSON.parse(row.resultContent)
  424. this.changeScore = resData.score / 20
  425. this.diagnosisResult = resData
  426. this.resultShowVisible = true
  427. },
  428. async getImgUrl(ossId) {
  429. const { data } = await getListByIdsApi(ossId)
  430. const newUrl = data[0].url
  431. return newUrl
  432. },
  433. async beginExecute(row) {
  434. this.progressVisible = true
  435. let myTimer = setInterval(() => {
  436. if (this.percentage < 100) {
  437. if (this.percentage === 99) {
  438. this.percentage = 99
  439. } else {
  440. this.percentage += 1
  441. }
  442. }
  443. }, 30)
  444. try {
  445. this.form.dataId = row.id
  446. const res = await executeFaultIntell(this.form)
  447. clearInterval(myTimer)
  448. if (res?.code === 200) {
  449. this.percentage = 100
  450. this.$message({
  451. type: 'success',
  452. message: '执行成功!'
  453. })
  454. const resData = JSON.parse(res.data)
  455. this.changeScore = resData.score / 20
  456. this.diagnosisResult = resData
  457. // this.resultVisible = true
  458. this.resultShowVisible = true
  459. this.getDataImportAPI({ aircraftId: this.aircaftModelIdList })
  460. this.handleClose()
  461. }
  462. } catch (error) {
  463. clearInterval(myTimer)
  464. } finally {
  465. this.progressVisible = false
  466. this.percentage = 0
  467. }
  468. },
  469. handelQA(question) {
  470. this.loading = true
  471. this.tagLoading = true
  472. const sendInput = {
  473. question: question,
  474. userId: String(store.state.user.userInfo.user.userId)
  475. }
  476. handlerQAndA(sendInput)
  477. .then((res) => {
  478. if (res.code == 200) {
  479. this.QAData = this.handleData(res.data)
  480. this.QAVisible = true
  481. }
  482. })
  483. .catch((e) => {
  484. console.log(e)
  485. })
  486. .finally(() => {
  487. this.loading = false
  488. this.tagLoading = false
  489. })
  490. },
  491. handleData(data) {
  492. if (data.graphAnswer?.graph) {
  493. const categories = []
  494. data.graphAnswer.graph.data.forEach((node) => {
  495. const flag = categories.find((item) => {
  496. return item.name === node.category
  497. })
  498. if (!flag) {
  499. categories.push({ name: node.category })
  500. }
  501. })
  502. data.graphAnswer.graph.categories = categories
  503. }
  504. return data
  505. },
  506. QADialogClose() {
  507. this.QAData = {
  508. userId: '',
  509. graphAnswer: {}
  510. }
  511. this.QAVisible = false
  512. }
  513. }
  514. }
  515. </script>
  516. <style lang="scss" scoped>
  517. @import '../index.scss';
  518. .fault {
  519. cursor: pointer;
  520. }
  521. </style>