index.vue 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963
  1. <template>
  2. <div class="table-box">
  3. <ProTable ref="proTable" :columns="columns" row-key="id" :data="bizProcessList">
  4. <!-- 表格 header 按钮 -->
  5. <template #tableHeader="scope">
  6. <!-- #tableHeader="scope"-->
  7. <!-- <el-button type="primary" v-auth="['identification:identificationSubtaskDetails:add']" icon="CirclePlus" @click="openDialog(1, '算法业务处理新增')"> 新增 </el-button>
  8. <el-button type="primary" v-auth="['identification:identificationSubtaskDetails:import']" icon="Upload" plain @click="batchAdd"> 导入 </el-button> -->
  9. <!-- <el-button type="primary" v-auth="['identification:identificationSubtaskDetails:export']" icon="Download" plain @click="downloadFile">-->
  10. <!-- 导出-->
  11. <!-- </el-button>-->
  12. <!-- <el-button-->
  13. <!-- type="danger"-->
  14. <!-- v-auth="['identification:identificationSubtaskDetails:remove']"-->
  15. <!-- icon="Delete"-->
  16. <!-- plain-->
  17. <!-- :disabled="!scope.isSelected"-->
  18. <!-- @click="batchDelete(scope.selectedListIds)"-->
  19. <!-- >-->
  20. <!-- 批量删除-->
  21. <!-- </el-button>-->
  22. <el-button type="primary" icon="View" @click="showCompareResult"> 验证指标对比 </el-button>
  23. <!-- <el-button type="primary" icon="View" @click="showValResult(true)"> 验证结果 </el-button>-->
  24. <el-button type="primary" icon="View" @click="showValResult(false && scope.row)"> 测试结果 </el-button>
  25. <el-button type="primary" icon="View" @click="showValResult(true)"> 验证结果 </el-button>
  26. </template>
  27. <!-- 表格操作 -->
  28. <template #operation="scope">
  29. <!-- <el-button-->
  30. <!-- type="primary"-->
  31. <!-- link-->
  32. <!-- icon="View"-->
  33. <!-- v-auth="['identification:identificationSubtaskDetails:query']"-->
  34. <!-- @click="openDialog(3, '算法业务处理查看', scope.row)"-->
  35. <!-- >-->
  36. <!-- 查看详情-->
  37. <!-- </el-button>-->
  38. <el-button v-if="scope.row.name.indexOf('训练') === -1" type="primary" link icon="Refresh" @click="reRunTask(scope.row)">
  39. 执行训练
  40. </el-button>
  41. <el-button v-else type="primary" link icon="Refresh" @click="startTask(scope.row)"> 开始训练 </el-button>
  42. <el-button type="primary" link icon="finished" @click="showResult(scope.row)"> 结果图 </el-button>
  43. <el-button v-show="scope.row.name.indexOf('训练') === -1" type="primary" link icon="search" @click="showExecutedTime(scope.row)">
  44. 执行时间
  45. </el-button>
  46. <el-button type="primary" link icon="document" v-auth="['identification:identificationSubtaskDetails:query']" @click="viewLog(scope.row)">
  47. 查看日志
  48. </el-button>
  49. <el-button type="primary" link icon="Refresh" @click="exportData(scope.row)"> 导出结果 </el-button>
  50. <!-- <el-button type="primary" link icon="EditPen" v-auth="['identification:identificationSubtaskDetails:edit']" @click="openDialog(2, '算法业务处理编辑', scope.row)">-->
  51. <!-- 编辑-->
  52. <!-- </el-button>-->
  53. <!-- <el-button type="primary" link icon="Delete" v-auth="['identification:identificationSubtaskDetails:remove']" @click="deleteBizProcess(scope.row)"> 删除 </el-button>-->
  54. </template>
  55. </ProTable>
  56. <FormDialog ref="formDialogRef" />
  57. <ImportExcel ref="dialogRef" />
  58. <el-dialog v-model="executedTimeVisible" title="执行时间统计" width="70%">
  59. <el-container v-for="(item, index) in executedTimeData.data" :key="index">
  60. <el-container style="display: flex; flex-direction: column">
  61. <h4 v-if="index === 0" style="color: greenyellow">单幅图像最短运行时间</h4>
  62. <h4 v-else-if="index === 1" style="color: greenyellow">单幅图像最长运行时间</h4>
  63. <h4 v-else-if="index === 2" style="color: greenyellow">单幅图像平均运行时间</h4>
  64. <div style="display: flex; flex-direction: row">
  65. <h5>前处理用时: {{ item['preprocess'] }}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</h5>
  66. <h5>推理用时: {{ item['inference'] }}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</h5>
  67. <h5>后处理用时: {{ item['postprocess'] }}</h5>
  68. </div>
  69. </el-container>
  70. </el-container>
  71. <el-container>
  72. <div v-if="executedTimeData.isVal">
  73. <h4 style="color: greenyellow">虚警率</h4>
  74. <h5>{{ executedTimeData.falseAlarmRate }} &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</h5>
  75. <h4 style="color: greenyellow">漏检率</h4>
  76. <h5>{{ executedTimeData.omitDetectRate }}</h5>
  77. </div>
  78. </el-container>
  79. </el-dialog>
  80. <el-dialog v-model="dialogVisible" title="日志" width="70%">
  81. <div class="log" ref="logRef">
  82. <div class="p" v-for="(item, index) in logInfo" :key="index">{{ item }}</div>
  83. </div>
  84. </el-dialog>
  85. <el-dialog v-model="resultVisible" title="对比结果" width="70%">
  86. <div v-if="!resultsFlag" class="resultShow">
  87. <el-row class="headerRow">
  88. <el-col class="col" :span="spanNum" v-for="(agloName, index) in resultsData['agNameList']" :key="index">
  89. <span>{{ agloName }}</span>
  90. </el-col>
  91. </el-row>
  92. <el-row class="row">
  93. <el-col class="col" :span="spanNum" v-for="(RCurveUrl, index) in resultsData['rcureList']" :key="index">
  94. <div v-if="index === 0" :span="4" class="oneCol">{{ RCurveUrl }}</div>
  95. <ImagePreview
  96. class="img"
  97. v-else
  98. :width="100"
  99. :height="100"
  100. :src="'/api/profile' + RCurveUrl"
  101. :preview-src-list="['/api/profile' + RCurveUrl]"
  102. />
  103. </el-col>
  104. </el-row>
  105. <el-row class="row">
  106. <el-col class="col" :span="spanNum" v-for="(PCurveUrl, index) in resultsData['pcureList']" :key="index">
  107. <div v-if="index === 0" class="oneCol">{{ PCurveUrl }}</div>
  108. <ImagePreview
  109. class="img"
  110. v-else
  111. :width="100"
  112. :height="100"
  113. :src="'/api/profile' + PCurveUrl"
  114. :preview-src-list="['/api/profile' + PCurveUrl]"
  115. />
  116. </el-col>
  117. </el-row>
  118. <el-row class="row">
  119. <el-col class="col" :span="spanNum" v-for="(F1CurveUrl, index) in resultsData['f1cureList']" :key="index">
  120. <div v-if="index === 0" class="oneCol">{{ F1CurveUrl }}</div>
  121. <ImagePreview
  122. class="img"
  123. v-else
  124. :width="100"
  125. :height="100"
  126. :src="'/api/profile' + F1CurveUrl"
  127. :preview-src-list="['/api/profile' + F1CurveUrl]"
  128. />
  129. </el-col>
  130. </el-row>
  131. </div>
  132. <div v-if="resultsFlag" class="resultShow">
  133. <el-row class="headerRow">
  134. <el-col class="col" :span="spanNum" v-for="(agloName, index) in testResultsData['agNameList']" :key="index">
  135. <span>{{ agloName }}</span>
  136. </el-col>
  137. </el-row>
  138. <el-row class="row" v-for="(item, index) in testResultsData['resultList']" :key="index">
  139. <el-col class="col" :span="spanNum" v-for="(url, index1) in item" :key="index1">
  140. <!-- <span>{{ url }}</span> -->
  141. <ImagePreview class="img" :width="100" :height="100" :src="'/api/profile' + url" :preview-src-list="['/api/profile' + url]" />
  142. </el-col>
  143. </el-row>
  144. </div>
  145. </el-dialog>
  146. <el-dialog v-model="resultDialogVisible" title="执行结果">
  147. <!-- style="width: 70vw"-->
  148. <el-card :body-style="{ padding: '0px' }">
  149. <div style="padding: 14px">
  150. <span>P_curve</span>
  151. </div>
  152. <el-image :src="PATH_PREFIX + refSelectData.resultPath + '/P_curve.png'"></el-image>
  153. </el-card>
  154. <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
  155. <div style="padding: 14px">
  156. <span>R_curve</span>
  157. </div>
  158. <el-image :src="PATH_PREFIX + refSelectData.resultPath + '/R_curve.png'"></el-image>
  159. </el-card>
  160. <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
  161. <div style="padding: 14px">
  162. <span>PR_curve</span>
  163. </div>
  164. <el-image :src="PATH_PREFIX + refSelectData.resultPath + '/PR_curve.png'"></el-image>
  165. </el-card>
  166. <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
  167. <div style="padding: 14px">
  168. <span>F1_curve</span>
  169. </div>
  170. <el-image :src="PATH_PREFIX + refSelectData.resultPath + '/F1_curve.png'"></el-image>
  171. </el-card>
  172. </el-dialog>
  173. <el-dialog v-model="compareDialogVisible" title="验证指标对比">
  174. <!-- style="width: 70vw"-->
  175. <el-container style="display: flex; flex-direction: row">
  176. <el-container v-for="(item, index) in valListData" :key="index" style="display: flex; flex-direction: column">
  177. <!-- <el-button @click="console.log(item)">test</el-button>-->
  178. <span style="font-size: 18px"> {{ item.name }} </span>
  179. <el-card :body-style="{ padding: '0px' }" style="margin-top: 3px">
  180. <div style="padding: 14px">
  181. <span>P_curve</span>
  182. </div>
  183. <el-image :src="PATH_PREFIX + item.resultPath + '/P_curve.png'"></el-image>
  184. </el-card>
  185. <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
  186. <div style="padding: 14px">
  187. <span>R_curve</span>
  188. </div>
  189. <el-image :src="PATH_PREFIX + item.resultPath + '/R_curve.png'"></el-image>
  190. </el-card>
  191. <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
  192. <div style="padding: 14px">
  193. <span>PR_curve</span>
  194. </div>
  195. <el-image :src="PATH_PREFIX + item.resultPath + '/PR_curve.png'"></el-image>
  196. </el-card>
  197. <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
  198. <div style="padding: 14px">
  199. <span>F1_curve</span>
  200. </div>
  201. <el-image :src="PATH_PREFIX + item.resultPath + '/F1_curve.png'"></el-image>
  202. </el-card>
  203. </el-container>
  204. </el-container>
  205. </el-dialog>
  206. <el-dialog v-model="valDialogVisible" :title="titleMsg" style="width: 85vw; height: 85vh">
  207. <!-- style="width: 70vw"-->
  208. <el-container style="display: flex; flex-direction: row">
  209. <el-container v-for="(item, index) in imgDataList" :key="index" style="display: flex; flex-direction: column">
  210. <!-- <el-button @click="console.log(item)">test</el-button>-->
  211. <el-container v-for="(_item, _index) in item" :key="_index">
  212. <span
  213. v-if="_item.name"
  214. :style="{
  215. color: _item.color ? _item.color : '#ffffff',
  216. width: '80px',
  217. 'margin-right': '20px'
  218. }"
  219. >
  220. {{ _item.name }}
  221. </span>
  222. <el-image v-if="_item.srcUrl" :src="_item.srcUrl" style="width: 200px; height: 200px"></el-image>
  223. <span v-if="_item.imgUrl">标签</span>
  224. <ImgMaker
  225. :canvas-id="_item.name + '_' + _index"
  226. style="width: 200px; height: 200px"
  227. ref="imgMaker"
  228. v-if="_item.imgUrl"
  229. :is-pic-only="true"
  230. :src="_item.imgUrl"
  231. :width="200"
  232. :height="200"
  233. :c-width="200"
  234. :c-height="200"
  235. :class-def="typeDefs"
  236. :json-data="_item.jsonData"
  237. ></ImgMaker>
  238. <el-image v-if="_item.resUrl" :src="_item.resUrl" style="width: 200px; height: 200px"></el-image>
  239. </el-container>
  240. </el-container>
  241. </el-container>
  242. </el-dialog>
  243. </div>
  244. </template>
  245. <script setup lang="tsx" name="BizProcess">
  246. import { ref, reactive, onMounted, onUnmounted, watch, nextTick } from 'vue'
  247. import { useHandleData } from '@/hooks/useHandleData'
  248. import { useDownload } from '@/hooks/useDownload'
  249. import { ElMessageBox, ElMessage } from 'element-plus'
  250. import ProTable from '@/components/ProTable/index.vue'
  251. import ImportExcel from '@/components/ImportExcel/index.vue'
  252. import FormDialog from '@/components/FormDialog/index.vue'
  253. import { ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
  254. import ImgMaker from '../../demo/components/img-maker'
  255. import {
  256. listBizProcessApi,
  257. delBizProcessApi,
  258. addBizProcessApi,
  259. updateBizProcessApi,
  260. exportBizProcessApi,
  261. getBizProcessApi,
  262. getTrainResultApi,
  263. getVerifyResultApi,
  264. getTestResultApi,
  265. getImgList
  266. } from '@/api/modules/task/bizProcessNew'
  267. // getTrainResultApi,getVerifyResultApi,getTestResultApi
  268. import { getSubtaskApi } from '@/api/modules/task/subtask'
  269. import { getDictsApi, listDataApi as listDictDataApi } from '@/api/modules/system/dictData'
  270. import { useRoute } from 'vue-router'
  271. import ImagePreview from '@/components/ImagePreview/index.vue'
  272. import http from '@/api'
  273. const route = useRoute()
  274. const PATH_PREFIX = 'api/profile/task'
  275. let resultDialogVisible = ref(false)
  276. let refSelectData = ref(reactive({}))
  277. const reRunTask = row => {
  278. http.post<any>('/identification/identificationSubtaskDetails/execute', { taskId: row.id }, { loading: false }).then(res => {
  279. if (res.code !== 200) {
  280. ElMessage.error(res.msg)
  281. } else {
  282. ElMessage.success('启动成功')
  283. }
  284. })
  285. }
  286. const startTask = row => {
  287. http.post<any>('/identification/identificationSubtaskDetails/startTask', { taskId: row.id }, { loading: false }).then(res => {
  288. if (res.code !== 200) {
  289. ElMessage.error(res.msg)
  290. } else {
  291. ElMessage.success('启动成功')
  292. }
  293. })
  294. }
  295. const typeDefs = ref(reactive([]))
  296. const exportData = row => {
  297. http.get<any>('/identification/identificationSubtaskDetails/resultZip', { taskId: row.id }, { loading: false }).then(res => {
  298. console.log(res)
  299. if (res.code === 200) {
  300. window.open(res.msg, '_blank')
  301. }
  302. })
  303. }
  304. let executedTimeVisible = ref(false)
  305. let executedTimeData = ref({})
  306. const showExecutedTime = row => {
  307. try {
  308. console.log('row', row)
  309. executedTimeData.value.isVal = row.name.includes('验证')
  310. http.get('/profile/task' + row.resultPath + '/time.json').then(res => {
  311. executedTimeData.value.data = []
  312. executedTimeData.value.data.push(res[0].min)
  313. executedTimeData.value.data.push(res[0].max)
  314. executedTimeData.value.data.push(res[0].average)
  315. if (executedTimeData.value.isVal) {
  316. setTimeout(() => {
  317. http.get('/profile/task' + row.resultPath + '/matrics.json').then(res => {
  318. executedTimeData.value.omitDetectRate = res[1].omit_detect_rate
  319. executedTimeData.value.falseAlarmRate = res[0].false_alarm_rate
  320. })
  321. }, 550)
  322. }
  323. })
  324. } catch (e) {
  325. ElMessage.error('解析JSON错误,请检查任务是否已完成')
  326. return
  327. }
  328. executedTimeVisible.value = true
  329. }
  330. let imgDataList = ref(reactive([]))
  331. let titleMsg = ref('')
  332. let valDialogVisible = ref(false)
  333. const showValResult = async isVal => {
  334. let hasInit = false
  335. titleMsg.value = isVal ? '验证结果对比' : '测试结果对比'
  336. imgDataList.value = reactive([])
  337. for (let i = 0; i < listData.value.length; i++) {
  338. if (listData.value[i].name.indexOf(isVal ? '验证' : '测试') !== -1) {
  339. console.log(listData.value[i])
  340. if (!hasInit) {
  341. hasInit = true
  342. let res = await getImgList({
  343. taskId: listData.value[i].preprocessPath.substring(1).split('/')[0],
  344. subPath: 'images'
  345. })
  346. console.log('res data', res)
  347. imgDataList.value.push([])
  348. for (let j = 0; j < res.data.length; j++) {
  349. let jList = res.data[j].split('.')
  350. jList[jList.length - 1] = 'txt'
  351. let obj = {
  352. name: res.data[j],
  353. srcUrl: 'api/profile/task' + listData.value[i].preprocessPath + (isVal ? '/images/' : '/') + res.data[j]
  354. // imgUrl: 'api/profile/task' + listData.value[i].preprocessPath + '/images/' + res.data[j],
  355. // labelUrl: 'api/profile/task' + listData.value[i].preprocessPath + '/labels/' + jList.join('.')
  356. }
  357. imgDataList.value[imgDataList.value.length - 1].push(obj)
  358. if (isVal) {
  359. obj.imgUrl = 'api/profile/task' + listData.value[i].preprocessPath + '/images/' + res.data[j]
  360. obj.labelUrl = '/profile/task' + listData.value[i].preprocessPath + '/labels/' + jList.join('.')
  361. console.log('url is', obj.labelUrl)
  362. setDetail(obj)
  363. }
  364. }
  365. }
  366. let res = await getImgList({
  367. taskId: listData.value[i].preprocessPath.substring(1).split('/')[0],
  368. subPath: 'images'
  369. })
  370. console.log(res.data)
  371. if (isVal) {
  372. await loadVerifyResult(listData.value[i].name, '/profile/task' + listData.value[i].preprocessPath + '/result/verify_result.txt')
  373. }
  374. console.log('load result', verifyResult.value)
  375. imgDataList.value.push([])
  376. for (let j = 0; j < res.data.length; j++) {
  377. console.log('temp log', verifyResult.value)
  378. let jList = res.data[j].split('.')
  379. jList[jList - 1] = 'txt'
  380. imgDataList.value[imgDataList.value.length - 1].push({
  381. resUrl: 'api/profile/task' + listData.value[i].resultPath + '/' + res.data[j],
  382. name: listData.value[i].name,
  383. picName: res.data[j],
  384. color: verifyResult.value[res.data[j]] ? '#00ff00' : '#ff0000'
  385. })
  386. }
  387. }
  388. }
  389. console.log('datalist', imgDataList)
  390. valDialogVisible.value = true
  391. }
  392. const verifyResult = ref({})
  393. const loadVerifyResult = async (name, filepath) => {
  394. // console.log('filepath', filepath)
  395. try {
  396. verifyResult.value = {}
  397. let res = await http.get(filepath)
  398. console.log(res)
  399. // verifyResult.value[name] = {}
  400. let arr = res.replace('\r', '').split('\n')
  401. arr.forEach(str => {
  402. let vals = str.split(' ')
  403. verifyResult.value[vals[0]] = vals[1] === '1'
  404. })
  405. console.log('verifyResult', verifyResult.value)
  406. } catch (e) {
  407. verifyResult.value = {}
  408. }
  409. }
  410. const setDetail = obj => {
  411. http.get<any>(obj.labelUrl).then(res => {
  412. obj.jsonData = []
  413. // console.log('result', res)
  414. let arr = res.replace('\r', '').split('\n')
  415. // console.log(arr)
  416. for (let i = 0; i < arr.length; i++) {
  417. let subArr = arr[i].split(' ')
  418. // console.log(subArr)
  419. let cssVal = '#000000'
  420. let label = '-1'
  421. for (let j = 0; j < typeDefs.value.length; j++) {
  422. if (typeDefs.value[j].label === subArr[0]) {
  423. cssVal = typeDefs.value[j].color
  424. label = typeDefs.value[j].label
  425. break
  426. }
  427. }
  428. const ww = 200
  429. const hh = 200
  430. obj.jsonData.push({
  431. subArr: subArr,
  432. pathString:
  433. 'M ' +
  434. subArr[1] * ww +
  435. ' ' +
  436. subArr[2] * hh +
  437. ' L ' +
  438. subArr[3] * ww +
  439. ' ' +
  440. subArr[4] * hh +
  441. ' L ' +
  442. subArr[5] * ww +
  443. ' ' +
  444. subArr[6] * hh +
  445. ' L ' +
  446. subArr[7] * ww +
  447. ' ' +
  448. subArr[8] * hh +
  449. ' z',
  450. // left: 0,
  451. // top: 0,
  452. fill: '',
  453. stroke: cssVal,
  454. strokeWidth: 5,
  455. label: label
  456. })
  457. }
  458. })
  459. }
  460. let compareDialogVisible = ref(false)
  461. const valListData = ref([])
  462. let listData = ref(reactive([]))
  463. const showCompareResult = () => {
  464. console.log(listData.value)
  465. valListData.value = listData.value.filter(item => {
  466. return item.name.includes('验证')
  467. })
  468. compareDialogVisible.value = true
  469. console.log('vallist', valListData.value)
  470. }
  471. const showResult = row => {
  472. refSelectData.value = reactive(row)
  473. resultDialogVisible.value = true
  474. console.log(row)
  475. }
  476. const subTaskId = route.query.id as string
  477. // ProTable 实例
  478. const proTable = ref<ProTableInstance>()
  479. const dialogVisible = ref(false)
  480. const resultVisible = ref(false)
  481. let resultsData = ref({})
  482. let testResultsData = ref({})
  483. let resultsFlag = ref(false)
  484. let spanNum = ref<number>(4)
  485. // let logList = ref()
  486. const logRef = ref<HTMLElement | null>(null) // 显式声明 logRef 的类型;
  487. let logInfo = ref([] as any[])
  488. let taskType = ref()
  489. let taskStatus = ref()
  490. let bizProcessList = ref()
  491. // 每隔10秒请求一下列表
  492. const timer = ref() // 定时器
  493. const refreshList = () => {
  494. setTimeout(() => {
  495. listBizProcessApi({
  496. pageNum: 1,
  497. pageSize: 100,
  498. subtaskId: subTaskId
  499. }).then(res => {
  500. bizProcessList.value = res.data['list'].sort((a, b) => b.index - a.index)
  501. listData.value = reactive(res.data['list'].sort((a, b) => b.index - a.index))
  502. })
  503. }, 0)
  504. }
  505. onMounted(() => {
  506. listDictDataApi({
  507. pageNum: 1,
  508. pageSize: 10,
  509. dictType: 'class_definition'
  510. }).then(res => {
  511. // console.log(res)
  512. for (let i = 0; i < res.data.list.length; i++) {
  513. typeDefs.value.push({
  514. name: res.data.list[i].dictLabel,
  515. color: res.data.list[i].cssClass,
  516. label: res.data.list[i].dictValue
  517. })
  518. }
  519. })
  520. getSubtaskApi(subTaskId).then(res => {
  521. taskType.value = res.data.type
  522. taskStatus.value = res.data.status
  523. })
  524. refreshList()
  525. timer.value = setInterval(() => {
  526. refreshList()
  527. }, 10000)
  528. // 组件挂载后,logRef 将指向实际的 DOM 元素
  529. if (logRef.value) {
  530. // 操作 logRef 对应的 DOM 元素
  531. logRef.value.scrollTop = logRef.value.scrollHeight
  532. }
  533. })
  534. // 删除算法业务处理信息
  535. const deleteBizProcess = async (params: any) => {
  536. await useHandleData(delBizProcessApi, params.id, '删除【' + params.id + '】算法业务处理')
  537. proTable.value?.getTableList()
  538. }
  539. // 批量删除算法业务处理信息
  540. const batchDelete = async (ids: string[]) => {
  541. await useHandleData(delBizProcessApi, ids, '删除所选算法业务处理信息')
  542. proTable.value?.clearSelection()
  543. proTable.value?.getTableList()
  544. }
  545. // 导出算法业务处理列表
  546. const downloadFile = async () => {
  547. ElMessageBox.confirm('确认导出算法业务处理数据?', '温馨提示', { type: 'warning' }).then(() =>
  548. useDownload(exportBizProcessApi, '算法业务处理列表', proTable.value?.searchParam)
  549. )
  550. }
  551. // 批量添加算法业务处理
  552. const dialogRef = ref<InstanceType<typeof ImportExcel> | null>(null)
  553. // const batchAdd = () => {
  554. // const params = {
  555. // title: '算法业务处理',
  556. // tempApi: importTemplateApi,
  557. // importApi: importBizProcessDataApi,
  558. // getTableList: proTable.value?.getTableList
  559. // }
  560. // dialogRef.value?.acceptParams(params)
  561. // }
  562. // 对比结果
  563. const contrastResults = () => {
  564. const statusFlag = bizProcessList.value.every(item => {
  565. return item.status == '2'
  566. })
  567. if (statusFlag) {
  568. switch (taskType.value) {
  569. case '1':
  570. getTrainResultApi(subTaskId).then(res => {
  571. resultsFlag.value = false
  572. handleResultData(res.data)
  573. resultVisible.value = true
  574. })
  575. break
  576. case '2':
  577. getVerifyResultApi(subTaskId).then(res => {
  578. resultsFlag.value = false
  579. handleResultData(res.data)
  580. resultVisible.value = true
  581. })
  582. break
  583. case '3':
  584. getTestResultApi(subTaskId).then(res => {
  585. console.log('333', res)
  586. resultsFlag.value = true
  587. testResultsData.value = res.data as any
  588. const num = testResultsData.value['agNameList'].length
  589. spanNum.value = 24 / num <= 4 ? 4 : Math.floor(24 / num)
  590. resultVisible.value = true
  591. })
  592. break
  593. default:
  594. break
  595. }
  596. } else {
  597. ElMessage.warning(`所有算法状态为‘已完成’,才可以对比`)
  598. }
  599. }
  600. const handleResultData = data => {
  601. resultsData.value = data
  602. resultsData.value['agNameList'].unshift('')
  603. const num = resultsData.value['agNameList'].length + 1
  604. spanNum.value = 24 / num <= 4 ? 4 : Math.floor(24 / num)
  605. resultsData.value['rcureList'].unshift('R_curve')
  606. resultsData.value['pcureList'].unshift('P_curve')
  607. resultsData.value['f1cureList'].unshift('F1_curve')
  608. }
  609. // 查看日志
  610. let timer2 = ref()
  611. const viewLog = row => {
  612. // if (row.status === '0') {
  613. // ElMessage.warning('算法状态为待处理,暂无日志')
  614. // return
  615. // }
  616. const url = `/api/profile/task` + row.resultPath + '/log/log.log'
  617. logShow(url)
  618. dialogVisible.value = true
  619. timer2.value = setInterval(() => {
  620. if (dialogVisible.value) {
  621. logShow(url)
  622. }
  623. }, 5000)
  624. }
  625. // 日志读取
  626. const logShow = (url: any) => {
  627. fetchLogFile(url)
  628. .then(text => {
  629. logInfo.value = []
  630. logInfo.value = text.split('\n')
  631. nextTick(() => {
  632. const logContainer = logRef.value
  633. if (logContainer) {
  634. ;(logContainer as HTMLElement).scrollTop = (logContainer as HTMLElement).scrollHeight
  635. }
  636. })
  637. })
  638. .catch(error => {
  639. console.error('Failed to fetch the log file:', error)
  640. ElMessage.error('日志读取错误')
  641. })
  642. }
  643. const fetchLogFile = async url => {
  644. try {
  645. const response = await fetch(url, { method: 'GET' })
  646. if (!response.ok) {
  647. throw new Error(`HTTP error! status: ${response.status}`)
  648. }
  649. return await response.text()
  650. } catch (error) {
  651. throw error
  652. }
  653. }
  654. const formDialogRef = ref<InstanceType<typeof FormDialog> | null>(null)
  655. // 打开弹框的功能
  656. const openDialog = async (type: number, title: string, row?: any) => {
  657. let res = { data: {} }
  658. if (row?.id) {
  659. res = await getBizProcessApi(row?.id || null)
  660. }
  661. // 重置表单
  662. setItemsOptions()
  663. const params = {
  664. title,
  665. width: 580,
  666. isEdit: type !== 3,
  667. itemsOptions: itemsOptions,
  668. model: type == 1 ? {} : res.data,
  669. api: type == 1 ? addBizProcessApi : updateBizProcessApi,
  670. getTableList: proTable.value?.getTableList
  671. }
  672. formDialogRef.value?.openDialog(params)
  673. }
  674. onUnmounted(() => {
  675. clearInterval(timer.value)
  676. timer.value = null
  677. })
  678. watch(
  679. () => dialogVisible.value,
  680. val => {
  681. if (!val) {
  682. clearInterval(timer2.value)
  683. timer2.value = null
  684. }
  685. }
  686. )
  687. // 表格配置项
  688. const columns = reactive<ColumnProps<any>[]>([
  689. { type: 'selection', fixed: 'left', width: 70 },
  690. // { prop: 'id', label: '主键ID' },
  691. // {
  692. // prop: 'subTaskId',
  693. // label: '子任务id',
  694. // search: {
  695. // el: 'input'
  696. // },
  697. // width: 120
  698. // },
  699. {
  700. prop: 'name',
  701. label: '任务名称',
  702. search: {
  703. el: 'input'
  704. },
  705. width: 120
  706. },
  707. // {
  708. // prop: 'type',
  709. // label: '任务类型',
  710. // search: {
  711. // el: 'input'
  712. // },
  713. // width: 120
  714. // },
  715. {
  716. prop: 'status',
  717. label: '任务状态',
  718. tag: true,
  719. enum: () => getDictsApi('biz_task_status'),
  720. search: {
  721. el: 'tree-select'
  722. },
  723. width: 100,
  724. fieldNames: { label: 'dictLabel', value: 'dictValue' }
  725. },
  726. // {
  727. // prop: 'algorithmId',
  728. // label: '算法',
  729. // width: 120
  730. // },
  731. // {
  732. // prop: 'modelId',
  733. // label: '模型',
  734. // width: 120
  735. // },
  736. {
  737. prop: 'parameters',
  738. label: '调用算法时所用的参数'
  739. },
  740. {
  741. prop: 'preprocessPath',
  742. label: '预处理数据路径'
  743. },
  744. {
  745. prop: 'resultPath',
  746. label: '结果数据路径'
  747. },
  748. {
  749. prop: 'index',
  750. label: '序号',
  751. width: 120
  752. },
  753. {
  754. prop: 'startTime',
  755. label: '开始时间'
  756. },
  757. {
  758. prop: 'endTime',
  759. label: '结束时间'
  760. },
  761. {
  762. prop: 'costSecond',
  763. label: '耗时(ms)'
  764. },
  765. {
  766. prop: 'remarks',
  767. label: '日志'
  768. },
  769. { prop: 'operation', label: '操作', width: 230, fixed: 'right' }
  770. ])
  771. // 结果表格配置项
  772. // const resultColumns = reactive<ColumnProps<any>[]>([])
  773. // 表单配置项
  774. let itemsOptions: ProForm.ItemsOptions[] = []
  775. const setItemsOptions = () => {
  776. itemsOptions = [
  777. {
  778. label: '子任务id',
  779. prop: 'subTaskId',
  780. compOptions: {
  781. placeholder: '请输入子任务id'
  782. }
  783. },
  784. {
  785. label: '任务名称',
  786. prop: 'name',
  787. compOptions: {
  788. placeholder: '请输入任务名称'
  789. }
  790. },
  791. {
  792. label: '任务类型',
  793. prop: 'type',
  794. compOptions: {
  795. placeholder: '请输入任务类型'
  796. }
  797. },
  798. {
  799. label: '任务状态',
  800. prop: 'status',
  801. compOptions: {
  802. elTagName: 'select',
  803. labelKey: 'dictLabel',
  804. valueKey: 'dictValue',
  805. enum: () => getDictsApi('biz_task_status'),
  806. placeholder: '请选择任务状态'
  807. }
  808. },
  809. {
  810. label: '算法',
  811. prop: 'algorithmId',
  812. compOptions: {
  813. placeholder: '请输入算法'
  814. }
  815. },
  816. {
  817. label: '模型',
  818. prop: 'modelId',
  819. compOptions: {
  820. placeholder: '请输入模型'
  821. }
  822. },
  823. {
  824. label: '调用算法时所用的参数',
  825. prop: 'parameters',
  826. compOptions: {
  827. type: 'textarea',
  828. clearable: true,
  829. placeholder: '请输入内容'
  830. }
  831. },
  832. {
  833. label: '预处理数据路径',
  834. prop: 'preprocessPath',
  835. compOptions: {
  836. placeholder: '请输入预处理数据路径'
  837. }
  838. },
  839. {
  840. label: '结果数据路径',
  841. prop: 'resultPath',
  842. compOptions: {
  843. placeholder: '请输入结果数据路径'
  844. }
  845. },
  846. {
  847. label: '序号',
  848. prop: 'index',
  849. compOptions: {
  850. placeholder: '请输入序号'
  851. }
  852. },
  853. {
  854. label: '开始时间',
  855. prop: 'startTime',
  856. compOptions: {
  857. elTagName: 'date-picker',
  858. type: 'date',
  859. placeholder: '请选择开始时间'
  860. }
  861. },
  862. {
  863. label: '结束时间',
  864. prop: 'endTime',
  865. compOptions: {
  866. elTagName: 'date-picker',
  867. type: 'date',
  868. placeholder: '请选择结束时间'
  869. }
  870. },
  871. {
  872. label: '耗时',
  873. prop: 'costSecond',
  874. compOptions: {
  875. placeholder: '请输入耗时'
  876. }
  877. },
  878. {
  879. label: '日志',
  880. prop: 'log',
  881. compOptions: {
  882. type: 'textarea',
  883. clearable: true,
  884. placeholder: '请输入内容'
  885. }
  886. }
  887. ]
  888. }
  889. </script>
  890. <style scoped lang="scss">
  891. .log {
  892. width: 90%;
  893. height: 60vh; /* 根据需要调整 */
  894. padding: 10px;
  895. // padding-bottom: 80px;
  896. margin-left: 50px;
  897. overflow-y: auto;
  898. font-family: 'Courier New', monospace;
  899. color: #4aff84;
  900. background-color: #1e1e1e;
  901. }
  902. .p {
  903. padding-left: 10px;
  904. margin-bottom: 5px;
  905. border-left: 3px solid #4aff84;
  906. }
  907. .resultShow {
  908. width: 100%;
  909. height: 60vh;
  910. overflow: hidden;
  911. overflow: scroll scroll;
  912. .headerRow {
  913. height: 50px;
  914. font-size: 1.2rem;
  915. line-height: 50px;
  916. text-align: center;
  917. }
  918. .row {
  919. .col {
  920. text-align: center;
  921. .oneCol {
  922. margin-top: 45px;
  923. font-size: 1.2rem;
  924. }
  925. .img {
  926. margin: 10px 0;
  927. }
  928. }
  929. }
  930. }
  931. </style>