index.vue 28 KB

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