001project_wildgrowth/backend/public/course-builder.html

425 lines
12 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>课程内容批量录入工具 - Wild Growth</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
overflow: hidden;
}
.header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
text-align: center;
}
.header h1 {
font-size: 28px;
margin-bottom: 10px;
}
.header p {
opacity: 0.9;
font-size: 14px;
}
.content {
padding: 30px;
}
.form-group {
margin-bottom: 25px;
}
label {
display: block;
font-weight: 600;
margin-bottom: 8px;
color: #333;
font-size: 14px;
}
input, textarea {
width: 100%;
padding: 12px;
border: 2px solid #e0e0e0;
border-radius: 8px;
font-size: 14px;
transition: border-color 0.3s;
}
input:focus, textarea:focus {
outline: none;
border-color: #667eea;
}
textarea {
min-height: 200px;
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
font-size: 13px;
line-height: 1.6;
}
.button-group {
display: flex;
gap: 12px;
margin-top: 20px;
}
button {
flex: 1;
padding: 14px 24px;
border: none;
border-radius: 8px;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
}
.btn-primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
}
.btn-secondary {
background: #f5f5f5;
color: #333;
}
.btn-secondary:hover {
background: #e0e0e0;
}
.result {
margin-top: 25px;
padding: 20px;
border-radius: 8px;
display: none;
}
.result.success {
background: #d4edda;
border: 1px solid #c3e6cb;
color: #155724;
}
.result.error {
background: #f8d7da;
border: 1px solid #f5c6cb;
color: #721c24;
}
.result pre {
margin-top: 10px;
padding: 12px;
background: rgba(0, 0, 0, 0.05);
border-radius: 4px;
overflow-x: auto;
font-size: 12px;
}
.example {
background: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 25px;
border-left: 4px solid #667eea;
}
.example h3 {
margin-bottom: 12px;
color: #667eea;
font-size: 16px;
}
.example code {
display: block;
background: white;
padding: 12px;
border-radius: 4px;
font-family: 'Monaco', 'Menlo', 'Courier New', monospace;
font-size: 12px;
overflow-x: auto;
white-space: pre;
}
.info-box {
background: #e7f3ff;
border: 1px solid #b3d9ff;
border-radius: 8px;
padding: 15px;
margin-bottom: 25px;
color: #004085;
}
.info-box strong {
display: block;
margin-bottom: 8px;
}
.info-box ul {
margin-left: 20px;
margin-top: 8px;
}
.info-box li {
margin-bottom: 4px;
}
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>📚 课程内容批量录入工具</h1>
<p>Wild Growth - 批量创建章节和节点</p>
</div>
<div class="content">
<div class="info-box">
<strong>📋 使用说明:</strong>
<ul>
<li>1. 填写课程 IDcourse_001</li>
<li>2. 填写 JWT Token从登录接口获取</li>
<li>3. 在 JSON 编辑器中输入章节和节点数据</li>
<li>4. 点击"提交创建"按钮</li>
<li>5. 查看创建结果</li>
</ul>
</div>
<div class="form-group">
<label for="apiUrl">API 地址</label>
<input type="text" id="apiUrl" value="http://localhost:3000" placeholder="http://localhost:3000">
</div>
<div class="form-group">
<label for="courseId">课程 ID *</label>
<input type="text" id="courseId" value="course_001" placeholder="course_001" required>
</div>
<div class="form-group">
<label for="token">JWT Token *</label>
<input type="text" id="token" placeholder="从登录接口获取的 token" required>
</div>
<div class="example">
<h3>📝 JSON 数据格式示例:</h3>
<code>{
"chapters": [
{
"title": "第一章:重新认识大脑",
"order": 1,
"nodes": [
{
"id": "node_01_01",
"title": "我们为什么会痛苦?",
"subtitle": "理解痛苦的根源",
"duration": 5
},
{
"id": "node_01_02",
"title": "大脑的节能机制",
"subtitle": "认识大脑的工作原理",
"duration": 6
}
]
},
{
"title": "第二章:潜意识的智慧",
"order": 2,
"nodes": [
{
"id": "node_02_01",
"title": "潜意识的运作",
"subtitle": "探索潜意识",
"duration": 5
}
]
}
]
}</code>
</div>
<div class="form-group">
<label for="jsonData">章节和节点数据 (JSON) *</label>
<textarea id="jsonData" placeholder='请输入 JSON 数据...'></textarea>
</div>
<div class="button-group">
<button class="btn-secondary" onclick="loadExample()">加载示例数据</button>
<button class="btn-secondary" onclick="formatJSON()">格式化 JSON</button>
<button class="btn-primary" onclick="submitData()">提交创建</button>
</div>
<div id="result" class="result"></div>
</div>
</div>
<script>
function loadExample() {
const example = {
chapters: [
{
title: "第一章:重新认识大脑",
order: 1,
nodes: [
{
id: "node_01_01",
title: "我们为什么会痛苦?",
subtitle: "理解痛苦的根源",
duration: 5
},
{
id: "node_01_02",
title: "大脑的节能机制",
subtitle: "认识大脑的工作原理",
duration: 6
},
{
id: "node_01_03",
title: "认知偏差的陷阱",
subtitle: "识别认知陷阱",
duration: 7
}
]
},
{
title: "第二章:潜意识的智慧",
order: 2,
nodes: [
{
id: "node_02_01",
title: "潜意识的运作",
subtitle: "探索潜意识",
duration: 5
},
{
id: "node_02_02",
title: "直觉与理性",
subtitle: "平衡直觉与理性",
duration: 6
}
]
}
]
};
document.getElementById('jsonData').value = JSON.stringify(example, null, 2);
}
function formatJSON() {
const textarea = document.getElementById('jsonData');
try {
const data = JSON.parse(textarea.value);
textarea.value = JSON.stringify(data, null, 2);
showResult('success', 'JSON 格式化成功!');
} catch (e) {
showResult('error', 'JSON 格式错误:' + e.message);
}
}
async function submitData() {
const apiUrl = document.getElementById('apiUrl').value.trim();
const courseId = document.getElementById('courseId').value.trim();
const token = document.getElementById('token').value.trim();
const jsonData = document.getElementById('jsonData').value.trim();
if (!courseId || !token || !jsonData) {
showResult('error', '请填写所有必填字段!');
return;
}
let data;
try {
data = JSON.parse(jsonData);
} catch (e) {
showResult('error', 'JSON 格式错误:' + e.message);
return;
}
// 验证数据结构
if (!data.chapters || !Array.isArray(data.chapters)) {
showResult('error', 'JSON 数据格式错误:必须包含 chapters 数组');
return;
}
const url = `${apiUrl}/api/courses/${courseId}/chapters-nodes`;
try {
showResult('success', '正在提交...', true);
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify(data)
});
const result = await response.json();
if (response.ok) {
showResult('success', `✅ 创建成功!\n\n章节数:${result.data.chapters_created}\n节点数:${result.data.nodes_created}\n总节点数:${result.data.total_nodes}`, false, result);
} else {
showResult('error', `❌ 创建失败:${result.message || '未知错误'}`, false, result);
}
} catch (error) {
showResult('error', `❌ 请求失败:${error.message}`);
}
}
function showResult(type, message, loading = false, data = null) {
const resultDiv = document.getElementById('result');
resultDiv.className = `result ${type}`;
resultDiv.style.display = 'block';
let content = `<strong>${message}</strong>`;
if (data) {
content += `<pre>${JSON.stringify(data, null, 2)}</pre>`;
}
resultDiv.innerHTML = content;
if (!loading) {
resultDiv.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
}
}
// 页面加载时自动加载示例数据
window.onload = function() {
loadExample();
};
</script>
</body>
</html>