index.vue 29 KB

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