import { useRef, useState } from "react"; import { Icon } from "../../../components/Icon"; import { DOCUMENT_ACCEPT, formatBytes, validateDocumentFile } from "../file"; import type { DocumentJob, UploadPhase, UploadWorkflowState } from "../types"; interface DocumentUploadPanelProps { workflow: UploadWorkflowState; job: DocumentJob | undefined; onUpload: (file: File) => void; onCancel: () => void; } const ACTIVE_PHASES: UploadPhase[] = [ "hashing", "declaring", "uploading", "completing", "parsing", "indexing", ]; const STEPS = [ { phases: ["hashing", "declaring", "uploading", "completing"], label: "校验并隔离上传" }, { phases: ["parsing"], label: "本地安全解析" }, { phases: ["review"], label: "人工复核出域清单" }, { phases: ["indexing", "indexed"], label: "向量化与激活" }, ] as const; function stepState(phase: UploadPhase, index: number): "pending" | "active" | "done" { if (phase === "indexed") return "done"; const activeIndex = STEPS.findIndex((step) => (step.phases as readonly string[]).includes(phase)); if (activeIndex === -1) return "pending"; if (index < activeIndex) return "done"; return index === activeIndex ? "active" : "pending"; } export function DocumentUploadPanel({ workflow, job, onUpload, onCancel, }: DocumentUploadPanelProps) { const inputRef = useRef(null); const [file, setFile] = useState(null); const [selectionError, setSelectionError] = useState(null); const busy = ACTIVE_PHASES.includes(workflow.phase); function chooseFile(next: File | undefined) { if (next === undefined) return; const error = validateDocumentFile(next); setSelectionError(error); setFile(error === null ? next : null); } return (
INGESTION GATE

导入本地资料

≤ 100 MiB
event.preventDefault()} onDrop={(event) => { event.preventDefault(); if (!busy) chooseFile(event.dataTransfer.files[0]); }} > {file === null ? "选择或拖入地质资料" : file.name} {file === null ? "支持 TXT、Markdown、DOCX、PDF" : `${formatBytes(file.size)} · 等待本地校验`} chooseFile(event.target.files?.[0])} type="file" />
{selectionError !== null && (

{selectionError}

)}
{STEPS.map((step, index) => { const state = stepState(workflow.phase, index); return (
{step.label}
); })}
{workflow.filename ?? "尚未启动上传"} {workflow.message ?? "文件只在浏览器计算摘要,模型密钥不会进入页面。"}
{job !== undefined && (
{job.stage} {job.progress}%
)}
{busy && ( )}

PDF 当前可能进入 OCR_REQUIRED;这不等同于地图空间理解,也不会自动提交云端模型。

); }