- 题目卡片新增「📋 复制题目」按钮,按题型组织提示词复制到剪贴板 - 导入弹窗新增可折叠的 AI 生成提示词区域,支持 7 种题型切换与复制 - 更新 architecture.md 和 requirements.md 文档 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
+450
-2
@@ -163,6 +163,26 @@ body { font-family: -apple-system, 'Segoe UI', sans-serif; background: var(--bg)
|
||||
.file-input { display: block; width: 100%; padding: 12px; border: 1px dashed var(--surface2); border-radius: 8px; text-align: center; color: var(--text2); cursor: pointer; font-size: 13px; }
|
||||
.file-input:hover { border-color: var(--accent); }
|
||||
|
||||
/* Copy button in question card */
|
||||
.q-header .copy-btn { padding: 3px 10px; font-size: 12px; border-radius: 6px; border: 1px solid var(--surface2); background: transparent; color: var(--text2); cursor: pointer; transition: all 0.2s; white-space: nowrap; }
|
||||
.q-header .copy-btn:hover { border-color: var(--accent); color: var(--accent); }
|
||||
.q-header .copy-btn.copied { border-color: var(--green); color: var(--green); }
|
||||
|
||||
/* Import prompt section */
|
||||
.prompt-section { margin-bottom: 16px; border: 1px solid var(--surface2); border-radius: 8px; overflow: hidden; }
|
||||
.prompt-toggle { width: 100%; padding: 10px 14px; background: var(--bg); border: none; color: var(--text2); cursor: pointer; font-size: 13px; text-align: left; display: flex; align-items: center; gap: 6px; transition: color 0.2s; }
|
||||
.prompt-toggle:hover { color: var(--text); }
|
||||
.prompt-toggle .chevron { font-size: 10px; transition: transform 0.2s; margin-left: auto; }
|
||||
.prompt-section.open .prompt-toggle .chevron { transform: rotate(90deg); }
|
||||
.prompt-body { display: none; padding: 12px 14px; border-top: 1px solid var(--surface2); }
|
||||
.prompt-section.open .prompt-body { display: block; }
|
||||
.prompt-type-select { width: 100%; padding: 8px 10px; border-radius: 6px; border: 1px solid var(--surface2); background: var(--surface); color: var(--text); font-size: 13px; margin-bottom: 10px; outline: none; }
|
||||
.prompt-type-select:focus { border-color: var(--accent); }
|
||||
.prompt-preview { background: var(--bg); border: 1px solid var(--surface2); border-radius: 6px; padding: 10px; font-size: 12px; font-family: monospace; color: var(--text2); max-height: 200px; overflow-y: auto; white-space: pre-wrap; word-break: break-all; line-height: 1.5; margin-bottom: 10px; }
|
||||
.prompt-copy-btn { width: 100%; padding: 8px; border-radius: 6px; border: 1px solid var(--accent); background: transparent; color: var(--accent); cursor: pointer; font-size: 13px; font-weight: 600; transition: all 0.2s; }
|
||||
.prompt-copy-btn:hover { background: var(--accent); color: white; }
|
||||
.prompt-copy-btn.copied { border-color: var(--green); color: var(--green); background: transparent; }
|
||||
|
||||
/* Responsive */
|
||||
@media (max-width: 768px) {
|
||||
.app { flex-direction: column; }
|
||||
@@ -199,7 +219,28 @@ body { font-family: -apple-system, 'Segoe UI', sans-serif; background: var(--bg)
|
||||
<div class="modal-overlay" id="importModal">
|
||||
<div class="modal">
|
||||
<h3>📥 导入题目</h3>
|
||||
<p style="color:var(--text2);font-size:13px;margin-bottom:12px;">粘符合规范的 JSON 数据(topics 格式)</p>
|
||||
<p style="color:var(--text2);font-size:13px;margin-bottom:12px;">粘贴符合规范的 JSON 数据(topics 格式)</p>
|
||||
|
||||
<div class="prompt-section" id="promptSection">
|
||||
<button class="prompt-toggle" onclick="togglePromptSection()">
|
||||
📋 复制 AI 生成提示词(让 AI 帮你生成标准格式的题目 JSON)
|
||||
<span class="chevron">▶</span>
|
||||
</button>
|
||||
<div class="prompt-body">
|
||||
<select class="prompt-type-select" id="promptTypeSelect" onchange="updatePromptPreview()">
|
||||
<option value="single_choice">单选题 (single_choice)</option>
|
||||
<option value="multiple_choice">多选题 (multiple_choice)</option>
|
||||
<option value="true_false">判断题 (true_false)</option>
|
||||
<option value="fill_blank">填空题 (fill_blank)</option>
|
||||
<option value="short_answer">简答题 (short_answer)</option>
|
||||
<option value="code_reading">代码阅读题 (code_reading)</option>
|
||||
<option value="scenario">场景分析题 (scenario)</option>
|
||||
</select>
|
||||
<div class="prompt-preview" id="promptPreview"></div>
|
||||
<button class="prompt-copy-btn" id="promptCopyBtn" onclick="copyImportPrompt()">📋 复制提示词</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<textarea id="importText" placeholder='{"topic":"custom","type":"single_choice","questions":[...]}'></textarea>
|
||||
<div class="modal-or">— 或者 —</div>
|
||||
<label class="file-input">
|
||||
@@ -420,6 +461,7 @@ function renderQuestionCard(q, i) {
|
||||
let html = `<div class="q-card ${cardClass} type-${q.type}" id="card-${q.id}">
|
||||
<div class="q-header">
|
||||
<span class="q-number">#${i + 1}</span>
|
||||
<button class="copy-btn" data-copy="${q.id}" onclick="copyQuestionPrompt('${q.id}')">📋 复制题目</button>
|
||||
<div class="q-difficulty">${stars}</div>
|
||||
</div>
|
||||
<div class="q-text">`;
|
||||
@@ -583,7 +625,10 @@ function resetAll() {
|
||||
}
|
||||
|
||||
// ─── Import ───
|
||||
function openImportModal() { document.getElementById('importModal').classList.add('show'); }
|
||||
function openImportModal() {
|
||||
document.getElementById('importModal').classList.add('show');
|
||||
updatePromptPreview();
|
||||
}
|
||||
function closeImportModal() { document.getElementById('importModal').classList.remove('show'); }
|
||||
|
||||
function doImport() {
|
||||
@@ -612,6 +657,409 @@ function handleFileImport(event) {
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
// ─── Import Prompt Templates ───
|
||||
const IMPORT_PROMPTS = {
|
||||
single_choice: `你是一个计算机科学教育专家。请根据以下要求生成题目。
|
||||
|
||||
**主题**: {在此填写主题名称}
|
||||
**数量**: 10
|
||||
**难度范围**: 1-5
|
||||
**知识点标签**: {在此填写相关知识点}
|
||||
|
||||
请严格按照以下 JSON 格式输出,不要添加任何额外内容:
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"topic": "{topic_slug}",
|
||||
"type": "single_choice",
|
||||
"schema_version": "1.0.0",
|
||||
"generated": "{ISO-8601 时间戳}",
|
||||
"questions": [
|
||||
{
|
||||
"id": "sc-001",
|
||||
"type": "single_choice",
|
||||
"difficulty": 3,
|
||||
"tags": ["知识点1", "知识点2"],
|
||||
"question": "题目内容?",
|
||||
"options": {
|
||||
"A": "选项 A 的内容",
|
||||
"B": "选项 B 的内容",
|
||||
"C": "选项 C 的内容",
|
||||
"D": "选项 D 的内容"
|
||||
},
|
||||
"answer": "A",
|
||||
"explanation": "详细解析:A 正确因为……,B 错误因为……,C 错误因为……,D 错误因为……",
|
||||
"source": null,
|
||||
"related": []
|
||||
}
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
要求:
|
||||
- 每题 4 个选项,只有 1 个正确答案
|
||||
- 选项应具有合理的干扰性
|
||||
- explanation 需说明正确答案正确的原因和其他选项错误的原因`,
|
||||
|
||||
multiple_choice: `你是一个计算机科学教育专家。请根据以下要求生成题目。
|
||||
|
||||
**主题**: {在此填写主题名称}
|
||||
**数量**: 10
|
||||
**难度范围**: 1-5
|
||||
**知识点标签**: {在此填写相关知识点}
|
||||
|
||||
请严格按照以下 JSON 格式输出,不要添加任何额外内容:
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"topic": "{topic_slug}",
|
||||
"type": "multiple_choice",
|
||||
"schema_version": "1.0.0",
|
||||
"generated": "{ISO-8601 时间戳}",
|
||||
"questions": [
|
||||
{
|
||||
"id": "mc-001",
|
||||
"type": "multiple_choice",
|
||||
"difficulty": 3,
|
||||
"tags": ["知识点1", "知识点2"],
|
||||
"question": "以下哪些说法正确?(多选)",
|
||||
"options": {
|
||||
"A": "选项 A 的内容",
|
||||
"B": "选项 B 的内容",
|
||||
"C": "选项 C 的内容",
|
||||
"D": "选项 D 的内容"
|
||||
},
|
||||
"answer": ["A", "C"],
|
||||
"explanation": "A 正确因为……,B 错误因为……,C 正确因为……,D 错误因为……",
|
||||
"source": null,
|
||||
"related": []
|
||||
}
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
要求:
|
||||
- 每题 4-6 个选项,2-3 个正确答案
|
||||
- answer 为正确选项字母的数组
|
||||
- explanation 需说明每个选项对错的原因`,
|
||||
|
||||
true_false: `你是一个计算机科学教育专家。请根据以下要求生成题目。
|
||||
|
||||
**主题**: {在此填写主题名称}
|
||||
**数量**: 10
|
||||
**难度范围**: 1-5
|
||||
**知识点标签**: {在此填写相关知识点}
|
||||
|
||||
请严格按照以下 JSON 格式输出,不要添加任何额外内容:
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"topic": "{topic_slug}",
|
||||
"type": "true_false",
|
||||
"schema_version": "1.0.0",
|
||||
"generated": "{ISO-8601 时间戳}",
|
||||
"questions": [
|
||||
{
|
||||
"id": "tf-001",
|
||||
"type": "true_false",
|
||||
"difficulty": 2,
|
||||
"tags": ["知识点1", "知识点2"],
|
||||
"question": "判断以下陈述是否正确:某概念的描述。",
|
||||
"answer": true,
|
||||
"explanation": "该陈述正确/错误,因为……",
|
||||
"source": null,
|
||||
"related": []
|
||||
}
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
要求:
|
||||
- 陈述一个明确的事实或概念
|
||||
- answer 为 true 或 false(布尔值)
|
||||
- explanation 需解释为什么对或错`,
|
||||
|
||||
fill_blank: `你是一个计算机科学教育专家。请根据以下要求生成题目。
|
||||
|
||||
**主题**: {在此填写主题名称}
|
||||
**数量**: 10
|
||||
**难度范围**: 1-5
|
||||
**知识点标签**: {在此填写相关知识点}
|
||||
|
||||
请严格按照以下 JSON 格式输出,不要添加任何额外内容:
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"topic": "{topic_slug}",
|
||||
"type": "fill_blank",
|
||||
"schema_version": "1.0.0",
|
||||
"generated": "{ISO-8601 时间戳}",
|
||||
"questions": [
|
||||
{
|
||||
"id": "fb-001",
|
||||
"type": "fill_blank",
|
||||
"difficulty": 3,
|
||||
"tags": ["知识点1", "知识点2"],
|
||||
"question": "______ 协议工作在 OSI 模型的第 ______ 层。",
|
||||
"answer": ["HTTP", "7"],
|
||||
"answer_rule": "ordered",
|
||||
"explanation": "HTTP 是应用层协议,工作在 OSI 模型的第 7 层(应用层)。",
|
||||
"source": null,
|
||||
"related": []
|
||||
}
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
要求:
|
||||
- 题目中用 ______(至少 3 个下划线)标记空白处
|
||||
- answer 数组可包含多个可接受的答案
|
||||
- answer_rule: "any"(任一匹配)、"all"(全部匹配)、"ordered"(按顺序匹配)`,
|
||||
|
||||
short_answer: `你是一个计算机科学教育专家。请根据以下要求生成题目。
|
||||
|
||||
**主题**: {在此填写主题名称}
|
||||
**数量**: 10
|
||||
**难度范围**: 1-5
|
||||
**知识点标签**: {在此填写相关知识点}
|
||||
|
||||
请严格按照以下 JSON 格式输出,不要添加任何额外内容:
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"topic": "{topic_slug}",
|
||||
"type": "short_answer",
|
||||
"schema_version": "1.0.0",
|
||||
"generated": "{ISO-8601 时间戳}",
|
||||
"questions": [
|
||||
{
|
||||
"id": "sa-001",
|
||||
"type": "short_answer",
|
||||
"difficulty": 4,
|
||||
"tags": ["知识点1", "知识点2"],
|
||||
"question": "请简述某概念的原理和应用场景。",
|
||||
"answer": "参考答案内容……",
|
||||
"keywords": ["关键词1", "关键词2", "关键词3"],
|
||||
"scoring_rubric": "答对核心原理得 60 分;举出应用场景得 20 分;说明优缺点得 20 分。",
|
||||
"explanation": "完整解析……",
|
||||
"source": null,
|
||||
"related": []
|
||||
}
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
要求:
|
||||
- 提出开放性问题
|
||||
- answer 为参考答案
|
||||
- keywords 用于自动评分匹配
|
||||
- scoring_rubric 描述评分标准`,
|
||||
|
||||
code_reading: `你是一个计算机科学教育专家。请根据以下要求生成题目。
|
||||
|
||||
**主题**: {在此填写主题名称}
|
||||
**数量**: 5
|
||||
**难度范围**: 3-5
|
||||
**知识点标签**: {在此填写相关知识点}
|
||||
|
||||
请严格按照以下 JSON 格式输出,不要添加任何额外内容:
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"topic": "{topic_slug}",
|
||||
"type": "code_reading",
|
||||
"schema_version": "1.0.0",
|
||||
"generated": "{ISO-8601 时间戳}",
|
||||
"questions": [
|
||||
{
|
||||
"id": "cr-001",
|
||||
"type": "code_reading",
|
||||
"difficulty": 4,
|
||||
"tags": ["知识点1", "知识点2"],
|
||||
"question": "阅读以下代码,回答问题。",
|
||||
"code": "// 完整的代码片段",
|
||||
"language": "python",
|
||||
"sub_questions": [
|
||||
{
|
||||
"index": 1,
|
||||
"type": "single_choice",
|
||||
"question": "子问题 1 的内容?",
|
||||
"options": { "A": "选项 A", "B": "选项 B", "C": "选项 C", "D": "选项 D" },
|
||||
"answer": "A",
|
||||
"explanation": "解析……"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"type": "short_answer",
|
||||
"question": "子问题 2 的内容?",
|
||||
"answer": "参考答案",
|
||||
"keywords": ["关键词"],
|
||||
"explanation": "解析……"
|
||||
}
|
||||
],
|
||||
"explanation": "整体代码解析……",
|
||||
"source": null,
|
||||
"related": []
|
||||
}
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
要求:
|
||||
- 提供完整的、有教学价值的代码片段
|
||||
- 设置 2-3 个子问题(可以是 single_choice / true_false / short_answer 等)
|
||||
- language 指定代码语言`,
|
||||
|
||||
scenario: `你是一个计算机科学教育专家。请根据以下要求生成题目。
|
||||
|
||||
**主题**: {在此填写主题名称}
|
||||
**数量**: 5
|
||||
**难度范围**: 3-5
|
||||
**知识点标签**: {在此填写相关知识点}
|
||||
|
||||
请严格按照以下 JSON 格式输出,不要添加任何额外内容:
|
||||
|
||||
\`\`\`json
|
||||
{
|
||||
"topic": "{topic_slug}",
|
||||
"type": "scenario",
|
||||
"schema_version": "1.0.0",
|
||||
"generated": "{ISO-8601 时间戳}",
|
||||
"questions": [
|
||||
{
|
||||
"id": "sn-001",
|
||||
"type": "scenario",
|
||||
"difficulty": 5,
|
||||
"tags": ["知识点1", "知识点2"],
|
||||
"question": "请分析以下技术场景。",
|
||||
"context": "详细场景描述……",
|
||||
"code": null,
|
||||
"sub_questions": [
|
||||
{
|
||||
"index": 1,
|
||||
"type": "short_answer",
|
||||
"question": "分析性问题 1",
|
||||
"answer": "参考答案",
|
||||
"keywords": ["关键词"],
|
||||
"scoring_rubric": "评分标准",
|
||||
"explanation": "解析……"
|
||||
},
|
||||
{
|
||||
"index": 2,
|
||||
"type": "multiple_choice",
|
||||
"question": "选择性问题 2",
|
||||
"options": { "A": "选项 A", "B": "选项 B", "C": "选项 C", "D": "选项 D" },
|
||||
"answer": ["A", "C"],
|
||||
"explanation": "解析……"
|
||||
}
|
||||
],
|
||||
"explanation": "整体场景分析……",
|
||||
"source": null,
|
||||
"related": []
|
||||
}
|
||||
]
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
要求:
|
||||
- 描述一个实际的技术场景
|
||||
- context 为场景描述,code 可为 null 或附带代码
|
||||
- 设置 2-3 个分析性子问题`
|
||||
};
|
||||
|
||||
function togglePromptSection() {
|
||||
document.getElementById('promptSection').classList.toggle('open');
|
||||
}
|
||||
|
||||
function updatePromptPreview() {
|
||||
const type = document.getElementById('promptTypeSelect').value;
|
||||
document.getElementById('promptPreview').textContent = IMPORT_PROMPTS[type] || '';
|
||||
}
|
||||
|
||||
function copyImportPrompt() {
|
||||
const type = document.getElementById('promptTypeSelect').value;
|
||||
const text = IMPORT_PROMPTS[type];
|
||||
if (!text) return;
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
const btn = document.getElementById('promptCopyBtn');
|
||||
btn.textContent = '✅ 已复制';
|
||||
btn.classList.add('copied');
|
||||
setTimeout(() => { btn.textContent = '📋 复制提示词'; btn.classList.remove('copied'); }, 1500);
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Copy Question as Prompt ───
|
||||
function copyQuestionPrompt(qId) {
|
||||
const q = [...currentQuestions, ...customQuestions].find(x => x.id === qId);
|
||||
if (!q) return;
|
||||
|
||||
const typeNames = {
|
||||
fill_blank: '填空题', single_choice: '单选题', multiple_choice: '多选题',
|
||||
true_false: '判断题', short_answer: '简答题', code_reading: '代码阅读题', scenario: '场景分析题'
|
||||
};
|
||||
const typeName = typeNames[q.type] || q.type;
|
||||
|
||||
let prompt = `请帮我解析以下${typeName}题目,给出答案和详细解析。\n\n`;
|
||||
prompt += `【题型】${typeName}\n`;
|
||||
prompt += `【难度】${'★'.repeat(q.difficulty)}${'☆'.repeat(5 - q.difficulty)}\n`;
|
||||
if (q.tags?.length) prompt += `【知识点】${q.tags.join('、')}\n`;
|
||||
prompt += `\n【题目】${q.question}\n`;
|
||||
|
||||
if (q.type === 'fill_blank') {
|
||||
prompt += `\n请填写空白处的内容。`;
|
||||
} else if (q.type === 'single_choice' || q.type === 'multiple_choice') {
|
||||
if (q.options) {
|
||||
prompt += `\n【选项】\n`;
|
||||
for (const [letter, text] of Object.entries(q.options)) {
|
||||
prompt += `${letter}. ${text}\n`;
|
||||
}
|
||||
}
|
||||
if (q.type === 'multiple_choice') prompt += `\n(多选)`;
|
||||
} else if (q.type === 'true_false') {
|
||||
prompt += `\n请判断该陈述是否正确。`;
|
||||
} else if (q.type === 'short_answer') {
|
||||
prompt += `\n请给出完整回答。`;
|
||||
} else if (q.type === 'code_reading') {
|
||||
if (q.code) {
|
||||
prompt += `\n【代码】\n\`\`\`${q.language || ''}\n${q.code}\n\`\`\``;
|
||||
}
|
||||
if (q.sub_questions?.length) {
|
||||
prompt += `\n\n【子问题】\n`;
|
||||
q.sub_questions.forEach(sq => {
|
||||
prompt += `${sq.index}. ${sq.question}\n`;
|
||||
if (sq.options) {
|
||||
for (const [letter, text] of Object.entries(sq.options)) {
|
||||
prompt += ` ${letter}. ${text}\n`;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (q.type === 'scenario') {
|
||||
if (q.context) prompt += `\n【场景】\n${q.context}`;
|
||||
if (q.code) prompt += `\n\n【代码】\n\`\`\`\n${q.code}\n\`\`\``;
|
||||
if (q.sub_questions?.length) {
|
||||
prompt += `\n\n【子问题】\n`;
|
||||
q.sub_questions.forEach(sq => {
|
||||
prompt += `${sq.index}. ${sq.question}\n`;
|
||||
if (sq.options) {
|
||||
for (const [letter, text] of Object.entries(sq.options)) {
|
||||
prompt += ` ${letter}. ${text}\n`;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
navigator.clipboard.writeText(prompt).then(() => {
|
||||
const btn = document.querySelector(`[data-copy="${qId}"]`);
|
||||
if (btn) {
|
||||
btn.textContent = '✅ 已复制';
|
||||
btn.classList.add('copied');
|
||||
setTimeout(() => { btn.textContent = '📋 复制题目'; btn.classList.remove('copied'); }, 1500);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// ─── Util ───
|
||||
function escapeHtml(s) {
|
||||
if (!s) return '';
|
||||
|
||||
Reference in New Issue
Block a user