chenkun 3 veckor sedan
förälder
incheckning
2a807f2845

+ 5 - 2
src/api/document.ts

@@ -132,8 +132,11 @@ export const documentApi = {
   },
 
   // 批量加入任务中心
-  batchAddToTask(ids: string[]): Promise<ApiResponse<any>> {
-    return request.post(`${API_PREFIX}/documents/batch-add-to-task`, { ids })
+  batchAddToTask(ids: string[], projectName: string): Promise<ApiResponse<any>> {
+    return request.post(`${API_PREFIX}/documents/batch-add-to-task`, {
+      doc_ids: ids,
+      project_name: projectName
+    })
   },
 
   // 启动转换

+ 5 - 2
src/api/image.ts

@@ -96,7 +96,10 @@ export const imageApi = {
   },
 
   // 批量加入任务中心
-  batchAddToTask(ids: string[]): Promise<ApiResponse<null>> {
-    return request.post(`${API_PREFIX}/batch-add-to-task`, { ids })
+  batchAddToTask(ids: string[], projectName: string): Promise<ApiResponse<null>> {
+    return request.post(`${API_PREFIX}/batch-add-to-task`, {
+      ids: ids,
+      project_name: projectName
+    })
   }
 }

+ 105 - 2
src/views/admin/TaskManagement.vue

@@ -2,7 +2,7 @@
 <template>
   <div class="task-management">
     <div class="page-header">
-      <h2>任务管理中心</h2>
+      <h2>标注任务中心</h2>
       <p>管理数据处理和图片处理的异步任务</p>
     </div>
 
@@ -19,6 +19,28 @@
                 <span v-else class="placeholder">暂无任务ID</span>
               </template>
             </el-table-column>
+            <el-table-column label="操作" width="220" fixed="right">
+              <template #default="{ row }">
+                <el-button-group>
+                  <el-button 
+                    size="small" 
+                    type="primary" 
+                    :disabled="!row.task_id"
+                    @click="handleCheckProgress(row)"
+                  >
+                    查看进度
+                  </el-button>
+                  <el-button 
+                    size="small" 
+                    type="success" 
+                    :disabled="!row.task_id"
+                    @click="handleExportData(row)"
+                  >
+                    导出结果
+                  </el-button>
+                </el-button-group>
+              </template>
+            </el-table-column>
           </el-table>
         </el-tab-pane>
 
@@ -52,6 +74,28 @@
                 <span v-else class="placeholder">暂无任务ID</span>
               </template>
             </el-table-column>
+            <el-table-column label="操作" width="220" fixed="right">
+              <template #default="{ row }">
+                <el-button-group>
+                  <el-button 
+                    size="small" 
+                    type="primary" 
+                    :disabled="!row.task_id"
+                    @click="handleCheckProgress(row)"
+                  >
+                    查看进度
+                  </el-button>
+                  <el-button 
+                    size="small" 
+                    type="success" 
+                    :disabled="!row.task_id"
+                    @click="handleExportData(row)"
+                  >
+                    导出结果
+                  </el-button>
+                </el-button-group>
+              </template>
+            </el-table-column>
           </el-table>
         </el-tab-pane>
       </el-tabs>
@@ -61,7 +105,7 @@
 
 <script setup lang="ts">
 import { ref, onMounted } from 'vue'
-import { ElMessage } from 'element-plus'
+import { ElMessage, ElMessageBox } from 'element-plus'
 import { Picture } from '@element-plus/icons-vue'
 import request from '@/api/request'
 
@@ -69,6 +113,7 @@ interface TaskItem {
   id: number
   business_id: string
   task_id: string | null
+  project_id: string  // 项目名称
   type: string
   name: string
   image_url?: string
@@ -109,6 +154,64 @@ const handleTabChange = () => {
   loadTasks()
 }
 
+// 查看进度
+const handleCheckProgress = async (row: TaskItem) => {
+  try {
+    const response = await request.get<any, ApiResponse<any>>('/api/v1/sample/external/projects/progress', {
+      params: { project_id: row.project_id }
+    })
+    
+    if (response.code === 0) {
+      const data = response.data
+      const message = `
+        项目名称: ${data.project_name}
+        当前状态: ${data.status}
+        总任务数: ${data.total_tasks}
+        已完成数: ${data.completed_tasks}
+        完成进度: ${data.completion_percentage}%
+      `
+      ElMessageBox.alert(message, '标注进度详情', {
+        confirmButtonText: '确定',
+        dangerouslyUseHTMLString: true,
+        callback: () => {}
+      })
+    } else {
+      ElMessage.error(response.message || '获取进度失败')
+    }
+  } catch (error) {
+    console.error('获取进度出错:', error)
+    ElMessage.error('获取进度失败')
+  }
+}
+
+// 导出结果
+const handleExportData = async (row: TaskItem) => {
+  try {
+    ElMessage.info('正在请求导出,请稍候...')
+    const response = await request.post<any, ApiResponse<any>>('/api/v1/sample/external/projects/export', {
+      project_id: row.project_id,
+      format: 'json'
+    })
+    
+    if (response.code === 0) {
+      const fileUrl = response.data.file_url
+      if (fileUrl) {
+        ElMessage.success('导出成功,即将开始下载')
+        // 如果是相对路径,可能需要拼接基础 URL
+        const downloadUrl = fileUrl.startsWith('http') ? fileUrl : `http://192.168.92.61:9003${fileUrl}`
+        window.open(downloadUrl, '_blank')
+      } else {
+        ElMessage.warning('导出成功但未获取到下载链接')
+      }
+    } else {
+      ElMessage.error(response.message || '导出失败')
+    }
+  } catch (error) {
+    console.error('导出数据出错:', error)
+    ElMessage.error('导出失败')
+  }
+}
+
 onMounted(() => {
   loadTasks()
 })

+ 2 - 2
src/views/basic-info/Standard.vue

@@ -774,8 +774,8 @@ const loadKnowledgeBases = async () => {
     })
     if (res.code === 0) {
       knowledgeBases.value = res.data
-      if (knowledgeBases.value.length > 0 && !ingestForm.kb_id) {
-        ingestForm.kb_id = knowledgeBases.value[0].id
+      if (knowledgeBases.value.length > 0 && !editForm.kb_id) {
+        editForm.kb_id = knowledgeBases.value[0].id
       }
     }
   } catch (error) {

+ 71 - 21
src/views/documents/Index.vue

@@ -26,7 +26,7 @@
           <el-icon><CircleCheck /></el-icon> 批量入库
         </el-button>
         <el-button type="primary" :disabled="selectedIds.length === 0" @click="handleBatchAddToTask">
-          <el-icon><Tickets /></el-icon> 批量加入任务
+          <el-icon><Tickets /></el-icon> 批量加入标注任务
         </el-button>
         <el-button type="success" class="upload-btn" @click="handleUpload">
           <el-icon><Upload /></el-icon> 上传文档
@@ -85,6 +85,7 @@
       <el-table 
         v-else 
         :data="documents" 
+        row-key="id"
         style="width: 100%" 
         border 
         stripe 
@@ -92,7 +93,7 @@
         @selection-change="handleSelectionChange"
         :row-class-name="tableRowClassName"
       >
-        <el-table-column type="selection" width="40" :selectable="canSelect" />
+        <el-table-column type="selection" width="40" :selectable="canSelect" :reserve-selection="true" />
         
         <el-table-column prop="title" label="文档名称" min-width="280" show-overflow-tooltip>
           <template #default="scope">
@@ -347,6 +348,23 @@
       </template>
     </el-dialog>
 
+    <!-- 加入任务设置弹窗 -->
+    <el-dialog v-model="taskDialogVisible" title="加入标注任务" width="400px">
+      <el-form :model="taskForm" :rules="taskRules" ref="taskFormRef" label-width="100px">
+        <el-form-item label="项目名称" prop="project_name">
+          <el-input v-model="taskForm.project_name" placeholder="请输入项目名称" clearable />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="taskDialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="confirmAddTask" :loading="taskAdding">
+            确认加入
+          </el-button>
+        </span>
+      </template>
+    </el-dialog>
+
       <div class="pagination-container" v-if="total > 0">
         <el-pagination
           v-model:current-page="searchQuery.page"
@@ -780,12 +798,21 @@ const ingestForm = reactive({
   separator: '。'
 })
 
-const ingestRules = {
-  kb_method: [
-    { required: true, message: '请选择切分方式', trigger: 'change' }
+// 任务相关状态
+const taskAdding = ref(false)
+const taskDialogVisible = ref(false)
+const taskForm = reactive({
+  project_name: ''
+})
+
+const taskRules = {
+  project_name: [
+    { required: true, message: '请输入项目名称', trigger: 'blur' },
+    { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
   ]
 }
 
+const taskFormRef = ref()
 const ingestFormRef = ref()
 const uploadFormRef = ref()
 const editFormRef = ref()
@@ -1096,31 +1123,39 @@ const handleBatchAddToTask = async () => {
     return
   }
 
+  taskForm.project_name = '' // 重置项目名称
+  if (taskFormRef.value) {
+    taskFormRef.value.clearValidate()
+  }
+  taskDialogVisible.value = true
+}
+
+const confirmAddTask = async () => {
+  if (!taskFormRef.value) return
+  
   try {
-    await ElMessageBox.confirm(
-      `确定要将选中的 ${selectedIds.value.length} 份文档加入任务中心吗?`,
-      '批量加入任务',
-      {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'info',
-      }
-    )
+    await taskFormRef.value.validate()
+  } catch (error) {
+    return
+  }
 
-    const res = await documentApi.batchAddToTask(selectedIds.value)
+  taskAdding.value = true
+  try {
+    const res = await documentApi.batchAddToTask(selectedIds.value, taskForm.project_name)
 
     if (res.code === 0) {
       ElMessage.success(res.message || '操作成功')
       selectedIds.value = []
+      taskDialogVisible.value = false
       fetchDocuments()
     } else {
       ElMessage.error(res.message || '操作失败')
     }
   } catch (error: any) {
-    if (error !== 'cancel') {
-      console.error('批量加入任务失败:', error)
-      ElMessage.error('操作失败')
-    }
+    console.error('批量加入任务失败:', error)
+    ElMessage.error('操作失败')
+  } finally {
+    taskAdding.value = false
   }
 }
 
@@ -1271,6 +1306,19 @@ const refreshDocumentsSilently = async () => {
       total.value = res.data.total
       statistics.value.allTotal = res.data.all_total || 0
       statistics.value.totalEntered = res.data.total_entered || 0
+      
+      // 检查是否还需要继续轮询
+      const hasConverting = documents.value.some(doc => doc.conversion_status === 1)
+      if (!hasConverting) {
+        // 如果当前没有转换中的文档,不要立即停止,延迟一个周期再检查
+        // 这样可以避免转换任务刚提交但状态还没更新到 1 的情况
+        setTimeout(() => {
+          const stillNoConverting = documents.value.some(doc => doc.conversion_status === 1)
+          if (!stillNoConverting) {
+            stopPolling()
+          }
+        }, 5000)
+      }
     }
   } catch (error) {
     console.error('静默刷新失败:', error)
@@ -1375,6 +1423,8 @@ const submitUpload = async () => {
       ElMessage.success('上传成功')
       uploadDialogVisible.value = false
       fetchDocuments()
+      // 上传后可能立即开始转换,启动轮询监控状态
+      startPolling()
     }
   } catch (error) {
     console.error('上传失败:', error)
@@ -1595,6 +1645,8 @@ const handleConvert = async (row: DocumentItem) => {
     const res = await documentApi.convert(row.id)
     if (res.code === 0) {
       ElMessage.success(res.message || '转换任务已启动')
+      // 启动轮询以监控转换进度
+      startPolling()
       // 触发一次静默刷新以更新状态
       refreshDocumentsSilently()
     } else {
@@ -1618,8 +1670,6 @@ const openInNewWindow = () => {
 onMounted(() => {
   fetchDocuments()
   loadKbOptions()
-  // 5秒后开始第一次静默刷新,之后递归调用
-  refreshTimer.value = window.setTimeout(refreshDocumentsSilently, 5000)
 })
 
 onUnmounted(() => {

+ 57 - 3
src/views/images/Index.vue

@@ -44,7 +44,7 @@
               @click="handleBatchAddToTask"
               :disabled="selectedIds.length === 0"
             >
-              批量加入任务
+              批量加入标注任务
             </el-button>
             <el-upload
               ref="uploadRef"
@@ -169,6 +169,23 @@
         <el-button type="primary" :loading="uploading" @click="startBatchUpload">开始上传</el-button>
       </template>
     </el-dialog>
+
+    <!-- 加入任务设置弹窗 -->
+    <el-dialog v-model="taskDialogVisible" title="加入标注任务" width="400px">
+      <el-form :model="taskForm" :rules="taskRules" ref="taskFormRef" label-width="100px">
+        <el-form-item label="项目名称" prop="project_name">
+          <el-input v-model="taskForm.project_name" placeholder="请输入项目名称" clearable />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button @click="taskDialogVisible = false">取消</el-button>
+          <el-button type="primary" @click="confirmAddTask" :loading="taskAdding">
+            确认加入
+          </el-button>
+        </span>
+      </template>
+    </el-dialog>
   </div>
 </template>
 
@@ -206,6 +223,22 @@ const uploadRef = ref()
 const imageRefs = reactive<any>({})
 const selectedIds = ref<string[]>([])
 
+// 任务相关状态
+const taskAdding = ref(false)
+const taskDialogVisible = ref(false)
+const taskForm = reactive({
+  project_name: ''
+})
+
+const taskRules = {
+  project_name: [
+    { required: true, message: '请输入项目名称', trigger: 'blur' },
+    { min: 2, max: 50, message: '长度在 2 到 50 个字符', trigger: 'blur' }
+  ]
+}
+
+const taskFormRef = ref()
+
 const categoryForm = reactive({
   id: '',
   type_name: '',
@@ -262,17 +295,38 @@ const handleSelectionChange = (selection: ImageItem[]) => {
 const handleBatchAddToTask = async () => {
   if (selectedIds.value.length === 0) return
   
+  taskForm.project_name = '' // 重置项目名称
+  if (taskFormRef.value) {
+    taskFormRef.value.clearValidate()
+  }
+  taskDialogVisible.value = true
+}
+
+const confirmAddTask = async () => {
+  if (!taskFormRef.value) return
+  
+  try {
+    await taskFormRef.value.validate()
+  } catch (error) {
+    return
+  }
+
+  taskAdding.value = true
   try {
-    const { code, message } = await imageApi.batchAddToTask(selectedIds.value)
+    const { code, message } = await imageApi.batchAddToTask(selectedIds.value, taskForm.project_name)
     if (code === 0) {
       ElMessage.success(message || '批量加入任务成功')
-      // 可以在此处刷新列表或清除选择
+      selectedIds.value = []
+      taskDialogVisible.value = false
+      fetchImages()
     } else {
       ElMessage.error(message || '操作失败')
     }
   } catch (error) {
     console.error('批量加入任务失败:', error)
     ElMessage.error('操作异常')
+  } finally {
+    taskAdding.value = false
   }
 }