83 lines
2.0 KiB
TypeScript
83 lines
2.0 KiB
TypeScript
'use client';
|
|
|
|
import { useRef, useState } from 'react';
|
|
import { FileUp, Loader2 } from 'lucide-react';
|
|
import { Button } from '@/components/ui/button';
|
|
|
|
interface MarkdownImportProps {
|
|
/** 上传成功后回调,传入 MD 原文和文件名 */
|
|
onImport: (markdown: string, filename: string) => void;
|
|
/** 错误回调 */
|
|
onError?: (msg: string) => void;
|
|
/** 单文件大小上限(字节),默认 2MB */
|
|
maxSize?: number;
|
|
}
|
|
|
|
/**
|
|
* Markdown 文档上传按钮:
|
|
* - 仅接受 .md / .markdown
|
|
* - 读取为纯文本后通过 onImport 回调(不经过后端上传接口)
|
|
*/
|
|
export function MarkdownImport({
|
|
onImport,
|
|
onError,
|
|
maxSize = 2 * 1024 * 1024,
|
|
}: MarkdownImportProps) {
|
|
const inputRef = useRef<HTMLInputElement>(null);
|
|
const [loading, setLoading] = useState(false);
|
|
|
|
const handleFile = async (file: File) => {
|
|
const isMd =
|
|
/\.md$/i.test(file.name) ||
|
|
/\.markdown$/i.test(file.name) ||
|
|
file.type === 'text/markdown';
|
|
if (!isMd) {
|
|
onError?.('请选择 .md 或 .markdown 文件');
|
|
return;
|
|
}
|
|
if (file.size > maxSize) {
|
|
onError?.('文件不能超过 2M');
|
|
return;
|
|
}
|
|
setLoading(true);
|
|
try {
|
|
const text = await file.text();
|
|
onImport(text, file.name);
|
|
} catch (e) {
|
|
onError?.((e as Error).message);
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<>
|
|
<input
|
|
ref={inputRef}
|
|
type="file"
|
|
accept=".md,.markdown,text/markdown"
|
|
className="hidden"
|
|
onChange={(e) => {
|
|
const f = e.target.files?.[0];
|
|
if (f) void handleFile(f);
|
|
e.target.value = '';
|
|
}}
|
|
/>
|
|
<Button
|
|
type="button"
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={() => inputRef.current?.click()}
|
|
disabled={loading}
|
|
>
|
|
{loading ? (
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
) : (
|
|
<FileUp className="h-4 w-4" />
|
|
)}
|
|
上传 Markdown 文档
|
|
</Button>
|
|
</>
|
|
);
|
|
}
|