index.vue 31 KB

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