From 269408b66ff7e321c736581e98dcc42d11817bc4 Mon Sep 17 00:00:00 2001 From: MT-Mint <798521692@qq.com> Date: Fri, 12 Jun 2026 11:39:02 +0800 Subject: [PATCH] =?UTF-8?q?feat(table):=20=E8=A1=A8=E6=A0=BC=E4=B8=8B?= =?UTF-8?q?=E8=BD=BD=E6=94=AF=E6=8C=81=20CSV=20=E5=92=8C=20Markdown=20?= =?UTF-8?q?=E5=8F=8C=E6=A0=BC=E5=BC=8F=E4=B8=8B=E6=8B=89=E9=80=89=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 重构下载功能,将单一下载按钮改为 DropdownMenu 下拉菜单,新增 CSV 导出和 escapeCsvCell 辅助函数,downloadTextFile 支持自定义 MIME 类型。 --- .../workspace/messages/markdown-content.tsx | 62 +++++++++++++++---- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/frontend/src/components/workspace/messages/markdown-content.tsx b/frontend/src/components/workspace/messages/markdown-content.tsx index 2c7026b0..e2dab4fb 100644 --- a/frontend/src/components/workspace/messages/markdown-content.tsx +++ b/frontend/src/components/workspace/messages/markdown-content.tsx @@ -12,6 +12,12 @@ import { MessageResponse, type MessageResponseProps, } from "@/components/ai-elements/message"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { useI18n } from "@/core/i18n/hooks"; import { streamdownPlugins } from "@/core/streamdown"; import { CopyButton } from "@/components/workspace/copy-button"; @@ -58,9 +64,22 @@ function toMarkdownTable(data: TableData): string { return [headerLine, dividerLine, ...rowLines].join("\n"); } -function downloadMarkdownFile(content: string, filename: string) { +function escapeCsvCell(cell: string): string { + const normalized = cell.replace(/\r\n/g, "\n"); + if (!/["\n,]/.test(normalized)) return normalized; + return `"${normalized.replace(/"/g, '""')}"`; +} + +function toCsvTable(data: TableData): string { + if (data.headers.length === 0) return ""; + const headerLine = data.headers.map(escapeCsvCell).join(","); + const rowLines = data.rows.map((row) => row.map(escapeCsvCell).join(",")); + return [headerLine, ...rowLines].join("\r\n"); +} + +function downloadTextFile(content: string, filename: string, mimeType: string) { const blob = new Blob(["\uFEFF", content], { - type: "text/markdown;charset=utf-8", + type: mimeType, }); const url = URL.createObjectURL(blob); const anchor = document.createElement("a"); @@ -99,13 +118,23 @@ export function MarkdownTable({ })(); const handleDownload = useCallback(() => { + const table = tableRef.current; + if (!table) return; + const data = parseTableData(table); + if (!data) return; + const csv = toCsvTable(data); + if (!csv) return; + downloadTextFile(csv, "table.csv", "text/csv;charset=utf-8"); + }, []); + + const handleDownloadMarkdown = useCallback(() => { const table = tableRef.current; if (!table) return; const data = parseTableData(table); if (!data) return; const markdown = toMarkdownTable(data); if (!markdown) return; - downloadMarkdownFile(markdown, "table.md"); + downloadTextFile(markdown, "table.md", "text/markdown;charset=utf-8"); }, []); return ( @@ -115,15 +144,24 @@ export function MarkdownTable({ >