From 137cf81d291c3a5fe1bf7157c0ab414663316ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=BB=84=E5=BB=BA=E6=AD=A6?= Date: Fri, 9 May 2025 16:08:18 +0800 Subject: [PATCH] =?UTF-8?q?fix(markdown):=20=E4=BF=AE=E5=A4=8D=20Markdown?= =?UTF-8?q?=20=E7=BB=84=E4=BB=B6=E4=BB=A5=E6=8F=90=E9=AB=98=E5=8F=AF?= =?UTF-8?q?=E8=AF=BB=E6=80=A7=E5=92=8C=E7=BB=B4=E6=8A=A4=E6=80=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 格式化代码以提高可读性 -优化组件结构以提高维护性- 调整样式和布局以提升用户体验 --- .../HomePage/components/MarkdownHeader.tsx | 294 ++++----- .../HomePage/components/MarkdownViewer.tsx | 608 +++++++++--------- 2 files changed, 451 insertions(+), 451 deletions(-) diff --git a/BillNote_frontend/src/pages/HomePage/components/MarkdownHeader.tsx b/BillNote_frontend/src/pages/HomePage/components/MarkdownHeader.tsx index 65e343a..80b66e1 100644 --- a/BillNote_frontend/src/pages/HomePage/components/MarkdownHeader.tsx +++ b/BillNote_frontend/src/pages/HomePage/components/MarkdownHeader.tsx @@ -1,167 +1,169 @@ -"use client" +'use client' -import { useEffect, useState } from "react" -import { Copy, Download } from "lucide-react" -import { Button } from "@/components/ui/button" -import { Select, SelectContent, SelectItem, SelectTrigger } from "@/components/ui/select" -import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" -import { Badge } from "@/components/ui/badge" +import { useEffect, useState } from 'react' +import { Copy, Download } from 'lucide-react' +import { Button } from '@/components/ui/button' +import { Select, SelectContent, SelectItem, SelectTrigger } from '@/components/ui/select' +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip' +import { Badge } from '@/components/ui/badge' interface VersionNote { - ver_id: string - model_name?: string - style?: string - created_at?: string + ver_id: string + model_name?: string + style?: string + created_at?: string } interface NoteHeaderProps { - currentTask?: { - markdown: VersionNote[] | string - } - isMultiVersion: boolean - currentVerId: string - setCurrentVerId: (id: string) => void - modelName: string - style: string - noteStyles: { value: string; label: string }[] - onCopy: () => void - onDownload: () => void - createAt?: string | Date - setShowTranscribe: (show: boolean) => void + currentTask?: { + markdown: VersionNote[] | string + } + isMultiVersion: boolean + currentVerId: string + setCurrentVerId: (id: string) => void + modelName: string + style: string + noteStyles: { value: string; label: string }[] + onCopy: () => void + onDownload: () => void + createAt?: string | Date + setShowTranscribe: (show: boolean) => void } export function MarkdownHeader({ - currentTask, - isMultiVersion, - currentVerId, - setCurrentVerId, - modelName, - style, - noteStyles, - onCopy, - onDownload, - createAt, - showTranscribe, - setShowTranscribe - }: NoteHeaderProps) { - const [copied, setCopied] = useState(false) + currentTask, + isMultiVersion, + currentVerId, + setCurrentVerId, + modelName, + style, + noteStyles, + onCopy, + onDownload, + createAt, + showTranscribe, + setShowTranscribe, +}: NoteHeaderProps) { + const [copied, setCopied] = useState(false) - useEffect(() => { - let timer: NodeJS.Timeout - if (copied) { - timer = setTimeout(() => setCopied(false), 2000) - } - return () => clearTimeout(timer) - }, [copied]) - - const handleCopy = () => { - onCopy() - setCopied(true) + useEffect(() => { + let timer: NodeJS.Timeout + if (copied) { + timer = setTimeout(() => setCopied(false), 2000) } + return () => clearTimeout(timer) + }, [copied]) - const styleName = noteStyles.find((v) => v.value === style)?.label || style + const handleCopy = () => { + onCopy() + setCopied(true) + } - const reversedMarkdown: VersionNote[] = - Array.isArray(currentTask?.markdown) ? [...currentTask!.markdown].reverse() : [] + const styleName = noteStyles.find(v => v.value === style)?.label || style - const formatDate = (date: string | Date | undefined) => { - if (!date) return "" - const d = typeof date === "string" ? new Date(date) : date - if (isNaN(d.getTime())) return "" - return d - .toLocaleString("zh-CN", { - year: "numeric", - month: "2-digit", - day: "2-digit", - hour: "2-digit", - minute: "2-digit", - }) - .replace(/\//g, "-") - } + const reversedMarkdown: VersionNote[] = Array.isArray(currentTask?.markdown) + ? [...currentTask!.markdown].reverse() + : [] - return ( -
- {/* 左侧区域:版本 + 标签 + 创建时间 */} -
- {isMultiVersion && ( - - )} + return ( +
+ {/* 左侧区域:版本 + 标签 + 创建时间 */} +
+ {isMultiVersion && ( + + )} - {createAt && ( -
- 创建时间: {formatDate(createAt)} -
- )} -
+ + {modelName} + + + {styleName} + - {/* 右侧操作按钮 */} -
- - - - - - 复制内容 - - + {createAt && ( +
创建时间: {formatDate(createAt)}
+ )} +
- - - - - - 下载为 Markdown 文件 - - - - - - - - 原文参照 - - -
-
- ) + {/* 右侧操作按钮 */} +
+ + + + + + 复制内容 + + + + + + + + + 下载为 Markdown 文件 + + + + + + + + 原文参照 + + +
+
+ ) } diff --git a/BillNote_frontend/src/pages/HomePage/components/MarkdownViewer.tsx b/BillNote_frontend/src/pages/HomePage/components/MarkdownViewer.tsx index 91d087f..ecfc303 100644 --- a/BillNote_frontend/src/pages/HomePage/components/MarkdownViewer.tsx +++ b/BillNote_frontend/src/pages/HomePage/components/MarkdownViewer.tsx @@ -1,7 +1,7 @@ import { useState, useEffect } from 'react' import ReactMarkdown from 'react-markdown' import { Button } from '@/components/ui/button.tsx' -import { Copy, Download, ArrowRight,Play,ExternalLink } from 'lucide-react' +import { Copy, Download, ArrowRight, Play, ExternalLink } from 'lucide-react' import { toast } from 'react-hot-toast' import Error from '@/components/Lottie/error.tsx' import Loading from '@/components/Lottie/Loading.tsx' @@ -21,7 +21,7 @@ import { ScrollArea } from '@/components/ui/scroll-area.tsx' import { useTaskStore } from '@/store/taskStore' import { noteStyles } from '@/constant/note.ts' import { MarkdownHeader } from '@/pages/HomePage/components/MarkdownHeader.tsx' -import TranscriptViewer from "@/pages/HomePage/components/transcriptViewer.tsx"; +import TranscriptViewer from '@/pages/HomePage/components/transcriptViewer.tsx' interface VersionNote { ver_id: string @@ -57,10 +57,10 @@ const MarkdownViewer: FC = ({ status }) => { const taskStatus = currentTask?.status || 'PENDING' const retryTask = useTaskStore.getState().retryTask const isMultiVersion = Array.isArray(currentTask?.markdown) - const [showTranscribe, setShowTranscribe]=useState(false) + const [showTranscribe, setShowTranscribe] = useState(false) // 多版本内容处理 useEffect(() => { - if (!currentTask) return; + if (!currentTask) return if (!isMultiVersion) { setCurrentVerId('') // 清空旧版本 ID @@ -69,12 +69,17 @@ const MarkdownViewer: FC = ({ status }) => { setCreateTime(currentTask.createdAt) setSelectedContent(currentTask?.markdown) } else { - const latestVerId = currentTask.markdown[currentTask.markdown.length - 1]?.ver_id - setCurrentVerId(latestVerId) // 重置为最新版本 + const latestVersion = [...currentTask.markdown].sort( + (a, b) => new Date(b.created_at).getTime() - new Date(a.created_at).getTime() + )[0] + + if (latestVersion) { + setCurrentVerId(latestVersion.ver_id) + } } - }, [currentTask?.id,taskStatus]) + }, [currentTask?.id, taskStatus]) useEffect(() => { - if (!currentTask || !isMultiVersion) return; + if (!currentTask || !isMultiVersion) return const currentVer = currentTask.markdown.find(v => v.ver_id === currentVerId) if (currentVer) { @@ -109,331 +114,324 @@ const MarkdownViewer: FC = ({ status }) => { if (status === 'loading') { return ( -
- - -
-

正在生成笔记,请稍候…

-

这可能需要几秒钟时间,取决于视频长度

-
+
+ + +
+

正在生成笔记,请稍候…

+

这可能需要几秒钟时间,取决于视频长度

+
) } if (status === 'idle') { return ( -
- -
-

输入视频链接并点击“生成笔记”

-

支持哔哩哔哩、YouTube 、抖音等视频平台

-
+
+ +
+

输入视频链接并点击“生成笔记”

+

支持哔哩哔哩、YouTube 、抖音等视频平台

+
) } if (status === 'failed' && !isMultiVersion) { return ( -
- -
-

笔记生成失败

-

请检查后台或稍后再试

- -
+
+ +
+

笔记生成失败

+

请检查后台或稍后再试

+
+
) } - return ( -
- +
+ - {/* 中间内容区域:滚动容器 */} -
- {selectedContent && selectedContent !== 'loading' && selectedContent !== 'empty' ? ( - <> - -
+ {/* 中间内容区域:滚动容器 */} +
+ {selectedContent && selectedContent !== 'loading' && selectedContent !== 'empty' ? ( + <> + +
+ ( +

+ {children} +

+ ), + h2: ({ children, ...props }) => ( +

+ {children} +

+ ), + h3: ({ children, ...props }) => ( +

+ {children} +

+ ), + h4: ({ children, ...props }) => ( +

+ {children} +

+ ), + // Paragraphs with better line height + p: ({ children, ...props }) => ( +

+ {children} +

+ ), - ( -

{ + const isOriginLink = + typeof children[0] === 'string' && + (children[0] as string).startsWith('原片 @') + + if (isOriginLink) { + const timeMatch = (children[0] as string).match(/原片 @ (\d{2}:\d{2})/) + const timeText = timeMatch ? timeMatch[1] : '原片' + + return ( + + + + 原片({timeText}) + + + ) + } + + // Default link styling with external indicator + return ( + + {children} + {href?.startsWith('http') && ( + + )} + + ) + }, + + // Enhanced image with zoom capability + img: ({ node, ...props }) => ( +
+ + + +
+ ), + + // Better strong/bold text + strong: ({ children, ...props }) => ( + + {children} + + ), + + // Enhanced list items with support for "fake headings" + li: ({ children, ...props }) => { + const rawText = String(children) + const isFakeHeading = /^(\*\*.+\*\*)$/.test(rawText.trim()) + + if (isFakeHeading) { + return
{children}
+ } + + return ( +
  • + {children} +
  • + ) + }, + + // Enhanced unordered lists + ul: ({ children, ...props }) => ( +
      + {children} +
    + ), + + // Enhanced ordered lists + ol: ({ children, ...props }) => ( +
      + {children} +
    + ), + + // Enhanced blockquotes + blockquote: ({ children, ...props }) => ( +
    + {children} +
    + ), + + // Enhanced code blocks with syntax highlighting and copy button + code: ({ inline, className, children, ...props }) => { + const match = /language-(\w+)/.exec(className || '') + const codeContent = String(children).replace(/\n$/, '') + + if (!inline && match) { + return ( +
    +
    +
    {match[1].toUpperCase()}
    +

    - ), - h2: ({ children, ...props }) => ( -

    - {children} -

    - ), - h3: ({ children, ...props }) => ( -

    - {children} -

    - ), - h4: ({ children, ...props }) => ( -

    - {children} -

    - ), + + 复制 + +
    + + {codeContent} + +
    + ) + } - // Paragraphs with better line height - p: ({ children, ...props }) => ( -

    - {children} -

    - ), + // Inline code styling + return ( + + {children} + + ) + }, - // Enhanced links with special handling for "原片" links - a: ({ href, children, ...props }) => { - const isOriginLink = typeof children[0] === 'string' && (children[0] as string).startsWith('原片 @') + // Enhanced tables + table: ({ children, ...props }) => ( +
    + + {children} +
    +
    + ), - if (isOriginLink) { - const timeMatch = (children[0] as string).match(/原片 @ (\d{2}:\d{2})/) - const timeText = timeMatch ? timeMatch[1] : '原片' + // Table headers + th: ({ children, ...props }) => ( + + {children} + + ), - return ( - - - - 原片({timeText}) - - - ) - } + // Table cells + td: ({ children, ...props }) => ( + + {children} + + ), - // Default link styling with external indicator - return ( - - {children} - {href?.startsWith('http') && } - - ) - }, - - // Enhanced image with zoom capability - img: ({ node, ...props }) => ( -
    - - - -
    - ), - - // Better strong/bold text - strong: ({ children, ...props }) => ( - - {children} - - ), - - // Enhanced list items with support for "fake headings" - li: ({ children, ...props }) => { - const rawText = String(children) - const isFakeHeading = /^(\*\*.+\*\*)$/.test(rawText.trim()) - - if (isFakeHeading) { - return ( -
    - {children} -
    - ) - } - - return ( -
  • - {children} -
  • - ) - }, - - // Enhanced unordered lists - ul: ({ children, ...props }) => ( -
      - {children} -
    - ), - - // Enhanced ordered lists - ol: ({ children, ...props }) => ( -
      - {children} -
    - ), - - // Enhanced blockquotes - blockquote: ({ children, ...props }) => ( -
    - {children} -
    - ), - - // Enhanced code blocks with syntax highlighting and copy button - code: ({ inline, className, children, ...props }) => { - const match = /language-(\w+)/.exec(className || '') - const codeContent = String(children).replace(/\n$/, '') - - if (!inline && match) { - return ( -
    -
    -
    {match[1].toUpperCase()}
    - -
    - - {codeContent} - -
    - ) - } - - // Inline code styling - return ( - - {children} - - ) - }, - - // Enhanced tables - table: ({ children, ...props }) => ( -
    - - {children} -
    -
    - ), - - // Table headers - th: ({ children, ...props }) => ( - - {children} - - ), - - // Table cells - td: ({ children, ...props }) => ( - - {children} - - ), - - // Horizontal rule - hr: ({ ...props }) => ( -
    - ), - }} - > - {selectedContent} - - -
    -
    - { - showTranscribe && ( -
    - -
    - - ) - - } - - - ) : ( -
    -
    -
    - -
    -

    输入视频链接并点击"生成笔记"按钮

    -

    支持哔哩哔哩、YouTube等视频网站

    -
    + // Horizontal rule + hr: ({ ...props }) => ( +
    + ), + }} + > + {selectedContent} +
    - )} -
    + + {showTranscribe && ( +
    + +
    + )} + + ) : ( +
    +
    +
    + +
    +

    输入视频链接并点击"生成笔记"按钮

    +

    支持哔哩哔哩、YouTube等视频网站

    +
    +
    + )}
    +
    ) }