Bladeren bron

Merge branch 'develop' of http://47.108.150.237:10000/www/taais-web into dev-lzy

Eureka 8 maanden geleden
bovenliggende
commit
c5f9ad2961

+ 139 - 141
src/api/interface/task/taskConfiguration.ts

@@ -1,143 +1,141 @@
 import { PageQuery, BaseEntity } from '@/api/interface/index'
 export interface TaskConfigurationVO extends BaseEntity {
-    /**
-    * 主键ID
-    */
-        id: string | number;
-
-    /**
-    * 算法任务名称
-    */
-        name: string;
-
-    /**
-    * 训练算法地址
-    */
-        trainUrl: string;
-
-    /**
-    * 训练超参配置
-    */
-        trainParams: string;
-
-    /**
-    * 验证算法地址
-    */
-        verifyUrl: string;
-
-    /**
-    * 验证超参配置
-    */
-        verifyParams: string;
-
-    /**
-    * 测试算法地址
-    */
-        testUrl: string;
-
-    /**
-    * 测试超参配置
-    */
-        testParams: string;
-
-    /**
-    * 备注
-    */
-        remark: string;
-
-    }
-
-    export interface TaskConfigurationForm {
-        /**
-        * 主键ID
-        */
-        id?: string | number;
-
-        /**
-        * 算法任务名称
-        */
-        name?: string;
-
-        /**
-        * 训练算法地址
-        */
-        trainUrl?: string;
-
-        /**
-        * 训练超参配置
-        */
-        trainParams?: string;
-
-        /**
-        * 验证算法地址
-        */
-        verifyUrl?: string;
-
-        /**
-        * 验证超参配置
-        */
-        verifyParams?: string;
-
-        /**
-        * 测试算法地址
-        */
-        testUrl?: string;
-
-        /**
-        * 测试超参配置
-        */
-        testParams?: string;
-
-        /**
-        * 备注
-        */
-        remark?: string;
-
-        /**
-        * 乐观锁
-        */
-        version?: number;
-
-    }
-
-    export interface TaskConfigurationQuery extends PageQuery {
-        /**
-        * 算法任务名称
-        */
-        name?: string;
-
-        /**
-        * 训练算法地址
-        */
-        trainUrl?: string;
-
-        /**
-        * 训练超参配置
-        */
-        trainParams?: string;
-
-        /**
-        * 验证算法地址
-        */
-        verifyUrl?: string;
-
-        /**
-        * 验证超参配置
-        */
-        verifyParams?: string;
-
-        /**
-        * 测试算法地址
-        */
-        testUrl?: string;
-
-        /**
-        * 测试超参配置
-        */
-        testParams?: string;
-
-    /**
-    * 日期范围参数
-    */
-    params?: any;
-    }
+  /**
+   * 主键ID
+   */
+  id: string | number
+
+  /**
+   * 算法任务名称
+   */
+  name: string
+
+  /**
+   * 训练算法地址
+   */
+  trainUrl: string
+
+  /**
+   * 训练超参配置
+   */
+  trainParams: string
+
+  /**
+   * 验证算法地址
+   */
+  verifyUrl: string
+
+  /**
+   * 验证超参配置
+   */
+  verifyParams: string
+
+  /**
+   * 测试算法地址
+   */
+  testUrl: string
+
+  /**
+   * 测试超参配置
+   */
+  testParams: string
+
+  /**
+   * 备注
+   */
+  remark: string
+}
+
+export interface TaskConfigurationForm {
+  /**
+   * 主键ID
+   */
+  id?: string | number
+
+  /**
+   * 算法任务名称
+   */
+  name?: string
+
+  /**
+   * 训练算法地址
+   */
+  trainUrl?: string
+
+  /**
+   * 训练超参配置
+   */
+  trainParams?: string
+
+  /**
+   * 验证算法地址
+   */
+  verifyUrl?: string
+
+  /**
+   * 验证超参配置
+   */
+  verifyParams?: string
+
+  /**
+   * 测试算法地址
+   */
+  testUrl?: string
+
+  /**
+   * 测试超参配置
+   */
+  testParams?: string
+
+  /**
+   * 备注
+   */
+  remark?: string
+
+  /**
+   * 乐观锁
+   */
+  version?: number
+}
+
+export interface TaskConfigurationQuery extends PageQuery {
+  /**
+   * 算法任务名称
+   */
+  name?: string
+
+  /**
+   * 训练算法地址
+   */
+  trainUrl?: string
+
+  /**
+   * 训练超参配置
+   */
+  trainParams?: string
+
+  /**
+   * 验证算法地址
+   */
+  verifyUrl?: string
+
+  /**
+   * 验证超参配置
+   */
+  verifyParams?: string
+
+  /**
+   * 测试算法地址
+   */
+  testUrl?: string
+
+  /**
+   * 测试超参配置
+   */
+  testParams?: string
+
+  /**
+   * 日期范围参数
+   */
+  params?: any
+}

+ 4 - 0
src/api/modules/ag/model.ts

@@ -27,6 +27,10 @@ export const addModelApi = (data: ModelForm) => {
   return http.post<any>('/ag/model', data, { loading: false })
 }
 
+export const queryModelList = (data: ModelForm) => {
+  return http.get<any>('/ag/model/algoList', data, { loading: false })
+}
+
 export const addAlgorithmTaskConfigurationApi = (data: ModelForm) => {
   return http.post<any>('/task/taskConfiguration/importModel', data, { loading: false })
 }

+ 13 - 0
src/api/modules/demo/data.ts

@@ -16,6 +16,19 @@ export const listDataApi = (query: any) => {
   return http.get<any[]>('/demo/data/list', query, { loading: true })
 }
 
+export const getFormSelectsApi = (query: { field: string }) => {
+  return http.get<string[]>('/demo/data/getFromSelects', query, { loading: false })
+}
+
+/**
+ * @name 提交
+ * @param id id
+ * @returns returns
+ */
+export const amplifyApi = (data: any) => {
+  return http.post<any>(`/demo/data/amplify`, data, { loading: false })
+}
+
 /**
  * @name 查询数据管理详细
  * @param id id

+ 1 - 1
src/api/modules/task/subtaskNew.ts

@@ -4,7 +4,7 @@ import http from '@/api'
  * @param query 参数
  * @returns 返回列表
  */
