'use client'; import { useEffect, useRef, useState, useCallback, useMemo } from 'react'; import Reveal from 'reveal.js'; import 'reveal.js/dist/reveal.css'; import { marked } from 'marked'; // 简单的动态主题加载器 const loadTheme = async (themeName) => { if (!themeName) return; try { // 1. 优先尝试加载自定义主题 await import(`./themes/${themeName}.css`); console.log(`加载自定义主题: ${themeName}`); } catch (error) { try { // 2. 尝试加载 Reveal.js 内置主题 await import(`reveal.js/dist/theme/${themeName}.css`); console.log(`加载内置主题: ${themeName}`); } catch (error2) { // 3. 如果都失败,使用默认主题 console.warn(`主题 ${themeName} 未找到,使用默认主题`); await import('reveal.js/dist/theme/black.css'); } } }; // Reveal.js配置 const REVEAL_CONFIG = { hash: true, transition: 'slide', transitionSpeed: 'default', backgroundTransition: 'fade', controls: true, progress: true, center: true, touch: true, loop: false, rtl: false, navigationMode: 'default', shuffle: false, fragments: true, fragmentInURL: false, embedded: false, help: true, showNotes: true, autoPlayMedia: null, preloadIframes: null, autoSlide: 0, autoSlideStoppable: true, autoSlideMethod: Reveal.navigateNext, defaultTiming: null, mouseWheel: false, hideInactiveCursor: true, hideCursorTime: 5000, previewLinks: false, postMessage: true, postMessageEvents: false, focusBodyOnPageVisibilityChange: true, viewDistance: 3, mobileViewDistance: 2, display: 'block', hideAnchorsOnUrl: false, plugins: [] }; // 配置marked解析器(只配置一次) marked.setOptions({ breaks: true, gfm: true, headerIds: false, mangle: false, }); export default function Presentation({ data }) { const deckRef = useRef(null); const revealRef = useRef(null); const [mounted, setMounted] = useState(false); const [error, setError] = useState(null); // 客户端挂载检测 useEffect(() => { setMounted(true); }, []); // 解析Markdown内容为幻灯片 const parseMarkdownToSlides = useCallback((data) => { if (!data) { return `

演示文稿

暂无内容

`; } try { // 如果有 content 字段,直接使用 markdown 内容 if (data.content) { return parseContentSlides(data); } // 如果没有 content 字段,返回默认内容 return `

${data.title || '演示文稿'}

暂无内容

`; } catch (err) { console.error('解析幻灯片内容失败:', err); return `

解析错误

内容解析失败,请检查数据格式

`; } }, []); // 解析content字段的幻灯片 const parseContentSlides = (data) => { const slideSeparators = /^---$/gm; const sections = data.content.split(slideSeparators); return sections.map((section, index) => { const trimmedSection = section.trim(); if (!trimmedSection) return ''; // 检查是否有背景设置 const backgroundMatch = trimmedSection.match(//); let backgroundAttr = ''; let contentWithoutBackground = trimmedSection; if (backgroundMatch) { backgroundAttr = ` data-background="${backgroundMatch[1]}"`; contentWithoutBackground = trimmedSection.replace(//g, ''); } const htmlContent = marked(contentWithoutBackground); // 如果是第一个幻灯片且没有标题,添加标题 if (index === 0 && !htmlContent.includes('

') && data.title) { return `

${data.title}

${htmlContent}`; } return `${htmlContent}`; }).join(''); }; // 初始化Reveal.js const initializeReveal = useCallback(async () => { if (!deckRef.current || !data) return; try { // 清理之前的实例 if (revealRef.current) { revealRef.current.destroy(); } // 动态加载主题 const themeName = data.theme || 'default'; await loadTheme(themeName); // 创建新实例 revealRef.current = new Reveal(deckRef.current, REVEAL_CONFIG); revealRef.current.initialize(); // 更新slides内容 const slides = parseMarkdownToSlides(data); const slidesContainer = deckRef.current.querySelector('.slides'); if (slidesContainer) { slidesContainer.innerHTML = slides; } setError(null); } catch (err) { setError('PPT初始化失败,请刷新页面重试'); } }, [data, parseMarkdownToSlides]); // 数据变化时重新初始化 useEffect(() => { if (mounted && data) { initializeReveal(); } return () => { if (revealRef.current) { revealRef.current.destroy(); } }; }, [mounted, data, initializeReveal]); // 错误状态 if (error) { return (

PPT加载失败

{error}

); } // 加载状态 if (!mounted) { return (
正在加载PPT...
); } return ( <>

正在加载内容...

); }