Files
dsProject/dsLightRag/static/teacherHelper/index.html
2025-09-05 07:13:48 +08:00

410 lines
20 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>教师辅助工具 - 教学资源生成</title>
<!-- Bootstrap CSS -->
<link href="https://gcore.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://gcore.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
<style>
:root {
--primary-color: #2c3e50;
--secondary-color: #3498db;
--accent-color: #e74c3c;
--light-color: #ecf0f1;
--dark-color: #34495e;
}
body {
font-family: 'Microsoft YaHei', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background-color: #f8f9fa;
color: var(--dark-color);
padding-top: 20px;
}
.navbar {
background-color: var(--primary-color);
box-shadow: 0 2px 4px rgba(0, 0, 0, .1);
}
.navbar-brand {
font-weight: bold;
color: white !important;
}
.card {
border: none;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, .1);
margin-bottom: 20px;
transition: transform 0.3s ease;
}
.card:hover {
transform: translateY(-5px);
}
.card-header {
background-color: var(--primary-color);
color: white;
border-radius: 10px 10px 0 0 !important;
padding: 15px 20px;
font-weight: bold;
}
.btn-primary {
background-color: var(--secondary-color);
border-color: var(--secondary-color);
transition: all 0.3s ease;
}
.btn-primary:hover {
background-color: #2980b9;
border-color: #2980b9;
}
.form-control {
border-radius: 5px;
border: 1px solid #ddd;
padding: 10px;
}
.form-control:focus {
border-color: var(--secondary-color);
box-shadow: 0 0 0 0.25rem rgba(52, 152, 219, 0.25);
}
.result-container {
background-color: white;
border-radius: 10px;
padding: 20px;
margin-top: 20px;
box-shadow: 0 4px 6px rgba(0, 0, 0, .1);
min-height: 200px;
max-height: 500px;
overflow-y: auto;
}
.loading-container {
display: none;
justify-content: center;
align-items: center;
height: 200px;
}
.loader {
width: 50px;
height: 50px;
border: 5px solid #f3f3f3;
border-top: 5px solid #3498db;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
/* 按钮禁用样式 */
.btn-disabled {
background-color: #cccccc !important;
border-color: #cccccc !important;
cursor: not-allowed;
opacity: 0.7;
}
/* 下载按钮样式 */
.download-btn {
display: inline-block;
margin-top: 15px;
padding: 10px 20px;
background-color: #27ae60;
color: white;
border-radius: 5px;
text-decoration: none;
transition: background-color 0.3s;
}
.download-btn:hover {
background-color: #219653;
color: white;
}
/* Markdown内容样式 */
.result-container h1, .result-container h2, .result-container h3,
.result-container h4, .result-container h5, .result-container h6 {
margin-top: 2rem;
margin-bottom: 1.2rem;
font-weight: 600;
color: #2c3e50;
}
.result-container p { margin-bottom: 1.2rem; line-height: 1.8; }
.result-container ul, .result-container ol { margin-bottom: 1.2rem; padding-left: 2rem; }
.result-container li { margin-bottom: 0.5rem; }
.result-container blockquote { margin: 0 0 1.2rem; padding: 1rem 1.5rem; border-left: 4px solid #3498db; background-color: #f8f9fa; }
.result-container code { padding: 0.2em 0.4em; background-color: rgba(27, 31, 35, 0.05); border-radius: 3px; font-family: 'Courier New', monospace; }
.result-container pre { padding: 16px; overflow: auto; background-color: #f6f8fa; border-radius: 3px; margin-bottom: 1.2rem; border: 1px solid #e1e4e8; }
.result-container table { border-collapse: collapse; width: 100%; margin-bottom: 1.2rem; }
.result-container table th, .result-container table td { padding: 0.75rem; border: 1px solid #dfe2e5; text-align: left; }
.result-container table th { background-color: #f6f8fa; font-weight: 600; }
</style>
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-dark">
<div class="container">
<a class="navbar-brand" href="#">
<i class="bi bi-mortarboard-fill me-2"></i>AI教学资源生成工具
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item">
<a class="nav-link active" href="#">教学资源生成</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container mt-4">
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<i class="bi bi-book me-2"></i>教学资源生成工具
</div>
<div class="card-body">
<p class="text-muted">
使用此工具生成万有引力相关的导学案、教案、课件和作业。您可以自定义提示词以获得更符合需求的内容。</p>
<ul class="nav nav-tabs" id="resourceTabs" role="tablist">
<li class="nav-item" role="presentation">
<button class="nav-link active" id="daoXueAn-tab" data-bs-toggle="tab" data-bs-target="#daoXueAn" type="button" role="tab">导学案</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="jiaoAn-tab" data-bs-toggle="tab" data-bs-target="#jiaoAn" type="button" role="tab">教案</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="keJian-tab" data-bs-toggle="tab" data-bs-target="#keJian" type="button" role="tab">课件</button>
</li>
<li class="nav-item" role="presentation">
<button class="nav-link" id="zuoYe-tab" data-bs-toggle="tab" data-bs-target="#zuoYe" type="button" role="tab">作业</button>
</li>
</ul>
<div class="tab-content" id="resourceTabsContent">
<!-- 导学案标签页 -->
<div class="tab-pane fade show active" id="daoXueAn" role="tabpanel">
<form id="daoXueAnForm">
<div class="mb-3">
<label for="daoXueAnSystemPrompt" class="form-label">系统提示词</label>
<textarea class="form-control" id="daoXueAnSystemPrompt" rows="3">你是市级骨干教师,撰写导学案时严格对标"核心素养四维度(物理观念、科学思维、探究能力、态度责任)",并采用"目标—评价—活动"逆向设计模板UbD</textarea>
</div>
<!-- 新增难度选择器 -->
<div class="mb-3">
<label for="daoXueAnDifficulty" class="form-label">难度级别</label>
<select class="form-select" id="daoXueAnDifficulty">
<option value="basic">基础</option>
<option value="intermediate">提高</option>
<option value="advanced">进阶</option>
</select>
</div>
<div class="mb-3">
<label for="daoXueAnUserPrompt" class="form-label">用户提示词</label>
<textarea class="form-control" id="daoXueAnUserPrompt" rows="6">请输出"万有引力"第1课时的导学案...</textarea>
</div>
<button type="button" class="btn btn-primary" id="generateDaoXueAn" onclick="generateResource('daoXueAn')">
<i class="bi bi-magic me-2"></i>生成导学案
</button>
</form>
<div id="daoXueAnLoading" class="loading-container"><div class="loader"></div></div>
<div id="daoXueAnResult" class="result-container"></div>
</div>
<!-- 教案标签页(已移除难度选择器) -->
<div class="tab-pane fade" id="jiaoAn" role="tabpanel">
<form id="jiaoAnForm">
<div class="mb-3">
<label for="jiaoAnSystemPrompt" class="form-label">系统提示词</label>
<textarea class="form-control" id="jiaoAnSystemPrompt" rows="3">你是市级骨干教师,撰写教案时严格对标"核心素养四维度(物理观念、科学思维、探究能力、态度责任)"。</textarea>
</div>
<!-- 已删除难度选择器代码 -->
<div class="mb-3">
<label for="jiaoAnUserPrompt" class="form-label">用户提示词</label>
<textarea class="form-control" id="jiaoAnUserPrompt" rows="6">请输出"万有引力"第1课时的教案...</textarea>
</div>
<button type="button" class="btn btn-primary" id="generateJiaoAn" onclick="generateResource('jiaoAn')">
<i class="bi bi-magic me-2"></i>生成教案
</button>
</form>
<div id="jiaoAnLoading" class="loading-container"><div class="loader"></div></div>
<div id="jiaoAnResult" class="result-container"></div>
</div>
<!-- 课件标签页(已移除难度选择器) -->
<div class="tab-pane fade" id="keJian" role="tabpanel">
<form id="keJianForm">
<div class="mb-3">
<label for="keJianSystemPrompt" class="form-label">系统提示词</label>
<textarea class="form-control" id="keJianSystemPrompt" rows="3">你是PPT视觉设计教练遵循"6×6原则"每页≤6行每行≤6词字体≥28pt。</textarea>
</div>
<!-- 已删除难度选择器代码 -->
<div class="mb-3">
<label for="keJianUserPrompt" class="form-label">用户提示词</label>
<textarea class="form-control" id="keJianUserPrompt" rows="6">为"万有引力"生成12页PPT大纲...</textarea>
</div>
<button type="button" class="btn btn-primary" id="generateKeJian" onclick="generateResource('keJian')">
<i class="bi bi-magic me-2"></i>生成课件
</button>
</form>
<div id="keJianLoading" class="loading-container"><div class="loader"></div></div>
<div id="keJianResult" class="result-container"></div>
</div>
<!-- 作业标签页 -->
<div class="tab-pane fade" id="zuoYe" role="tabpanel">
<form id="zuoYeForm">
<div class="mb-3">
<label for="zuoYeSystemPrompt" class="form-label">系统提示词</label>
<textarea class="form-control" id="zuoYeSystemPrompt" rows="3">你是命题专家,熟悉布鲁姆认知分类和'双减'政策题量控制为20分钟完成。</textarea>
</div>
<!-- 新增难度选择器 -->
<div class="mb-3">
<label for="zuoYeDifficulty" class="form-label">难度级别</label>
<select class="form-select" id="zuoYeDifficulty">
<option value="basic">基础</option>
<option value="intermediate">提高</option>
<option value="advanced">进阶</option>
</select>
</div>
<div class="mb-3">
<label for="zuoYeUserPrompt" class="form-label">用户提示词</label>
<textarea class="form-control" id="zuoYeUserPrompt" rows="6">请根据万有引力知识点生成课后作业...</textarea>
</div>
<button type="button" class="btn btn-primary" id="generateZuoYe" onclick="generateResource('zuoYe')">
<i class="bi bi-magic me-2"></i>生成作业
</button>
</form>
<div id="zuoYeLoading" class="loading-container"><div class="loader"></div></div>
<div id="zuoYeResult" class="result-container"></div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<script src="https://gcore.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
<script>
// 资源类型名称映射
function getResourceName(resourceType) {
const resourceNames = {
'daoXueAn': '导学案',
'jiaoAn': '教案',
'keJian': '课件',
'zuoYe': '作业'
};
return resourceNames[resourceType] || resourceType;
}
// 生成资源主函数
async function generateResource(resourceType) {
// 获取元素
const generateButton = document.getElementById(`generate${resourceType.charAt(0).toUpperCase() + resourceType.slice(1)}`);
const loadingElement = document.getElementById(`${resourceType}Loading`);
const resultContainer = document.getElementById(`${resourceType}Result`);
const systemPrompt = document.getElementById(`${resourceType}SystemPrompt`).value;
// 已实现的逻辑(无需修改)
const difficultyElement = document.getElementById(`${resourceType}Difficulty`);
let userPrompt = document.getElementById(`${resourceType}UserPrompt`).value;
// 仅当难度选择器存在时才添加分层提示词
if (difficultyElement) {
const difficulty = difficultyElement.value;
const difficultyPrompts = {
basic: "【分层要求】侧重基础知识讲解和简单应用,例题和练习以教材基础题为主,适合基础薄弱学生。",
intermediate: "【分层要求】增加知识综合应用,例题包含教材变式题,练习加入少量拓展题,适合中等水平学生。",
advanced: "【分层要求】强调知识迁移和拓展探究,例题为综合应用题,练习包含挑战性问题和跨学科联系,适合优秀学生。"
};
userPrompt = `${userPrompt}\n\n${difficultyPrompts[difficulty]}`;
}
// 发送请求(保持原有代码不变)
const response = await fetch(`/api/teacher/${resourceType}`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
system_prompt: systemPrompt,
user_prompt: userPrompt // 发送拼接后的提示词
})
});
if (!response.ok) throw new Error(`HTTP错误: ${response.status}`);
// 处理流式响应
const reader = response.body.getReader();
const decoder = new TextDecoder();
let done = false;
while (!done) {
const { value, done: doneReading } = await reader.read();
done = doneReading;
const chunk = decoder.decode(value, { stream: true });
if (chunk) handleStreamData(chunk, resourceType, resultContainer, loadingElement, generateButton);
}
}
// 处理流数据
function handleStreamData(data, resourceType, resultContainer, loadingElement, generateButton) {
const lines = data.split('\n').filter(line => line.trim());
let shouldHideLoading = false;
lines.forEach(line => {
// 移除data:前缀
const cleanLine = line.replace(/^data:/, '').trim();
if (cleanLine.includes('[下载链接]')) {
const url = cleanLine.split('[下载链接]')[1].trim();
const fileName = `${getResourceName(resourceType)}_${new Date().toLocaleDateString().replace(/\//g, '-')}.docx`;
resultContainer.innerHTML += `<a href="${url}" class="download-btn" download="${fileName}"><i class="bi bi-download me-2"></i>下载${getResourceName(resourceType)}\n</a>`;
shouldHideLoading = true;
} else if (cleanLine.includes('[转换失败]')) {
const errorMsg = cleanLine.split('[转换失败]')[1].trim();
resultContainer.innerHTML += `<div class="alert alert-danger"><i class="bi bi-x-circle me-2"></i>转换失败: ${errorMsg}</div>`;
shouldHideLoading = true;
} else if (cleanLine.includes('[DONE]')) {
shouldHideLoading = true;
} else if (cleanLine) {
// 普通消息显示已移除data:前缀)
resultContainer.innerHTML += `<div class="alert alert-info"><i class="bi bi-info-circle me-2"></i>${cleanLine}</div>`;
resultContainer.scrollTop = resultContainer.scrollHeight;
}
});
// 隐藏加载动画
if (shouldHideLoading) {
loadingElement.style.display = 'none';
generateButton.disabled = false;
generateButton.classList.remove('btn-disabled');
generateButton.innerHTML = `<i class="bi bi-magic me-2"></i>生成${getResourceName(resourceType).replace(/^./, c => c.toLowerCase())}`;
}
}
// 重置状态
function resetState(loadingElement, generateButton) {
loadingElement.style.display = 'none';
generateButton.disabled = false;
generateButton.classList.remove('btn-disabled');
generateButton.innerHTML = `<i class="bi bi-magic me-2"></i>生成${getResourceName(resourceType).replace(/^./, c => c.toLowerCase())}`;
}
</script>
</body>
</html>