Files
deerflow-code/frontend-web/src/components/workspace/export-trigger.tsx
T
吉鑫升andClaude Opus 4.7 d339db4799 feat: 公开定时任务页面、Word 导出、定时任务实时刷新与技能详情重构
- 新增 /public/scheduled-tasks/:taskId 公开只读访问页与对应的
  /api/public/scheduled-tasks 后端路由(无需登录)
- 新增对话整体 / 单条消息 / Markdown 工件 "另存为 Word (.docx)" 能力
- 定时任务列表与运行列表在任务执行中自动轮询刷新,并新增"正在执行"标记
- 定时任务运行消息接口在事件存储为空时回退到 LangGraph checkpoint,
  让正在执行的任务也能立即看到提示词与流式输出
- 技能详情对话框由折叠面板改为 Tab 切换(Detail / SKILL.md),
  并支持 Rendered / Raw 切换、一键复制
- config.yaml 增加 database (MySQL) 与 checkpointer (SQLite) 配置块

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-20 17:04:58 +08:00

93 lines
2.8 KiB
TypeScript

"use client";
import { Download, FileJson, FileText, FileType2 } from "lucide-react";
import { useCallback } from "react";
import { toast } from "sonner";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import { useI18n } from "@/core/i18n/hooks";
import {
exportThreadAsJSON,
exportThreadAsMarkdown,
exportThreadAsWord,
} from "@/core/threads/export";
import type { AgentThread } from "@/core/threads/types";
import { useThread } from "./messages/context";
import { Tooltip } from "./tooltip";
export function ExportTrigger({ threadId }: { threadId: string }) {
const { t } = useI18n();
const { thread } = useThread();
const messages = thread.messages;
const handleExport = useCallback(
async (format: "markdown" | "json" | "word") => {
if (messages.length === 0) {
toast.error(t.conversation.noMessages);
return;
}
const agentThread = {
thread_id: threadId,
updated_at: new Date().toISOString(),
values: thread.values,
} as AgentThread;
try {
if (format === "markdown") {
exportThreadAsMarkdown(agentThread, messages);
} else if (format === "json") {
exportThreadAsJSON(agentThread, messages);
} else {
await exportThreadAsWord(agentThread, messages);
}
toast.success(t.common.exportSuccess);
} catch (error) {
toast.error(error instanceof Error ? error.message : "导出失败");
}
},
[messages, thread.values, threadId, t],
);
if (messages.length === 0) {
return null;
}
return (
<DropdownMenu>
<Tooltip content={t.common.export}>
<DropdownMenuTrigger asChild>
<Button
className="text-muted-foreground hover:text-foreground"
variant="ghost"
>
<Download />
{t.common.export}
</Button>
</DropdownMenuTrigger>
</Tooltip>
<DropdownMenuContent align="end">
<DropdownMenuItem onSelect={() => void handleExport("markdown")}>
<FileText className="text-muted-foreground" />
<span>{t.common.exportAsMarkdown}</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => void handleExport("word")}>
<FileType2 className="text-muted-foreground" />
<span>{t.common.exportAsWord}</span>
</DropdownMenuItem>
<DropdownMenuItem onSelect={() => void handleExport("json")}>
<FileJson className="text-muted-foreground" />
<span>{t.common.exportAsJSON}</span>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}