diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 8434bb0..97629ca 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -4,6 +4,7 @@ import { Line, LineChart, CartesianGrid, XAxis, YAxis, Tooltip, ResponsiveContai import { BookOpen, Bot, + Camera, ChartNoAxesCombined, FileDown, FolderOpen, @@ -553,7 +554,7 @@ function ResourceModule() { ); } -function MistakeModule() { +function MistakeModule({ quickCaptureTask, onQuickCaptureHandled }) { const [items, setItems] = useState([]); const [selectedId, setSelectedId] = useState(null); const [selectedExportIds, setSelectedExportIds] = useState([]); @@ -726,6 +727,14 @@ function MistakeModule() { } }; + const uploadImageFileOnly = async (file) => { + if (!file) return ""; + const fd = new FormData(); + fd.append("file", file); + const res = await api.post("/api/upload", fd, { headers: { "Content-Type": "multipart/form-data" } }); + return res.data.url; + }; + const rerunOcrForEditing = async () => { const imageUrl = String(editingItem?.image_url || "").trim(); if (!imageUrl) { @@ -900,6 +909,59 @@ function MistakeModule() { show("已清空勾选"); }; + useEffect(() => { + if (!quickCaptureTask?.id) return; + let cancelled = false; + + const run = async () => { + try { + const files = Array.isArray(quickCaptureTask.files) ? quickCaptureTask.files : []; + if (!files.length) return; + + if (quickCaptureTask.mode === "single") { + setShowAdd(true); + await uploadImage(files[0], true); + return; + } + + let ok = 0; + let fail = 0; + for (const file of files) { + try { + const url = await uploadImageFileOnly(file); + await api.post("/api/mistakes", { + title: "待补录图片错题", + image_url: url, + category: "其他", + difficulty: "medium", + question_content: "", + answer: "", + explanation: "", + note: "", + wrong_count: 1 + }); + ok += 1; + } catch { + fail += 1; + } + } + if (!cancelled) { + await load(); + show(`连拍导入完成:成功 ${ok},失败 ${fail}`); + } + } finally { + if (!cancelled) { + onQuickCaptureHandled?.(quickCaptureTask.id); + } + } + }; + + run(); + return () => { + cancelled = true; + }; + }, [quickCaptureTask?.id]); + const diffLabel = { easy: "易", medium: "中", hard: "难" }; return ( @@ -1680,6 +1742,29 @@ function AiModule() { export default function App() { const [mainTab, setMainTab] = useState("mistake"); const [extraTab, setExtraTab] = useState("resource"); + const [showQuickCamera, setShowQuickCamera] = useState(false); + const [quickCaptureMode, setQuickCaptureMode] = useState("single"); + const [quickCaptureTask, setQuickCaptureTask] = useState(null); + const quickCaptureInputRef = useRef(null); + + const triggerQuickCapture = () => { + quickCaptureInputRef.current?.click(); + }; + + const onQuickCapturePicked = (filesLike) => { + const files = Array.from(filesLike || []); + if (!files.length) return; + setMainTab("mistake"); + setQuickCaptureTask({ + id: Date.now(), + mode: quickCaptureMode, + files + }); + setShowQuickCamera(false); + if (quickCaptureInputRef.current) { + quickCaptureInputRef.current.value = ""; + } + }; return (