feat: 添加ESLint和Prettier配置并优化代码结构
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
Some checks failed
CI / Lint & TypeCheck (push) Has been cancelled
CI / Unit Tests (push) Has been cancelled
CI / Build Frontend (push) Has been cancelled
CI / Rust Check (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
CI / E2E Tests (push) Has been cancelled
style: 格式化代码文件并修复样式问题 docs: 新增部署文档和系统要求文档 test: 更新测试截图和覆盖率报告 refactor: 重构SchedulerPanel加载状态逻辑 ci: 添加lint和format脚本到package.json build: 更新依赖项并添加开发工具 chore: 添加验证报告和上线审查计划
This commit is contained in:
@@ -349,9 +349,38 @@ export function ClassroomPreviewer({
|
||||
const handleExport = (format: 'pptx' | 'html' | 'pdf') => {
|
||||
if (onExport) {
|
||||
onExport(format);
|
||||
} else {
|
||||
toast(`导出 ${format.toUpperCase()} 功能开发中...`, 'info');
|
||||
return;
|
||||
}
|
||||
|
||||
// Default export implementation
|
||||
toast(`正在导出 ${format.toUpperCase()} 格式...`, 'info');
|
||||
|
||||
setTimeout(() => {
|
||||
try {
|
||||
if (format === 'html') {
|
||||
const htmlContent = generateClassroomHTML(data);
|
||||
downloadFile(htmlContent, `${data.title}.html`, 'text/html');
|
||||
toast('HTML 导出成功', 'success');
|
||||
} else if (format === 'pptx') {
|
||||
// Export as JSON for conversion
|
||||
const pptxData = JSON.stringify(data, null, 2);
|
||||
downloadFile(pptxData, `${data.title}.slides.json`, 'application/json');
|
||||
toast('幻灯片数据已导出(JSON格式)', 'success');
|
||||
} else if (format === 'pdf') {
|
||||
const htmlContent = generatePrintableHTML(data);
|
||||
const printWindow = window.open('', '_blank');
|
||||
if (printWindow) {
|
||||
printWindow.document.write(htmlContent);
|
||||
printWindow.document.close();
|
||||
printWindow.print();
|
||||
toast('已打开打印预览', 'success');
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
const errorMsg = err instanceof Error ? err.message : '导出失败';
|
||||
toast(`导出失败: ${errorMsg}`, 'error');
|
||||
}
|
||||
}, 300);
|
||||
};
|
||||
|
||||
return (
|
||||
@@ -530,3 +559,135 @@ export function ClassroomPreviewer({
|
||||
}
|
||||
|
||||
export default ClassroomPreviewer;
|
||||
|
||||
// === Helper Functions ===
|
||||
|
||||
function downloadFile(content: string, filename: string, mimeType: string) {
|
||||
const blob = new Blob([content], { type: mimeType });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = filename;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
|
||||
function generateClassroomHTML(data: ClassroomData): string {
|
||||
const scenesHTML = data.scenes.map((scene, index) => `
|
||||
<section class="slide" data-index="${index}">
|
||||
<div class="slide-content ${scene.type}">
|
||||
<h2>${scene.content.heading || scene.title}</h2>
|
||||
${scene.content.bullets ? `
|
||||
<ul>
|
||||
${scene.content.bullets.map((b: string) => `<li>${b}</li>`).join('')}
|
||||
</ul>
|
||||
` : ''}
|
||||
${scene.narration ? `<p class="narration">${scene.narration}</p>` : ''}
|
||||
</div>
|
||||
</section>
|
||||
`).join('');
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>${data.title}</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||
background: linear-gradient(135deg, #3b82f6, #8b5cf6, #6366f1);
|
||||
min-height: 100vh;
|
||||
color: white;
|
||||
}
|
||||
.presentation { max-width: 1200px; margin: 0 auto; padding: 2rem; }
|
||||
header { text-align: center; padding: 2rem 0; border-bottom: 1px solid rgba(255,255,255,0.2); margin-bottom: 2rem; }
|
||||
h1 { font-size: 2.5rem; margin-bottom: 0.5rem; }
|
||||
.meta { opacity: 0.8; font-size: 0.9rem; }
|
||||
.slide {
|
||||
background: rgba(255,255,255,0.1);
|
||||
border-radius: 1rem;
|
||||
padding: 2rem;
|
||||
margin-bottom: 1.5rem;
|
||||
backdrop-filter: blur(10px);
|
||||
}
|
||||
.slide h2 { font-size: 1.8rem; margin-bottom: 1rem; }
|
||||
.slide ul { list-style: none; padding-left: 1rem; }
|
||||
.slide li { margin-bottom: 0.75rem; font-size: 1.1rem; }
|
||||
.slide li::before { content: '•'; color: #60a5fa; margin-right: 0.5rem; }
|
||||
.narration {
|
||||
background: rgba(0,0,0,0.3);
|
||||
padding: 1rem;
|
||||
border-radius: 0.5rem;
|
||||
margin-top: 1rem;
|
||||
font-style: italic;
|
||||
opacity: 0.9;
|
||||
}
|
||||
.title .slide-content { text-align: center; min-height: 200px; display: flex; flex-direction: column; justify-content: center; }
|
||||
.quiz { background: rgba(34, 197, 94, 0.2); }
|
||||
.summary { background: rgba(168, 85, 247, 0.2); }
|
||||
footer { text-align: center; padding: 2rem 0; border-top: 1px solid rgba(255,255,255,0.2); margin-top: 2rem; opacity: 0.6; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="presentation">
|
||||
<header>
|
||||
<h1>${data.title}</h1>
|
||||
<p class="meta">${data.subject} · ${data.difficulty} · ${data.duration} 分钟</p>
|
||||
</header>
|
||||
<main>${scenesHTML}</main>
|
||||
<footer><p>由 ZCLAW 课堂生成器创建</p></footer>
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
function generatePrintableHTML(data: ClassroomData): string {
|
||||
const scenesHTML = data.scenes.map((scene, index) => `
|
||||
<div class="page" style="page-break-after: always;">
|
||||
<div class="slide-print">
|
||||
<h1 style="font-size: 24pt; margin-bottom: 20pt;">${scene.content.heading || scene.title}</h1>
|
||||
${scene.content.bullets ? `
|
||||
<ul style="font-size: 14pt; line-height: 1.8;">
|
||||
${scene.content.bullets.map((b: string) => `<li>${b}</li>`).join('')}
|
||||
</ul>
|
||||
` : ''}
|
||||
${scene.narration ? `<p style="background: #f0f0f0; padding: 10pt; margin-top: 20pt; font-style: italic;">${scene.narration}</p>` : ''}
|
||||
<p style="position: absolute; bottom: 20pt; right: 20pt; color: #999; font-size: 10pt;">${index + 1} / ${data.scenes.length}</p>
|
||||
</div>
|
||||
</div>
|
||||
`).join('');
|
||||
|
||||
return `<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>${data.title} - 打印版</title>
|
||||
<style>
|
||||
@media print {
|
||||
body { margin: 0; }
|
||||
.page { page-break-after: always; }
|
||||
}
|
||||
body { font-family: 'Microsoft YaHei', sans-serif; }
|
||||
.slide-print {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
padding: 40pt;
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="document">
|
||||
<header style="text-align: center; margin-bottom: 30pt;">
|
||||
<h1 style="font-size: 32pt;">${data.title}</h1>
|
||||
<p style="color: #666;">${data.subject} · ${data.difficulty} · ${data.duration} 分钟</p>
|
||||
</header>
|
||||
${scenesHTML}
|
||||
</div>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user