-export const listSubtaskApi = (query) => {
+export const listSubtaskApi = query => {
   return http.get<any[]>('/identification/identificationSubtask/list', query, { loading: false })
 }
 

+ 9 - 9
src/api/modules/task/taskConfiguration.ts

@@ -1,12 +1,12 @@
 import http from '@/api'
-import { TaskConfigurationVO, TaskConfigurationForm, TaskConfigurationQuery  } from '@/api/interface/task/taskConfiguration'
+import { TaskConfigurationVO, TaskConfigurationForm, TaskConfigurationQuery } from '@/api/interface/task/taskConfiguration'
 /**
  * @name 查询算法任务列表
  * @param query 参数
  * @returns 返回列表
  */
 export const listTaskConfigurationApi = (query: TaskConfigurationQuery) => {
-    return http.get<TaskConfigurationVO[]>('/task/taskConfiguration/list', query, { loading: true })
+  return http.get<TaskConfigurationVO[]>('/task/taskConfiguration/list', query, { loading: true })
 }
 
 /**
@@ -15,7 +15,7 @@ export const listTaskConfigurationApi = (query: TaskConfigurationQuery) => {
  * @returns returns
  */
 export const getTaskConfigurationApi = (id: string | number) => {
-    return http.get<TaskConfigurationVO>(`/task/taskConfiguration/${id}`)
+  return http.get<TaskConfigurationVO>(`/task/taskConfiguration/${id}`)
 }
 
 /**
@@ -24,7 +24,7 @@ export const getTaskConfigurationApi = (id: string | number) => {
  * @returns returns
  */
 export const addTaskConfigurationApi = (data: TaskConfigurationForm) => {
-    return http.post<any>('/task/taskConfiguration', data, { loading: false })
+  return http.post<any>('/task/taskConfiguration', data, { loading: false })
 }
 
 /**
@@ -33,7 +33,7 @@ export const addTaskConfigurationApi = (data: TaskConfigurationForm) => {
  * @returns returns
  */
 export const updateTaskConfigurationApi = (data: TaskConfigurationForm) => {
-    return http.put<any>('/task/taskConfiguration', data, { loading: false })
+  return http.put<any>('/task/taskConfiguration', data, { loading: false })
 }
 
 /**
@@ -42,7 +42,7 @@ export const updateTaskConfigurationApi = (data: TaskConfigurationForm) => {
  * @returns returns
  */
 export const delTaskConfigurationApi = (id: string | number | Array<string | number>) => {
-    return http.delete<any>(`/task/taskConfiguration/${id}`)
+  return http.delete<any>(`/task/taskConfiguration/${id}`)
 }
 
 /**
@@ -50,7 +50,7 @@ export const delTaskConfigurationApi = (id: string | number | Array<string | num
  * @returns returns
  */
 export const importTemplateApi = () => {
-    return http.downloadPost('/task/taskConfiguration/importTemplate', {})
+  return http.downloadPost('/task/taskConfiguration/importTemplate', {})
 }
 
 /**
@@ -58,7 +58,7 @@ export const importTemplateApi = () => {
  * @returns returns
  */
 export const importTaskConfigurationDataApi = (data: any) => {
-    return http.post('/task/taskConfiguration/importData', data)
+  return http.post('/task/taskConfiguration/importData', data)
 }
 
 /**
@@ -66,5 +66,5 @@ export const importTaskConfigurationDataApi = (data: any) => {
  * @returns returns
  */
 export const exportTaskConfigurationApi = (data: any) => {
-    return http.downloadPost('/task/taskConfiguration/export', data)
+  return http.downloadPost('/task/taskConfiguration/export', data)
 }

+ 20 - 5
src/components/FormDialog/index.vue

@@ -10,7 +10,7 @@
   >
     <ProFrom ref="proFormRef" :items-options="parameter.itemsOptions" :form-options="_options" :model="parameter.model">
       <template #modelAddress="{}">
-        <FileUpload :file-size="4096" :file-type="['pt']" v-on:update:modelValue="setModelAddr"/>
+        <FileUpload :file-size="4096" :file-type="['pt']" @update:model-value="setModelAddr" />
       </template>
     </ProFrom>
     <template #footer>
@@ -23,10 +23,11 @@
 </template>
 
 <script setup lang="ts" name="FormDialog">
-import {ref, ComputedRef, computed, reactive} from 'vue'
+import { ref, ComputedRef, computed, reactive } from 'vue'
 import ProFrom from '@/components/ProForm/index.vue'
 import { ElMessage } from 'element-plus'
 import FileUpload from '@/components/Upload/File.vue'
+// import mittBus from '@/utils/mittBus'
 
 interface EmitEvent {
   (e: 'update'): void
@@ -87,7 +88,11 @@ const handleSubmit = () => {
   if (!formEl) return
   formEl.validate(valid => {
     if (valid) {
-      parameter.value.api!({ ...formModel, ...parameter.value.model }).then(res => {
+      const _params = { ...formModel, ...parameter.value.model }
+      if (!_params.algorithmId) {
+        _params.algorithmId = algorithmModelId.value
+      }
+      parameter.value.api!(_params).then(res => {
         if (res.code == 200) {
           proFormRef.value?.resetForm(formEl)
           ElMessage.success('操作成功')
@@ -103,7 +108,14 @@ const handleSubmit = () => {
     butLoading.value = false
   })
 }
-
+// mittBus.on('data:fileName', (fileName: string) => {
+//   if (fileName !== undefined && fileName !== null) {
+//     console.log(proFormRef.value?.formModel)
+//     if(proFormRef.value?.formModel){
+//       proFormRef.value.formModel.name = fileName.split('.')[0]
+//     }
+//   }
+// })
 // 取消按钮,重置表单,关闭弹框
 const handleCancel = () => {
   console.log(parameter.value.model)
@@ -118,8 +130,11 @@ const handleCancel = () => {
   dialogVisible.value = false
 }
 
+let algorithmModelId = ref(0)
+
 // 接收父组件参数
-const openDialog = (params: FormParameterProps) => {
+const openDialog = (params: FormParameterProps, algoModelId) => {
+  algorithmModelId.value = algoModelId
   parameter.value = { ...parameter.value, ...params }
   _options.value.disabled = !parameter.value.isEdit
   butLoading.value = false

+ 5 - 5
src/components/ImportPicDataset/index.vue

@@ -169,11 +169,11 @@ const handleSubmit = async () => {
     parameter.value.getTableList && parameter.value.getTableList()
     dialogVisible.value = false
   } catch (error) {
-    ElNotification({
-      title: '温馨提示',
-      message: `文件上传失败,请重新上传`,
-      type: 'error'
-    })
+    // ElNotification({
+    //   // title: '温馨提示',
+    //   // message: `文件上传失败,请重新上传`,
+    //   // type: 'error'
+    // })
   }
 }
 

+ 25 - 10
src/components/Upload/Img.vue

@@ -18,15 +18,21 @@
         <img :src="'/api' + imageUrl" class="upload-image" />
         <div class="upload-handle" @click.stop>
           <div v-if="!self_disabled" class="handle-icon" @click="editImg">
-            <el-icon><Edit /></el-icon>
+            <el-icon>
+              <Edit />
+            </el-icon>
             <span>编辑</span>
           </div>
           <div class="handle-icon" @click="imgViewVisible = true">
-            <el-icon><ZoomIn /></el-icon>
+            <el-icon>
+              <ZoomIn />
+            </el-icon>
             <span>查看</span>
           </div>
           <div v-if="!self_disabled" class="handle-icon" @click="deleteImg">
-            <el-icon><Delete /></el-icon>
+            <el-icon>
+              <Delete />
+            </el-icon>
             <span>删除</span>
           </div>
         </div>
@@ -34,7 +40,9 @@
       <template v-else>
         <div class="upload-empty">
           <slot name="empty">
-            <el-icon><Plus /></el-icon>
+            <el-icon>
+              <Plus />
+            </el-icon>
             <!-- <span>请上传图片</span> -->
           </slot>
         </div>
@@ -43,12 +51,18 @@
     <div class="el-upload__tip">
       <slot name="tip"></slot>
     </div>
-<!--    <el-dialog id="dialog" v-if="isShowData" :visible="imgViewVisible">-->
-<!--      <el-image-viewer :url-list="['/api' + imageUrl]" />-->
-<!--    </el-dialog>-->
-<!--    <el-image-viewer v-else-if="imgViewVisible" :url-list="['/api' + imageUrl]" @close="imgViewVisible = false" />-->
+    <!--    <el-dialog id="dialog" v-if="isShowData" :visible="imgViewVisible">-->
+    <!--      <el-image-viewer :url-list="['/api' + imageUrl]" />-->
+    <!--    </el-dialog>-->
+    <!--    <el-image-viewer v-else-if="imgViewVisible" :url-list="['/api' + imageUrl]" @close="imgViewVisible = false" />-->
 
-    <el-image-viewer v-if="imgViewVisible" :url-list="['/api' + imageUrl]" @close="imgViewVisible = false" />
+    <el-image-viewer
+      v-if="imgViewVisible"
+      style="position: absolute; z-index: 9999"
+      :z-index="9999"
+      :url-list="['/api' + imageUrl]"
+      @close="imgViewVisible = false"
+    />
   </div>
 </template>
 
@@ -58,6 +72,7 @@ import { generateUUID } from '@/utils'
 import { uploadImg } from '@/api/modules/upload'
 import { ElNotification, formContextKey, formItemContextKey } from 'element-plus'
 import type { UploadProps, UploadRequestOptions } from 'element-plus'
+import mittBus from '@/utils/mittBus'
 
 interface UploadFileProps {
   imageUrl: string // 图片地址 ==> 必传
@@ -112,8 +127,8 @@ const handleHttpUpload = async (options: UploadRequestOptions) => {
   try {
     const api = props.api ?? uploadImg
     const res = await api(formData)
-
     emit('update:imageUrl', res.data.url)
+    mittBus.emit('data:fileName', res.data.fileName)
     // 调用 el-form 内部的校验方法(可自动校验)
     formItemContext?.prop && formContext?.validateField([formItemContext.prop as string])
   } catch (error) {

+ 15 - 0
src/routers/modules/routerData.json

@@ -45,6 +45,21 @@
         "activeMenu": "/index"
       }
     },
+    {
+      "path": "/data/amplify",
+      "name": "amplify",
+      "component": "demo/data/amplify",
+      "hidden": true,
+      "meta": {
+        "icon": "HomeFilled",
+        "title": "数据扩增",
+        "link": "",
+        "full": false,
+        "affix": false,
+        "noCache": false,
+        "activeMenu": "/demo/data"
+      }
+    },
     {
       "path": "/logPage/:flag",
       "name": "logPage",

+ 3 - 2
src/views/ag/model/index.vue

@@ -53,6 +53,7 @@ import {
   getModelApi
 } from '@/api/modules/ag/model'
 import { getAlgorithmOptionApi } from '@/api/modules/task/task'
+import { listTaskConfigurationApi } from '@/api/modules/task/taskConfiguration'
 
 // ProTable 实例
 const proTable = ref<ProTableInstance>()
@@ -216,8 +217,8 @@ const setItemsOptions = () => {
   ]
 }
 onMounted(() => {
-  getAlgorithmOptionApi().then(res => {
-    allAgloData.value = res.data
+  listTaskConfigurationApi().then(res => {
+    allAgloData.value = res.data.list
   })
 })
 </script>

+ 24 - 6
src/views/demo/components/img-detect.vue

@@ -1,11 +1,27 @@
 <template>
   <el-dialog class="modal-canvas" v-model="visible" title="标注" width="980px" height="700px">
-    <ImgMaker ref="imgMaker" v-if="visible" :src="cover" :area="area" :width="width" :height="height" :class-def="classDef" :json-data="jsonData"></ImgMaker>
+    <ImgMaker
+      ref="imgMaker"
+      v-if="visible"
+      :src="cover"
+      :area="area"
+      :width="width"
+      :height="height"
+      :class-def="classDef"
+      :json-data="jsonData"
+    ></ImgMaker>
     <div style="margin-top: 10px; text-align: center">
       标签
-      <el-button v-for="(item, index) in classes" :key="index" type="primary" @click="onClassChange(item)" :disabled="classDef.label===item.label">{{ item.name }}</el-button>
+      <el-button
+        v-for="(item, index) in classes"
+        :key="index"
+        type="primary"
+        @click="onClassChange(item)"
+        :disabled="classDef.label === item.label"
+        >{{ item.name }}</el-button
+      >
     </div>
-    <div style=" margin-top: 10px;text-align: center">
+    <div style="margin-top: 10px; text-align: center">
       <!-- <el-space> -->
       <el-button type="primary" @click="onClearLast">撤销最后一次的操作</el-button>
       <el-button type="primary" @click="onClearAll">清空所有</el-button>
@@ -16,7 +32,7 @@
   </el-dialog>
 </template>
 <script lang="ts" setup>
-import {reactive, ref, toRefs, defineProps, watchEffect, watch} from 'vue'
+import { reactive, ref, toRefs, defineProps, watchEffect, watch } from 'vue'
 import ImgMaker from './img-maker.vue'
 const props = defineProps({
   area: {
@@ -53,7 +69,7 @@ const state = reactive({
 watch(
   () => props.classes,
   value => {
-    state.classDef = (value && value.length > 0) ? value[0] : { name: 'default', color: '#FF00FF', classValue: 0 }
+    state.classDef = value && value.length > 0 ? value[0] : { name: 'default', color: '#FF00FF', classValue: 0 }
     // console.log(state.classDef)
   }
 )
@@ -93,7 +109,9 @@ const onSubmit = () => {
     let label = points[i]['label']
     // console.log(label)
     emitData.data += label + ' '
-    let index = -1, yMin = 10000000, xMax = -1
+    let index = -1,
+      yMin = 10000000,
+      xMax = -1
     for (let j = 0; j < points[i]['nodes'].length; j++) {
       if (points[i]['nodes'][j].y < yMin) {
         index = j

+ 58 - 40
src/views/demo/components/img-maker.vue

@@ -4,22 +4,16 @@
       <el-button plain type="primary" class="shape-border" @click="drawTypeChange('rectangle')" :disabled="!state.dragMode">标注模式</el-button>
       <el-button plain type="primary" class="shape-border" @click="enableDragMode" :disabled="state.dragMode">移动图片</el-button>
 
-      <el-button plain type="primary" style="margin-left: 30px" class="shape-border" @click="selectLastObject" :disabled="!!state.activeTarget">选择最后一个标注(w)</el-button>
-      <el-button
-        plain
-        type="primary"
-        class="shape-border"
-        @click="rotate(8)"
-        :disabled="!state.activeTarget"
+      <el-button plain type="primary" style="margin-left: 30px" class="shape-border" @click="selectLastObject" :disabled="!!state.activeTarget"
+        >选择最后一个标注(w)</el-button
       >
-        顺时针旋转(r)
-      </el-button>
+      <el-button plain type="primary" class="shape-border" @click="rotate(8)" :disabled="!state.activeTarget"> 顺时针旋转(r) </el-button>
       <el-button plain type="primary" class="shape-border" @click="rotate(-8)" :disabled="!state.activeTarget">逆时针旋转(e)</el-button>
       <el-button plain type="primary" style="margin-left: 30px" class="shape-border" @click="changeZoom(1.1)">放大图片</el-button>
       <el-button plain type="primary" class="shape-border" @click="changeZoom(0.9)">缩小图片</el-button>
       <!-- <el-button plain type="primary" class="shape-border" @click="drawPolygon('polygon')">多边形</el-button> -->
     </div>
-    <canvas id="canvas" :width="cWidth" :height="cHeight"></canvas>
+    <canvas :id="props.canvasId" :width="cWidth" :height="cHeight"></canvas>
   </div>
 </template>
 <script lang="ts" setup>
@@ -62,6 +56,10 @@ const props = defineProps({
   isPicOnly: {
     type: Boolean,
     default: false
+  },
+  canvasId: {
+    type: String,
+    default: 'canvas'
   }
 })
 const state = reactive({
@@ -138,28 +136,31 @@ const changeZoom = multi => {
 const loadInit = () => {
   drawTypeChange('rectangle')
   state.color = props.classDef && props.classDef.color ? props.classDef.color : '#E34F51'
-  document.addEventListener('keydown', function (e) {
-    if (e.key === 'w' || e.key === 'W') {
-      selectLastObject()
-      return
-    }
-    if (state.activeTarget) {
-      if (e.key === 'r' || e.key === 'R') {
-        // 旋转矩形对象
-        state.activeTarget.rotate(state.activeTarget.angle + 5)
-        state.canvas.renderAll()
-      } else if (e.key === 'e' || e.key === 'E') {
-        // 旋转矩形对象
-        state.activeTarget.rotate(state.activeTarget.angle - 5)
-        state.canvas.renderAll()
+  if (!props.isPicOnly) {
+    document.addEventListener('keydown', function (e) {
+      if (e.key === 'w' || e.key === 'W') {
+        selectLastObject()
+        return
       }
-    }
-  })
+      if (state.activeTarget) {
+        if (e.key === 'r' || e.key === 'R') {
+          // 旋转矩形对象
+          state.activeTarget.rotate(state.activeTarget.angle + 5)
+          state.canvas.renderAll()
+        } else if (e.key === 'e' || e.key === 'E') {
+          // 旋转矩形对象
+          state.activeTarget.rotate(state.activeTarget.angle - 5)
+          state.canvas.renderAll()
+        }
+      }
+    })
+  }
+
   if (props.src == '') {
     return
   }
   state.loading = true
-  state.canvas = new fabric.Canvas('canvas', {})
+  state.canvas = new fabric.Canvas(props.canvasId, {})
   state.canvas.selectionColor = 'rgba(0,0,0,0.05)'
   state.canvas.on('mouse:down', mousedown)
   state.canvas.on('mouse:move', mousemove)
@@ -168,11 +169,11 @@ const loadInit = () => {
   let imgElement = new Image()
   imgElement.src = props.src
   imgElement.onload = () => {
-    console.log(imgElement.height, imgElement.width)
+    // console.log(imgElement.height, imgElement.width)
     // 区域大小/图片原始大小 缩放比例
     state.radio =
       props.cWidth / imgElement.width > props.cHeight / imgElement.height ? props.cHeight / imgElement.height : props.cWidth / imgElement.width
-    console.log('state.radio', state.radio)
+    // console.log('state.radio', state.radio)
 
     // 屏幕分辨率/图片原始大小
     state.realRadioX = props.width / imgElement.width
@@ -192,28 +193,42 @@ const loadInit = () => {
       scaleY: state.radio
     })
     state.canvas.add(imgInstance)
-    console.log('props', props)
-    console.log('state', state)
+    // console.log('props', props)
+    // console.log('state', state)
     if (props.jsonData && props.jsonData.length > 0) {
       for (let i = 0; i < props.jsonData.length; i++) {
         let config = props.jsonData[i]
         let subArr = config.subArr
         const lW = imgInstance.width * state.radio
         const lH = imgInstance.height * state.radio
-        let obj = new fabric.Path('M ' + subArr[1] * lW + ' ' + subArr[2] * lH +
-          ' L ' + subArr[3] * lW + ' ' + subArr[4] * lH +
-          ' L ' + subArr[5] * lW + ' '  + subArr[6] * lH +
-          ' L ' + subArr[7] * lW + ' '  + subArr[8] * lH +
-          ' z',
-          config)
+        let obj = new fabric.Path(
+          'M ' +
+            subArr[1] * lW +
+            ' ' +
+            subArr[2] * lH +
+            ' L ' +
+            subArr[3] * lW +
+            ' ' +
+            subArr[4] * lH +
+            ' L ' +
+            subArr[5] * lW +
+            ' ' +
+            subArr[6] * lH +
+            ' L ' +
+            subArr[7] * lW +
+            ' ' +
+            subArr[8] * lH +
+            ' z',
+          config
+        )
         state.canvas.add(obj)
       }
       state.canvas.renderAll()
     }
     state.radio = state.canvas.getZoom()
     state.loading = false
-    console.log(state.canvas.getObjects())
-    console.log(props.isPicOnly)
+    // console.log('objects', state.canvas.getObjects())
+    // console.log(props.isPicOnly)
     // console.log(state.realRadioX)
     // console.log(state.realRadioY)
   }
@@ -314,6 +329,9 @@ const drawTypeChange = e => {
 }
 // 鼠标按下时触发
 const mousedown = e => {
+  if (props.isPicOnly) {
+    return
+  }
   if (state.dragMode) {
     state.isDragging = true
     state.lastPosX = e.e.clientX - state.canvas.getElement().offsetLeft
@@ -415,7 +433,7 @@ const mousemove = e => {
       //     evented: true
       //   });
       // });
-      state.canvas.requestRenderAll();
+      state.canvas.requestRenderAll()
     }
     return
   }

+ 170 - 0
src/views/demo/data/amplify.vue

@@ -0,0 +1,170 @@
+<template>
+  <div class="amplify-container">
+    <div class="amplify-main">
+      <ProForm :items-options="items" :model="model"> </ProForm>
+      <el-button type="primary" style="margin-left: 50px" @click="showDataSelectionDialog()">选择批次</el-button>
+    </div>
+
+    <el-footer>
+      <el-button class="submit" type="primary" @click="handleSubmit">提交</el-button>
+    </el-footer>
+    <el-dialog v-model="dataDialogVisible" title="选择数据批次" style="width: 70vw">
+      <el-container>
+        <el-table :data="batchDataList" tooltip-effect="dark" style="width: 100%; margin-bottom: 30px" @selection-change="handleSelectionChange">
+          <el-table-column type="selection"> </el-table-column>
+          <el-table-column prop="batchNum" label="所有批次"> </el-table-column>
+          <el-table-column prop="batchSize" label="数量"> </el-table-column>
+        </el-table>
+
+        <el-container style="display: flex; flex-direction: column; align-items: center; justify-content: center; margin: 0 30px">
+          <el-button type="primary" icon="ArrowRightBold" :disabled="!canSelect" @click="clickSelectData"></el-button>
+          <el-button
+            type="primary"
+            style="margin: 10px 0 0"
+            icon="ArrowLeftBold"
+            :disabled="!canDeselect"
+            @click="clickDeselectData"
+          ></el-button>
+        </el-container>
+
+        <el-table :data="selectedBatchDataList" tooltip-effect="dark" style="width: 100%" @selection-change="handleDeselectionChange">
+          <el-table-column type="selection"> </el-table-column>
+          <el-table-column prop="batchNum" label="已选批次"> </el-table-column>
+          <el-table-column prop="batchSize" label="数量"> </el-table-column>
+        </el-table>
+      </el-container>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="dataDialogVisible = false">取 消</el-button>
+          <el-button type="primary" @click="dataDialogVisible = false"> 确 定 </el-button>
+        </div>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+<script setup lang="tsx" name="amplify">
+import { reactive, ref, computed, onMounted } from 'vue'
+import ProForm from '@/components/ProForm/index.vue'
+import { batchListDataApi, amplifyApi } from '@/api/modules/demo/data'
+import { getDictsApi } from '@/api/modules/system/dictData'
+let model = {
+  taskName: 'ssss',
+  transfer: null
+}
+const dataDialogVisible = ref(false)
+let batchDataList = ref(reactive([] as any[]))
+let selectedBatchDataList = ref(reactive([] as any[]))
+let tempSelectedBatchDataList = ref(reactive([] as any[]))
+let tempDeselectedBatchDataList = ref(reactive([] as any[]))
+let parameList = ref(reactive([] as any[]))
+let queryBatchData = ref(reactive([] as any[]))
+onMounted(() => {
+  batchListDataApi().then(res => {
+    // batchDataList.value = reactive(res.data)
+    queryBatchData.value = reactive(res.data)
+  })
+  getDictsApi('expand_data_params').then(res => {
+    parameList.value = reactive(JSON.parse(res.data[0].remark))
+    parameList.value.forEach(item => {
+      items.push({
+        label: item.name,
+        prop: item.agName,
+        span: 12,
+        rules: [{ required: item.required, message: '不能为空' }],
+        compOptions: {
+          elTagName: 'input',
+          clearable: true,
+          placeholder: '请输入..'
+        }
+      })
+      model[`${item.agName}`] = item.defaultValue
+    })
+  })
+})
+const handleSelectionChange = data => {
+  tempSelectedBatchDataList.value = reactive(data)
+}
+let canSelect = computed(() => {
+  return tempSelectedBatchDataList.value.length > 0
+})
+const handleDeselectionChange = data => {
+  tempDeselectedBatchDataList.value = reactive(data)
+}
+const clickSelectData = () => {
+  let set = new Set()
+  for (let i = 0; i < tempSelectedBatchDataList.value.length; i++) {
+    set.add(tempSelectedBatchDataList.value[i].batchNum)
+    selectedBatchDataList.value.push(tempSelectedBatchDataList.value[i])
+  }
+  let newArray = [] as any[]
+  for (let i = 0; i < batchDataList.value.length; i++) {
+    if (!set.has(batchDataList.value[i].batchNum)) {
+      newArray.push(batchDataList.value[i])
+    }
+  }
+  batchDataList.value = reactive(newArray)
+}
+const canDeselect = computed(() => {
+  return tempDeselectedBatchDataList.value.length > 0
+})
+const clickDeselectData = () => {
+  let set = new Set()
+  for (let i = 0; i < tempDeselectedBatchDataList.value.length; i++) {
+    set.add(tempDeselectedBatchDataList.value[i].batchNum)
+    batchDataList.value.push(tempDeselectedBatchDataList.value[i])
+  }
+  let newArray = [] as any[]
+  for (let i = 0; i < selectedBatchDataList.value.length; i++) {
+    if (!set.has(selectedBatchDataList.value[i].batchNum)) {
+      newArray.push(selectedBatchDataList.value[i])
+    }
+  }
+  selectedBatchDataList.value = reactive(newArray)
+}
+const showDataSelectionDialog = () => {
+  // bIsTrainData.value = isTrainData
+  if (selectedBatchDataList.value.length === 0) {
+    batchDataList.value = queryBatchData.value
+  }
+  dataDialogVisible.value = true
+}
+const handleSubmit = () => {
+  let val = [] as any[]
+  for (let i = 0; i < selectedBatchDataList.value.length; i++) {
+    val.push(selectedBatchDataList.value[i].batchNum)
+  }
+  const batchNum = val.join()
+  const otherParams = [] as any[]
+
+  parameList.value.map(item => {
+    item.value = model[`${item.agName}`]
+  })
+  console.log('parameList.value', parameList.value)
+  const data = {
+    batchNum,
+    taskName: model.taskName,
+    otherParams: JSON.stringify(parameList.value)
+  }
+  console.log('data', data)
+  // amplifyApi(data).then(res => {
+  //   console.log(res)
+  // })
+}
+let items: ProForm.ItemsOptions[] = reactive([
+  {
+    label: '任务名称',
+    prop: 'taskName',
+    span: 14,
+    // labelWidth: '90px',
+    rules: [{ required: true, message: '请输入任务名称' }],
+    compOptions: {
+      elTagName: 'input',
+      clearable: true,
+      placeholder: '请输入用户名'
+    }
+  }
+])
+</script>
+<style scoped lang="scss">
+@import './index.scss';
+</style>

+ 29 - 0
src/views/demo/data/index.scss

@@ -0,0 +1,29 @@
+.amplify-container {
+  width: 100%;
+  height: 100%;
+  padding-top: 50px;
+
+  // position: relative;
+  .amplify-main {
+    width: 80%;
+    height: calc(100% - 50px);
+    margin: 0 auto;
+    overflow-y: scroll;
+  }
+  .el-footer {
+    position: absolute;
+    bottom: 0;
+    left: 50%;
+    height: 50px;
+    .submit {
+      right: 0;
+    }
+  }
+}
+::-webkit-scrollbar {
+  display: none;
+}
+.dialog-footer {
+  // margin-top: 30px;
+  margin: 0 auto;
+}

+ 52 - 30
src/views/demo/data/index.vue

@@ -1,9 +1,11 @@
 <template>
   <div class="table-box">
+    <el-image-viewer v-if="imgViewVisible" :url-list="['/api' + imageUrl]" @close="imgViewVisible = false" />
     <ProTable ref="proTable" :columns="columns" row-key="id" :request-api="listDataApi" :init-param="initParam">
       <template #yuan="scope">
-        <uploadImg :is-show-data="true" :disabled="true" :image-url="scope.row.url" />
-<!--        <el-image style="width: 100px" :src="'/api' + scope.row.url" @click="markImg(scope.row)" />-->
+        <el-image style=" width: 200px;height: 200px" :src="'/api' + scope.row.url" @click="loadImg(scope.row.url)"></el-image>
+        <!--        <uploadImg :is-show-data="true" :disabled="true" :image-url="scope.row.url" @click="loadImg(scope.row.url)"/>-->
+        <!--        <el-image style="width: 100px" :src="'/api' + scope.row.url" @click="markImg(scope.row)" />-->
       </template>
       <!-- 表格 header 按钮 -->
       <template #tableHeader="scope">
@@ -76,7 +78,7 @@ import { listDataApi as listDictDataApi } from '@/api/modules/system/dictData'
 import { uploadPure } from '@/api/modules/upload'
 import http from '@/api'
 
-onMounted (() => {
+onMounted(() => {
   state.cacheData.url = 'http://localhost:9090/profile/upload/2024/08/08/144745610/test.png'
   // handleImgSuccess({
   //   data: 'test change data',
@@ -85,6 +87,13 @@ onMounted (() => {
   // })
 })
 
+const imageUrl = ref('')
+const imgViewVisible = ref(false)
+const loadImg = url => {
+  imageUrl.value = url
+  imgViewVisible.value = true
+}
+
 const imgDetect = ref()
 const state = reactive({
   area: '' as string,
@@ -106,7 +115,7 @@ const state = reactive({
   // ],
   jsonData: [],
   cacheData: {}
-    // '{"version":"5.3.0","objects":[{"type":"image","version":"5.3.0","originX":"left","originY":"top","left":0,"top":0,"width":918,"height":789,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":0,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":0.68,"scaleY":0.68,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"cropX":0,"cropY":0,"src":"http://localhost:8848/api/profile/upload/2024/08/08/144745610/3-3.jpg","crossOrigin":null,"filters":[]},{"type":"rect","version":"5.3.0","originX":"left","originY":"top","left":181.38,"top":251.38,"width":244,"height":162,"fill":"rgba(255, 255, 255, 0)","stroke":"#E34F51","strokeWidth":5,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":-25,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"rx":0,"ry":0}]}'
+  // '{"version":"5.3.0","objects":[{"type":"image","version":"5.3.0","originX":"left","originY":"top","left":0,"top":0,"width":918,"height":789,"fill":"rgb(0,0,0)","stroke":null,"strokeWidth":0,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":0.68,"scaleY":0.68,"angle":0,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"cropX":0,"cropY":0,"src":"http://localhost:8848/api/profile/upload/2024/08/08/144745610/3-3.jpg","crossOrigin":null,"filters":[]},{"type":"rect","version":"5.3.0","originX":"left","originY":"top","left":181.38,"top":251.38,"width":244,"height":162,"fill":"rgba(255, 255, 255, 0)","stroke":"#E34F51","strokeWidth":5,"strokeDashArray":null,"strokeLineCap":"butt","strokeDashOffset":0,"strokeLineJoin":"miter","strokeUniform":false,"strokeMiterLimit":4,"scaleX":1,"scaleY":1,"angle":-25,"flipX":false,"flipY":false,"opacity":1,"shadow":null,"visible":true,"backgroundColor":"","fillRule":"nonzero","paintFirst":"fill","globalCompositeOperation":"source-over","skewX":0,"skewY":0,"rx":0,"ry":0}]}'
 })
 const { area, width, height, cover, classes, jsonData } = toRefs(state)
 
@@ -128,7 +137,7 @@ const markImg = data => {
   console.log('data is', data)
   state.cacheData = data
   state.jsonData = []
-    if (!data.url || data.url.length === 0) {
+  if (!data.url || data.url.length === 0) {
     ElMessage.warning('缺失图像,暂时不能进行区域添加!')
     return
   }
@@ -150,8 +159,9 @@ const markImg = data => {
       }
 
       if (state.cacheData.labelurl && state.cacheData.labelurl.length > 0) {
-        console.log('get label jsonData',state.cacheData.labelurl)
-        http.get<any>(state.cacheData.labelurl)
+        console.log('get label jsonData', state.cacheData.labelurl)
+        http
+          .get<any>(state.cacheData.labelurl)
           .then(res => {
             state.jsonData = []
             console.log(res)
@@ -171,10 +181,23 @@ const markImg = data => {
               }
               state.jsonData.push({
                 subArr: subArr,
-                pathString: 'M ' + subArr[1] * 1920 + ' ' + subArr[2] * 1080 +
-                  ' L ' + subArr[3] * 1920 + ' ' + subArr[4] * 1080 +
-                  ' L ' + subArr[5] * 1920 + ' '  + subArr[6] * 1080 +
-                  ' L ' + subArr[7] * 1920 + ' '  + subArr[8] * 1080 +
+                pathString:
+                  'M ' +
+                  subArr[1] * 1920 +
+                  ' ' +
+                  subArr[2] * 1080 +
+                  ' L ' +
+                  subArr[3] * 1920 +
+                  ' ' +
+                  subArr[4] * 1080 +
+                  ' L ' +
+                  subArr[5] * 1920 +
+                  ' ' +
+                  subArr[6] * 1080 +
+                  ' L ' +
+                  subArr[7] * 1920 +
+                  ' ' +
+                  subArr[8] * 1080 +
                   ' z',
                 // left: 0,
                 // top: 0,
@@ -239,33 +262,32 @@ const handleImgSuccess = data => {
 
   let arr = state.cacheData.url.split('upload')
   // console.log(arr)
-  let filenames = (arr[arr.length - 1]).split('.')
+  let filenames = arr[arr.length - 1].split('.')
   filenames[filenames.length - 1] = 'txt'
   // console.log(filenames.join('.'))
   let filename = filenames.join('.')
-  filename = filename.replace(/\\/g, "/")
+  filename = filename.replace(/\\/g, '/')
   if (filename.startsWith('/')) {
     filename = filename.substring(1)
   }
   // console.log(filename)
 
-  labelFile(data['data'], filename)
-    .then(res => {
-      // console.log(res)
-      if (res.code === 200) {
-        state.cacheData.labelurl = res.data.url
-        updateDataApi(state.cacheData)
-          .then(res => {
-            // console.log(res)
-            if (res.data) {
-              ElMessage.success('操作成功')
-            }
-          })
-          .catch(err => {
-            console.log(err)
-          })
-      }
-    })
+  labelFile(data['data'], filename).then(res => {
+    // console.log(res)
+    if (res.code === 200) {
+      state.cacheData.labelurl = res.data.url
+      updateDataApi(state.cacheData)
+        .then(res => {
+          // console.log(res)
+          if (res.data) {
+            ElMessage.success('操作成功')
+          }
+        })
+        .catch(err => {
+          console.log(err)
+        })
+    }
+  })
 
   // data.forEach(item => {
   //   if (item.startsWith('{')) {

+ 4 - 4
src/views/taais/homePage/index.vue

@@ -4,13 +4,13 @@
       <div class="table-box">
         <ProTable ref="proTable" :columns="columns" row-key="id" :request-api="listITaskNewApi" :tool-button="false">
           <template #tableHeader="">
-<!--            <el-button type="primary" v-auth="['system:user:add']" :icon="CirclePlus" @click="createTask()"> 创建任务 </el-button>-->
+            <!--            <el-button type="primary" v-auth="['system:user:add']" :icon="CirclePlus" @click="createTask()"> 创建任务 </el-button>-->
             <el-button type="primary" v-auth="['system:user:add']" :icon="CirclePlus" @click="createTaskNew()"> 创建任务 </el-button>
           </template>
           <!-- 表格操作 -->
           <template #operation="scope">
             <el-button type="primary" link icon="View" v-auth="['task:task:query']" @click="viewDetails(scope.row)"> 查看详情 </el-button>
-<!--            <el-button type="primary" link :icon="View" @click="openDialog(3, '任务查看', scope.row)">查看</el-button>-->
+            <!--            <el-button type="primary" link :icon="View" @click="openDialog(3, '任务查看', scope.row)">查看</el-button>-->
           </template>
         </ProTable>
         <!-- <ProTable
@@ -120,14 +120,14 @@ const columns = reactive<ColumnProps<User.ResUserList>[]>([
   // },
   {
     prop: 'createTime',
-    label: '创建时间',
+    label: '创建时间'
     // search: {
     //   el: 'input'
     // }
   },
   {
     prop: 'updateTime',
-    label: '更新时间',
+    label: '更新时间'
     // search: {
     //   el: 'input'
     // }

+ 205 - 288
src/views/taais/homePage/task/index.vue

@@ -4,13 +4,7 @@
       <el-container style="height: 30px"></el-container>
       <el-container>
         <span class="span_class">任务名称</span>
-        <el-input
-          style="width: 300px"
-          type="text"
-          placeholder="请输入任务名称"
-          v-model="formData.taskName"
-        >
-        </el-input>
+        <el-input style="width: 300px" type="text" placeholder="请输入任务名称" v-model="formData.taskName"> </el-input>
       </el-container>
 
       <el-container style="height: 10px"></el-container>
@@ -34,27 +28,17 @@
         <span class="span_class">选择算法</span>
         <el-container style="display: flex; flex-direction: column">
           <el-container v-for="(item, index) in formData.algorithms" :key="index" style="margin-top: 5px">
-            <el-select style="width: 200px" v-model="formData.algoIdx[index]" placeholder="请选择" @change="onModelSelect(index)">
-              <el-option
-                v-for="(item1,index) in algorithms"
-                :key="item1.id"
-                :label="item1.name"
-                :value="index"
-              >
-              </el-option>
+            <el-select style="width: 200px" v-model="formData.algoIdx[index]" placeholder="请选择" @change="onAlgorithmModelSelect(index)">
+              <el-option v-for="(item1, index) in algorithms" :key="item1.id" :label="item1.name" :value="index"> </el-option>
             </el-select>
-            <el-button style="margin-left: 20px" @click="showSuperParameterConfig(item, index)">
-              超参配置
-            </el-button>
-            <el-button @click="showAddModelDialog">
-              导入模型
-            </el-button>
-            <el-button v-show="formData.algorithms.length>1" @click="removeItem(index)">
-              删除算法
-            </el-button>
-            <el-button v-show="formData.taskType === '1' && index + 1 === formData.algorithms.length" @click="appendNewItem">
-              添加算法
-            </el-button>
+            <el-button style="margin-left: 20px" @click="showSuperParameterConfig(item, index)"> 超参配置 </el-button>
+            <span class="span_class1">选择模型</span>
+            <el-select style="width: 200px" v-model="formData.algoModelId[index]" placeholder="未选择将使用默认模型">
+              <el-option v-for="item1 in formData.algoModelList[index]" :key="item1.id" :label="item1.modelName" :value="item1.id"> </el-option>
+            </el-select>
+            <el-button @click="showAddModelDialog(item, index)"> 导入模型 </el-button>
+            <el-button v-show="formData.algorithms.length > 1" @click="removeItem(index)"> 删除算法 </el-button>
+            <el-button v-show="formData.taskType === '1' && index + 1 === formData.algorithms.length" @click="appendNewItem"> 添加算法 </el-button>
           </el-container>
         </el-container>
       </el-container>
@@ -64,15 +48,15 @@
         <span class="span_class">选择训练数据</span>
         <el-container style="display: flex; flex-direction: column">
           <el-container v-for="(item, index) in formData.data" :key="index" style="margin-top: 5px">
-            <el-button v-show="formData.data" @click="showDataSelectionDialog(index, true)">
-              选择批次
-            </el-button>
-            <span v-if="!item || item.length === 0" class="span_class" style="color: red; font-size: 15px">未完成</span>
-            <span v-else class="span_class" style="color: greenyellow; font-size: 15px">已选择</span>
-            <el-button v-show="formData.data.length>1" style="margin-left: 20px" @click="removeDataItem(index, true)">
-              删除数据
-            </el-button>
-            <el-button v-show="formData.taskType === '2' && index + 1 === formData.data.length" style="margin-left: 20px" @click="appendNewDataItem(true)">
+            <el-button v-show="formData.data" @click="showDataSelectionDialog(index, true)"> 选择批次 </el-button>
+            <span v-if="!item || item.length === 0" class="span_class" style=" font-size: 15px;color: red">未完成</span>
+            <span v-else class="span_class" style=" font-size: 15px;color: greenyellow">已选择</span>
+            <el-button v-show="formData.data.length > 1" style="margin-left: 20px" @click="removeDataItem(index, true)"> 删除数据 </el-button>
+            <el-button
+              v-show="formData.taskType === '2' && index + 1 === formData.data.length"
+              style="margin-left: 20px"
+              @click="appendNewDataItem(true)"
+            >
               添加数据
             </el-button>
           </el-container>
@@ -84,15 +68,15 @@
         <span class="span_class">选择测试数据</span>
         <el-container style="display: flex; flex-direction: column">
           <el-container v-for="(item, index) in formData.testData" :key="index" style="margin-top: 5px">
-            <el-button v-show="formData.testData" @click="showDataSelectionDialog(index, false)">
-              选择批次
-            </el-button>
-            <span v-if="!item || item.length === 0" class="span_class" style="color: red; font-size: 15px">未完成</span>
-            <span v-else class="span_class" style="color: greenyellow; font-size: 15px">已选择</span>
-            <el-button v-show="formData.testData.length>1" style="margin-left: 20px" @click="removeDataItem(index, false)">
-              删除数据
-            </el-button>
-            <el-button v-show="formData.taskType === '2' && index + 1 === formData.testData.length" style="margin-left: 20px" @click="appendNewDataItem(false)">
+            <el-button v-show="formData.testData" @click="showDataSelectionDialog(index, false)"> 选择批次 </el-button>
+            <span v-if="!item || item.length === 0" class="span_class" style=" font-size: 15px;color: red">未完成</span>
+            <span v-else class="span_class" style=" font-size: 15px;color: greenyellow">已选择</span>
+            <el-button v-show="formData.testData.length > 1" style="margin-left: 20px" @click="removeDataItem(index, false)"> 删除数据 </el-button>
+            <el-button
+              v-show="formData.taskType === '2' && index + 1 === formData.testData.length"
+              style="margin-left: 20px"
+              @click="appendNewDataItem(false)"
+            >
               添加数据
             </el-button>
           </el-container>
@@ -103,161 +87,83 @@
       <el-container>
         <span class="span_class">是否训练扩增</span>
         <el-checkbox v-model="formData.expandData">扩增</el-checkbox>
-        <el-button v-show="formData.expandData" style="margin-left: 20px" @click="showExpandDataSuperParameterConfig">
-          超参配置
-        </el-button>
+        <el-button v-show="formData.expandData" style="margin-left: 20px" @click="showExpandDataSuperParameterConfig"> 超参配置 </el-button>
       </el-container>
 
-      <el-form-item style="margin-left: 30px; margin-top: 20px">
-        <el-button type="primary" @click="submit">
-          提交
-        </el-button>
+      <el-form-item style=" margin-top: 20px;margin-left: 30px">
+        <el-button type="primary" @click="submit"> 提交 </el-button>
 
-        <el-button @click="cancel">
-          取消
-        </el-button>
+        <el-button @click="cancel"> 取消 </el-button>
       </el-form-item>
     </dv-border-box1>
 
-    <FormDialog ref="formDialogRef" v-on:update="handleUpdateModelList" />
+    <FormDialog ref="formDialogRef" @update="handleUpdateModelList" />
 
-    <el-dialog
-      v-model="expandDataDialogVisible"
-      title="数据扩增超参配置"
-    >
-<!--      <span style="font-size: 16px; color: darkorange; font-weight: bold"> 数据扩增超参 </span>-->
+    <el-dialog v-model="expandDataDialogVisible" title="数据扩增超参配置">
+      <!--      <span style="font-size: 16px; color: darkorange; font-weight: bold"> 数据扩增超参 </span>-->
       <el-container v-for="(item, index) in expandDataConfig" :key="index" style="margin-top: 5px">
         <span class="span_class"> {{ item.name }}</span>
-        <el-input
-          style="width: 300px"
-          type="text"
-          :placeholder="'请输入'+item.name"
-          v-model="item.defaultValue"
-        >
-        </el-input>
+        <el-input style="width: 300px" type="text" :placeholder="'请输入' + item.name" v-model="item.defaultValue"> </el-input>
       </el-container>
       <el-container style="height: 10px"></el-container>
 
-      <span class="dialog-footer" >
+      <span class="dialog-footer">
         <el-button type="primary" @click="submitExpandDataConfigDialog">确 定</el-button>
         <el-button @click="expandDataDialogVisible = false">取 消</el-button>
       </span>
     </el-dialog>
 
-    <el-dialog
-      v-model="dialogVisible"
-      :title="superParameterFormData.title"
-    >
-      <span style="font-size: 16px; color: darkorange; font-weight: bold"> 训练超参 </span>
+    <el-dialog v-model="dialogVisible" :title="superParameterFormData.title">
+      <span style="font-size: 16px; font-weight: bold; color: darkorange"> 训练超参 </span>
       <el-container v-for="(item, index) in superParameterFormData.trainParams" :key="index">
         <span class="span_class"> {{ item.name }}</span>
-        <el-input
-          style="width: 300px"
-          type="text"
-          :placeholder="'请输入'+item.name"
-          v-model="item.defaultValue"
-        >
-        </el-input>
+        <el-input style="width: 300px" type="text" :placeholder="'请输入' + item.name" v-model="item.defaultValue"> </el-input>
       </el-container>
       <el-container style="height: 10px"></el-container>
 
-      <span style="font-size: 16px; color: darkorange; font-weight: bold"> 验证超参 </span>
+      <span style="font-size: 16px; font-weight: bold; color: darkorange"> 验证超参 </span>
       <el-container v-for="(item, index) in superParameterFormData.verifyParams" :key="index">
         <span class="span_class"> {{ item.name }}</span>
-        <el-input
-          style="width: 300px"
-          type="text"
-          :placeholder="'请输入'+item.name"
-          v-model="item.defaultValue"
-        >
-        </el-input>
+        <el-input style="width: 300px" type="text" :placeholder="'请输入' + item.name" v-model="item.defaultValue"> </el-input>
       </el-container>
       <el-container style="height: 10px"></el-container>
 
-      <span style="font-size: 16px; color: darkorange; font-weight: bold"> 测试超参 </span>
+      <span style="font-size: 16px; font-weight: bold; color: darkorange"> 测试超参 </span>
       <el-container v-for="(item, index) in superParameterFormData.testParams" :key="index">
         <span class="span_class"> {{ item.name }}</span>
-        <el-input
-          style="width: 300px"
-          type="text"
-          :placeholder="'请输入'+item.name"
-          v-model="item.defaultValue"
-        >
-        </el-input>
+        <el-input style="width: 300px" type="text" :placeholder="'请输入' + item.name" v-model="item.defaultValue"> </el-input>
       </el-container>
       <el-container style="height: 10px"></el-container>
 
-      <span class="dialog-footer" >
+      <span class="dialog-footer">
         <el-button type="primary" @click="submitConfigDialog">确 定</el-button>
         <el-button @click="dialogVisible = false">取 消</el-button>
       </span>
     </el-dialog>
 
-    <el-dialog
-      v-model="dataDialogVisible"
-      title="选择数据批次"
-      style="width: 70vw"
-    >
+    <el-dialog v-model="dataDialogVisible" title="选择数据批次" style="width: 70vw">
       <el-container>
-        <el-table
-          :data="batchDataList"
-          tooltip-effect="dark"
-          style="width: 100%"
-          @selection-change="handleSelectionChange"
-        >
-          <el-table-column
-            type="selection"
-            width="55"
-          >
-          </el-table-column>
-          <el-table-column
-            prop="batchNum"
-            label="所有批次"
-            width="120"
-          >
-          </el-table-column>
-          <el-table-column
-            prop="batchSize"
-            label="数量"
-            width="80"
-          >
-          </el-table-column>
+        <el-table :data="batchDataList" tooltip-effect="dark" style="width: 100%" @selection-change="handleSelectionChange">
+          <el-table-column type="selection" width="55"> </el-table-column>
+          <el-table-column prop="batchNum" label="所有批次" width="120"> </el-table-column>
+          <el-table-column prop="batchSize" label="数量" width="80"> </el-table-column>
         </el-table>
 
-        <el-container style="margin-left: 10px; margin-right: 10px; display: flex; flex-direction: column; justify-content: center">
+        <el-container style=" display: flex; flex-direction: column; justify-content: center; margin-right: 10px;margin-left: 10px">
           <el-button type="primary" :disabled="!canSelect" @click="clickSelectData"> {{ '=>' }} </el-button>
           <el-container style="height: 10px"></el-container>
           <el-button type="primary" :disabled="!canDeselect" @click="clickDeselectData"> {{ '<=' }} </el-button>
         </el-container>
 
-        <el-table
-          :data="selectedBatchDataList"
-          tooltip-effect="dark"
-          style="width: 100%"
-          @selection-change="handleDeselectionChange"
-        >
-          <el-table-column
-            type="selection"
-            width="55"
-          >
-          </el-table-column>
-          <el-table-column
-            prop="batchNum"
-            label="已选批次"
-            width="120"
-          >
-          </el-table-column>
-          <el-table-column
-            prop="batchSize"
-            label="数量"
-            width="80"
-          >
-          </el-table-column>
+        <el-table :data="selectedBatchDataList" tooltip-effect="dark" style="width: 100%" @selection-change="handleDeselectionChange">
+          <el-table-column type="selection" width="55"> </el-table-column>
+          <el-table-column prop="batchNum" label="已选批次" width="120"> </el-table-column>
+          <el-table-column prop="batchSize" label="数量" width="80"> </el-table-column>
         </el-table>
       </el-container>
 
       <el-container style="height: 10px"></el-container>
-      <span class="dialog-footer" >
+      <span class="dialog-footer">
         <el-button type="primary" @click="submitDataDialog">确 定</el-button>
         <el-button @click="dataDialogVisible = false">取 消</el-button>
       </span>
@@ -266,15 +172,15 @@
 </template>
 
 <script setup lang="ts">
-import {reactive, ref, onMounted, computed} from "vue";
-import {addAlgorithmTaskConfigurationApi} from "@/api/modules/ag/model";
-import {createTaskApi, getAlgorithmOptionApi} from "@/api/modules/task/task";
-import FormDialog from "@/components/FormDialog/index.vue";
+import { reactive, ref, onMounted, computed } from 'vue'
+import { addModelApi, queryModelList } from '@/api/modules/ag/model'
+import { createTaskApi } from '@/api/modules/task/task'
+import FormDialog from '@/components/FormDialog/index.vue'
 import { useRouter } from 'vue-router'
-import {listTaskConfigurationApi} from "@/api/modules/task/taskConfiguration";
-import {batchListDataApi} from "@/api/modules/demo/data";
-import {listDataApi} from "@/api/modules/system/dictData";
-import {ElMessage} from "element-plus";
+import { listTaskConfigurationApi } from '@/api/modules/task/taskConfiguration'
+import { batchListDataApi } from '@/api/modules/demo/data'
+import { listDataApi } from '@/api/modules/system/dictData'
+import { ElMessage } from 'element-plus'
 const valid = data => {
   let ret = {
     code: 400,
@@ -327,6 +233,7 @@ const valid = data => {
 }
 
 const submit = () => {
+  console.log('submit data, formdata is: ', formData)
   const params = {
     taskName: formData.taskName,
     taskType: formData.taskType,
@@ -344,14 +251,33 @@ const submit = () => {
     return
   }
 
+  let __idx = 0
   params.algTaskList.forEach(obj => {
-    obj.algorithmId = obj.id;
+    obj.algorithmId = obj.id
     // if (!obj.trainParams) {
     //   return
     // }
-    obj.params = JSON.stringify(JSON.parse(obj.trainParams)) + ";;;" +
-      JSON.stringify(JSON.parse(obj.verifyParams)) + ";;;" +
-      JSON.stringify(JSON.parse(obj.testParams))
+    let o1 = JSON.parse(obj.trainParams)
+    let o2 = JSON.parse(obj.verifyParams)
+    let o3 = JSON.parse(obj.testParams)
+    if (formData.algoModelId[__idx] && formData.algoModelId[__idx].length > 0) {
+      const __trained = {
+        agName: 'pretrained_model',
+        defaultValue: true,
+        modelId: formData.algoModelId[__idx]
+      }
+      o1.push(__trained)
+      o2.push(__trained)
+      o3.push(__trained)
+    }
+
+    obj.params =
+      JSON.stringify(o1) +
+      ';;;' +
+      JSON.stringify(o2) +
+      ';;;' +
+      JSON.stringify(o3)
+    __idx++
   })
   // console.log('submit', params)
 
@@ -403,12 +329,14 @@ const submitExpandDataConfigDialog = () => {
 
 const expandDataDialogVisible = ref(false)
 
-let expandDataConfig = ref(reactive([
-  // {
-  //   name: 'config1',
-  //   defaultValue: 'a'
-  // }
-]))
+let expandDataConfig = ref(
+  reactive([
+    // {
+    //   name: 'config1',
+    //   defaultValue: 'a'
+    // }
+  ])
+)
 
 const submitConfigDialog = () => {
   dialogVisible.value = false
@@ -439,24 +367,18 @@ const canDeselect = computed(() => {
 })
 
 const handleUpdateModelList = () => {
-  listTaskConfigurationApi({ pageNum: 1, pageSize: 10000 })
-    .then(res => {
-      // console.log(res)
-      algorithms = reactive(res.data.list)
-    })
+  updateModelList(UPDATE_INDEX.value.id, UPDATE_INDEX.value.idx)
 }
 
 onMounted(() => {
-  listTaskConfigurationApi({ pageNum: 1, pageSize: 10000 })
-    .then(res => {
-      // console.log(res)
-      algorithms = reactive(res.data.list)
-    })
-  batchListDataApi()
-    .then(res => {
-      // console.log(res)
-      queryBatchData.value = reactive(res.data)
-    })
+  listTaskConfigurationApi({ pageNum: 1, pageSize: 10000 }).then(res => {
+    // console.log(res)
+    algorithms = reactive(res.data.list)
+  })
+  batchListDataApi().then(res => {
+    // console.log(res)
+    queryBatchData.value = reactive(res.data)
+  })
 })
 let queryBatchData = ref(reactive([]))
 const handleSelectionChange = data => {
@@ -468,7 +390,7 @@ const handleDeselectionChange = data => {
   // console.log(data)
 }
 const clickSelectData = () => {
-  let set = new Set
+  let set = new Set()
   for (let i = 0; i < tempSelectedBatchDataList.value.length; i++) {
     set.add(tempSelectedBatchDataList.value[i].batchNum)
     selectedBatchDataList.value.push(tempSelectedBatchDataList.value[i])
@@ -483,7 +405,7 @@ const clickSelectData = () => {
   // console.log(batchDataList)
 }
 const clickDeselectData = () => {
-  let set = new Set
+  let set = new Set()
   for (let i = 0; i < tempDeselectedBatchDataList.value.length; i++) {
     set.add(tempDeselectedBatchDataList.value[i].batchNum)
     batchDataList.value.push(tempDeselectedBatchDataList.value[i])
@@ -508,20 +430,14 @@ const formData = reactive({
   taskName: null,
   taskType: '1',
   tasks: [],
-  algorithms: [
-    {}
-  ],
-  algoIdx: [
-    null
-  ],
+  algorithms: [{}],
+  algoIdx: [null],
+  algoModelId: [null],
+  algoModelList: [[]],
   // 训练数据
-  data: [
-    null
-  ],
+  data: [null],
   // 测试数据
-  testData: [
-    null
-  ],
+  testData: [null],
   expandData: false,
   expandConfig: ''
 })
@@ -549,17 +465,16 @@ const fillCurrentDataDialogList = (index, isTrainData) => {
   batchDataList.value = []
   selectedBatchDataList.value = []
 
-  batchListDataApi()
-    .then(res => {
-      // console.log(res)
-      for (let i = 0; i < res.data.length; i++) {
-        if (selected.has(res.data[i].batchNum)) {
-          selectedBatchDataList.value.push(res.data[i])
-        } else {
-          batchDataList.value.push(res.data[i])
-        }
+  batchListDataApi().then(res => {
+    // console.log(res)
+    for (let i = 0; i < res.data.length; i++) {
+      if (selected.has(res.data[i].batchNum)) {
+        selectedBatchDataList.value.push(res.data[i])
+      } else {
+        batchDataList.value.push(res.data[i])
       }
-    })
+    }
+  })
 }
 
 let superParameterFormData = reactive({})
@@ -578,96 +493,90 @@ const showSuperParameterConfig = (item, index) => {
 
 let algorithms = reactive([])
 
-const onModelSelect = (index) => {
+const onAlgorithmModelSelect = index => {
   formData.algorithms[index] = algorithms[formData.algoIdx[index]]
+  updateModelList(formData.algorithms[index].id, index)
 }
 
 const formDialogRef = ref<InstanceType<typeof FormDialog> | null>(null)
 
-const showAddModelDialog = () => {
-  getAlgorithmOptionApi().then(res => {
-    let allAgloData = {
-      value: res.data
-    }
-    const params = {
-      title: '算法模型配置',
-      width: 580,
-      isEdit: true,
-      itemsOptions: [
-        {
-          label: '模型名称',
-          prop: 'modelName',
-          rules: [{ required: true, message: '模型名称不能为空', trigger: 'blur' }],
-          compOptions: {
-            placeholder: '请输入模型名称'
-          }
-        },
-        {
-          label: '模型',
-          prop: 'modelAddress',
-          rules: [],//{ required: true, message: '模型不能为空', trigger: 'blur' }
-          compOptions: {
-            elTagName: 'slot'
-          }
-        },
-        {
-          label: '训练算法地址',
-          prop: 'trainUrl',
-          rules: [{ required: true, message: '训练算法地址不能为空', trigger: 'blur' }],
-          compOptions: {
-            placeholder: '请输入训练算法地址'
-          }
-        },
-        {
-          label: '训练超参配置',
-          prop: 'trainParams',
-          compOptions: {
-            placeholder: '请输入训练超参'
-          }
-        },
-        {
-          label: '验证算法地址',
-          prop: 'valUrl',
-          rules: [{ required: true, message: '验证算法地址不能为空', trigger: 'blur' }],
-          compOptions: {
-            placeholder: '请输入验证算法地址'
-          }
-        },
-        {
-          label: '验证超参配置',
-          prop: 'valParams',
-          compOptions: {
-            placeholder: '请输入验证超参'
-          }
-        },
-        {
-          label: '测试算法地址',
-          prop: 'testUrl',
-          rules: [{ required: true, message: '测试算法地址不能为空', trigger: 'blur' }],
-          compOptions: {
-            placeholder: '请输入测试算法地址'
-          }
-        },
-        {
-          label: '测试超参配置',
-          prop: 'testParams',
-          compOptions: {
-            placeholder: '请输入测试超参'
-          }
-        },
-
-      ],
-      model: {},
-      api: addAlgorithmTaskConfigurationApi
-    }
 
-    formDialogRef.value?.openDialog(params)
+let UPDATE_INDEX = ref({ id: null, idx: null })
+const showAddModelDialog = (item, index) => {
+  let _id = null
+  try {
+    _id = item.id
+  } catch (e) {
+    ElMessage.error("请先选择算法模型")
+    return
+  }
+  if (!_id || _id.length === 0) {
+    ElMessage.error("请先选择算法模型")
+    return
+  }
+  UPDATE_INDEX.value = {
+    id: _id,
+    idx: index
+  }
+  const params = {
+    title: '算法模型配置',
+    width: 580,
+    isEdit: true,
+    itemsOptions: [
+      {
+        label: '模型名称',
+        prop: 'modelName',
+        rules: [{ required: true, message: '模型名称不能为空', trigger: 'blur' }],
+        compOptions: {
+          placeholder: '请输入模型名称'
+        }
+      },
+      {
+        label: '模型',
+        prop: 'modelAddress',
+        rules: [], //{ required: true, message: '模型不能为空', trigger: 'blur' }
+        compOptions: {
+          elTagName: 'slot'
+        }
+      },
+      {
+        label: '训练样本数',
+        prop: 'sampleNumber',
+        compOptions: {
+          placeholder: '请输入训练样本数'
+        }
+      },
+      {
+        label: '训练循环次数',
+        prop: 'cycleEpoch',
+        compOptions: {
+          placeholder: '请输入训练循环次数'
+        }
+      }
+    ],
+    model: {},
+    api: addModelApi
+  }
+  console.log('check item:', item)
+  formDialogRef.value?.openDialog(params, item.id)
+}
+
+// 获取算法模型绑定列表
+const updateModelList = (id, index) => {
+  // 获取算法列表?
+  queryModelList({id: id}).then(res => {
+    console.log('algorithm task model get: ', res)
+    if (res.code === 200) {
+      formData.algoModelList[index] = [ {id: null, modelName: '未选择'} , ...res.data]
+    }
   })
 }
 
 const appendNewItem = () => {
   formData.algorithms.push({})
   formData.algoIdx.push(null)
+  formData.algoModelId.push(null)
+  formData.algoModelList.push([])
 }
 const appendNewDataItem = isTrainData => {
   if (isTrainData) {
@@ -679,6 +588,8 @@ const appendNewDataItem = isTrainData => {
 const removeItem = idx => {
   formData.algorithms.splice(idx, 1)
   formData.algoIdx.splice(idx, 1)
+  formData.algoModelId.splice(idx, 1)
+  formData.algoModelList.splice(idx, 1)
 }
 const removeDataItem = (idx, isTrainData) => {
   if (isTrainData) {
@@ -691,7 +602,6 @@ const cancel = () => {
   // console.log('cancel')
   router.push(`/index`)
 }
-
 </script>
 
 <style scoped>
@@ -704,10 +614,17 @@ const cancel = () => {
   background-size: 100% 100%;
 }
 .span_class {
+  display: flex;
+  align-self: center;
+  justify-content: center;
   width: 120px;
   font-size: 13px;
+}
+.span_class1 {
   display: flex;
   align-self: center;
-  justify-content: center
+  justify-content: center;
+  width: 65px;
+  font-size: 13px;
 }
 </style>

+ 233 - 108
src/views/task/bizProcess/index.vue

@@ -5,7 +5,9 @@
       <template #tableHeader="scope">
         <!-- <el-button type="primary" v-auth="['identification:identificationSubtaskDetails:add']" icon="CirclePlus" @click="openDialog(1, '算法业务处理新增')"> 新增 </el-button>
         <el-button type="primary" v-auth="['identification:identificationSubtaskDetails:import']" icon="Upload" plain @click="batchAdd"> 导入 </el-button> -->
-        <el-button type="primary" v-auth="['identification:identificationSubtaskDetails:export']" icon="Download" plain @click="downloadFile"> 导出 </el-button>
+        <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']"
@@ -16,37 +18,63 @@
         >
           批量删除
         </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="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" v-auth="['identification:identificationSubtaskDetails:query']" @click="openDialog(3, '算法业务处理查看', scope.row)">
+        <el-button
+          type="primary"
+          link
+          icon="View"
+          v-auth="['identification:identificationSubtaskDetails:query']"
+          @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="finished" @click="showResult(scope.row)"> 执行结果 </el-button>
 
-        <el-button type="primary" link icon="Refresh" @click="reRunTask(scope.row)">
-          重新执行
+        <el-button v-show="scope.row.name.indexOf('训练') === -1" type="primary" link icon="search" @click="showExecutedTime(scope.row)">
+          执行时间
         </el-button>
 
-        <el-button type="primary" link icon="Refresh" @click="exportData(scope.row)">
-          导出结果
+        <el-button v-if="scope.row.name.indexOf('训练') === -1" type="primary" link icon="Refresh" @click="reRunTask(scope.row)">
+          执行训练
         </el-button>
+        <el-button v-else 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="EditPen" v-auth="['identification:identificationSubtaskDetails:edit']" @click="openDialog(2, '算法业务处理编辑', scope.row)">-->
-<!--          编辑-->
-<!--        </el-button>-->
-<!--        <el-button type="primary" link icon="Delete" v-auth="['identification:identificationSubtaskDetails:remove']" @click="deleteBizProcess(scope.row)"> 删除 </el-button>-->
-        <el-button type="primary" link icon="document" v-auth="['identification:identificationSubtaskDetails:query']" @click="viewLog(scope.row)"> 查看日志 </el-button>
+        <!--        <el-button type="primary" link icon="EditPen" v-auth="['identification:identificationSubtaskDetails:edit']" @click="openDialog(2, '算法业务处理编辑', scope.row)">-->
+        <!--          编辑-->
+        <!--        </el-button>-->
+        <!--        <el-button type="primary" link icon="Delete" v-auth="['identification:identificationSubtaskDetails:remove']" @click="deleteBizProcess(scope.row)"> 删除 </el-button>-->
+        <el-button type="primary" link icon="document" v-auth="['identification:identificationSubtaskDetails:query']" @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'] }}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</h5>
+            <h5>推理用时: {{ item['inference'] }}&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</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>
@@ -114,73 +142,66 @@
       </div>
     </el-dialog>
 
-    <el-dialog
-      v-model="resultDialogVisible"
-      title="执行结果"
-    >
-<!--      style="width: 70vw"-->
+    <el-dialog v-model="resultDialogVisible" title="执行结果">
+      <!--      style="width: 70vw"-->
       <el-card :body-style="{ padding: '0px' }">
-        <div style="padding: 14px;">
+        <div style="padding: 14px">
           <span>P_curve</span>
         </div>
         <el-image :src="PATH_PREFIX + refSelectData.resultPath + '/P_curve.png'"></el-image>
       </el-card>
 
       <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
-        <div style="padding: 14px;">
+        <div style="padding: 14px">
           <span>R_curve</span>
         </div>
         <el-image :src="PATH_PREFIX + refSelectData.resultPath + '/R_curve.png'"></el-image>
       </el-card>
 
       <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
-        <div style="padding: 14px;">
+        <div style="padding: 14px">
           <span>PR_curve</span>
         </div>
         <el-image :src="PATH_PREFIX + refSelectData.resultPath + '/PR_curve.png'"></el-image>
       </el-card>
 
       <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
-        <div style="padding: 14px;">
+        <div style="padding: 14px">
           <span>F1_curve</span>
         </div>
         <el-image :src="PATH_PREFIX + refSelectData.resultPath + '/F1_curve.png'"></el-image>
       </el-card>
-
     </el-dialog>
 
-    <el-dialog
-      v-model="compareDialogVisible"
-      title="执行结果对比"
-    >
+    <el-dialog v-model="compareDialogVisible" title="验证指标对比">
       <!--      style="width: 70vw"-->
       <el-container style="display: flex; flex-direction: row">
-        <el-container v-for="(item, index) in listData" :key="index" style="display: flex; flex-direction: column">
-<!--          <el-button @click="console.log(item)">test</el-button>-->
+        <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;">
+            <div style="padding: 14px">
               <span>P_curve</span>
             </div>
             <el-image :src="PATH_PREFIX + item.resultPath + '/P_curve.png'"></el-image>
           </el-card>
 
           <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
-            <div style="padding: 14px;">
+            <div style="padding: 14px">
               <span>R_curve</span>
             </div>
             <el-image :src="PATH_PREFIX + item.resultPath + '/R_curve.png'"></el-image>
           </el-card>
 
           <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
-            <div style="padding: 14px;">
+            <div style="padding: 14px">
               <span>PR_curve</span>
             </div>
             <el-image :src="PATH_PREFIX + item.resultPath + '/PR_curve.png'"></el-image>
           </el-card>
 
           <el-card :body-style="{ padding: '0px' }" style="margin-top: 10px">
-            <div style="padding: 14px;">
+            <div style="padding: 14px">
               <span>F1_curve</span>
             </div>
             <el-image :src="PATH_PREFIX + item.resultPath + '/F1_curve.png'"></el-image>
@@ -189,22 +210,40 @@
       </el-container>
     </el-dialog>
 
-    <el-dialog
-      v-model="valDialogVisible"
-      :title="titleMsg"
-      style="width: 85vw; height: 85vh"
-    >
+    <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="width: 80px; margin-right: 20px"> {{ _item.name }} </span>
-            <el-image v-if="_item.srcUrl" :src="_item.srcUrl" style="height: 200px; width:200px"></el-image>
+            <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 style="height: 200px; width:200px" ref="imgMaker" v-if="_item.imgUrl" :is-pic-only="true" :src="_item.imgUrl" :area="area" :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="height: 200px; width:200px"></el-image>
+            <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>
@@ -236,27 +275,70 @@ import {
 } 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 { 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 http from '@/api'
 const route = useRoute()
 const PATH_PREFIX = 'api/profile/task'
 let resultDialogVisible = ref(false)
 let refSelectData = ref(reactive({}))
 const reRunTask = row => {
-  http.post<any>('/identification/identificationSubtaskDetails/execute', {taskId: row.id}, { loading: false })
+  http.post<any>('/identification/identificationSubtaskDetails/execute', { taskId: row.id }, { loading: false }).then(res => {
+    if (res.code !== 200) {
+      ElMessage.error(res.msg)
+    } else {
+      ElMessage.success('启动成功')
+    }
+  })
+}
+const startTask = row => {
+  http.post<any>('/identification/identificationSubtaskDetails/startTask', { taskId: 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');
-      }
-    })
+  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 => {
+  // row.remarks = '[\n' +
+  //   '{\n' +
+  //   '"preprocess": 1,\n' +
+  //   '"inference": 2,\n' +
+  //   '"postprocess": 3\n' +
+  //   '},\n' +
+  //   '{\n' +
+  //   '"preprocess": 11,\n' +
+  //   '"inference": 22,\n' +
+  //   '"postprocess": 33\n' +
+  //   '},\n' +
+  //   '{\n' +
+  //   '"preprocess": 111,\n' +
+  //   '"inference": 233,\n' +
+  //   '"postprocess": 333\n' +
+  //   '}\n' +
+  //   ']'
+  try {
+    executedTimeData.value = JSON.parse(row.remarks)
+  } catch (e) {
+    ElMessage.error('解析JSON错误,请检查任务是否已完成')
+    return
+  }
+  executedTimeVisible.value = true
 }
 
 let imgDataList = ref(reactive([]))
@@ -270,28 +352,28 @@ const showValResult = async isVal => {
     if (listData.value[i].name.indexOf(isVal ? '验证' : '测试') !== -1) {
       console.log(listData.value[i])
       if (!hasInit) {
-        hasInit = true;
+        hasInit = true
         let res = await getImgList({
           taskId: listData.value[i].preprocessPath.substring(1).split('/')[0],
           subPath: 'images'
         })
         console.log('res data', res)
         imgDataList.value.push([])
-        for (let j = 0 ; j < res.data.length; j++) {
+        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].preprocessPath + '/' + res.data[j],
+            srcUrl: 'api/profile/task' + listData.value[i].preprocessPath + (isVal ? '/images/' : '/') + res.data[j]
             // imgUrl: 'api/profile/task' + listData.value[i].preprocessPath + '/images/' + res.data[j],
             // labelUrl: 'api/profile/task' + listData.value[i].preprocessPath + '/labels/' + jList.join('.')
           }
 
           imgDataList.value[imgDataList.value.length - 1].push(obj)
           if (isVal) {
-            obj.imgUrl = 'api/profile/task' + listData.value[i].preprocessPath + '/images/' + res.data[j];
+            obj.imgUrl = 'api/profile/task' + listData.value[i].preprocessPath + '/images/' + res.data[j]
             obj.labelUrl = '/profile/task' + listData.value[i].preprocessPath + '/labels/' + jList.join('.')
-            console.log( 'url is', obj.labelUrl)
+            console.log('url is', obj.labelUrl)
             setDetail(obj)
           }
         }
@@ -301,64 +383,108 @@ const showValResult = async isVal => {
         subPath: 'images'
       })
       console.log(res.data)
+      if (isVal) {
+        await loadVerifyResult(listData.value[i].name, '/profile/task' + listData.value[i].preprocessPath + '/result/verify_result.txt')
+      }
+      console.log('load result', verifyResult.value)
       imgDataList.value.push([])
-      for (let j = 0 ; j < res.data.length; j++) {
+      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].resultPath + '/' + res.data[j],
-          name: listData.value[i].name
+          name: listData.value[i].name,
+          picName: res.data[j],
+          color: verifyResult.value[res.data[j]] ? '#00ff00' : '#ff0000'
         })
       }
     }
   }
-  console.log(imgDataList)
+  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.split('\r\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
-          }
+  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({
-          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
-        })
       }
-    })
+      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)
@@ -400,17 +526,16 @@ onMounted(() => {
     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
+      })
+    }
   })
-    .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
@@ -659,10 +784,10 @@ const columns = reactive<ColumnProps<any>[]>([
   },
   {
     prop: 'costSecond',
-    label: '耗时'
+    label: '耗时(ms)'
   },
   {
-    prop: 'log',
+    prop: 'remarks',
     label: '日志'
   },
   { prop: 'operation', label: '操作', width: 230, fixed: 'right' }

+ 13 - 3
src/views/task/subtask/index.vue

@@ -19,11 +19,21 @@
       </template>
       <!-- 表格操作 -->
       <template #operation="scope">
-        <el-button type="primary" link icon="View" v-auth="['identification:identificationTask:query']" @click="viewDetails(scope.row)"> 查看详情 </el-button>
-        <el-button type="primary" link icon="EditPen" v-auth="['identification:identificationTask:edit']" @click="openDialog(2, '算法子任务编辑', scope.row)">
+        <el-button type="primary" link icon="View" v-auth="['identification:identificationTask:query']" @click="viewDetails(scope.row)">
+          查看详情
+        </el-button>
+        <el-button
+          type="primary"
+          link
+          icon="EditPen"
+          v-auth="['identification:identificationTask:edit']"
+          @click="openDialog(2, '算法子任务编辑', scope.row)"
+        >
           编辑
         </el-button>
-        <el-button type="primary" link icon="Delete" v-auth="['identification:identificationTask:remove']" @click="deleteSubtask(scope.row)"> 删除 </el-button>
+        <el-button type="primary" link icon="Delete" v-auth="['identification:identificationTask:remove']" @click="deleteSubtask(scope.row)">
+          删除
+        </el-button>
       </template>
     </ProTable>
     <FormDialog ref="formDialogRef" />

+ 35 - 6
src/views/task/subtaskDetail/index.vue

@@ -3,11 +3,20 @@
     <ProTable ref="proTable" :columns="columns" row-key="id" :request-api="listSubtaskDetailApi">
       <!-- 表格 header 按钮 -->
       <template #tableHeader="scope">
-        <el-button type="primary" v-auth="['identification:identificationSubtaskDetails:add']" icon="CirclePlus" @click="openDialog(1, '算法子任务详情新增')">
+        <el-button
+          type="primary"
+          v-auth="['identification:identificationSubtaskDetails:add']"
+          icon="CirclePlus"
+          @click="openDialog(1, '算法子任务详情新增')"
+        >
           新增
         </el-button>
-        <el-button type="primary" v-auth="['identification:identificationSubtaskDetails:import']" icon="Upload" plain @click="batchAdd"> 导入 </el-button>
-        <el-button type="primary" v-auth="['identification:identificationSubtaskDetails:export']" icon="Download" plain @click="downloadFile"> 导出 </el-button>
+        <el-button type="primary" v-auth="['identification:identificationSubtaskDetails:import']" icon="Upload" plain @click="batchAdd">
+          导入
+        </el-button>
+        <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']"
@@ -21,13 +30,33 @@
       </template>
       <!-- 表格操作 -->
       <template #operation="scope">
-        <el-button type="primary" link icon="View" v-auth="['identification:identificationSubtaskDetails:query']" @click="openDialog(3, '算法子任务详情查看', scope.row)">
+        <el-button
+          type="primary"
+          link
+          icon="View"
+          v-auth="['identification:identificationSubtaskDetails:query']"
+          @click="openDialog(3, '算法子任务详情查看', scope.row)"
+        >
           查看
         </el-button>
-        <el-button type="primary" link icon="EditPen" v-auth="['identification:identificationSubtaskDetails:edit']" @click="openDialog(2, '算法子任务详情编辑', scope.row)">
+        <el-button
+          type="primary"
+          link
+          icon="EditPen"
+          v-auth="['identification:identificationSubtaskDetails:edit']"
+          @click="openDialog(2, '算法子任务详情编辑', scope.row)"
+        >
           编辑
         </el-button>
-        <el-button type="primary" link icon="Delete" v-auth="['identification:identificationSubtaskDetails:remove']" @click="deleteSubtaskDetail(scope.row)"> 删除 </el-button>
+        <el-button
+          type="primary"
+          link
+          icon="Delete"
+          v-auth="['identification:identificationSubtaskDetails:remove']"
+          @click="deleteSubtaskDetail(scope.row)"
+        >
+          删除
+        </el-button>
       </template>
     </ProTable>
     <FormDialog ref="formDialogRef" />

+ 244 - 262
src/views/task/taskConfiguration/index.vue

@@ -1,275 +1,257 @@
 <template>
-    <div class="table-box">
-        <ProTable ref="proTable" :columns="columns" row-key="id" :request-api="listTaskConfigurationApi" >
-            <!-- 表格 header 按钮 -->
-            <template #tableHeader="scope">
-                <el-button type="primary" v-auth="['task:taskConfiguration:add']" icon="CirclePlus" @click="openDialog(1, '算法任务新增')">
-                    新增
-                </el-button>
-                <el-button type="primary" v-auth="['task:taskConfiguration:import']" icon="Upload" plain @click="batchAdd">
-                    导入
-                </el-button>
-                <el-button type="primary" v-auth="['task:taskConfiguration:export']" icon="Download" plain @click="downloadFile">
-                    导出
-                </el-button>
-                <el-button
-                    type="danger"
-                    v-auth="['task:taskConfiguration:remove']"
-                    icon="Delete"
-                    plain
-                    :disabled="!scope.isSelected"
-                    @click="batchDelete(scope.selectedListIds)"
-                >
-                    批量删除
-                </el-button>
-            </template>
-            <!-- 表格操作 -->
-            <template #operation="scope">
-                <el-button
-                    type="primary"
-                    link
-                    icon="View"
-                    v-auth="['task:taskConfiguration:query']"
-                    @click="openDialog(3, '算法任务查看', scope.row)"
-                >
-                    查看
-                </el-button>
-                <el-button
-                    type="primary"
-                    link
-                    icon="EditPen"
-                    v-auth="['task:taskConfiguration:edit']"
-                    @click="openDialog(2, '算法任务编辑', scope.row)"
-                >
-                    编辑
-                </el-button>
-                <el-button type="primary" link icon="Delete" v-auth="['task:taskConfiguration:remove']" @click="deleteTaskConfiguration(scope.row)">
-                    删除
-                </el-button>
-            </template>
-        </ProTable>
-        <FormDialog ref="formDialogRef" />
-        <ImportExcel ref="dialogRef" />
-    </div>
+  <div class="table-box">
+    <ProTable ref="proTable" :columns="columns" row-key="id" :request-api="listTaskConfigurationApi">
+      <!-- 表格 header 按钮 -->
+      <template #tableHeader="scope">
+        <el-button type="primary" v-auth="['task:taskConfiguration:add']" icon="CirclePlus" @click="openDialog(1, '算法任务新增')"> 新增 </el-button>
+        <el-button type="primary" v-auth="['task:taskConfiguration:import']" icon="Upload" plain @click="batchAdd"> 导入 </el-button>
+        <el-button type="primary" v-auth="['task:taskConfiguration:export']" icon="Download" plain @click="downloadFile"> 导出 </el-button>
+        <el-button
+          type="danger"
+          v-auth="['task:taskConfiguration:remove']"
+          icon="Delete"
+          plain
+          :disabled="!scope.isSelected"
+          @click="batchDelete(scope.selectedListIds)"
+        >
+          批量删除
+        </el-button>
+      </template>
+      <!-- 表格操作 -->
+      <template #operation="scope">
+        <el-button type="primary" link icon="View" v-auth="['task:taskConfiguration:query']" @click="openDialog(3, '算法任务查看', scope.row)">
+          查看
+        </el-button>
+        <el-button type="primary" link icon="EditPen" v-auth="['task:taskConfiguration:edit']" @click="openDialog(2, '算法任务编辑', scope.row)">
+          编辑
+        </el-button>
+        <el-button type="primary" link icon="Delete" v-auth="['task:taskConfiguration:remove']" @click="deleteTaskConfiguration(scope.row)">
+          删除
+        </el-button>
+      </template>
+    </ProTable>
+    <FormDialog ref="formDialogRef" />
+    <ImportExcel ref="dialogRef" />
+  </div>
 </template>
 
 <script setup lang="tsx" name="TaskConfiguration">
-    import { ref, reactive } from 'vue'
-    import { useHandleData } from '@/hooks/useHandleData'
-    import { useDownload } from '@/hooks/useDownload'
-    import { ElMessageBox } 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 {
-        listTaskConfigurationApi,
-        delTaskConfigurationApi,
-        addTaskConfigurationApi,
-        updateTaskConfigurationApi,
-        importTemplateApi,
-        importTaskConfigurationDataApi,
-        exportTaskConfigurationApi,
-        getTaskConfigurationApi
-    } from '@/api/modules/task/taskConfiguration'
+import { ref, reactive } from 'vue'
+import { useHandleData } from '@/hooks/useHandleData'
+import { useDownload } from '@/hooks/useDownload'
+import { ElMessageBox } 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 {
+  listTaskConfigurationApi,
+  delTaskConfigurationApi,
+  addTaskConfigurationApi,
+  updateTaskConfigurationApi,
+  importTemplateApi,
+  importTaskConfigurationDataApi,
+  exportTaskConfigurationApi,
+  getTaskConfigurationApi
+} from '@/api/modules/task/taskConfiguration'
 
-    // ProTable 实例
-    const proTable = ref<ProTableInstance>()
+// ProTable 实例
+const proTable = ref<ProTableInstance>()
 
-    // 删除算法任务信息
-    const deleteTaskConfiguration = async (params: any) => {
-        await useHandleData(delTaskConfigurationApi, params.id, '删除【' + params.id + '】算法任务')
-        proTable.value?.getTableList()
-    }
+// 删除算法任务信息
+const deleteTaskConfiguration = async (params: any) => {
+  await useHandleData(delTaskConfigurationApi, params.id, '删除【' + params.id + '】算法任务')
+  proTable.value?.getTableList()
+}
 
-    // 批量删除算法任务信息
-    const batchDelete = async (ids: string[]) => {
-        await useHandleData(delTaskConfigurationApi, ids, '删除所选算法任务信息')
-        proTable.value?.clearSelection()
-        proTable.value?.getTableList()
-    }
+// 批量删除算法任务信息
+const batchDelete = async (ids: string[]) => {
+  await useHandleData(delTaskConfigurationApi, ids, '删除所选算法任务信息')
+  proTable.value?.clearSelection()
+  proTable.value?.getTableList()
+}
 
-    // 导出算法任务列表
-    const downloadFile = async () => {
-        ElMessageBox.confirm('确认导出算法任务数据?', '温馨提示', { type: 'warning' }).then(() =>
-            useDownload(exportTaskConfigurationApi, '算法任务列表', proTable.value?.searchParam)
-        )
-    }
+// 导出算法任务列表
+const downloadFile = async () => {
+  ElMessageBox.confirm('确认导出算法任务数据?', '温馨提示', { type: 'warning' }).then(() =>
+    useDownload(exportTaskConfigurationApi, '算法任务列表', proTable.value?.searchParam)
+  )
+}
 
-    // 批量添加算法任务
-    const dialogRef = ref<InstanceType<typeof ImportExcel> | null>(null)
-    const batchAdd = () => {
-        const params = {
-            title: '算法任务',
-            tempApi: importTemplateApi,
-            importApi: importTaskConfigurationDataApi,
-            getTableList: proTable.value?.getTableList
-        }
-        dialogRef.value?.acceptParams(params)
-    }
+// 批量添加算法任务
+const dialogRef = ref<InstanceType<typeof ImportExcel> | null>(null)
+const batchAdd = () => {
+  const params = {
+    title: '算法任务',
+    tempApi: importTemplateApi,
+    importApi: importTaskConfigurationDataApi,
+    getTableList: proTable.value?.getTableList
+  }
+  dialogRef.value?.acceptParams(params)
+}
 
-    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 getTaskConfigurationApi(row?.id || null)
-        }
-        // 重置表单
-        setItemsOptions()
-        const params = {
-            title,
-            width: 580,
-            isEdit: type !== 3,
-            itemsOptions: itemsOptions,
-            model: type == 1 ? {} : res.data,
-            api: type == 1 ? addTaskConfigurationApi : updateTaskConfigurationApi,
-            getTableList: proTable.value?.getTableList
-        }
-        formDialogRef.value?.openDialog(params)
-    }
+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 getTaskConfigurationApi(row?.id || null)
+  }
+  // 重置表单
+  setItemsOptions()
+  const params = {
+    title,
+    width: 580,
+    isEdit: type !== 3,
+    itemsOptions: itemsOptions,
+    model: type == 1 ? {} : res.data,
+    api: type == 1 ? addTaskConfigurationApi : updateTaskConfigurationApi,
+    getTableList: proTable.value?.getTableList
+  }
+  formDialogRef.value?.openDialog(params)
+}
 
-    // 表格配置项
-    const columns = reactive<ColumnProps<any>[]>([
-        { type: 'selection', fixed: 'left', width: 70 },
-                { prop: 'id', label: '主键ID' },
-                {
-                    prop: 'name',
-                    label: '算法任务名称',
-                        search: {
-                            el: 'input'
-                        },
-                    width: 120
-                },
-                {
-                    prop: 'trainUrl',
-                    label: '训练算法地址',
-                        search: {
-                            el: 'input'
-                        },
-                    width: 120
-                },
-                {
-                    prop: 'trainParams',
-                    label: '训练超参配置',
-                        search: {
-                            el: 'input'
-                        },
-                    width: 120
-                },
-                {
-                    prop: 'verifyUrl',
-                    label: '验证算法地址',
-                        search: {
-                            el: 'input'
-                        },
-                    width: 120
-                },
-                {
-                    prop: 'verifyParams',
-                    label: '验证超参配置',
-                        search: {
-                            el: 'input'
-                        },
-                    width: 120
-                },
-                {
-                    prop: 'testUrl',
-                    label: '测试算法地址',
-                        search: {
-                            el: 'input'
-                        },
-                    width: 120
-                },
-                {
-                    prop: 'testParams',
-                    label: '测试超参配置',
-                        search: {
-                            el: 'input'
-                        },
-                    width: 120
-                },
-                {
-                    prop: 'remark',
-                    label: '备注',
-                    width: 120
-                },
-        { prop: 'operation', label: '操作', width: 230, fixed: 'right' }
-    ])
-    // 表单配置项
-    let itemsOptions: ProForm.ItemsOptions[] = []
-    const setItemsOptions = () => {
-        itemsOptions = [
-                            {
-                                label: '算法任务名称',
-                                prop: 'name',
-                                    rules: [{required: true, message: '算法任务名称不能为空', trigger: 'blur'}],
-                                compOptions: {
-                                    placeholder: '请输入算法任务名称'
-                                }
-                            },
-                            {
-                                label: '训练算法地址',
-                                prop: 'trainUrl',
-                                    rules: [{required: true, message: '训练算法地址不能为空', trigger: 'blur'}],
-                                compOptions: {
-                                    placeholder: '请输入训练算法地址'
-                                }
-                            },
-                            {
-                                label: '训练超参配置',
-                                prop: 'trainParams',
-                                    rules: [{required: true, message: '训练超参配置不能为空', trigger: 'blur'}],
-                                compOptions: {
-                                    type: 'textarea',
-                                    clearable: true,
-                                    placeholder: '请输入内容'
-                                }
-                            },
-                            {
-                                label: '验证算法地址',
-                                prop: 'verifyUrl',
-                                    rules: [{required: true, message: '验证算法地址不能为空', trigger: 'blur'}],
-                                compOptions: {
-                                    placeholder: '请输入验证算法地址'
-                                }
-                            },
-                            {
-                                label: '验证超参配置',
-                                prop: 'verifyParams',
-                                    rules: [{required: true, message: '验证超参配置不能为空', trigger: 'blur'}],
-                                compOptions: {
-                                    type: 'textarea',
-                                    clearable: true,
-                                    placeholder: '请输入内容'
-                                }
-                            },
-                            {
-                                label: '测试算法地址',
-                                prop: 'testUrl',
-                                    rules: [{required: true, message: '测试算法地址不能为空', trigger: 'blur'}],
-                                compOptions: {
-                                    placeholder: '请输入测试算法地址'
-                                }
-                            },
-                            {
-                                label: '测试超参配置',
-                                prop: 'testParams',
-                                    rules: [{required: true, message: '测试超参配置不能为空', trigger: 'blur'}],
-                                compOptions: {
-                                    type: 'textarea',
-                                    clearable: true,
-                                    placeholder: '请输入内容'
-                                }
-                            },
-                            {
-                                label: '备注',
-                                prop: 'remark',
-                                    rules: [{required: true, message: '备注不能为空', trigger: 'blur'}],
-                                compOptions: {
-                                    placeholder: '请输入备注'
-                                }
-                            },
-        ]
+// 表格配置项
+const columns = reactive<ColumnProps<any>[]>([
+  { type: 'selection', fixed: 'left', width: 70 },
+  { prop: 'id', label: '主键ID' },
+  {
+    prop: 'name',
+    label: '算法任务名称',
+    search: {
+      el: 'input'
+    },
+    width: 120
+  },
+  {
+    prop: 'trainUrl',
+    label: '训练算法地址',
+    search: {
+      el: 'input'
+    },
+    width: 120
+  },
+  {
+    prop: 'trainParams',
+    label: '训练超参配置',
+    search: {
+      el: 'input'
+    },
+    width: 120
+  },
+  {
+    prop: 'verifyUrl',
+    label: '验证算法地址',
+    search: {
+      el: 'input'
+    },
+    width: 120
+  },
+  {
+    prop: 'verifyParams',
+    label: '验证超参配置',
+    search: {
+      el: 'input'
+    },
+    width: 120
+  },
+  {
+    prop: 'testUrl',
+    label: '测试算法地址',
+    search: {
+      el: 'input'
+    },
+    width: 120
+  },
+  {
+    prop: 'testParams',
+    label: '测试超参配置',
+    search: {
+      el: 'input'
+    },
+    width: 120
+  },
+  {
+    prop: 'remark',
+    label: '备注',
+    width: 120
+  },
+  { prop: 'operation', label: '操作', width: 230, fixed: 'right' }
+])
+// 表单配置项
+let itemsOptions: ProForm.ItemsOptions[] = []
+const setItemsOptions = () => {
+  itemsOptions = [
+    {
+      label: '算法任务名称',
+      prop: 'name',
+      rules: [{ required: true, message: '算法任务名称不能为空', trigger: 'blur' }],
+      compOptions: {
+        placeholder: '请输入算法任务名称'
+      }
+    },
+    {
+      label: '训练算法地址',
+      prop: 'trainUrl',
+      rules: [{ required: true, message: '训练算法地址不能为空', trigger: 'blur' }],
+      compOptions: {
+        placeholder: '请输入训练算法地址'
+      }
+    },
+    {
+      label: '训练超参配置',
+      prop: 'trainParams',
+      rules: [{ required: true, message: '训练超参配置不能为空', trigger: 'blur' }],
+      compOptions: {
+        type: 'textarea',
+        clearable: true,
+        placeholder: '请输入内容'
+      }
+    },
+    {
+      label: '验证算法地址',
+      prop: 'verifyUrl',
+      rules: [{ required: true, message: '验证算法地址不能为空', trigger: 'blur' }],
+      compOptions: {
+        placeholder: '请输入验证算法地址'
+      }
+    },
+    {
+      label: '验证超参配置',
+      prop: 'verifyParams',
+      rules: [{ required: true, message: '验证超参配置不能为空', trigger: 'blur' }],
+      compOptions: {
+        type: 'textarea',
+        clearable: true,
+        placeholder: '请输入内容'
+      }
+    },
+    {
+      label: '测试算法地址',
+      prop: 'testUrl',
+      rules: [{ required: true, message: '测试算法地址不能为空', trigger: 'blur' }],
+      compOptions: {
+        placeholder: '请输入测试算法地址'
+      }
+    },
+    {
+      label: '测试超参配置',
+      prop: 'testParams',
+      rules: [{ required: true, message: '测试超参配置不能为空', trigger: 'blur' }],
+      compOptions: {
+        type: 'textarea',
+        clearable: true,
+        placeholder: '请输入内容'
+      }
+    },
+    {
+      label: '备注',
+      prop: 'remark',
+      rules: [{ required: true, message: '备注不能为空', trigger: 'blur' }],
+      compOptions: {
+        placeholder: '请输入备注'
+      }
     }
+  ]
+}
 </script>