'commit'
This commit is contained in:
@@ -1,7 +1,13 @@
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import json
|
||||
import subprocess
|
||||
import tempfile
|
||||
from datetime import datetime
|
||||
from fastapi.responses import FileResponse
|
||||
|
||||
from fastapi import APIRouter, Request, HTTPException
|
||||
from fastapi import APIRouter, Request, HTTPException, Depends
|
||||
from fastapi.responses import StreamingResponse
|
||||
|
||||
from TeacherHelper.Kit.TeacherHelper import (
|
||||
@@ -17,17 +23,20 @@ router = APIRouter(prefix="/api", tags=["教师辅助工具"])
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@router.post("/teacher/daoXueAn")
|
||||
@router.api_route("/teacher/daoXueAn", methods=["GET", "POST"]) # 修改这里
|
||||
async def generate_dao_xue_an(request: Request):
|
||||
"""生成导学案接口"""
|
||||
try:
|
||||
# 解析请求体
|
||||
# 获取请求参数(兼容GET和POST)
|
||||
if request.method == "GET":
|
||||
params = dict(request.query_params)
|
||||
else:
|
||||
body = await request.body()
|
||||
data = json.loads(body) if body else {}
|
||||
params = json.loads(body) if body else {}
|
||||
|
||||
# 获取提示词参数,如果没有提供则使用默认值
|
||||
system_prompt = data.get("system_prompt")
|
||||
user_prompt = data.get("user_prompt")
|
||||
# 获取提示词参数
|
||||
system_prompt = params.get("system_prompt")
|
||||
user_prompt = params.get("user_prompt")
|
||||
|
||||
# 生成导学案
|
||||
content_generator = await generate_lesson_plan(system_prompt, user_prompt)
|
||||
@@ -35,9 +44,51 @@ async def generate_dao_xue_an(request: Request):
|
||||
# 定义SSE响应生成器
|
||||
async def generate_sse_response():
|
||||
try:
|
||||
buffer = [] # 用于收集完整内容
|
||||
yield "data: 开始生成导学案...\n\n"
|
||||
async for chunk in content_generator:
|
||||
yield f"data: {chunk}\n\n"
|
||||
buffer.append(chunk) # 仅收集内容,不实时发送
|
||||
|
||||
# 保存为Markdown并转换为Word
|
||||
if buffer:
|
||||
# 创建保存目录 - 修改保存路径到static下
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
save_dir = os.path.join(project_root, "static", "teacherHelpergenerated_files")
|
||||
os.makedirs(save_dir, exist_ok=True)
|
||||
|
||||
# 生成唯一文件名
|
||||
timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
|
||||
|
||||
# 创建临时Markdown文件
|
||||
with tempfile.NamedTemporaryFile(mode='w', encoding='utf-8', suffix='.md', delete=False) as temp_md:
|
||||
temp_md.write(''.join(buffer))
|
||||
temp_md_path = temp_md.name
|
||||
|
||||
# 使用Pandoc转换为Word
|
||||
docx_filename = f"导学案_{timestamp}.docx"
|
||||
docx_path = os.path.join(save_dir, docx_filename)
|
||||
|
||||
try:
|
||||
yield "data: 正在转换为Word文档...\n\n"
|
||||
# 执行Pandoc命令
|
||||
result = subprocess.run(
|
||||
["pandoc", temp_md_path, "-o", docx_path],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=True
|
||||
)
|
||||
|
||||
# 清理临时文件
|
||||
os.unlink(temp_md_path)
|
||||
|
||||
logger.info(f"导学案已转换为Word文档: {docx_path}")
|
||||
# 修改下载链接为静态文件路径
|
||||
yield f"data: [下载链接] /static/teacherHelpergenerated_files/{docx_filename}\n\n"
|
||||
except subprocess.CalledProcessError as e:
|
||||
yield f"data: [转换失败] Pandoc错误: {e.stderr}\n\n"
|
||||
except Exception as e:
|
||||
yield f"data: [转换失败] {str(e)}\n\n"
|
||||
|
||||
yield "data: [DONE]\n\n"
|
||||
except Exception as e:
|
||||
logger.error(f"生成导学案时发生异常: {str(e)}")
|
||||
@@ -176,3 +227,20 @@ async def generate_zuo_ye(request: Request):
|
||||
except Exception as e:
|
||||
logger.error(f"作业接口异常: {str(e)}")
|
||||
raise HTTPException(status_code=500, detail=f"服务器内部错误: {str(e)}")
|
||||
|
||||
|
||||
@router.get("/teacher/download/{filename}")
|
||||
async def download_word_file(filename: str):
|
||||
"""下载生成的Word文件"""
|
||||
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
save_dir = os.path.join(project_root, "static", "teacherHelpergenerated_files")
|
||||
file_path = os.path.join(save_dir, filename)
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
raise HTTPException(status_code=404, detail="文件不存在")
|
||||
|
||||
return FileResponse(
|
||||
path=file_path,
|
||||
filename=filename,
|
||||
media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
||||
)
|
Binary file not shown.
87
dsLightRag/generated_files/导学案_20250904165056.md
Normal file
87
dsLightRag/generated_files/导学案_20250904165056.md
Normal file
@@ -0,0 +1,87 @@
|
||||
### 1. 教材与学情分析
|
||||
|
||||
本节课选自人教版八年级下册第十章第2节《万有引力》。学生在前期已学习了重力、匀速圆周运动等知识,具备一定的力学基础和科学探究意识。本节内容是牛顿力学体系中的重要组成部分,通过引导学生理解万有引力的提出背景、基本概念及其在天体运动中的应用,有助于培养其物理观念、科学思维和探究能力。
|
||||
|
||||
---
|
||||
|
||||
### 2. 四维目标(对应核心素养)
|
||||
|
||||
| 核心素养维度 | 学习目标 |
|
||||
|--------------|----------|
|
||||
| 物理观念 | 通过观察和分析,理解万有引力的基本概念及其作用规律。 |
|
||||
| 科学思维 | 能够运用归纳推理和类比思维,理解牛顿提出万有引力的过程。 |
|
||||
| 探究能力 | 通过小组合作探究,设计实验验证万有引力与质量、距离的关系(定性)。 |
|
||||
| 态度责任 | 培与科学探索的兴趣,体会科学家探索自然规律的坚持与智慧。 |
|
||||
|
||||
---
|
||||
|
||||
### 3. 重难点
|
||||
|
||||
| 类型 | 内容(行为动词+知识内容) |
|
||||
|------|-----------------------------|
|
||||
| 教学重点 | 分析牛顿提出万有引力的思想过程;理解万有引力的定义与表达式。 |
|
||||
| 教学难点 | 推理万有引力与质量、距离之间的定性关系;理解万有引力在天体运动中的作用。 |
|
||||
|
||||
---
|
||||
|
||||
### 4. 教学准备
|
||||
|
||||
| 类型 | 内容 |
|
||||
|------|------|
|
||||
| 实验器材 | 演示实验:弹簧秤、质量块若干、天体模型(地球与月球) |
|
||||
| 数字资源 | Padlet在线协作平台、PPT课件、视频片段《牛顿发现万有引力》 |
|
||||
|
||||
---
|
||||
|
||||
### 5. 教学过程(时间轴表格)
|
||||
|
||||
| 时间 | 教师行为 | 期望的学生行为 | 评价要点 |
|
||||
|------|----------|------------------|-----------|
|
||||
| 0-5分钟 | 情境导入:播放牛顿发现苹果落地的视频,提问“为什么苹果会落地?” | 学生观看视频,思考问题,尝试回答 | 观察学生是否能联系重力知识进行初步解释 |
|
||||
| 5-10分钟 | 演示实验:用弹簧秤测量不同质量物体的重力,引导学生观察数据变化 | 学生观察实验,记录数据,思考质量与重力的关系 | 评价学生是否理解质量与引力的关系 |
|
||||
| 10-15分钟 | 讲解牛顿提出万有引力的过程,结合地球绕太阳运动进行类比说明 | 学生倾听,记录关键点,尝试用自己的语言复述 | 评价学生是否能理解“类比思维”在科学发现中的作用 |
|
||||
| 15-25分钟 | 小组探究:每组设计一个实验(定性)验证万有引力与质量、距离的关系(可用天体模型模拟) | 学生分组讨论,设计实验方案,进行模拟演示 | 评价学生是否能运用控制变量法进行探究设计 |
|
||||
| 25-30分钟 | Padlet即时反馈:学生将探究结果上传至Padlet平台,教师选取典型方案进行展示与点评 | 学生上传探究结果,浏览他人方案,参与互评 | 评价学生是否能清晰表达探究过程与结论 |
|
||||
| 30-40分钟 | 总结提升:教师引导学生归纳万有引力的基本概念与表达式(F=Gm₁m₂/r²),并解释其在宇宙中的应用 | 学生整理笔记,参与互动问答,尝试解释天体运动现象 | 评价学生是否掌握万有引力的基本表达与应用 |
|
||||
| 40-45分钟 | 课堂小结与作业布置:回顾本节课重点,布置分层作业 | 学生记录作业内容,提出疑问 | 评价学生是否理解学习目标与任务 |
|
||||
|
||||
---
|
||||
|
||||
### 6. 板书设计(ASCII示意图)
|
||||
|
||||
```
|
||||
万有引力
|
||||
┌───────────────┐
|
||||
│ 一、牛顿的发现过程 │
|
||||
│ - 苹果落地与月球绕地 │
|
||||
│ - 类比思维的应用 │
|
||||
├───────────────┤
|
||||
│ 二、万有引力概念 │
|
||||
│ - 定义:任何两个物体之间都存在引力 │
|
||||
│ - 表达式:F = Gm₁m₂ / r² │
|
||||
├───────────────┤
|
||||
│ 三、探究活动 │
|
||||
│ - 探究目的:质量、距离对引力的影响 │
|
||||
│ - 探究方法:控制变量法 + 模拟实验 │
|
||||
└───────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7. 作业布置(与导学案分层衔接)
|
||||
|
||||
| 层次 | 内容 |
|
||||
|------|------|
|
||||
| 基础层 | 教材P58练习题1-3题,完成导学案“基础巩固”部分 |
|
||||
| 提高层 | 设计一个实验方案,验证“两个物体之间的引力与它们的距离平方成反比”(可用文字+图示表达) |
|
||||
| 拓展层 | 查阅资料,简述“万有引力定律”对现代航天技术的影响(200字左右) |
|
||||
|
||||
---
|
||||
|
||||
### 8. 教学反思提示(空白)
|
||||
|
||||
1. ___________________________________________________________
|
||||
|
||||
2. ___________________________________________________________
|
||||
|
||||
3. ___________________________________________________________
|
BIN
dsLightRag/generated_files/导学案_20250904170005.docx
Normal file
BIN
dsLightRag/generated_files/导学案_20250904170005.docx
Normal file
Binary file not shown.
@@ -4,8 +4,11 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>教师辅助工具 - 教学资源生成</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.8.1/font/bootstrap-icons.css" rel="stylesheet">
|
||||
<!-- 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;
|
||||
@@ -85,52 +88,77 @@
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.loading {
|
||||
.loading-container {
|
||||
display: none;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.spinner-border {
|
||||
width: 3rem;
|
||||
height: 3rem;
|
||||
.loader {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 5px solid #f3f3f3;
|
||||
border-top: 5px solid #3498db;
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link {
|
||||
color: var(--dark-color);
|
||||
font-weight: 500;
|
||||
@keyframes spin {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.nav-tabs .nav-link.active {
|
||||
color: var(--secondary-color);
|
||||
font-weight: bold;
|
||||
/* 按钮禁用样式 */
|
||||
.btn-disabled {
|
||||
background-color: #cccccc !important;
|
||||
border-color: #cccccc !important;
|
||||
cursor: not-allowed;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 40px;
|
||||
padding: 20px 0;
|
||||
background-color: var(--primary-color);
|
||||
/* 下载按钮样式 */
|
||||
.download-btn {
|
||||
display: inline-block;
|
||||
margin-top: 15px;
|
||||
padding: 10px 20px;
|
||||
background-color: #27ae60;
|
||||
color: white;
|
||||
text-align: center;
|
||||
border-radius: 5px;
|
||||
text-decoration: none;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.toast-container {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
right: 20px;
|
||||
z-index: 1050;
|
||||
.download-btn:hover {
|
||||
background-color: #219653;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
padding-top: 20px;
|
||||
/* 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>教师辅助工具
|
||||
<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>
|
||||
@@ -153,7 +181,8 @@
|
||||
<i class="bi bi-book me-2"></i>教学资源生成工具
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">使用此工具生成万有引力相关的导学案、教案、课件和作业。您可以自定义提示词以获得更符合需求的内容。</p>
|
||||
<p class="text-muted">
|
||||
使用此工具生成万有引力相关的导学案、教案、课件和作业。您可以自定义提示词以获得更符合需求的内容。</p>
|
||||
|
||||
<ul class="nav nav-tabs" id="resourceTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
@@ -176,42 +205,18 @@
|
||||
<form id="daoXueAnForm">
|
||||
<div class="mb-3">
|
||||
<label for="daoXueAnSystemPrompt" class="form-label">系统提示词</label>
|
||||
<textarea class="form-control" id="daoXueAnSystemPrompt" rows="3">你是市级骨干教师,撰写导学案时严格对标"核心素养四维度(物理观念、科学思维、探究能力、态度责任)",并采用"目标—评价—活动"逆向设计模板(UbD)。活动设计需包含"教师行为+期望的学生行为+评价要点"三列表格。</textarea>
|
||||
<textarea class="form-control" id="daoXueAnSystemPrompt" rows="3">你是市级骨干教师,撰写导学案时严格对标"核心素养四维度(物理观念、科学思维、探究能力、态度责任)",并采用"目标—评价—活动"逆向设计模板(UbD)。</textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="daoXueAnUserPrompt" class="form-label">用户提示词</label>
|
||||
<textarea class="form-control" id="daoXueAnUserPrompt" rows="6">请输出"万有引力"第1课时的导学案,要求:
|
||||
1. 教学重难点用"行为动词+知识内容"表述;
|
||||
2. 至少1个"演示实验"+1个"小组探究"+1个"即时反馈"技术(如Padlet);
|
||||
3. 时间轴精确到分钟;
|
||||
4. 课后"教学反思提示"留3条空白,供教师手写。
|
||||
|
||||
【格式要求】
|
||||
### 1. 教材与学情分析
|
||||
### 2. 四维目标(对应核心素养)
|
||||
### 3. 重难点
|
||||
### 4. 教学准备(器材+数字资源)
|
||||
### 5. 教学过程(时间轴表格)
|
||||
### 6. 板书设计(ASCII示意图)
|
||||
### 7. 作业布置(与导学案分层衔接)
|
||||
### 8. 教学反思提示(空白)
|
||||
|
||||
【变量】
|
||||
教材版本:{人教版八年级下第十章第2节}
|
||||
学生基础:{已学过重力、匀速圆周运动}
|
||||
课时长度:{45分钟}</textarea>
|
||||
<textarea class="form-control" id="daoXueAnUserPrompt" rows="6">请输出"万有引力"第1课时的导学案,要求包含教材学情分析、四维目标、重难点、教学准备、教学过程时间轴表格、板书设计和作业布置。</textarea>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" onclick="generateResource('daoXueAn')">
|
||||
<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">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">加载中...</span>
|
||||
</div>
|
||||
<p class="mt-2">正在生成导学案,请稍候...</p>
|
||||
</div>
|
||||
<div id="daoXueAnResult" class="result-container" style="display: none;"></div>
|
||||
<div id="daoXueAnLoading" class="loading-container"><div class="loader"></div></div>
|
||||
<div id="daoXueAnResult" class="result-container"></div>
|
||||
</div>
|
||||
|
||||
<!-- 教案标签页 -->
|
||||
@@ -219,42 +224,18 @@
|
||||
<form id="jiaoAnForm">
|
||||
<div class="mb-3">
|
||||
<label for="jiaoAnSystemPrompt" class="form-label">系统提示词</label>
|
||||
<textarea class="form-control" id="jiaoAnSystemPrompt" rows="3">你是市级骨干教师,撰写教案时严格对标"核心素养四维度(物理观念、科学思维、探究能力、态度责任)",并采用"目标—评价—活动"逆向设计模板(UbD)。活动设计需包含"教师行为+期望的学生行为+评价要点"三列表格。</textarea>
|
||||
<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课时的详案,要求:
|
||||
1. 教学重难点用"行为动词+知识内容"表述;
|
||||
2. 至少1个"演示实验"+1个"小组探究"+1个"即时反馈"技术(如Padlet);
|
||||
3. 时间轴精确到分钟;
|
||||
4. 课后"教学反思提示"留3条空白,供教师手写。
|
||||
|
||||
【格式要求】
|
||||
### 1. 教材与学情分析
|
||||
### 2. 四维目标(对应核心素养)
|
||||
### 3. 重难点
|
||||
### 4. 教学准备(器材+数字资源)
|
||||
### 5. 教学过程(时间轴表格)
|
||||
### 6. 板书设计(ASCII示意图)
|
||||
### 7. 作业布置(与导学案分层衔接)
|
||||
### 8. 教学反思提示(空白)
|
||||
|
||||
【变量】
|
||||
教材版本:{人教版八年级下第十章第2节}
|
||||
学生基础:{已学过重力、匀速圆周运动}
|
||||
课时长度:{45分钟}</textarea>
|
||||
<textarea class="form-control" id="jiaoAnUserPrompt" rows="6">请输出"万有引力"第1课时的教案,要求包含教材学情分析、四维目标、重难点、教学准备、教学过程时间轴表格、板书设计和作业布置。</textarea>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" onclick="generateResource('jiaoAn')">
|
||||
<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">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">加载中...</span>
|
||||
</div>
|
||||
<p class="mt-2">正在生成教案,请稍候...</p>
|
||||
</div>
|
||||
<div id="jiaoAnResult" class="result-container" style="display: none;"></div>
|
||||
<div id="jiaoAnLoading" class="loading-container"><div class="loader"></div></div>
|
||||
<div id="jiaoAnResult" class="result-container"></div>
|
||||
</div>
|
||||
|
||||
<!-- 课件标签页 -->
|
||||
@@ -262,36 +243,18 @@
|
||||
<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,主色调#005BAC(教育蓝),强调色#FFB703(暖黄)。所有动画≤0.5s,禁止花哨。需要给出演示者备注栏(<备注>)。</textarea>
|
||||
<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">为"万有引力"生成可直接导入PowerPoint的Markdown大纲,共12页,含:
|
||||
1. 封面(课程名+章节+教师姓名留白)
|
||||
2. 情境导入(1个30s短视频建议+2张图片提示)
|
||||
3. 概念建构(苹果落地+月亮绕地对比图)
|
||||
4. 规律探究(卡文迪许实验GIF占位)
|
||||
5. 公式推导(F=G·m₁m₂/r²分三步行)
|
||||
6. 例题精讲(2道,step-by-step动画)
|
||||
7. 当堂检测(Padlet二维码占位)
|
||||
8. 小结(思维导图,可一键转SmartArt)
|
||||
9. 作业二维码(链接到在线表单)
|
||||
10. 结束页("思考:如果没有万有引力?"留白)
|
||||
|
||||
【格式要求】
|
||||
每页用三级标题###表示,下方用<ul>列要点;如需图片,用! `https://via.placeholder.com/800x450?text=Image` 占位并给出版权提示;在要点后另起一行写<备注>演示者话术。</textarea>
|
||||
<textarea class="form-control" id="keJianUserPrompt" rows="6">为"万有引力"生成12页PPT大纲,包含封面、情境导入、概念建构、规律探究、公式推导、例题精讲、当堂检测、小结和作业布置。</textarea>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" onclick="generateResource('keJian')">
|
||||
<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">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">加载中...</span>
|
||||
</div>
|
||||
<p class="mt-2">正在生成课件,请稍候...</p>
|
||||
</div>
|
||||
<div id="keJianResult" class="result-container" style="display: none;"></div>
|
||||
<div id="keJianLoading" class="loading-container"><div class="loader"></div></div>
|
||||
<div id="keJianResult" class="result-container"></div>
|
||||
</div>
|
||||
|
||||
<!-- 作业标签页 -->
|
||||
@@ -299,33 +262,18 @@
|
||||
<form id="zuoYeForm">
|
||||
<div class="mb-3">
|
||||
<label for="zuoYeSystemPrompt" class="form-label">系统提示词</label>
|
||||
<textarea class="form-control" id="zuoYeSystemPrompt" rows="3">你是命题专家,熟悉布鲁姆认知分类和'双减'政策。你需要根据用户提供的题目材料来生成作业,而不是自己创造题目。客观题采用'四选一'单选,难度比例易:中:难=6:3:1;主观题设置2小问,第1问'解释现象'对应'理解',第2问'方案设计'对应'创新'。题量控制为20分钟完成。</textarea>
|
||||
<textarea class="form-control" id="zuoYeSystemPrompt" rows="3">你是命题专家,熟悉布鲁姆认知分类和'双减'政策,题量控制为20分钟完成。</textarea>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="zuoYeUserPrompt" class="form-label">用户提示词</label>
|
||||
<textarea class="form-control" id="zuoYeUserPrompt" rows="6">请根据以下'万有引力'相关题目材料,输出课后作业,满分100分,含:
|
||||
A. 客观题(8题×5分=40分)
|
||||
- 从提供的材料中选择题目,并按要求重新组织
|
||||
- 前3题考'史实&概念'识记
|
||||
- 中间3题考'公式变形&比例'理解
|
||||
- 后2题考'情境估算'应用
|
||||
B. 主观题(2题,30+30分)
|
||||
- 题1:结合'天问一号'发射新闻,解释地球与火星之间的引力如何变化
|
||||
- 题2:设计一个实验,用智能手机+免费APP估算地球质量,写出步骤与所需测量量
|
||||
C. 评分标准(主观题分点给分,每点10分)
|
||||
D. 参考答案与解析(客观题给出选项+一句话解析;主观题给出关键公式与评分关键词)</textarea>
|
||||
<textarea class="form-control" id="zuoYeUserPrompt" rows="6">请根据万有引力知识点生成课后作业,包含8道客观题(40分)和2道主观题(60分),并提供评分标准。</textarea>
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary" onclick="generateResource('zuoYe')">
|
||||
<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">
|
||||
<div class="spinner-border text-primary" role="status">
|
||||
<span class="visually-hidden">加载中...</span>
|
||||
</div>
|
||||
<p class="mt-2">正在生成作业,请稍候...</p>
|
||||
</div>
|
||||
<div id="zuoYeResult" class="result-container" style="display: none;"></div>
|
||||
<div id="zuoYeLoading" class="loading-container"><div class="loader"></div></div>
|
||||
<div id="zuoYeResult" class="result-container"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -334,152 +282,117 @@ D. 参考答案与解析(客观题给出选项+一句话解析;主观题给
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="container">
|
||||
<p class="mb-0">© 2023 教师辅助工具 - 万有引力教学资源生成</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Toast 通知容器 -->
|
||||
<div class="toast-container"></div>
|
||||
|
||||
<!-- Bootstrap JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
||||
|
||||
<!-- 自定义JavaScript -->
|
||||
<script src="https://gcore.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
// 生成资源函数
|
||||
async function generateResource(resourceType) {
|
||||
// 获取表单数据
|
||||
let systemPrompt, userPrompt, materialPath;
|
||||
|
||||
switch(resourceType) {
|
||||
case 'daoXueAn':
|
||||
systemPrompt = document.getElementById('daoXueAnSystemPrompt').value;
|
||||
userPrompt = document.getElementById('daoXueAnUserPrompt').value;
|
||||
break;
|
||||
case 'jiaoAn':
|
||||
systemPrompt = document.getElementById('jiaoAnSystemPrompt').value;
|
||||
userPrompt = document.getElementById('jiaoAnUserPrompt').value;
|
||||
break;
|
||||
case 'keJian':
|
||||
systemPrompt = document.getElementById('keJianSystemPrompt').value;
|
||||
userPrompt = document.getElementById('keJianUserPrompt').value;
|
||||
break;
|
||||
case 'zuoYe':
|
||||
systemPrompt = document.getElementById('zuoYeSystemPrompt').value;
|
||||
userPrompt = document.getElementById('zuoYeUserPrompt').value;
|
||||
// 设置默认的题目材料路径,不暴露给用户
|
||||
materialPath = '../static/YunXiao.txt';
|
||||
break;
|
||||
}
|
||||
|
||||
// 显示加载状态
|
||||
document.getElementById(resourceType + 'Loading').style.display = 'block';
|
||||
document.getElementById(resourceType + 'Result').style.display = 'none';
|
||||
|
||||
// 准备请求数据
|
||||
const requestData = {
|
||||
system_prompt: systemPrompt,
|
||||
user_prompt: userPrompt
|
||||
// 资源类型名称映射
|
||||
function getResourceName(resourceType) {
|
||||
const resourceNames = {
|
||||
'daoXueAn': '导学案',
|
||||
'jiaoAn': '教案',
|
||||
'keJian': '课件',
|
||||
'zuoYe': '作业'
|
||||
};
|
||||
|
||||
if (materialPath) {
|
||||
requestData.material_path = materialPath;
|
||||
return resourceNames[resourceType] || resourceType;
|
||||
}
|
||||
|
||||
// 生成资源主函数
|
||||
async function generateResource(resourceType) {
|
||||
// 获取元素
|
||||
const generateButton = document.getElementById(`generate${getResourceName(resourceType)}`);
|
||||
const loadingElement = document.getElementById(`${resourceType}Loading`);
|
||||
const resultContainer = document.getElementById(`${resourceType}Result`);
|
||||
const systemPrompt = document.getElementById(`${resourceType}SystemPrompt`).value;
|
||||
const userPrompt = document.getElementById(`${resourceType}UserPrompt`).value;
|
||||
|
||||
// 验证输入
|
||||
if (!userPrompt.trim()) {
|
||||
alert('请输入用户提示词');
|
||||
return;
|
||||
}
|
||||
|
||||
// 重置状态
|
||||
resultContainer.innerHTML = '';
|
||||
resultContainer.style.display = 'block';
|
||||
loadingElement.style.display = 'flex';
|
||||
|
||||
// 禁用按钮
|
||||
generateButton.disabled = true;
|
||||
generateButton.classList.add('btn-disabled');
|
||||
generateButton.innerHTML = '<i class="bi bi-hourglass-split me-2"></i>生成中...';
|
||||
|
||||
try {
|
||||
// 发送请求
|
||||
const response = await fetch(`/api/teacher/${resourceType}`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(requestData)
|
||||
headers: {'Content-Type': 'application/json'},
|
||||
body: JSON.stringify({
|
||||
system_prompt: systemPrompt,
|
||||
user_prompt: userPrompt
|
||||
})
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`请求失败: ${response.status}`);
|
||||
}
|
||||
if (!response.ok) throw new Error(`HTTP错误: ${response.status}`);
|
||||
|
||||
// 处理SSE响应
|
||||
// 处理流式响应
|
||||
const reader = response.body.getReader();
|
||||
const decoder = new TextDecoder();
|
||||
let resultText = '';
|
||||
let done = false;
|
||||
|
||||
// 显示结果容器
|
||||
document.getElementById(resourceType + 'Loading').style.display = 'none';
|
||||
document.getElementById(resourceType + 'Result').style.display = 'block';
|
||||
const resultContainer = document.getElementById(resourceType + 'Result');
|
||||
resultContainer.innerHTML = '';
|
||||
|
||||
while (true) {
|
||||
const { done, value } = await reader.read();
|
||||
|
||||
if (done) break;
|
||||
|
||||
const chunk = decoder.decode(value);
|
||||
const lines = chunk.split('\n\n');
|
||||
|
||||
for (const line of lines) {
|
||||
if (line.startsWith('data: ')) {
|
||||
const data = line.substring(6); // 移除 'data: ' 前缀
|
||||
|
||||
if (data === '[DONE]') {
|
||||
showToast('生成完成!', 'success');
|
||||
return;
|
||||
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);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('生成资源失败:', error);
|
||||
resultContainer.innerHTML = `<div class="alert alert-danger"><i class="bi bi-exclamation-triangle me-2"></i>生成失败: ${error.message}</div>`;
|
||||
resetState(loadingElement, generateButton);
|
||||
}
|
||||
}
|
||||
|
||||
if (data && !data.startsWith('开始生成')) {
|
||||
resultText += data;
|
||||
resultContainer.innerHTML = resultText;
|
||||
// 滚动到底部
|
||||
// 处理流数据
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showToast('生成完成!', 'success');
|
||||
} catch (error) {
|
||||
console.error('生成资源时出错:', error);
|
||||
document.getElementById(resourceType + 'Loading').style.display = 'none';
|
||||
showToast(`生成失败: ${error.message}`, 'danger');
|
||||
}
|
||||
}
|
||||
|
||||
// 显示Toast通知
|
||||
function showToast(message, type = 'info') {
|
||||
const toastContainer = document.querySelector('.toast-container');
|
||||
|
||||
const toastId = 'toast-' + Date.now();
|
||||
const toastHtml = `
|
||||
<div id="${toastId}" class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
||||
<div class="toast-header">
|
||||
<i class="bi bi-${type === 'success' ? 'check-circle-fill text-success' : type === 'danger' ? 'exclamation-triangle-fill text-danger' : 'info-circle-fill text-info'} me-2"></i>
|
||||
<strong class="me-auto">系统通知</strong>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="toast-body">
|
||||
${message}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
toastContainer.insertAdjacentHTML('beforeend', toastHtml);
|
||||
|
||||
const toastElement = document.getElementById(toastId);
|
||||
const toast = new bootstrap.Toast(toastElement, {
|
||||
autohide: true,
|
||||
delay: 3000
|
||||
});
|
||||
|
||||
toast.show();
|
||||
// 隐藏加载动画
|
||||
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())}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 监听隐藏事件,移除元素
|
||||
toastElement.addEventListener('hidden.bs.toast', () => {
|
||||
toastElement.remove();
|
||||
});
|
||||
// 重置状态
|
||||
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>
|
||||
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user