2025-11-23 10:55:04 +08:00
|
|
|
"use client";
|
|
|
|
|
|
|
|
|
|
import { useEffect, useMemo, useState } from "react";
|
|
|
|
|
import { useSessionContext } from "@supabase/auth-helpers-react";
|
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
|
|
|
import { Card, CardContent } from "@/components/ui/card";
|
2026-01-15 20:54:21 +08:00
|
|
|
import { getMnoteRuntimeConfig } from "@/lib/runtime-config";
|
2025-11-23 10:55:04 +08:00
|
|
|
|
|
|
|
|
interface TaskResponse {
|
|
|
|
|
task_id: string;
|
|
|
|
|
status: string;
|
|
|
|
|
progress: number;
|
|
|
|
|
message?: string | null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface Props {
|
|
|
|
|
documentId: string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function DocumentTaskPanel({ documentId }: Props) {
|
|
|
|
|
const { session } = useSessionContext();
|
|
|
|
|
const [task, setTask] = useState<TaskResponse | null>(null);
|
|
|
|
|
const [pending, setPending] = useState(false);
|
2026-01-15 20:54:21 +08:00
|
|
|
const backendUrl = useMemo(() => getMnoteRuntimeConfig().backendUrl, []);
|
2025-11-23 10:55:04 +08:00
|
|
|
|
|
|
|
|
const triggerTask = async () => {
|
|
|
|
|
if (!backendUrl || !session?.access_token) return;
|
|
|
|
|
setPending(true);
|
|
|
|
|
try {
|
|
|
|
|
const response = await fetch(`${backendUrl}/api/v1/tasks/ocr`, {
|
|
|
|
|
method: "POST",
|
|
|
|
|
headers: {
|
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
|
Authorization: `Bearer ${session.access_token}`,
|
|
|
|
|
},
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
document_id: documentId,
|
|
|
|
|
file_url: "https://example.com/sample.pdf",
|
|
|
|
|
}),
|
|
|
|
|
});
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
if (response.ok) {
|
|
|
|
|
setTask(data);
|
|
|
|
|
}
|
|
|
|
|
} finally {
|
|
|
|
|
setPending(false);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (!backendUrl || !session?.access_token || !task?.task_id) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const timer = setInterval(async () => {
|
|
|
|
|
const response = await fetch(`${backendUrl}/api/v1/tasks/${task.task_id}`, {
|
|
|
|
|
headers: {
|
|
|
|
|
Authorization: `Bearer ${session.access_token}`,
|
|
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
if (!response.ok) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
const data = (await response.json()) as TaskResponse;
|
|
|
|
|
setTask(data);
|
|
|
|
|
if (data.status === "completed") {
|
|
|
|
|
clearInterval(timer);
|
|
|
|
|
}
|
|
|
|
|
}, 2000);
|
|
|
|
|
return () => clearInterval(timer);
|
|
|
|
|
}, [backendUrl, session?.access_token, task?.task_id]);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Card className="mt-4 bg-white shadow-sm">
|
|
|
|
|
<CardContent className="flex items-center justify-between py-3 text-sm text-gray-600">
|
|
|
|
|
<div>
|
|
|
|
|
<div className="font-medium text-gray-900">后端 OCR 流程</div>
|
|
|
|
|
<div className="text-xs text-gray-500">
|
|
|
|
|
状态:{task ? task.status : "未开始"} · 进度:{task ? `${task.progress}%` : "0%"}
|
|
|
|
|
</div>
|
|
|
|
|
{task?.message && <div className="text-xs text-gray-500">提示:{task.message}</div>}
|
|
|
|
|
</div>
|
|
|
|
|
<Button onClick={triggerTask} disabled={pending} variant="outline">
|
|
|
|
|
{pending ? "触发中..." : "触发 OCR"}
|
|
|
|
|
</Button>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
);
|
|
|
|
|
}
|