|
@@ -0,0 +1,908 @@
|
|
|
+<template>
|
|
|
+ <div class="table-box">
|
|
|
+ <ProTable ref="proTable" :columns="columns" row-key="id" :data="amplifyList">
|
|
|
+ <!-- 表格 header 按钮 -->
|
|
|
+ <template #tableHeader="scope">
|
|
|
+ <el-button type="primary" v-auth="['identification:identificationSubtaskDetails:export']" icon="Download" plain @click="downloadFile">
|
|
|
+ 导出
|
|
|
+ </el-button>
|
|
|
+ <el-button
|
|
|
+ type="danger"
|
|
|
+ v-auth="['identification:identificationSubtaskDetails:remove']"
|
|
|
+ icon="Delete"
|
|
|
+ plain
|
|
|
+ :disabled="!scope.isSelected"
|
|
|
+ @click="batchDelete(scope.selectedListIds)"
|
|
|
+ >
|
|
|
+ 批量删除
|
|
|
+ </el-button>
|
|
|
+ <!-- <el-button type="primary" icon="View" @click="showCompareResult"> 验证指标对比</el-button>-->
|
|
|
+ <!-- <el-button type="primary" icon="View" @click="showValResult(true)"> 验证结果</el-button>-->
|
|
|
+ <!-- <el-button type="primary" icon="View" @click="showValResult(false)"> 测试结果</el-button>-->
|
|
|
+ <!-- <el-button type="primary" icon="View" @click="showValResult(true)"> 验证结果</el-button>-->
|
|
|
+ </template>
|
|
|
+ <!-- 表格操作 -->
|
|
|
+ <template #operation="scope">
|
|
|
+ <!-- <el-button-->
|
|
|
+ <!-- type="primary"-->
|
|
|
+ <!-- link-->
|
|
|
+ <!-- icon="View"-->
|
|
|
+ <!-- @click="openDialog(3, '扩增算法业务处理查看', scope.row)"-->
|
|
|
+ <!-- >-->
|
|
|
+ <!-- 查看详情-->
|
|
|
+ <!-- </el-button>-->
|
|
|
+
|
|
|
+ <!-- <el-button type="primary" link icon="finished" @click="showResult(scope.row)"> 执行结果</el-button>-->
|
|
|
+
|
|
|
+ <el-button type="primary" link icon="Refresh" @click="startTask(scope.row)"> 开始训练</el-button>
|
|
|
+
|
|
|
+ <!-- <el-button type="primary" link icon="Refresh" @click="exportData(scope.row)"> 导出结果</el-button>-->
|
|
|
+
|
|
|
+ <el-button type="primary" link icon="document" @click="viewLog(scope.row)"> 查看日志 </el-button>
|
|
|
+ </template>
|
|
|
+ </ProTable>
|
|
|
+ <FormDialog ref="formDialogRef" />
|
|
|
+ <ImportExcel ref="dialogRef" />
|
|
|
+
|
|
|
+ <el-dialog v-model="executedTimeVisible" title="执行时间统计" width="70%">
|
|
|
+ <el-container v-for="(item, index) in executedTimeData" :key="index">
|
|
|
+ <el-container style="display: flex; flex-direction: column">
|
|
|
+ <h4 v-if="index === 0">单幅图像最短运行时间</h4>
|
|
|
+ <h4 v-else-if="index === 1">单幅图像最短运行时间</h4>
|
|
|
+ <h4 v-else-if="index === 2">单幅图像最短运行时间</h4>
|
|
|
+ <div style="display: flex; flex-direction: row">
|
|
|
+ <h5>前处理用时: {{ item['preprocess'] }} </h5>
|
|
|
+ <h5>推理用时: {{ item['inference'] }} </h5>
|
|
|
+ <h5>后处理用时: {{ item['postprocess'] }}</h5>
|
|
|
+ </div>
|
|
|
+ </el-container>
|
|
|
+ </el-container>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog v-model="dialogVisible" title="日志" width="70%">
|
|
|
+ <div class="log" ref="logRef">
|
|
|
+ <div class="p" v-for="(item, index) in logInfo" :key="index">{{ item }}</div>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+ <el-dialog v-model="resultVisible" title="对比结果" width="70%">
|
|
|
+ <div v-if="!resultsFlag" class="resultShow">
|
|
|
+ <el-row class="headerRow">
|
|
|
+ <el-col class="col" :span="spanNum" v-for="(agloName, index) in resultsData['agNameList']" :key="index">
|
|
|
+ <span>{{ agloName }}</span>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row class="row">
|
|
|
+ <el-col class="col" :span="spanNum" v-for="(RCurveUrl, index) in resultsData['rcureList']" :key="index">
|
|
|
+ <div v-if="index === 0" :span="4" class="oneCol">{{ RCurveUrl }}</div>
|
|
|
+ <ImagePreview
|
|
|
+ class="img"
|
|
|
+ v-else
|
|
|
+ :width="100"
|
|
|
+ :height="100"
|
|
|
+ :src="'/api/profile' + RCurveUrl"
|
|
|
+ :preview-src-list="['/api/profile' + RCurveUrl]"
|
|
|
+ />
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row class="row">
|
|
|
+ <el-col class="col" :span="spanNum" v-for="(PCurveUrl, index) in resultsData['pcureList']" :key="index">
|
|
|
+ <div v-if="index === 0" class="oneCol">{{ PCurveUrl }}</div>
|
|
|
+ <ImagePreview
|
|
|
+ class="img"
|
|
|
+ v-else
|
|
|
+ :width="100"
|
|
|
+ :height="100"
|
|
|
+ :src="'/api/profile' + PCurveUrl"
|
|
|
+ :preview-src-list="['/api/profile' + PCurveUrl]"
|
|
|
+ />
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row class="row">
|
|
|
+ <el-col class="col" :span="spanNum" v-for="(F1CurveUrl, index) in resultsData['f1cureList']" :key="index">
|
|
|
+ <div v-if="index === 0" class="oneCol">{{ F1CurveUrl }}</div>
|
|
|
+ <ImagePreview
|
|
|
+ class="img"
|
|
|
+ v-else
|
|
|
+ :width="100"
|
|
|
+ :height="100"
|
|
|
+ :src="'/api/profile' + F1CurveUrl"
|
|
|
+ :preview-src-list="['/api/profile' + F1CurveUrl]"
|
|
|
+ />
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ <div v-if="resultsFlag" class="resultShow">
|
|
|
+ <el-row class="headerRow">
|
|
|
+ <el-col class="col" :span="spanNum" v-for="(agloName, index) in testResultsData['agNameList']" :key="index">
|
|
|
+ <span>{{ agloName }}</span>
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ <el-row class="row" v-for="(item, index) in testResultsData['resultList']" :key="index">
|
|
|
+ <el-col class="col" :span="spanNum" v-for="(url, index1) in item" :key="index1">
|
|
|
+ <!-- <span>{{ url }}</span> -->
|
|
|
+ <ImagePreview class="img" :width="100" :height="100" :src="'/api/profile' + url" :preview-src-list="['/api/profile' + url]" />
|
|
|
+ </el-col>
|
|
|
+ </el-row>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog v-model="resultDialogVisible" title="执行结果">
|
|
|
+ <!-- style="width: 70vw"-->
|
|
|
+ <el-card :body-style="{ padding: '0px' }">
|
|
|
+ <div style="padding: 14px">
|
|
|
+ <span>P_curve</span>
|
|
|
+ </div>
|
|
|
+ <el-image :src="PATH_PREFIX + refSelectData.outputImagePath + '/P_curve.png'"></el-image>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
|
|
|
+ <div style="padding: 14px">
|
|
|
+ <span>R_curve</span>
|
|
|
+ </div>
|
|
|
+ <el-image :src="PATH_PREFIX + refSelectData.outputImagePath + '/R_curve.png'"></el-image>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
|
|
|
+ <div style="padding: 14px">
|
|
|
+ <span>PR_curve</span>
|
|
|
+ </div>
|
|
|
+ <el-image :src="PATH_PREFIX + refSelectData.outputImagePath + '/PR_curve.png'"></el-image>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
|
|
|
+ <div style="padding: 14px">
|
|
|
+ <span>F1_curve</span>
|
|
|
+ </div>
|
|
|
+ <el-image :src="PATH_PREFIX + refSelectData.outputImagePath + '/F1_curve.png'"></el-image>
|
|
|
+ </el-card>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog v-model="compareDialogVisible" title="验证指标对比">
|
|
|
+ <!-- style="width: 70vw"-->
|
|
|
+ <el-container style="display: flex; flex-direction: row">
|
|
|
+ <el-container v-for="(item, index) in valListData" :key="index" style="display: flex; flex-direction: column">
|
|
|
+ <!-- <el-button @click="console.log(item)">test</el-button>-->
|
|
|
+ <span style="font-size: 18px"> {{ item.name }} </span>
|
|
|
+ <el-card :body-style="{ padding: '0px' }" style="margin-top: 3px">
|
|
|
+ <div style="padding: 14px">
|
|
|
+ <span>P_curve</span>
|
|
|
+ </div>
|
|
|
+ <el-image :src="PATH_PREFIX + item.outputImagePath + '/P_curve.png'"></el-image>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
|
|
|
+ <div style="padding: 14px">
|
|
|
+ <span>R_curve</span>
|
|
|
+ </div>
|
|
|
+ <el-image :src="PATH_PREFIX + item.outputImagePath + '/R_curve.png'"></el-image>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
|
|
|
+ <div style="padding: 14px">
|
|
|
+ <span>PR_curve</span>
|
|
|
+ </div>
|
|
|
+ <el-image :src="PATH_PREFIX + item.outputImagePath + '/PR_curve.png'"></el-image>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
|
|
|
+ <div style="padding: 14px">
|
|
|
+ <span>F1_curve</span>
|
|
|
+ </div>
|
|
|
+ <el-image :src="PATH_PREFIX + item.outputImagePath + '/F1_curve.png'"></el-image>
|
|
|
+ </el-card>
|
|
|
+ </el-container>
|
|
|
+ </el-container>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <el-dialog v-model="valDialogVisible" :title="titleMsg" style="width: 85vw; height: 85vh">
|
|
|
+ <!-- style="width: 70vw"-->
|
|
|
+ <el-container style="display: flex; flex-direction: row">
|
|
|
+ <el-container v-for="(item, index) in imgDataList" :key="index" style="display: flex; flex-direction: column">
|
|
|
+ <!-- <el-button @click="console.log(item)">test</el-button>-->
|
|
|
+
|
|
|
+ <el-container v-for="(_item, _index) in item" :key="_index">
|
|
|
+ <span
|
|
|
+ v-if="_item.name"
|
|
|
+ :style="{
|
|
|
+ color: _item.color ? _item.color : '#ffffff',
|
|
|
+ width: '80px',
|
|
|
+ 'margin-right': '20px'
|
|
|
+ }"
|
|
|
+ >
|
|
|
+ {{ _item.name }}
|
|
|
+ </span>
|
|
|
+ <el-image v-if="_item.srcUrl" :src="_item.srcUrl" style="width: 200px; height: 200px"></el-image>
|
|
|
+ <span v-if="_item.imgUrl">标签</span>
|
|
|
+ <ImgMaker
|
|
|
+ :canvas-id="_item.name + '_' + _index"
|
|
|
+ style="width: 200px; height: 200px"
|
|
|
+ ref="imgMaker"
|
|
|
+ v-if="_item.imgUrl"
|
|
|
+ :is-pic-only="true"
|
|
|
+ :src="_item.imgUrl"
|
|
|
+ :width="200"
|
|
|
+ :height="200"
|
|
|
+ :c-width="200"
|
|
|
+ :c-height="200"
|
|
|
+ :class-def="typeDefs"
|
|
|
+ :json-data="_item.jsonData"
|
|
|
+ ></ImgMaker>
|
|
|
+ <el-image v-if="_item.resUrl" :src="_item.resUrl" style="width: 200px; height: 200px"></el-image>
|
|
|
+ </el-container>
|
|
|
+ </el-container>
|
|
|
+ </el-container>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup lang="tsx" name="BizProcess">
|
|
|
+import { ref, reactive, onMounted, onUnmounted, watch, nextTick } from 'vue'
|
|
|
+import { useHandleData } from '@/hooks/useHandleData'
|
|
|
+import { useDownload } from '@/hooks/useDownload'
|
|
|
+import { ElMessageBox, ElMessage } from 'element-plus'
|
|
|
+import ProTable from '@/components/ProTable/index.vue'
|
|
|
+import ImportExcel from '@/components/ImportExcel/index.vue'
|
|
|
+import FormDialog from '@/components/FormDialog/index.vue'
|
|
|
+import { ProTableInstance, ColumnProps } from '@/components/ProTable/interface'
|
|
|
+import ImgMaker from '../../demo/components/img-maker'
|
|
|
+import {
|
|
|
+ delBizProcessApi,
|
|
|
+ exportBizProcessApi,
|
|
|
+ getBizProcessApi,
|
|
|
+ getTrainResultApi,
|
|
|
+ getVerifyResultApi,
|
|
|
+ getTestResultApi,
|
|
|
+ getImgList
|
|
|
+} from '@/api/modules/task/bizProcessNew'
|
|
|
+// getTrainResultApi,getVerifyResultApi,getTestResultApi
|
|
|
+import { getSubtaskApi } from '@/api/modules/task/subtask'
|
|
|
+import { getDictsApi, listDataApi as listDictDataApi } from '@/api/modules/system/dictData'
|
|
|
+import { useRoute } from 'vue-router'
|
|
|
+import ImagePreview from '@/components/ImagePreview/index.vue'
|
|
|
+import http from '@/api'
|
|
|
+import { addAmplifyApi, getAmplifyApi, listAmplifyApi, updateAmplifyApi } from '@/api/modules/task/bizAmplify'
|
|
|
+
|
|
|
+const route = useRoute()
|
|
|
+const PATH_PREFIX = 'api/profile/task'
|
|
|
+let resultDialogVisible = ref(false)
|
|
|
+let refSelectData = ref(reactive({}))
|
|
|
+
|
|
|
+const startTask = row => {
|
|
|
+ http.post<any>('/demo/data/amplifyForData?id=' + row.id, {}, { loading: false }).then(res => {
|
|
|
+ if (res.code !== 200) {
|
|
|
+ ElMessage.error(res.msg)
|
|
|
+ } else {
|
|
|
+ ElMessage.success('启动成功')
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+const typeDefs = ref(reactive([]))
|
|
|
+
|
|
|
+const exportData = row => {
|
|
|
+ http.get<any>('/identification/identificationSubtaskDetails/resultZip', { taskId: row.id }, { loading: false }).then(res => {
|
|
|
+ console.log(res)
|
|
|
+ if (res.code === 200) {
|
|
|
+ window.open(res.msg, '_blank')
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+let executedTimeVisible = ref(false)
|
|
|
+let executedTimeData = ref([])
|
|
|
+const showExecutedTime = row => {
|
|
|
+ try {
|
|
|
+ executedTimeData.value = JSON.parse(row.remarks)
|
|
|
+ } catch (e) {
|
|
|
+ ElMessage.error('解析JSON错误,请检查任务是否已完成')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ executedTimeVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+let imgDataList = ref(reactive([]))
|
|
|
+let titleMsg = ref('')
|
|
|
+let valDialogVisible = ref(false)
|
|
|
+const showValResult = async isVal => {
|
|
|
+ let hasInit = false
|
|
|
+ titleMsg.value = isVal ? '验证结果对比' : '测试结果对比'
|
|
|
+ imgDataList.value = reactive([])
|
|
|
+ for (let i = 0; i < listData.value.length; i++) {
|
|
|
+ if (listData.value[i].name.indexOf(isVal ? '验证' : '测试') !== -1) {
|
|
|
+ console.log(listData.value[i])
|
|
|
+ if (!hasInit) {
|
|
|
+ hasInit = true
|
|
|
+ let res = await getImgList({
|
|
|
+ taskId: listData.value[i].inputImagePath.substring(1).split('/')[0],
|
|
|
+ subPath: 'images'
|
|
|
+ })
|
|
|
+ console.log('res data', res)
|
|
|
+ imgDataList.value.push([])
|
|
|
+ for (let j = 0; j < res.data.length; j++) {
|
|
|
+ let jList = res.data[j].split('.')
|
|
|
+ jList[jList.length - 1] = 'txt'
|
|
|
+ let obj = {
|
|
|
+ name: res.data[j],
|
|
|
+ srcUrl: 'api/profile/task' + listData.value[i].inputImagePath + (isVal ? '/images/' : '/') + res.data[j]
|
|
|
+ // imgUrl: 'api/profile/task' + listData.value[i].inputImagePath + '/images/' + res.data[j],
|
|
|
+ // labelUrl: 'api/profile/task' + listData.value[i].inputImagePath + '/labels/' + jList.join('.')
|
|
|
+ }
|
|
|
+
|
|
|
+ imgDataList.value[imgDataList.value.length - 1].push(obj)
|
|
|
+ if (isVal) {
|
|
|
+ obj.imgUrl = 'api/profile/task' + listData.value[i].inputImagePath + '/images/' + res.data[j]
|
|
|
+ obj.labelUrl = '/profile/task' + listData.value[i].inputImagePath + '/labels/' + jList.join('.')
|
|
|
+ console.log('url is', obj.labelUrl)
|
|
|
+ setDetail(obj)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ let res = await getImgList({
|
|
|
+ taskId: listData.value[i].inputImagePath.substring(1).split('/')[0],
|
|
|
+ subPath: 'images'
|
|
|
+ })
|
|
|
+ console.log(res.data)
|
|
|
+ if (isVal) {
|
|
|
+ await loadVerifyResult(listData.value[i].name, '/profile/task' + listData.value[i].inputImagePath + '/result/verify_result.txt')
|
|
|
+ }
|
|
|
+ console.log('load result', verifyResult.value)
|
|
|
+ imgDataList.value.push([])
|
|
|
+ for (let j = 0; j < res.data.length; j++) {
|
|
|
+ console.log('temp log', verifyResult.value)
|
|
|
+ let jList = res.data[j].split('.')
|
|
|
+ jList[jList - 1] = 'txt'
|
|
|
+ imgDataList.value[imgDataList.value.length - 1].push({
|
|
|
+ resUrl: 'api/profile/task' + listData.value[i].outputImagePath + '/' + res.data[j],
|
|
|
+ name: listData.value[i].name,
|
|
|
+ picName: res.data[j],
|
|
|
+ color: verifyResult.value[res.data[j]] ? '#00ff00' : '#ff0000'
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ console.log('datalist', imgDataList)
|
|
|
+ valDialogVisible.value = true
|
|
|
+}
|
|
|
+
|
|
|
+const verifyResult = ref({})
|
|
|
+const loadVerifyResult = async (name, filepath) => {
|
|
|
+ // console.log('filepath', filepath)
|
|
|
+ try {
|
|
|
+ verifyResult.value = {}
|
|
|
+ let res = await http.get(filepath)
|
|
|
+ console.log(res)
|
|
|
+ // verifyResult.value[name] = {}
|
|
|
+ let arr = res.replace('\r', '').split('\n')
|
|
|
+ arr.forEach(str => {
|
|
|
+ let vals = str.split(' ')
|
|
|
+ verifyResult.value[vals[0]] = vals[1] === '1'
|
|
|
+ })
|
|
|
+ console.log('verifyResult', verifyResult.value)
|
|
|
+ } catch (e) {
|
|
|
+ verifyResult.value = {}
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+const setDetail = obj => {
|
|
|
+ http.get<any>(obj.labelUrl).then(res => {
|
|
|
+ obj.jsonData = []
|
|
|
+ // console.log('result', res)
|
|
|
+ let arr = res.replace('\r', '').split('\n')
|
|
|
+ // console.log(arr)
|
|
|
+ for (let i = 0; i < arr.length; i++) {
|
|
|
+ let subArr = arr[i].split(' ')
|
|
|
+ // console.log(subArr)
|
|
|
+ let cssVal = '#000000'
|
|
|
+ let label = '-1'
|
|
|
+ for (let j = 0; j < typeDefs.value.length; j++) {
|
|
|
+ if (typeDefs.value[j].label === subArr[0]) {
|
|
|
+ cssVal = typeDefs.value[j].color
|
|
|
+ label = typeDefs.value[j].label
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ const ww = 200
|
|
|
+ const hh = 200
|
|
|
+ obj.jsonData.push({
|
|
|
+ subArr: subArr,
|
|
|
+ pathString:
|
|
|
+ 'M ' +
|
|
|
+ subArr[1] * ww +
|
|
|
+ ' ' +
|
|
|
+ subArr[2] * hh +
|
|
|
+ ' L ' +
|
|
|
+ subArr[3] * ww +
|
|
|
+ ' ' +
|
|
|
+ subArr[4] * hh +
|
|
|
+ ' L ' +
|
|
|
+ subArr[5] * ww +
|
|
|
+ ' ' +
|
|
|
+ subArr[6] * hh +
|
|
|
+ ' L ' +
|
|
|
+ subArr[7] * ww +
|
|
|
+ ' ' +
|
|
|
+ subArr[8] * hh +
|
|
|
+ ' z',
|
|
|
+ // left: 0,
|
|
|
+ // top: 0,
|
|
|
+ fill: '',
|
|
|
+ stroke: cssVal,
|
|
|
+ strokeWidth: 5,
|
|
|
+ label: label
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+let compareDialogVisible = ref(false)
|
|
|
+const valListData = ref([])
|
|
|
+let listData = ref(reactive([]))
|
|
|
+const showCompareResult = () => {
|
|
|
+ console.log(listData.value)
|
|
|
+ valListData.value = listData.value.filter(item => {
|
|
|
+ return item.name.includes('验证')
|
|
|
+ })
|
|
|
+ compareDialogVisible.value = true
|
|
|
+ console.log('vallist', valListData.value)
|
|
|
+}
|
|
|
+const showResult = row => {
|
|
|
+ refSelectData.value = reactive(row)
|
|
|
+ resultDialogVisible.value = true
|
|
|
+ console.log(row)
|
|
|
+}
|
|
|
+const subTaskId = route.query.id as string
|
|
|
+// ProTable 实例
|
|
|
+const proTable = ref<ProTableInstance>()
|
|
|
+const dialogVisible = ref(false)
|
|
|
+const resultVisible = ref(false)
|
|
|
+let resultsData = ref({})
|
|
|
+let testResultsData = ref({})
|
|
|
+let resultsFlag = ref(false)
|
|
|
+let spanNum = ref<number>(4)
|
|
|
+// let logList = ref()
|
|
|
+const logRef = ref<HTMLElement | null>(null) // 显式声明 logRef 的类型;
|
|
|
+let logInfo = ref([] as any[])
|
|
|
+let taskType = ref()
|
|
|
+let taskStatus = ref()
|
|
|
+
|
|
|
+let amplifyList = ref()
|
|
|
+// 每隔10秒请求一下列表
|
|
|
+const timer = ref() // 定时器
|
|
|
+const refreshList = () => {
|
|
|
+ setTimeout(() => {
|
|
|
+ listAmplifyApi({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 100,
|
|
|
+ subTaskId: subTaskId
|
|
|
+ }).then(res => {
|
|
|
+ amplifyList.value = res.data['list'].sort((a, b) => b.index - a.index)
|
|
|
+ listData.value = reactive(res.data['list'].sort((a, b) => b.index - a.index))
|
|
|
+ })
|
|
|
+ }, 0)
|
|
|
+}
|
|
|
+onMounted(() => {
|
|
|
+ listDictDataApi({
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ dictType: 'class_definition'
|
|
|
+ }).then(res => {
|
|
|
+ // console.log(res)
|
|
|
+ for (let i = 0; i < res.data.list.length; i++) {
|
|
|
+ typeDefs.value.push({
|
|
|
+ name: res.data.list[i].dictLabel,
|
|
|
+ color: res.data.list[i].cssClass,
|
|
|
+ label: res.data.list[i].dictValue
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ getSubtaskApi(subTaskId).then(res => {
|
|
|
+ taskType.value = res.data.type
|
|
|
+ taskStatus.value = res.data.status
|
|
|
+ })
|
|
|
+ refreshList()
|
|
|
+ timer.value = setInterval(() => {
|
|
|
+ refreshList()
|
|
|
+ }, 10000)
|
|
|
+ // 组件挂载后,logRef 将指向实际的 DOM 元素
|
|
|
+ if (logRef.value) {
|
|
|
+ // 操作 logRef 对应的 DOM 元素
|
|
|
+ logRef.value.scrollTop = logRef.value.scrollHeight
|
|
|
+ }
|
|
|
+})
|
|
|
+// 删除算法业务处理信息
|
|
|
+const deleteBizProcess = async (params: any) => {
|
|
|
+ await useHandleData(delBizProcessApi, params.id, '删除【' + params.id + '】算法业务处理')
|
|
|
+ proTable.value?.getTableList()
|
|
|
+}
|
|
|
+
|
|
|
+// 批量删除算法业务处理信息
|
|
|
+const batchDelete = async (ids: string[]) => {
|
|
|
+ await useHandleData(delBizProcessApi, ids, '删除所选算法业务处理信息')
|
|
|
+ proTable.value?.clearSelection()
|
|
|
+ proTable.value?.getTableList()
|
|
|
+}
|
|
|
+
|
|
|
+// 导出算法业务处理列表
|
|
|
+const downloadFile = async () => {
|
|
|
+ ElMessageBox.confirm('确认导出算法业务处理数据?', '温馨提示', { type: 'warning' }).then(() =>
|
|
|
+ useDownload(exportBizProcessApi, '算法业务处理列表', proTable.value?.searchParam)
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+// 批量添加算法业务处理
|
|
|
+const dialogRef = ref<InstanceType<typeof ImportExcel> | null>(null)
|
|
|
+
|
|
|
+// 对比结果
|
|
|
+const contrastResults = () => {
|
|
|
+ const statusFlag = amplifyList.value.every(item => {
|
|
|
+ return item.status == '2'
|
|
|
+ })
|
|
|
+ if (statusFlag) {
|
|
|
+ switch (taskType.value) {
|
|
|
+ case '1':
|
|
|
+ getTrainResultApi(subTaskId).then(res => {
|
|
|
+ resultsFlag.value = false
|
|
|
+ handleResultData(res.data)
|
|
|
+ resultVisible.value = true
|
|
|
+ })
|
|
|
+ break
|
|
|
+ case '2':
|
|
|
+ getVerifyResultApi(subTaskId).then(res => {
|
|
|
+ resultsFlag.value = false
|
|
|
+ handleResultData(res.data)
|
|
|
+ resultVisible.value = true
|
|
|
+ })
|
|
|
+ break
|
|
|
+ case '3':
|
|
|
+ getTestResultApi(subTaskId).then(res => {
|
|
|
+ console.log('333', res)
|
|
|
+ resultsFlag.value = true
|
|
|
+ testResultsData.value = res.data as any
|
|
|
+ const num = testResultsData.value['agNameList'].length
|
|
|
+ spanNum.value = 24 / num <= 4 ? 4 : Math.floor(24 / num)
|
|
|
+ resultVisible.value = true
|
|
|
+ })
|
|
|
+ break
|
|
|
+ default:
|
|
|
+ break
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ ElMessage.warning(`所有算法状态为‘已完成’,才可以对比`)
|
|
|
+ }
|
|
|
+}
|
|
|
+const handleResultData = data => {
|
|
|
+ resultsData.value = data
|
|
|
+ resultsData.value['agNameList'].unshift('')
|
|
|
+ const num = resultsData.value['agNameList'].length + 1
|
|
|
+ spanNum.value = 24 / num <= 4 ? 4 : Math.floor(24 / num)
|
|
|
+ resultsData.value['rcureList'].unshift('R_curve')
|
|
|
+ resultsData.value['pcureList'].unshift('P_curve')
|
|
|
+ resultsData.value['f1cureList'].unshift('F1_curve')
|
|
|
+}
|
|
|
+// 查看日志
|
|
|
+let timer2 = ref()
|
|
|
+const viewLog = row => {
|
|
|
+ if (row.status === '0') {
|
|
|
+ ElMessage.warning('算法状态为待处理,暂无日志')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const paths = row.outputImagePath.split('\n')
|
|
|
+ for (let path of paths) {
|
|
|
+ const url = `/api/profile/task/log/bizType/` + row.id + '/log.log'
|
|
|
+ logShow(url)
|
|
|
+ dialogVisible.value = true
|
|
|
+ timer2.value = setInterval(() => {
|
|
|
+ if (dialogVisible.value) {
|
|
|
+ logShow(url)
|
|
|
+ }
|
|
|
+ }, 5000)
|
|
|
+ }
|
|
|
+}
|
|
|
+// 日志读取
|
|
|
+const logShow = (url: any) => {
|
|
|
+ fetchLogFile(url)
|
|
|
+ .then(text => {
|
|
|
+ logInfo.value = []
|
|
|
+ logInfo.value = text.split('\n')
|
|
|
+ nextTick(() => {
|
|
|
+ const logContainer = logRef.value
|
|
|
+ if (logContainer) {
|
|
|
+ ;(logContainer as HTMLElement).scrollTop = (logContainer as HTMLElement).scrollHeight
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ .catch(error => {
|
|
|
+ console.error('Failed to fetch the log file:', error)
|
|
|
+ ElMessage.error('日志读取错误')
|
|
|
+ })
|
|
|
+}
|
|
|
+const fetchLogFile = async url => {
|
|
|
+ try {
|
|
|
+ const response = await fetch(url, { method: 'GET' })
|
|
|
+ if (!response.ok) {
|
|
|
+ throw new Error(`HTTP error! status: ${response.status}`)
|
|
|
+ }
|
|
|
+ return await response.text()
|
|
|
+ } catch (error) {
|
|
|
+ throw error
|
|
|
+ }
|
|
|
+}
|
|
|
+const formDialogRef = ref<InstanceType<typeof FormDialog> | null>(null)
|
|
|
+// 打开弹框的功能
|
|
|
+const openDialog = async (type: number, title: string, row?: any) => {
|
|
|
+ let res = { data: {} }
|
|
|
+ if (row?.id) {
|
|
|
+ res = await getAmplifyApi(row?.id || null)
|
|
|
+ }
|
|
|
+ // 重置表单
|
|
|
+ setItemsOptions()
|
|
|
+ const params = {
|
|
|
+ title,
|
|
|
+ width: 580,
|
|
|
+ isEdit: type !== 3,
|
|
|
+ itemsOptions: itemsOptions,
|
|
|
+ model: type == 1 ? {} : res.data,
|
|
|
+ api: type == 1 ? addAmplifyApi : updateAmplifyApi,
|
|
|
+ getTableList: proTable.value?.getTableList
|
|
|
+ }
|
|
|
+ formDialogRef.value?.openDialog(params)
|
|
|
+}
|
|
|
+onUnmounted(() => {
|
|
|
+ clearInterval(timer.value)
|
|
|
+ timer.value = null
|
|
|
+})
|
|
|
+watch(
|
|
|
+ () => dialogVisible.value,
|
|
|
+ val => {
|
|
|
+ if (!val) {
|
|
|
+ clearInterval(timer2.value)
|
|
|
+ timer2.value = null
|
|
|
+ }
|
|
|
+ }
|
|
|
+)
|
|
|
+// 表格配置项
|
|
|
+const columns = reactive<ColumnProps<any>[]>([
|
|
|
+ { type: 'selection', fixed: 'left', width: 70 },
|
|
|
+ // { prop: 'id', label: '主键ID' },
|
|
|
+ // {
|
|
|
+ // prop: 'subTaskId',
|
|
|
+ // label: '子任务id',
|
|
|
+ // search: {
|
|
|
+ // el: 'input'
|
|
|
+ // },
|
|
|
+ // width: 120
|
|
|
+ // },
|
|
|
+ {
|
|
|
+ prop: 'name',
|
|
|
+ label: '任务名称',
|
|
|
+ search: {
|
|
|
+ el: 'input'
|
|
|
+ },
|
|
|
+ width: 120
|
|
|
+ },
|
|
|
+ // {
|
|
|
+ // prop: 'type',
|
|
|
+ // label: '任务类型',
|
|
|
+ // search: {
|
|
|
+ // el: 'input'
|
|
|
+ // },
|
|
|
+ // width: 120
|
|
|
+ // },
|
|
|
+ {
|
|
|
+ prop: 'status',
|
|
|
+ label: '任务状态',
|
|
|
+ tag: true,
|
|
|
+ enum: () => getDictsApi('biz_task_status'),
|
|
|
+ search: {
|
|
|
+ el: 'tree-select'
|
|
|
+ },
|
|
|
+ width: 100,
|
|
|
+ fieldNames: { label: 'dictLabel', value: 'dictValue' }
|
|
|
+ },
|
|
|
+ // {
|
|
|
+ // prop: 'algorithmId',
|
|
|
+ // label: '算法',
|
|
|
+ // width: 120
|
|
|
+ // },
|
|
|
+ // {
|
|
|
+ // prop: 'modelId',
|
|
|
+ // label: '模型',
|
|
|
+ // width: 120
|
|
|
+ // },
|
|
|
+ {
|
|
|
+ prop: 'parameters',
|
|
|
+ label: '调用算法时所用的参数'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'inputImagePath',
|
|
|
+ label: '预处理数据路径'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'outputImagePath',
|
|
|
+ label: '结果数据路径'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'index',
|
|
|
+ label: '序号',
|
|
|
+ width: 120
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'startTime',
|
|
|
+ label: '开始时间'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'endTime',
|
|
|
+ label: '结束时间'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'costSecond',
|
|
|
+ label: '耗时(ms)'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ prop: 'log',
|
|
|
+ label: '日志'
|
|
|
+ },
|
|
|
+ { prop: 'operation', label: '操作', width: 230, fixed: 'right' }
|
|
|
+])
|
|
|
+// 结果表格配置项
|
|
|
+// const resultColumns = reactive<ColumnProps<any>[]>([])
|
|
|
+// 表单配置项
|
|
|
+let itemsOptions: ProForm.ItemsOptions[] = []
|
|
|
+const setItemsOptions = () => {
|
|
|
+ itemsOptions = [
|
|
|
+ {
|
|
|
+ label: '子任务id',
|
|
|
+ prop: 'subTaskId',
|
|
|
+ compOptions: {
|
|
|
+ placeholder: '请输入子任务id'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '任务名称',
|
|
|
+ prop: 'name',
|
|
|
+ compOptions: {
|
|
|
+ placeholder: '请输入任务名称'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '任务类型',
|
|
|
+ prop: 'type',
|
|
|
+ compOptions: {
|
|
|
+ placeholder: '请输入任务类型'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '任务状态',
|
|
|
+ prop: 'status',
|
|
|
+ compOptions: {
|
|
|
+ elTagName: 'select',
|
|
|
+ labelKey: 'dictLabel',
|
|
|
+ valueKey: 'dictValue',
|
|
|
+ enum: () => getDictsApi('biz_task_status'),
|
|
|
+ placeholder: '请选择任务状态'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '算法',
|
|
|
+ prop: 'algorithmId',
|
|
|
+ compOptions: {
|
|
|
+ placeholder: '请输入算法'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '模型',
|
|
|
+ prop: 'modelId',
|
|
|
+ compOptions: {
|
|
|
+ placeholder: '请输入模型'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '调用算法时所用的参数',
|
|
|
+ prop: 'parameters',
|
|
|
+ compOptions: {
|
|
|
+ type: 'textarea',
|
|
|
+ clearable: true,
|
|
|
+ placeholder: '请输入内容'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '预处理数据路径',
|
|
|
+ prop: 'inputImagePath',
|
|
|
+ compOptions: {
|
|
|
+ placeholder: '请输入预处理数据路径'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '结果数据路径',
|
|
|
+ prop: 'outputImagePath',
|
|
|
+ compOptions: {
|
|
|
+ placeholder: '请输入结果数据路径'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '序号',
|
|
|
+ prop: 'index',
|
|
|
+ compOptions: {
|
|
|
+ placeholder: '请输入序号'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '开始时间',
|
|
|
+ prop: 'startTime',
|
|
|
+ compOptions: {
|
|
|
+ elTagName: 'date-picker',
|
|
|
+ type: 'date',
|
|
|
+ placeholder: '请选择开始时间'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '结束时间',
|
|
|
+ prop: 'endTime',
|
|
|
+ compOptions: {
|
|
|
+ elTagName: 'date-picker',
|
|
|
+ type: 'date',
|
|
|
+ placeholder: '请选择结束时间'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '耗时',
|
|
|
+ prop: 'costSecond',
|
|
|
+ compOptions: {
|
|
|
+ placeholder: '请输入耗时'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ {
|
|
|
+ label: '日志',
|
|
|
+ prop: 'log',
|
|
|
+ compOptions: {
|
|
|
+ type: 'textarea',
|
|
|
+ clearable: true,
|
|
|
+ placeholder: '请输入内容'
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ]
|
|
|
+}
|
|
|
+</script>
|
|
|
+<style scoped lang="scss">
|
|
|
+.log {
|
|
|
+ width: 90%;
|
|
|
+ height: 60vh; /* 根据需要调整 */
|
|
|
+ padding: 10px;
|
|
|
+
|
|
|
+ // padding-bottom: 80px;
|
|
|
+ margin-left: 50px;
|
|
|
+ overflow-y: auto;
|
|
|
+ font-family: 'Courier New', monospace;
|
|
|
+ color: #4aff84;
|
|
|
+ background-color: #1e1e1e;
|
|
|
+}
|
|
|
+.p {
|
|
|
+ padding-left: 10px;
|
|
|
+ margin-bottom: 5px;
|
|
|
+ border-left: 3px solid #4aff84;
|
|
|
+}
|
|
|
+.resultShow {
|
|
|
+ width: 100%;
|
|
|
+ height: 60vh;
|
|
|
+ overflow: hidden;
|
|
|
+ overflow: scroll scroll;
|
|
|
+ .headerRow {
|
|
|
+ height: 50px;
|
|
|
+ font-size: 1.2rem;
|
|
|
+ line-height: 50px;
|
|
|
+ text-align: center;
|
|
|
+ }
|
|
|
+ .row {
|
|
|
+ .col {
|
|
|
+ text-align: center;
|
|
|
+ .oneCol {
|
|
|
+ margin-top: 45px;
|
|
|
+ font-size: 1.2rem;
|
|
|
+ }
|
|
|
+ .img {
|
|
|
+ margin: 10px 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|