feat(试题生成器): 支持多种答题形式和试题类型
This commit is contained in:
parent
f04821c6bf
commit
1d11050178
|
|
@ -56,7 +56,9 @@ export const QUESTION_MAX_TOKENS = 4096;
|
||||||
// 试题生成 System Prompt
|
// 试题生成 System Prompt
|
||||||
export const QUESTION_GENERATOR_PROMPT = `你是一位专业的英语试题出题专家。请根据用户指定的参数生成高质量的英语试题。
|
export const QUESTION_GENERATOR_PROMPT = `你是一位专业的英语试题出题专家。请根据用户指定的参数生成高质量的英语试题。
|
||||||
|
|
||||||
输出格式要求:
|
【重要】必须严格按照用户指定的"答题形式"生成对应格式的试题:
|
||||||
|
|
||||||
|
## 答题形式为"单选题"时的格式:
|
||||||
## 题目 [序号]
|
## 题目 [序号]
|
||||||
**题目类型**:[类型]
|
**题目类型**:[类型]
|
||||||
**难度等级**:[难度]
|
**难度等级**:[难度]
|
||||||
|
|
@ -71,15 +73,38 @@ D. [选项D内容]
|
||||||
**解析**:[详细解析]
|
**解析**:[详细解析]
|
||||||
**知识点**:[相关知识点]
|
**知识点**:[相关知识点]
|
||||||
|
|
||||||
|
## 答题形式为"填空题"时的格式:
|
||||||
|
## 题目 [序号]
|
||||||
|
**题目类型**:[类型]
|
||||||
|
**难度等级**:[难度]
|
||||||
|
**题目内容**:
|
||||||
|
[题目文本,用下划线_____标出需要填空的位置]
|
||||||
|
**正确答案**:[填空答案]
|
||||||
|
**解析**:[详细解析]
|
||||||
|
**知识点**:[相关知识点]
|
||||||
|
|
||||||
|
## 答题形式为"写作题"时的格式:
|
||||||
|
## 题目 [序号]
|
||||||
|
**题目类型**:[类型]
|
||||||
|
**难度等级**:[难度]
|
||||||
|
**题目内容**:
|
||||||
|
[写作题目要求]
|
||||||
|
**参考范文**:
|
||||||
|
[一篇高质量的范文]
|
||||||
|
**解析**:[写作要点解析,包括文章结构、关键表达等]
|
||||||
|
**知识点**:[相关知识点]
|
||||||
|
|
||||||
重要规则:
|
重要规则:
|
||||||
1. 所有题型都必须提供4个选项(A、B、C、D),即使是填空题、翻译题或阅读理解题
|
1. 【最高优先级】必须严格按照用户指定的答题形式(单选题/填空题/写作题)生成对应格式的试题
|
||||||
2. 对于填空题:选项为不同的填空答案选项
|
2. 【严禁超纲】如果用户指定了"教材章节",所有试题内容必须严格限定在该章节的知识范围内,绝对禁止出现超出该章节的词汇、语法、句型等内容
|
||||||
3. 对于翻译题:选项为不同的翻译版本
|
3. 【知识点约束】如果用户指定了"知识点",试题必须围绕该知识点出题,不得偏离
|
||||||
4. 对于阅读理解题:选项为对问题的不同回答选项
|
4. 单选题:必须提供4个选项(A、B、C、D),答案为选项字母
|
||||||
5. 题目质量要高,符合英语教学标准
|
5. 填空题:不提供选项,用_____标出填空位置,答案为填空内容
|
||||||
6. 答案准确无误
|
6. 写作题:提供写作要求和参考范文,不提供选项
|
||||||
7. 解析清晰易懂,有助于学生理解
|
7. 题目质量要高,符合英语教学标准
|
||||||
8. 知识点标注准确,便于分类学习`;
|
8. 答案准确无误
|
||||||
|
9. 解析清晰易懂,有助于学生理解
|
||||||
|
10. 知识点标注准确,便于分类学习`;
|
||||||
|
|
||||||
// ── 文件上传限制 ──
|
// ── 文件上传限制 ──
|
||||||
export const IMAGE_MAX_SIZE_MB = 10;
|
export const IMAGE_MAX_SIZE_MB = 10;
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,8 @@ const selectedKnowledgePoints = ref([]); // 选中的知识点
|
||||||
const customKnowledgePoint = ref(""); // 自定义知识点输入
|
const customKnowledgePoint = ref(""); // 自定义知识点输入
|
||||||
const questionFormat = ref("单项选择"); // 题型(预设)
|
const questionFormat = ref("单项选择"); // 题型(预设)
|
||||||
const customQuestionFormat = ref(""); // 自定义题型输入
|
const customQuestionFormat = ref(""); // 自定义题型输入
|
||||||
|
const answerFormat = ref("单选题"); // 答题形式:单选题、填空题、写作题
|
||||||
|
const examType = ref(""); // 试题类型
|
||||||
|
|
||||||
// ── 配置选项 ──
|
// ── 配置选项 ──
|
||||||
const DIFFICULTY_OPTIONS = [
|
const DIFFICULTY_OPTIONS = [
|
||||||
|
|
@ -77,7 +79,6 @@ const KNOWLEDGE_POINT_OPTIONS = [
|
||||||
|
|
||||||
// 题型预设选项
|
// 题型预设选项
|
||||||
const FORMAT_OPTIONS = [
|
const FORMAT_OPTIONS = [
|
||||||
{ value: "单项选择", label: "单项选择" },
|
|
||||||
{ value: "完形填空", label: "完形填空" },
|
{ value: "完形填空", label: "完形填空" },
|
||||||
{ value: "句型转换", label: "句型转换" },
|
{ value: "句型转换", label: "句型转换" },
|
||||||
{ value: "词汇运用", label: "词汇运用" },
|
{ value: "词汇运用", label: "词汇运用" },
|
||||||
|
|
@ -89,6 +90,20 @@ const FORMAT_OPTIONS = [
|
||||||
{ value: "选词填空", label: "选词填空" },
|
{ value: "选词填空", label: "选词填空" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 答题形式选项
|
||||||
|
const ANSWER_FORMAT_OPTIONS = [
|
||||||
|
{ value: "单选题", label: "单选题" },
|
||||||
|
{ value: "填空题", label: "填空题" },
|
||||||
|
{ value: "写作题", label: "写作题" },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 试题类型选项
|
||||||
|
const EXAM_TYPE_OPTIONS = [
|
||||||
|
{ value: "中考", label: "中考" },
|
||||||
|
{ value: "单元同义句", label: "单元同义句" },
|
||||||
|
{ value: "高频考点", label: "高频考点" },
|
||||||
|
];
|
||||||
|
|
||||||
// 切换知识点选中状态
|
// 切换知识点选中状态
|
||||||
const toggleKnowledgePoint = (value) => {
|
const toggleKnowledgePoint = (value) => {
|
||||||
const index = selectedKnowledgePoints.value.indexOf(value);
|
const index = selectedKnowledgePoints.value.indexOf(value);
|
||||||
|
|
@ -130,7 +145,10 @@ const parsedQuestions = computed(() => {
|
||||||
});
|
});
|
||||||
|
|
||||||
function extractField(text, fieldName) {
|
function extractField(text, fieldName) {
|
||||||
const regex = new RegExp(`\\*\\*${fieldName}\\*\\*[::]\\s*([\\s\\S]*?)(?=\\*\\*|$)`, "i");
|
const regex = new RegExp(
|
||||||
|
`\\*\\*${fieldName}\\*\\*[::]\\s*([\\s\\S]*?)(?=\\*\\*|$)`,
|
||||||
|
"i"
|
||||||
|
);
|
||||||
const match = text.match(regex);
|
const match = text.match(regex);
|
||||||
if (!match) return "";
|
if (!match) return "";
|
||||||
|
|
||||||
|
|
@ -142,15 +160,17 @@ function extractField(text, fieldName) {
|
||||||
|
|
||||||
function extractOptions(text) {
|
function extractOptions(text) {
|
||||||
const options = [];
|
const options = [];
|
||||||
|
|
||||||
// 先尝试提取"选项"字段的内容块
|
// 先尝试提取"选项"字段的内容块
|
||||||
const optionsBlockMatch = text.match(/\*\*选项\*\*[::]\s*([\s\S]*?)(?=\*\*正确答案\*\*|$)/i);
|
const optionsBlockMatch = text.match(
|
||||||
|
/\*\*选项\*\*[::]\s*([\s\S]*?)(?=\*\*正确答案\*\*|$)/i
|
||||||
|
);
|
||||||
|
|
||||||
if (optionsBlockMatch) {
|
if (optionsBlockMatch) {
|
||||||
const optionsBlock = optionsBlockMatch[1];
|
const optionsBlock = optionsBlockMatch[1];
|
||||||
|
|
||||||
// 在选项块中提取各个选项
|
// 在选项块中提取各个选项
|
||||||
const lines = optionsBlock.split('\n');
|
const lines = optionsBlock.split("\n");
|
||||||
for (const line of lines) {
|
for (const line of lines) {
|
||||||
const match = line.match(/^([A-D])[\..。::]\s*(.+)$/i);
|
const match = line.match(/^([A-D])[\..。::]\s*(.+)$/i);
|
||||||
if (match) {
|
if (match) {
|
||||||
|
|
@ -161,21 +181,23 @@ function extractOptions(text) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── 构建请求体 ──
|
// ── 构建请求体 ──
|
||||||
const buildRequestBody = () => {
|
const buildRequestBody = () => {
|
||||||
const difficultyLabel =
|
const difficultyLabel =
|
||||||
DIFFICULTY_OPTIONS.find((d) => d.value === difficulty.value)?.label || "中等";
|
DIFFICULTY_OPTIONS.find((d) => d.value === difficulty.value)?.label ||
|
||||||
|
"中等";
|
||||||
|
|
||||||
// 构建知识点字符串:预设选项 + 自定义输入
|
// 构建知识点字符串:预设选项 + 自定义输入
|
||||||
const allKnowledgePoints = [
|
const allKnowledgePoints = [
|
||||||
...selectedKnowledgePoints.value,
|
...selectedKnowledgePoints.value,
|
||||||
...(customKnowledgePoint.value ? [customKnowledgePoint.value] : []),
|
...(customKnowledgePoint.value ? [customKnowledgePoint.value] : []),
|
||||||
];
|
];
|
||||||
const knowledgePointStr = allKnowledgePoints.length > 0 ? allKnowledgePoints.join("、") : "";
|
const knowledgePointStr =
|
||||||
|
allKnowledgePoints.length > 0 ? allKnowledgePoints.join("、") : "";
|
||||||
|
|
||||||
// 教材章节:优先使用自定义输入,否则使用预设选择
|
// 教材章节:优先使用自定义输入,否则使用预设选择
|
||||||
const chapterStr = customTextbookChapter.value || textbookChapter.value || "";
|
const chapterStr = customTextbookChapter.value || textbookChapter.value || "";
|
||||||
|
|
@ -183,16 +205,30 @@ const buildRequestBody = () => {
|
||||||
// 题型:优先使用自定义输入,否则使用预设选择
|
// 题型:优先使用自定义输入,否则使用预设选择
|
||||||
const formatStr = customQuestionFormat.value || questionFormat.value || "";
|
const formatStr = customQuestionFormat.value || questionFormat.value || "";
|
||||||
|
|
||||||
const userPrompt = `请生成 ${questionCount.value} 道${difficultyLabel}难度的英语试题。
|
// 构建教材章节约束提示
|
||||||
|
const chapterConstraint = chapterStr
|
||||||
|
? `\n\n【重要约束】教材章节为"${chapterStr}",所有试题内容必须严格限定在该章节的知识范围内:
|
||||||
|
- 仅使用该章节已学过的词汇、短语和表达方式
|
||||||
|
- 仅考查该章节涉及的语法点和句型结构
|
||||||
|
- 绝对禁止出现超出该章节范围的知识点
|
||||||
|
- 如果试题类型为"中考",则按中考标准出题,但仍需体现该章节的核心内容`
|
||||||
|
: "";
|
||||||
|
|
||||||
|
const userPrompt = `请生成 ${
|
||||||
|
questionCount.value
|
||||||
|
} 道${difficultyLabel}难度的英语试题。
|
||||||
|
|
||||||
要求:
|
要求:
|
||||||
1. 题型:${formatStr}
|
1. 题型:${formatStr}
|
||||||
2. 难度等级:${difficultyLabel}
|
2. 答题形式:${answerFormat.value}
|
||||||
3. 题目数量:${questionCount.value} 道
|
3. 难度等级:${difficultyLabel}
|
||||||
${chapterStr ? `4. 教材章节:${chapterStr}` : ""}
|
4. 题目数量:${questionCount.value} 道
|
||||||
${knowledgePointStr ? `5. 知识点:${knowledgePointStr}` : ""}
|
${examType.value ? `5. 试题类型:${examType.value}` : ""}
|
||||||
|
${chapterStr ? `6. 教材章节:${chapterStr}` : ""}
|
||||||
|
${knowledgePointStr ? `7. 知识点:${knowledgePointStr}` : ""}
|
||||||
|
${chapterConstraint}
|
||||||
|
|
||||||
请严格按照指定的输出格式生成试题。`;
|
请严格按照指定的输出格式和答题形式生成试题。严禁出现超出指定范围的内容。`;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
model: QUESTION_MODEL,
|
model: QUESTION_MODEL,
|
||||||
|
|
@ -234,7 +270,11 @@ const filterThinkBlocks = (text) => {
|
||||||
|
|
||||||
// ── 开始生成 ──
|
// ── 开始生成 ──
|
||||||
const canGenerate = computed(() => {
|
const canGenerate = computed(() => {
|
||||||
return status.value !== "generating" && questionCount.value >= 1 && questionCount.value <= 20;
|
return (
|
||||||
|
status.value !== "generating" &&
|
||||||
|
questionCount.value >= 1 &&
|
||||||
|
questionCount.value <= 20
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const startGeneration = async () => {
|
const startGeneration = async () => {
|
||||||
|
|
@ -273,7 +313,9 @@ const startGeneration = async () => {
|
||||||
if (axios.isCancel(err)) return;
|
if (axios.isCancel(err)) return;
|
||||||
console.error(err);
|
console.error(err);
|
||||||
errorMsg.value =
|
errorMsg.value =
|
||||||
err?.response?.data?.error?.message || err.message || "请求失败,请稍后重试";
|
err?.response?.data?.error?.message ||
|
||||||
|
err.message ||
|
||||||
|
"请求失败,请稍后重试";
|
||||||
status.value = "error";
|
status.value = "error";
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
@ -287,6 +329,8 @@ const resetAll = () => {
|
||||||
const loadExample = () => {
|
const loadExample = () => {
|
||||||
difficulty.value = "medium";
|
difficulty.value = "medium";
|
||||||
questionCount.value = 3;
|
questionCount.value = 3;
|
||||||
|
answerFormat.value = "单选题";
|
||||||
|
examType.value = "";
|
||||||
textbookChapter.value = "Starter Unit2 Keep Tidy";
|
textbookChapter.value = "Starter Unit2 Keep Tidy";
|
||||||
customTextbookChapter.value = "";
|
customTextbookChapter.value = "";
|
||||||
selectedKnowledgePoints.value = ["语法", "冠词", "不定冠词"];
|
selectedKnowledgePoints.value = ["语法", "冠词", "不定冠词"];
|
||||||
|
|
@ -397,6 +441,40 @@ onUnmounted(() => {
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 试题类型 -->
|
||||||
|
<div class="config-item">
|
||||||
|
<label class="config-label">试题类型(可选)</label>
|
||||||
|
<div class="exam-type-group">
|
||||||
|
<button
|
||||||
|
v-for="opt in EXAM_TYPE_OPTIONS"
|
||||||
|
:key="opt.value"
|
||||||
|
class="exam-type-btn"
|
||||||
|
:class="{ active: examType === opt.value }"
|
||||||
|
@click="examType = examType === opt.value ? '' : opt.value"
|
||||||
|
:disabled="status === 'generating'"
|
||||||
|
>
|
||||||
|
{{ opt.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 答题形式 -->
|
||||||
|
<div class="config-item">
|
||||||
|
<label class="config-label">答题形式</label>
|
||||||
|
<div class="answer-format-group">
|
||||||
|
<button
|
||||||
|
v-for="opt in ANSWER_FORMAT_OPTIONS"
|
||||||
|
:key="opt.value"
|
||||||
|
class="answer-format-btn"
|
||||||
|
:class="{ active: answerFormat === opt.value }"
|
||||||
|
@click="answerFormat = opt.value"
|
||||||
|
:disabled="status === 'generating'"
|
||||||
|
>
|
||||||
|
{{ opt.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 教材章节 -->
|
<!-- 教材章节 -->
|
||||||
<div class="config-item">
|
<div class="config-item">
|
||||||
<label class="config-label">教材章节(可选)</label>
|
<label class="config-label">教材章节(可选)</label>
|
||||||
|
|
@ -431,7 +509,9 @@ onUnmounted(() => {
|
||||||
v-for="opt in KNOWLEDGE_POINT_OPTIONS"
|
v-for="opt in KNOWLEDGE_POINT_OPTIONS"
|
||||||
:key="opt.value"
|
:key="opt.value"
|
||||||
class="knowledge-btn"
|
class="knowledge-btn"
|
||||||
:class="{ active: selectedKnowledgePoints.includes(opt.value) }"
|
:class="{
|
||||||
|
active: selectedKnowledgePoints.includes(opt.value),
|
||||||
|
}"
|
||||||
@click="toggleKnowledgePoint(opt.value)"
|
@click="toggleKnowledgePoint(opt.value)"
|
||||||
:disabled="status === 'generating'"
|
:disabled="status === 'generating'"
|
||||||
>
|
>
|
||||||
|
|
@ -473,7 +553,11 @@ onUnmounted(() => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 示例按钮 -->
|
<!-- 示例按钮 -->
|
||||||
<button class="example-btn" @click="loadExample" :disabled="status === 'generating'">
|
<button
|
||||||
|
class="example-btn"
|
||||||
|
@click="loadExample"
|
||||||
|
:disabled="status === 'generating'"
|
||||||
|
>
|
||||||
<svg
|
<svg
|
||||||
width="16"
|
width="16"
|
||||||
height="16"
|
height="16"
|
||||||
|
|
@ -484,7 +568,9 @@ onUnmounted(() => {
|
||||||
stroke-linecap="round"
|
stroke-linecap="round"
|
||||||
stroke-linejoin="round"
|
stroke-linejoin="round"
|
||||||
>
|
>
|
||||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z" />
|
<path
|
||||||
|
d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"
|
||||||
|
/>
|
||||||
<polyline points="14 2 14 8 20 8" />
|
<polyline points="14 2 14 8 20 8" />
|
||||||
<line x1="16" y1="13" x2="8" y2="13" />
|
<line x1="16" y1="13" x2="8" y2="13" />
|
||||||
<line x1="16" y1="17" x2="8" y2="17" />
|
<line x1="16" y1="17" x2="8" y2="17" />
|
||||||
|
|
@ -515,7 +601,9 @@ onUnmounted(() => {
|
||||||
>
|
>
|
||||||
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
|
<polygon points="13 2 3 14 12 14 11 22 21 10 12 10 13 2" />
|
||||||
</svg>
|
</svg>
|
||||||
<span>{{ status === "generating" ? "生成中..." : "开始生成" }}</span>
|
<span>{{
|
||||||
|
status === "generating" ? "生成中..." : "开始生成"
|
||||||
|
}}</span>
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
v-if="status === 'done' || status === 'error'"
|
v-if="status === 'done' || status === 'error'"
|
||||||
|
|
@ -564,7 +652,10 @@ onUnmounted(() => {
|
||||||
status === "generating" ? "AI 正在生成..." : "生成完成"
|
status === "generating" ? "AI 正在生成..." : "生成完成"
|
||||||
}}</span>
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
<span v-if="parsedQuestions.length > 0" class="question-count-badge">
|
<span
|
||||||
|
v-if="parsedQuestions.length > 0"
|
||||||
|
class="question-count-badge"
|
||||||
|
>
|
||||||
已生成 {{ parsedQuestions.length }} 题
|
已生成 {{ parsedQuestions.length }} 题
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -580,7 +671,9 @@ onUnmounted(() => {
|
||||||
<div class="question-number">题目 {{ q.id }}</div>
|
<div class="question-number">题目 {{ q.id }}</div>
|
||||||
<div class="question-meta">
|
<div class="question-meta">
|
||||||
<span class="meta-tag type-tag">{{ q.type }}</span>
|
<span class="meta-tag type-tag">{{ q.type }}</span>
|
||||||
<span class="meta-tag difficulty-tag">{{ q.difficulty }}</span>
|
<span class="meta-tag difficulty-tag">{{
|
||||||
|
q.difficulty
|
||||||
|
}}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
@ -588,7 +681,10 @@ onUnmounted(() => {
|
||||||
<p class="content-text">{{ q.content }}</p>
|
<p class="content-text">{{ q.content }}</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div v-if="q.options && q.options.length > 0" class="options-grid">
|
<div
|
||||||
|
v-if="q.options && q.options.length > 0"
|
||||||
|
class="options-grid"
|
||||||
|
>
|
||||||
<div
|
<div
|
||||||
v-for="opt in q.options"
|
v-for="opt in q.options"
|
||||||
:key="opt.label"
|
:key="opt.label"
|
||||||
|
|
@ -942,6 +1038,70 @@ onUnmounted(() => {
|
||||||
cursor: not-allowed;
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Answer Format Selection */
|
||||||
|
.answer-format-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.answer-format-btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.answer-format-btn:hover:not(:disabled) {
|
||||||
|
background: rgba(16, 185, 129, 0.1);
|
||||||
|
border-color: rgba(16, 185, 129, 0.3);
|
||||||
|
}
|
||||||
|
.answer-format-btn.active {
|
||||||
|
background: rgba(16, 185, 129, 0.15);
|
||||||
|
border-color: #10b981;
|
||||||
|
color: #10b981;
|
||||||
|
}
|
||||||
|
.answer-format-btn:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Exam Type Selection */
|
||||||
|
.exam-type-group {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.exam-type-btn {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background: rgba(255, 255, 255, 0.02);
|
||||||
|
border: 1px solid rgba(255, 255, 255, 0.08);
|
||||||
|
border-radius: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.exam-type-btn:hover:not(:disabled) {
|
||||||
|
background: rgba(16, 185, 129, 0.1);
|
||||||
|
border-color: rgba(16, 185, 129, 0.3);
|
||||||
|
}
|
||||||
|
.exam-type-btn.active {
|
||||||
|
background: rgba(16, 185, 129, 0.15);
|
||||||
|
border-color: #10b981;
|
||||||
|
color: #10b981;
|
||||||
|
}
|
||||||
|
.exam-type-btn:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
/* Range Slider */
|
/* Range Slider */
|
||||||
.range-slider {
|
.range-slider {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue