Bläddra i källkod

前端修改数据模型后,修复上传worker url问题

mrh 9 månader sedan
förälder
incheckning
e9f9b0a836
3 ändrade filer med 125 tillägg och 40 borttagningar
  1. 99 1
      ui/backend/routers/worker.py
  2. 2 2
      ui/docs/gpt/home.md
  3. 24 37
      ui/fontend/src/components/Home.vue

+ 99 - 1
ui/backend/routers/worker.py

@@ -2,10 +2,12 @@ from fastapi import APIRouter, HTTPException, Request, Depends
 from fastapi.responses import JSONResponse, StreamingResponse
 import httpx
 import os
+from pathlib import Path
 from typing import Dict
-
 from pydantic import BaseModel
 from typing import Optional 
+import pathlib
+import os
 from utils.config import WORKER_SERVICE_URL,config,Browser
 from utils.logu import logger
 
@@ -16,6 +18,18 @@ class Endpoint(BaseModel):
     token: Optional[str] = "temp_token_123"
     health: Optional[Dict] = {}
 
+class BrowserPathValidationRequest(BaseModel):
+    exe_path: str
+
+class PathValidationRequest(BaseModel):
+    file_path: str
+
+class PathValidationResponse(BaseModel):
+    valid: bool
+    message: str
+    file_name: Optional[str] = None
+    file_size: Optional[int] = None
+
 class ResponseStatus(BaseModel):
     endpoint: Endpoint = Endpoint()
     browser_config: Optional[Browser] = None
@@ -45,6 +59,90 @@ async def status():
     """获取当前请求的端点"""
     return ResponseStatus(endpoint=Endpoint(health=health), browser_config=config.browser)
 
+@router.post("/validate_browser_path", tags=["worker"])
+async def validate_browser_path(request: BrowserPathValidationRequest):
+    """验证浏览器路径是否有效"""
+    path = Path(request.exe_path)
+    try:
+        if not path.exists():
+            return JSONResponse(status_code=400, content={
+                "valid": False,
+                "message": "路径不存在"
+            })
+            
+        if not path.is_file():
+            return JSONResponse(status_code=400, content={
+                "valid": False,
+                "message": "路径不是文件"
+            })
+            
+        if not os.access(path, os.X_OK):
+            return JSONResponse(status_code=400, content={
+                "valid": False,
+                "message": "文件不可执行"
+            })
+            
+        if path.suffix.lower() != '.exe':
+            return JSONResponse(status_code=400, content={
+                "valid": False,
+                "message": "仅支持可执行文件 (.exe)"
+            })
+            
+        return {
+            "valid": True,
+            "message": "浏览器路径有效"
+        }
+        
+    except Exception as e:
+        logger.error(f"浏览器路径验证失败: {str(e)}")
+        return JSONResponse(status_code=500, content={
+            "valid": False,
+            "message": f"路径验证异常: {str(e)}"
+        })
+
+@router.post("/validate_path", tags=["worker"])
+async def validate_file_path(request: PathValidationRequest):
+    """验证文件路径是否有效"""
+    path = Path(request.file_path)
+    try:
+        if not path.exists():
+            return JSONResponse(status_code=400, content={
+                "valid": False,
+                "message": "路径不存在"
+            })
+            
+        if not path.is_file():
+            return JSONResponse(status_code=400, content={
+                "valid": False,
+                "message": "路径不是文件"
+            })
+            
+        if not os.access(path, os.R_OK):
+            return JSONResponse(status_code=400, content={
+                "valid": False,
+                "message": "文件不可读"
+            })
+            
+        if path.suffix.lower() not in ('.xlsx', '.xls'):
+            return JSONResponse(status_code=400, content={
+                "valid": False,
+                "message": "仅支持Excel文件 (.xlsx/.xls)"
+            })
+            
+        return {
+            "valid": True,
+            "message": "文件有效",
+            "file_name": path.name,
+            "file_size": path.stat().st_size
+        }
+        
+    except Exception as e:
+        logger.error(f"路径验证失败: {str(e)}")
+        return JSONResponse(status_code=500, content={
+            "valid": False,
+            "message": f"路径验证异常: {str(e)}"
+        })
+
 async def health_check():
     """健康检查"""
     async with httpx.AsyncClient(base_url=WORKER_SERVICE_URL) as client:

+ 2 - 2
ui/docs/gpt/home.md

@@ -1,6 +1,6 @@
 # 配置浏览器
-由于浏览器无法获取完整本地路径,我想改为输入框的形式来粘贴路径。尽管前后端都是在同一台机器上运行,但是只能如此了,而不是在浏览器打开文件对话框。你觉得如何?或者你有更好的方案修改前端代码吗?
-推送路径后应该在后端检查路径是否正确,才能保存并返回信息
+由于浏览器无法获取完整本地路径,因此前端代码中,“修改浏览器路径”我想改为输入框的形式来粘贴路径。尽管前后端都是在同一台机器上运行,但是只能如此了,而不是在前端打开文件对话框。
+推送路径后应该在后端检查路径是否正确,才能保存并返回信息。你觉得如何
 
 # 数据模型
 vue 文件的变量似乎很混乱,能否重构这些变量。

+ 24 - 37
ui/fontend/src/components/Home.vue

@@ -1,19 +1,21 @@
 <template>
   <div>
     <el-row :span="8" justify="center" style="gap: 16px">
-      <el-input
-        v-model="filePath"
-        placeholder="请输入Excel文件绝对路径"
-        style="width: 400px"
+      <el-upload
+        action="#"
+        :http-request="handleUpload"
+        :show-file-list="false"
+        accept=".xlsx,.xls,.csv"
         :disabled="!isWorkerAvailable"
-      />
-      <el-button 
-        type="primary" 
-        :disabled="!isWorkerAvailable"
-        @click="handleManualInput"
       >
-        {{ isWorkerAvailable ? '确认路径' : '服务不可用' }}
-      </el-button>
+        <el-button 
+          type="primary" 
+          :disabled="!isWorkerAvailable"
+          class="mr-12"
+        >
+          {{ isWorkerAvailable ? '导入关键词' : '服务不可用' }}
+        </el-button>
+      </el-upload>
       <el-button
         type="primary"
       >开始运行</el-button>
@@ -177,9 +179,6 @@ const fetchTaskStatistics = async () => {
   }
 }
 
-// 文件路径输入
-const filePath = ref('')
-
 // 定义上传结果
 const uploadResult = ref<{
   name: string;
@@ -292,26 +291,18 @@ const copyToClipboard = async (text: string) => {
     ElMessage.error('复制失败');
   }
 };
-const handleManualInput = async () => {
+const handleUpload = async (options: any) => {
   if (!isWorkerAvailable.value) {
     ElMessage.warning(`worker 服务不可用,无法上传文件。状态码: ${store.workerHealth?.err}`)
     return
   }
-  if (!filePath.value) {
-    ElMessage.warning('请输入文件路径')
-    return
-  }
   try {
-    const loading = ElLoading.service({
-      lock: true,
-      text: '正在处理文件...',
-    })
-    const response = await fetch(`${workerServer.value}/keywords/upload`, {
+    const formData = new FormData()
+    formData.append('file', options.file)
+    
+    const response = await fetch(`${workerEndpoint.value.serviceUrl}/keywords/upload`, {
       method: 'POST',
-      headers: {
-        'Content-Type': 'application/json',
-      },
-      body: JSON.stringify({ file_path: filePath.value }),
+      body: formData
     })
     
     if (!response.ok) throw new Error('上传失败')
@@ -320,11 +311,9 @@ const handleManualInput = async () => {
     console.log('上传成功:', result)
 
     // 显示上传结果
-    loading.close()
-
     uploadResult.value = {
-      name: filePath.value.split(/[\\/]/).pop() || '',
-      size: 0, // 本地文件大小无法通过浏览器获取
+      name: options.file.name,
+      size: options.file.size,
       uploadTime: new Date().toLocaleString(),
       total_keywords: result.total_keywords,
       inserted_count: result.inserted_count,
@@ -332,16 +321,14 @@ const handleManualInput = async () => {
     }
 
     ElMessage.success(`成功导入 ${result.inserted_count} 个关键词(共 ${result.total_keywords} 个)`)
-    filePath.value = '' // 清空输入框
   } catch (error: any) {
-    loading.close()
     console.error('上传错误:', error)
     uploadResult.value = {
-      name: filePath.value,
-      size: 0,
+      name: options.file.name,
+      size: options.file.size,
       uploadTime: new Date().toLocaleString(),
       success: false,
-      error: error.message || '文件处理失败'
+      error: error.message || '文件上传失败'
     }
   } 
 }