'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 { await import(`./themes/${themeName}.css`); } catch (error) { try { await import(`reveal.js/dist/theme/${themeName}.css`); } catch (error2) { await import('reveal.js/dist/theme/black.css'); } } }; 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.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); }, []); const parseMarkdownToSlides = useCallback((data) => { if (!data) { return `

演示文稿

暂无内容

`; } try { if (data.content) { return parseContentSlides(data); } return `

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

暂无内容

`; } catch (err) { return `

解析错误

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

`; } }, []); 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(''); }; 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(); 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 (
正在加载内容...
); } return ( <>

正在加载内容...

); }