feat(ui): 笔记顶部新增视频封面 Banner

- 视频封面做模糊暗色背景,上方叠加视频信息
- 显示视频标题、作者/UP主、平台
- 右侧「原视频」按钮跳转原始链接
- 无封面时降级为渐变背景

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
huangjianwu
2026-03-23 15:54:36 +08:00
parent 05877a2197
commit 55cc3bcd63
2 changed files with 86 additions and 0 deletions

View File

@@ -24,6 +24,7 @@ import { MarkdownHeader } from '@/pages/HomePage/components/MarkdownHeader.tsx'
import TranscriptViewer from '@/pages/HomePage/components/transcriptViewer.tsx' import TranscriptViewer from '@/pages/HomePage/components/transcriptViewer.tsx'
import MarkmapEditor from '@/pages/HomePage/components/MarkmapComponent.tsx' import MarkmapEditor from '@/pages/HomePage/components/MarkmapComponent.tsx'
import ChatPanel from '@/pages/HomePage/components/ChatPanel.tsx' import ChatPanel from '@/pages/HomePage/components/ChatPanel.tsx'
import VideoBanner from '@/pages/HomePage/components/VideoBanner.tsx'
interface VersionNote { interface VersionNote {
ver_id: string ver_id: string
@@ -228,6 +229,12 @@ const MarkdownViewer: FC<MarkdownViewerProps> = ({ status }) => {
) : ( ) : (
<> <>
<ScrollArea className="min-w-0 flex-1"> <ScrollArea className="min-w-0 flex-1">
<div className="px-2">
<VideoBanner
audioMeta={currentTask?.audioMeta}
videoUrl={currentTask?.formData?.video_url}
/>
</div>
<div className={'markdown-body w-full px-2'}> <div className={'markdown-body w-full px-2'}>
<ReactMarkdown <ReactMarkdown
remarkPlugins={[gfm, remarkMath]} remarkPlugins={[gfm, remarkMath]}

View File

@@ -0,0 +1,79 @@
import { ExternalLink } from 'lucide-react'
import type { AudioMeta } from '@/store/taskStore'
interface VideoBannerProps {
audioMeta?: AudioMeta
videoUrl?: string
}
/** 平台 label 映射 */
const platformLabel: Record<string, string> = {
bilibili: '哔哩哔哩',
youtube: 'YouTube',
douyin: '抖音',
xiaohongshu: '小红书',
}
export default function VideoBanner({ audioMeta, videoUrl }: VideoBannerProps) {
if (!audioMeta) return null
const coverUrl = audioMeta.cover_url
const title = audioMeta.title
const uploader = audioMeta.raw_info?.uploader || ''
const platform = platformLabel[audioMeta.platform] || audioMeta.platform || ''
const originalUrl = videoUrl || audioMeta.raw_info?.webpage_url || ''
return (
<div className="relative mb-4 overflow-hidden rounded-lg">
{/* 模糊背景封面 */}
<div className="absolute inset-0">
{coverUrl ? (
<img
src={coverUrl}
alt=""
className="h-full w-full object-cover blur-md brightness-[0.4] scale-110"
/>
) : (
<div className="h-full w-full bg-gradient-to-r from-blue-600 to-indigo-700" />
)}
</div>
{/* 内容层 */}
<div className="relative flex items-center gap-4 px-5 py-4">
{/* 封面缩略图 */}
{coverUrl && (
<img
src={coverUrl}
alt={title}
className="h-16 w-28 shrink-0 rounded-md object-cover shadow-md"
/>
)}
{/* 文字信息 */}
<div className="min-w-0 flex-1">
<h2 className="truncate text-base font-bold text-white" title={title}>
{title}
</h2>
<div className="mt-1 flex flex-wrap items-center gap-2 text-sm text-white/70">
{uploader && <span>{uploader}</span>}
{uploader && platform && <span className="text-white/40">·</span>}
{platform && <span>{platform}</span>}
</div>
</div>
{/* 跳转原视频 */}
{originalUrl && (
<a
href={originalUrl}
target="_blank"
rel="noopener noreferrer"
className="flex shrink-0 items-center gap-1.5 rounded-full bg-white/15 px-3 py-1.5 text-xs font-medium text-white backdrop-blur-sm transition-colors hover:bg-white/25"
>
<ExternalLink className="h-3.5 w-3.5" />
<span></span>
</a>
)}
</div>
</div>
)
}