演示测试成功,控制台仍然有错误

This commit is contained in:
jingrow 2025-09-03 01:48:51 +08:00
parent ed6e484d94
commit a86ddfcbe3
3 changed files with 294 additions and 229 deletions

View File

@ -1,6 +1,5 @@
import Context from "@/context/Context";
export default function RootLayout({ children }) {
return (
<html lang="en">

View File

@ -0,0 +1,121 @@
.reveal-container {
height: 100vh;
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 1000;
background: black;
}
.reveal-container .reveal {
height: 100% !important;
width: 100% !important;
}
.reveal-container .reveal .slides {
height: 100% !important;
}
.reveal-container .reveal .slides section {
height: 100% !important;
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
text-align: center !important;
padding: 2rem !important;
color: white !important;
}
.reveal-container .reveal .slides section h1,
.reveal-container .reveal .slides section h2,
.reveal-container .reveal .slides section h3,
.reveal-container .reveal .slides section h4,
.reveal-container .reveal .slides section h5,
.reveal-container .reveal .slides section h6 {
margin-bottom: 1rem !important;
color: white !important;
}
.reveal-container .reveal .slides section p {
margin-bottom: 0.5rem !important;
line-height: 1.6 !important;
color: white !important;
}
.reveal-container .reveal .slides section ul,
.reveal-container .reveal .slides section ol {
text-align: left !important;
margin: 1rem 0 !important;
color: white !important;
}
.reveal-container .reveal .slides section li {
margin-bottom: 0.5rem !important;
color: white !important;
}
.reveal-container .reveal .slides section code {
background: #333 !important;
color: #fff !important;
padding: 0.2rem 0.4rem !important;
border-radius: 3px !important;
font-family: 'Courier New', monospace !important;
}
.reveal-container .reveal .slides section pre {
background: #333 !important;
color: #fff !important;
padding: 1rem !important;
border-radius: 5px !important;
text-align: left !important;
overflow-x: auto !important;
}
.reveal-container .reveal .slides section blockquote {
border-left: 4px solid #666 !important;
padding-left: 1rem !important;
margin: 1rem 0 !important;
text-align: left !important;
color: #ccc !important;
}
.reveal-container .reveal .slides section table {
border-collapse: collapse !important;
width: 100% !important;
margin: 1rem 0 !important;
color: white !important;
}
.reveal-container .reveal .slides section th,
.reveal-container .reveal .slides section td {
border: 1px solid #666 !important;
padding: 0.5rem !important;
text-align: left !important;
color: white !important;
}
.reveal-container .reveal .slides section th {
background: #444 !important;
}
.reveal-container .reveal .slides section img {
max-width: 100% !important;
height: auto !important;
margin: 1rem 0 !important;
}
.reveal-container .reveal .slides section .btn {
display: inline-block !important;
padding: 0.5rem 1rem !important;
background: #007bff !important;
color: white !important;
text-decoration: none !important;
border-radius: 0.25rem !important;
margin: 0.5rem !important;
}
.reveal-container .reveal .slides section .btn:hover {
background: #0056b3 !important;
}

View File

@ -1,153 +1,146 @@
'use client';
import { useEffect, useRef } from 'react';
import { useEffect, useRef, useState, useCallback, useMemo } from 'react';
import Reveal from 'reveal.js';
import 'reveal.js/dist/reveal.css';
import 'reveal.js/dist/theme/black.css';
import { marked } from 'marked';
import './Presentation.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: false,
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(() => {
if (!data) return;
setMounted(true);
}, []);
// marked
marked.setOptions({
breaks: true,
gfm: true,
headerIds: false,
mangle: false,
});
// Reveal.js
if (revealRef.current) {
revealRef.current.destroy();
}
revealRef.current = new Reveal(deckRef.current, {
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: false,
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: []
});
revealRef.current.initialize();
// Reveal.jsslides
setTimeout(() => {
if (deckRef.current) {
const slides = parseMarkdownToSlides(data);
const slidesContainer = deckRef.current.querySelector('.slides');
if (slidesContainer) {
slidesContainer.innerHTML = slides;
}
}
}, 100);
//
return () => {
if (revealRef.current) {
revealRef.current.destroy();
}
};
}, [data]);
//
const parseMarkdownToSlides = (data) => {
// Markdown
const parseMarkdownToSlides = useCallback((data) => {
if (!data) {
return `<section><h1>演示文稿</h1><p>暂无内容</p></section>`;
}
// content 使 markdown
if (data.content) {
const slideSeparators = /^---$/gm;
const sections = data.content.split(slideSeparators);
try {
// content 使 markdown
if (data.content) {
return parseContentSlides(data);
}
// content 使 items
const items = data.items || [];
if (items.length === 0) {
return parseSingleSlide(data);
}
return parseItemsSlides(items);
} catch (err) {
console.error('解析幻灯片内容失败:', err);
return `<section><h1>解析错误</h1><p>内容解析失败,请检查数据格式</p></section>`;
}
}, []);
// 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 '';
return sections.map((section, index) => {
const trimmedSection = section.trim();
if (!trimmedSection) return '';
const htmlContent = marked(trimmedSection);
//
if (index === 0 && !htmlContent.includes('<h1>') && data.title) {
return `<section><h1>${data.title}</h1>${htmlContent}</section>`;
}
return `<section>${htmlContent}</section>`;
}).join('');
}
const htmlContent = marked(trimmedSection);
//
if (index === 0 && !htmlContent.includes('<h1>') && data.title) {
return `<section><h1>${data.title}</h1>${htmlContent}</section>`;
}
return `<section>${htmlContent}</section>`;
}).join('');
};
// content 使 items
const items = data.items || [];
if (items.length === 0) {
// 使
return `<section>
<h1>${data.title || '演示文稿'}</h1>
${data.subtitle ? `<p class="lead">${data.subtitle}</p>` : ''}
${data.description ? `<div class="mt-4">${marked(data.description)}</div>` : ''}
${data.image ? `<img src="${data.image}" alt="${data.title}" style="max-width: 100%; height: auto; margin: 1rem 0;" />` : ''}
</section>`;
}
//
const parseSingleSlide = (data) => {
return `<section>
<h1>${data.title || '演示文稿'}</h1>
${data.subtitle ? `<p class="lead">${data.subtitle}</p>` : ''}
${data.description ? `<div class="mt-4">${marked(data.description)}</div>` : ''}
${data.image ? `<img src="${data.image}" alt="${data.title}" style="max-width: 100%; height: auto; margin: 1rem 0;" />` : ''}
</section>`;
};
// 使 items
// items
const parseItemsSlides = (items) => {
return items.map((item, index) => {
const slideContent = [];
//
if (item.item_title) {
slideContent.push(`<h1>${item.item_title}</h1>`);
}
//
if (item.item_subtitle) {
slideContent.push(`<p class="lead">${item.item_subtitle}</p>`);
}
//
if (item.item_description) {
slideContent.push(`<div class="mt-4">${marked(item.item_description)}</div>`);
}
//
if (item.item_image) {
slideContent.push(`<img src="${item.item_image}" alt="${item.item_title || 'Slide'}" style="max-width: 100%; height: auto; margin: 1rem 0;" />`);
}
//
if (item.item_video_src) {
slideContent.push(`<video controls style="max-width: 100%; height: auto; margin: 1rem 0;">
<source src="${item.item_video_src}" type="video/mp4">
@ -155,7 +148,6 @@ export default function Presentation({ data }) {
</video>`);
}
//
if (item.item_button_text && item.item_button_link) {
slideContent.push(`<div class="mt-6">
<a href="${item.item_button_link}" class="btn btn-primary" target="_blank" rel="noopener noreferrer">
@ -168,128 +160,81 @@ export default function Presentation({ data }) {
}).join('');
};
// Reveal.js
const initializeReveal = useCallback(() => {
if (!deckRef.current || !data) return;
try {
//
if (revealRef.current) {
revealRef.current.destroy();
}
//
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) {
console.error('Reveal.js初始化失败:', err);
setError('PPT初始化失败请刷新页面重试');
}
}, [data, parseMarkdownToSlides]);
//
useEffect(() => {
if (mounted && data) {
initializeReveal();
}
return () => {
if (revealRef.current) {
revealRef.current.destroy();
}
};
}, [mounted, data, initializeReveal]);
//
if (error) {
return (
<div className="min-h-screen bg-black flex items-center justify-center">
<div className="text-white text-xl text-center">
<p className="mb-4">PPT加载失败</p>
<p className="text-sm text-gray-400">{error}</p>
<button
onClick={() => window.location.reload()}
className="mt-4 px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
>
刷新页面
</button>
</div>
</div>
);
}
//
if (!mounted) {
return (
<div className="min-h-screen bg-black flex items-center justify-center">
<div className="text-white text-xl">正在加载PPT...</div>
</div>
);
}
return (
<div className="reveal-container" style={{ height: '100vh', width: '100%', position: 'fixed', top: 0, left: 0, zIndex: 1000, background: 'black' }}>
<style jsx global>{`
.reveal-container .reveal {
height: 100% !important;
width: 100% !important;
}
.reveal-container .reveal .slides {
height: 100% !important;
}
.reveal-container .reveal .slides section {
height: 100% !important;
display: flex !important;
flex-direction: column !important;
justify-content: center !important;
align-items: center !important;
text-align: center !important;
padding: 2rem !important;
color: white !important;
}
.reveal-container .reveal .slides section h1,
.reveal-container .reveal .slides section h2,
.reveal-container .reveal .slides section h3,
.reveal-container .reveal .slides section h4,
.reveal-container .reveal .slides section h5,
.reveal-container .reveal .slides section h6 {
margin-bottom: 1rem !important;
color: white !important;
}
.reveal-container .reveal .slides section p {
margin-bottom: 0.5rem !important;
line-height: 1.6 !important;
color: white !important;
}
.reveal-container .reveal .slides section ul,
.reveal-container .reveal .slides section ol {
text-align: left !important;
margin: 1rem 0 !important;
color: white !important;
}
.reveal-container .reveal .slides section li {
margin-bottom: 0.5rem !important;
color: white !important;
}
.reveal-container .reveal .slides section code {
background: #333 !important;
color: #fff !important;
padding: 0.2rem 0.4rem !important;
border-radius: 3px !important;
font-family: 'Courier New', monospace !important;
}
.reveal-container .reveal .slides section pre {
background: #333 !important;
color: #fff !important;
padding: 1rem !important;
border-radius: 5px !important;
text-align: left !important;
overflow-x: auto !important;
}
.reveal-container .reveal .slides section blockquote {
border-left: 4px solid #666 !important;
padding-left: 1rem !important;
margin: 1rem 0 !important;
text-align: left !important;
color: #ccc !important;
}
.reveal-container .reveal .slides section table {
border-collapse: collapse !important;
width: 100% !important;
margin: 1rem 0 !important;
color: white !important;
}
.reveal-container .reveal .slides section th,
.reveal-container .reveal .slides section td {
border: 1px solid #666 !important;
padding: 0.5rem !important;
text-align: left !important;
color: white !important;
}
.reveal-container .reveal .slides section th {
background: #444 !important;
}
.reveal-container .reveal .slides section img {
max-width: 100% !important;
height: auto !important;
margin: 1rem 0 !important;
}
.reveal-container .reveal .slides section .btn {
display: inline-block !important;
padding: 0.5rem 1rem !important;
background: #007bff !important;
color: white !important;
text-decoration: none !important;
border-radius: 0.25rem !important;
margin: 0.5rem !important;
}
.reveal-container .reveal .slides section .btn:hover {
background: #0056b3 !important;
}
`}</style>
<div className="reveal-container">
<div
ref={deckRef}
className="reveal"
style={{ height: '100%', width: '100%' }}
>
<div className="slides">
{/* 初始内容,会被 JavaScript 替换 */}
<section>
<p>正在加载内容...</p>
</section>