- 新增 /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>
56 lines
1.4 KiB
TypeScript
56 lines
1.4 KiB
TypeScript
import { FileText, Loader2 } from "lucide-react";
|
|
import { useState, type ComponentProps } from "react";
|
|
import { toast } from "sonner";
|
|
|
|
import { Button } from "@/components/ui/button";
|
|
import { downloadMarkdownAsDocx } from "@/core/scheduled-tasks/markdown-docx";
|
|
|
|
import { Tooltip } from "../tooltip";
|
|
|
|
export function DownloadWordButton({
|
|
markdown,
|
|
fileName,
|
|
title,
|
|
...props
|
|
}: ComponentProps<typeof Button> & {
|
|
markdown: string;
|
|
fileName?: string;
|
|
title?: string;
|
|
}) {
|
|
const [busy, setBusy] = useState(false);
|
|
const handleDownload = async () => {
|
|
const source = markdown?.trim();
|
|
if (!source) {
|
|
toast.error("无可下载的 Markdown 内容");
|
|
return;
|
|
}
|
|
setBusy(true);
|
|
try {
|
|
await downloadMarkdownAsDocx(source, fileName || "message", title);
|
|
toast.success("Word 文件已生成");
|
|
} catch (error) {
|
|
toast.error(error instanceof Error ? error.message : "生成 Word 失败");
|
|
} finally {
|
|
setBusy(false);
|
|
}
|
|
};
|
|
return (
|
|
<Tooltip content="下载为 Word">
|
|
<Button
|
|
size="icon-sm"
|
|
type="button"
|
|
variant="ghost"
|
|
disabled={busy}
|
|
onClick={handleDownload}
|
|
{...props}
|
|
>
|
|
{busy ? (
|
|
<Loader2 size={12} className="animate-spin" />
|
|
) : (
|
|
<FileText size={12} />
|
|
)}
|
|
</Button>
|
|
</Tooltip>
|
|
);
|
|
}
|