feat(题库生成): 重构参数配置为教材、知识点和题型

This commit is contained in:
cc 2026-03-25 19:36:53 +08:00
parent 18ca189d56
commit b993a94385
1 changed files with 248 additions and 89 deletions

View File

@ -24,25 +24,81 @@ const rawContent = ref("");
const cancelTokenSource = ref(null);
//
const questionType = ref("choice"); // 'choice' | 'fillBlank' | 'translation' | 'reading'
const difficulty = ref("medium"); // 'easy' | 'medium' | 'hard'
const questionCount = ref(5);
const topicInput = ref("");
//
const textbookChapter = ref(""); //
const customTextbookChapter = ref(""); //
const selectedKnowledgePoints = ref([]); //
const customKnowledgePoint = ref(""); //
const questionFormat = ref("单项选择"); //
const customQuestionFormat = ref(""); //
//
const TYPE_OPTIONS = [
{ value: "choice", label: "选择题", desc: "单项选择题" },
{ value: "fillBlank", label: "填空题", desc: "根据语境填空" },
{ value: "translation", label: "翻译题", desc: "英汉互译" },
{ value: "reading", label: "阅读理解", desc: "阅读文章并回答问题" },
const DIFFICULTY_OPTIONS = [
{ value: "very_easy", label: "容易", color: "#10b981" },
{ value: "easy", label: "较易", color: "#34d399" },
{ value: "medium", label: "适中", color: "#f59e0b" },
{ value: "hard", label: "较难", color: "#f97316" },
{ value: "very_hard", label: "困难", color: "#ef4444" },
];
const DIFFICULTY_OPTIONS = [
{ value: "easy", label: "简单", color: "#10b981" },
{ value: "medium", label: "中等", color: "#f59e0b" },
{ value: "hard", label: "困难", color: "#ef4444" },
//
const CHAPTER_OPTIONS = [
{ value: "Starter Unit1 You and Me", label: "Starter Unit1 You and Me" },
{ value: "Starter Unit2 Keep Tidy", label: "Starter Unit2 Keep Tidy" },
{ value: "Starter Unit3 Welcome!", label: "Starter Unit3 Welcome!" },
{ value: "Unit 1 You and Me", label: "Unit 1 You and Me" },
{ value: "Unit 2 We're Family!", label: "Unit 2 We're Family!" },
{ value: "Unit 3 My School", label: "Unit 3 My School" },
];
//
const KNOWLEDGE_POINT_OPTIONS = [
{ value: "语法", label: "语法" },
{ value: "词汇", label: "词汇" },
{ value: "冠词", label: "冠词" },
{ value: "不定冠词", label: "不定冠词" },
{ value: "定冠词", label: "定冠词" },
{ value: "名词", label: "名词" },
{ value: "动词", label: "动词" },
{ value: "形容词", label: "形容词" },
{ value: "代词", label: "代词" },
{ value: "介词", label: "介词" },
{ value: "时态", label: "时态" },
{ value: "一般现在时", label: "一般现在时" },
{ value: "现在进行时", label: "现在进行时" },
{ value: "一般过去时", label: "一般过去时" },
{ value: "句型结构", label: "句型结构" },
{ value: "阅读理解", label: "阅读理解" },
{ value: "写作", label: "写作" },
];
//
const FORMAT_OPTIONS = [
{ value: "单项选择", label: "单项选择" },
{ value: "完形填空", label: "完形填空" },
{ value: "句型转换", label: "句型转换" },
{ value: "词汇运用", label: "词汇运用" },
{ value: "翻译句子", label: "翻译句子" },
{ value: "阅读理解", label: "阅读理解" },
{ value: "书面表达", label: "书面表达" },
{ value: "短文填空", label: "短文填空" },
{ value: "语法填空", label: "语法填空" },
{ value: "选词填空", label: "选词填空" },
];
//
const toggleKnowledgePoint = (value) => {
const index = selectedKnowledgePoints.value.indexOf(value);
if (index > -1) {
selectedKnowledgePoints.value.splice(index, 1);
} else {
selectedKnowledgePoints.value.push(value);
}
};
//
const parsedQuestions = computed(() => {
const raw = rawContent.value;
@ -111,19 +167,30 @@ function extractOptions(text) {
//
const buildRequestBody = () => {
const typeLabel = TYPE_OPTIONS.find((t) => t.value === questionType.value)?.label || "选择题";
const difficultyLabel =
DIFFICULTY_OPTIONS.find((d) => d.value === difficulty.value)?.label || "中等";
const userPrompt = `请生成 ${questionCount.value}${difficultyLabel}难度的英语${typeLabel}${
topicInput.value ? `,主题为:${topicInput.value}` : ""
}
// +
const allKnowledgePoints = [
...selectedKnowledgePoints.value,
...(customKnowledgePoint.value ? [customKnowledgePoint.value] : []),
];
const knowledgePointStr = allKnowledgePoints.length > 0 ? allKnowledgePoints.join("、") : "";
// 使使
const chapterStr = customTextbookChapter.value || textbookChapter.value || "";
// 使使
const formatStr = customQuestionFormat.value || questionFormat.value || "";
const userPrompt = `请生成 ${questionCount.value}${difficultyLabel}难度的英语试题。
要求
1. 题目类型${typeLabel}
1. ${formatStr}
2. 难度等级${difficultyLabel}
3. 题目数量${questionCount.value}
${topicInput.value ? `4. 主题/知识点:${topicInput.value}` : ""}
${chapterStr ? `4. 教材章节:${chapterStr}` : ""}
${knowledgePointStr ? `5. 知识点:${knowledgePointStr}` : ""}
请严格按照指定的输出格式生成试题`;
@ -218,10 +285,14 @@ const resetAll = () => {
};
const loadExample = () => {
questionType.value = "choice";
difficulty.value = "medium";
questionCount.value = 3;
topicInput.value = "一般现在时";
textbookChapter.value = "Starter Unit2 Keep Tidy";
customTextbookChapter.value = "";
selectedKnowledgePoints.value = ["语法", "冠词", "不定冠词"];
customKnowledgePoint.value = "";
questionFormat.value = "句型转换";
customQuestionFormat.value = "";
};
const goBack = () => router.back();
@ -287,24 +358,6 @@ onUnmounted(() => {
</div>
<div class="config-body">
<!-- 题目类型 -->
<div class="config-item">
<label class="config-label">题目类型</label>
<div class="type-grid">
<button
v-for="opt in TYPE_OPTIONS"
:key="opt.value"
class="type-btn"
:class="{ active: questionType === opt.value }"
@click="questionType = opt.value"
:disabled="status === 'generating'"
>
<div class="type-label">{{ opt.label }}</div>
<div class="type-desc">{{ opt.desc }}</div>
</button>
</div>
</div>
<!-- 难度等级 -->
<div class="config-item">
<label class="config-label">难度等级</label>
@ -344,14 +397,77 @@ onUnmounted(() => {
</div>
</div>
<!-- 知识点/主题 -->
<!-- 教材章节 -->
<div class="config-item">
<label class="config-label">知识点/主题可选</label>
<label class="config-label">教材章节可选</label>
<select
v-model="textbookChapter"
class="select-input"
:disabled="status === 'generating'"
>
<option value="">请选择教材章节</option>
<option
v-for="opt in CHAPTER_OPTIONS"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</option>
</select>
<input
type="text"
v-model="topicInput"
class="topic-input"
placeholder="例如:一般现在时、定语从句、商务英语..."
v-model="customTextbookChapter"
class="topic-input mt-2"
placeholder="或输入自定义教材章节..."
:disabled="status === 'generating'"
/>
</div>
<!-- 知识点 -->
<div class="config-item">
<label class="config-label">知识点可多选</label>
<div class="knowledge-grid">
<button
v-for="opt in KNOWLEDGE_POINT_OPTIONS"
:key="opt.value"
class="knowledge-btn"
:class="{ active: selectedKnowledgePoints.includes(opt.value) }"
@click="toggleKnowledgePoint(opt.value)"
:disabled="status === 'generating'"
>
{{ opt.label }}
</button>
</div>
<input
type="text"
v-model="customKnowledgePoint"
class="topic-input mt-2"
placeholder="或输入自定义知识点..."
:disabled="status === 'generating'"
/>
</div>
<!-- 题型 -->
<div class="config-item">
<label class="config-label">题型</label>
<select
v-model="questionFormat"
class="select-input"
:disabled="status === 'generating'"
>
<option
v-for="opt in FORMAT_OPTIONS"
:key="opt.value"
:value="opt.value"
>
{{ opt.label }}
</option>
</select>
<input
type="text"
v-model="customQuestionFormat"
class="topic-input mt-2"
placeholder="或输入自定义题型..."
:disabled="status === 'generating'"
/>
</div>
@ -750,6 +866,27 @@ onUnmounted(() => {
display: flex;
flex-direction: column;
gap: 1.5rem;
overflow-y: auto;
min-height: 0;
}
.config-body::-webkit-scrollbar {
width: 6px;
}
.config-body::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
border-radius: 3px;
}
.config-body::-webkit-scrollbar-thumb {
background: rgba(16, 185, 129, 0.3);
border-radius: 3px;
transition: background 0.2s;
}
.config-body::-webkit-scrollbar-thumb:hover {
background: rgba(16, 185, 129, 0.5);
}
.config-item {
@ -773,50 +910,6 @@ onUnmounted(() => {
font-weight: 600;
}
/* Type Selection */
.type-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 0.75rem;
}
.type-btn {
display: flex;
flex-direction: column;
align-items: center;
gap: 0.25rem;
padding: 1rem;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 12px;
cursor: pointer;
transition: all 0.2s;
}
.type-btn:hover:not(:disabled) {
background: rgba(16, 185, 129, 0.06);
border-color: rgba(16, 185, 129, 0.3);
}
.type-btn.active {
background: rgba(16, 185, 129, 0.12);
border-color: #10b981;
}
.type-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.type-label {
font-size: 0.95rem;
font-weight: 600;
color: var(--text-primary);
}
.type-desc {
font-size: 0.75rem;
color: var(--text-secondary);
}
.type-btn.active .type-label {
color: #10b981;
}
/* Difficulty Selection */
.difficulty-group {
display: flex;
@ -858,6 +951,7 @@ onUnmounted(() => {
outline: none;
cursor: pointer;
-webkit-appearance: none;
appearance: none;
}
.range-slider::-webkit-slider-thumb {
-webkit-appearance: none;
@ -910,6 +1004,74 @@ onUnmounted(() => {
cursor: not-allowed;
}
/* Select Input */
.select-input {
width: 100%;
padding: 0.875rem 1rem;
background: rgba(0, 0, 0, 0.2);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 10px;
color: var(--text-primary);
font-size: 0.9rem;
outline: none;
transition: border-color 0.2s, box-shadow 0.2s;
box-sizing: border-box;
cursor: pointer;
-webkit-appearance: none;
appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2310b981' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 1rem center;
padding-right: 2.5rem;
}
.select-input:focus {
border-color: rgba(16, 185, 129, 0.5);
box-shadow: 0 0 0 3px rgba(16, 185, 129, 0.1);
}
.select-input:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.select-input option {
background: #1a1a2e;
color: var(--text-primary);
}
/* Knowledge Points Grid */
.knowledge-grid {
display: flex;
flex-wrap: wrap;
gap: 0.5rem;
}
.knowledge-btn {
padding: 0.5rem 0.875rem;
background: rgba(255, 255, 255, 0.02);
border: 1px solid rgba(255, 255, 255, 0.08);
border-radius: 8px;
color: var(--text-secondary);
font-size: 0.8rem;
cursor: pointer;
transition: all 0.2s;
}
.knowledge-btn:hover:not(:disabled) {
background: rgba(16, 185, 129, 0.1);
border-color: rgba(16, 185, 129, 0.3);
color: #10b981;
}
.knowledge-btn.active {
background: rgba(16, 185, 129, 0.15);
border-color: #10b981;
color: #10b981;
}
.knowledge-btn:disabled {
opacity: 0.4;
cursor: not-allowed;
}
.mt-2 {
margin-top: 0.75rem;
}
/* Example Button */
.example-btn {
display: flex;
@ -1275,9 +1437,6 @@ onUnmounted(() => {
.right-panel {
padding: 1.25rem 1rem;
}
.type-grid {
grid-template-columns: 1fr;
}
.options-grid {
grid-template-columns: 1fr;
}