diff --git a/web/src/app/(main)/function/tasks/detail/_tabs/report-tab.tsx b/web/src/app/(main)/function/tasks/detail/_tabs/report-tab.tsx index 056b786..a9bb3e8 100644 --- a/web/src/app/(main)/function/tasks/detail/_tabs/report-tab.tsx +++ b/web/src/app/(main)/function/tasks/detail/_tabs/report-tab.tsx @@ -10,6 +10,34 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { api } from "@/lib/api"; +async function copyText(text: string): Promise { + // 优先使用异步剪贴板 API(仅在 HTTPS / localhost 等安全上下文可用) + if (navigator.clipboard && window.isSecureContext) { + try { + await navigator.clipboard.writeText(text); + return true; + } catch { + // 继续走降级方案 + } + } + // 降级:HTTP 等非安全上下文下用 execCommand + try { + const textarea = document.createElement("textarea"); + textarea.value = text; + textarea.style.position = "fixed"; + textarea.style.left = "-9999px"; + textarea.style.top = "0"; + document.body.appendChild(textarea); + textarea.focus(); + textarea.select(); + const ok = document.execCommand("copy"); + document.body.removeChild(textarea); + return ok; + } catch { + return false; + } +} + export function ReportTab({ taskId }: { taskId: string }) { const [report, setReport] = React.useState(""); const [loading, setLoading] = React.useState(true); @@ -34,12 +62,16 @@ export function ReportTab({ taskId }: { taskId: string }) { }; }, [taskId]); - function copy() { + async function copy() { if (!report) return; - navigator.clipboard?.writeText(report); - setCopied(true); - toast.success("已复制 Markdown"); - setTimeout(() => setCopied(false), 1500); + const ok = await copyText(report); + if (ok) { + setCopied(true); + toast.success("已复制 Markdown"); + setTimeout(() => setCopied(false), 1500); + } else { + toast.error("复制失败,请手动选择文本复制"); + } } let content: React.ReactNode;