deerflow2/frontend/src/core/utils/markdown-download/use-markdown-download.ts

163 lines
3.8 KiB
TypeScript

import { useCallback, useState } from "react";
import {
downloadMarkdownAsDocx,
downloadMarkdownAsPdf,
type DocxOptions,
type PdfOptions,
} from "./converter";
/**
* Markdown 下载 Hook 配置选项
*/
export interface UseMarkdownDownloadOptions {
/**
* 下载开始时的回调
*/
onDownloadStart?: (format: "docx" | "pdf") => void;
/**
* 下载完成时的回调
*/
onDownloadEnd?: (format: "docx" | "pdf") => void;
/**
* 下载失败时的回调
*/
onError?: (error: Error, format: "docx" | "pdf") => void;
}
/**
* Markdown 下载 Hook 返回值
*/
export interface UseMarkdownDownloadReturn {
/**
* 当前下载状态
*/
isDownloading: "docx" | "pdf" | null;
/**
* 下载为 DOCX
*/
downloadAsDocx: (
markdown: string,
filename: string,
options?: DocxOptions,
) => Promise<void>;
/**
* 下载为 PDF
*/
downloadAsPdf: (
markdown: string,
filename: string,
options?: PdfOptions & {
resolveAssetUrl?: (
rawPath: string,
) => string | null | Promise<string | null>;
},
) => Promise<void>;
/**
* 是否可以下载(没有正在进行的下载)
*/
canDownload: boolean;
}
/**
* Markdown 文档下载 Hook
*
* @description
* 将 Markdown 内容转换为 DOCX 或 PDF 格式并下载。
* 可在任何 React + TypeScript 项目中使用。
*
* @example
* ```tsx
* import { useMarkdownDownload } from "./hooks/use-markdown-download";
*
* function MyComponent() {
* const { downloadAsDocx, downloadAsPdf, isDownloading, canDownload } = useMarkdownDownload({
* onError: (error, format) => {
* console.error(`Failed to download as ${format}:`, error);
* },
* });
*
* const handleDownload = () => {
* downloadAsDocx("# Hello World", "document");
* };
*
* return (
* <button onClick={handleDownload} disabled={!canDownload}>
* {isDownloading === "docx" ? "Converting..." : "Download DOCX"}
* </button>
* );
* }
* ```
*/
export function useMarkdownDownload(
options: UseMarkdownDownloadOptions = {},
): UseMarkdownDownloadReturn {
const { onDownloadStart, onDownloadEnd, onError } = options;
const [isDownloading, setIsDownloading] = useState<"docx" | "pdf" | null>(
null,
);
const downloadAsDocx = useCallback(
async (markdown: string, filename: string, options?: DocxOptions) => {
if (isDownloading) return;
setIsDownloading("docx");
onDownloadStart?.("docx");
try {
await downloadMarkdownAsDocx(markdown, filename, options);
} catch (error) {
onError?.(
error instanceof Error ? error : new Error(String(error)),
"docx",
);
} finally {
setIsDownloading(null);
onDownloadEnd?.("docx");
}
},
[isDownloading, onDownloadStart, onDownloadEnd, onError],
);
const downloadAsPdf = useCallback(
async (
markdown: string,
filename: string,
options?: PdfOptions & {
resolveAssetUrl?: (
rawPath: string,
) => string | null | Promise<string | null>;
},
) => {
if (isDownloading) return;
setIsDownloading("pdf");
onDownloadStart?.("pdf");
try {
await downloadMarkdownAsPdf(markdown, filename, options);
} catch (error) {
onError?.(
error instanceof Error ? error : new Error(String(error)),
"pdf",
);
} finally {
setIsDownloading(null);
onDownloadEnd?.("pdf");
}
},
[isDownloading, onDownloadStart, onDownloadEnd, onError],
);
return {
isDownloading,
downloadAsDocx,
downloadAsPdf,
canDownload: isDownloading === null,
};
}
// 导出转换函数,供非 React 环境使用
export { downloadMarkdownAsDocx, downloadMarkdownAsPdf } from "./converter";