diff --git a/src/views/teacherEnd/exam/editor.vue b/src/views/teacherEnd/exam/editor.vue index e5daecd..fc05062 100644 --- a/src/views/teacherEnd/exam/editor.vue +++ b/src/views/teacherEnd/exam/editor.vue @@ -51,9 +51,9 @@
- >>> + - >>> +
@@ -233,7 +233,7 @@ const totalScore = computed(() => form.questions.reduce((sum, item) => sum + Num const selectedTypes = computed(() => typeOptions.filter((item) => selectedByType(item.value))); onMounted(load); -async function load() { loading.value = true; try { const [classRes, questionRes] = await Promise.all([examApi.getExamClasses(), examApi.getQuestions({})]); classes.value = classRes.data || []; bank.value = questionRes.data || []; if (!isNew.value) { const { data } = await examApi.getTeacherExam(id.value); status.value = data.status; const bankIds = new Set(bank.value.map((item) => item.id)); bank.value = [...bank.value, ...data.questions.filter((item) => !bankIds.has(item.id))]; Object.assign(form, { name: data.name, startTime: formatValue(data.startTime), endTime: formatValue(data.endTime), classIds: data.classes.map((item) => item.id), questionOrderMode: data.questionOrderMode || "FIXED", optionOrderMode: data.optionOrderMode || "FIXED", allowCopyQuestion: toBoolean(data.allowCopyQuestion, false), allowPasteAnswer: toBoolean(data.allowPasteAnswer, false), allowRollback: toBoolean(data.allowRollback, true), multipleScoreMode: data.multipleScoreMode || "ALL_OR_NOTHING", questions: data.questions.map((item) => ({ questionId: item.id, score: Number(item.score) })) }); for (const type of typeOptions.map((item) => item.value)) { const first = data.questions.find((item) => item.type === type); if (first) typeScores[type] = Number(first.score) || 1; } duration.value = data.durationMinutes; } else { const start = new Date(Date.now() + 3600000); form.startTime = formatValue(start); form.endTime = formatValue(new Date(start.getTime() + 3600000)); } } finally { loading.value = false; } } +async function load() { loading.value = true; try { const [classRes, questionRes] = await Promise.all([examApi.getExamClasses(), examApi.getQuestions({ scope: "school" })]); classes.value = classRes.data || []; bank.value = questionRes.data || []; if (!isNew.value) { const { data } = await examApi.getTeacherExam(id.value); status.value = data.status; const bankIds = new Set(bank.value.map((item) => item.id)); bank.value = [...bank.value, ...data.questions.filter((item) => !bankIds.has(item.id))]; Object.assign(form, { name: data.name, startTime: formatValue(data.startTime), endTime: formatValue(data.endTime), classIds: data.classes.map((item) => item.id), questionOrderMode: data.questionOrderMode || "FIXED", optionOrderMode: data.optionOrderMode || "FIXED", allowCopyQuestion: toBoolean(data.allowCopyQuestion, false), allowPasteAnswer: toBoolean(data.allowPasteAnswer, false), allowRollback: toBoolean(data.allowRollback, true), multipleScoreMode: data.multipleScoreMode || "ALL_OR_NOTHING", questions: data.questions.map((item) => ({ questionId: item.id, score: Number(item.score) })) }); for (const type of typeOptions.map((item) => item.value)) { const first = data.questions.find((item) => item.type === type); if (first) typeScores[type] = Number(first.score) || 1; } duration.value = data.durationMinutes; } else { const start = new Date(Date.now() + 3600000); form.startTime = formatValue(start); form.endTime = formatValue(new Date(start.getTime() + 3600000)); } } finally { loading.value = false; } } function syncEndTime() { if (!form.startTime) return; form.endTime = formatValue(new Date(new Date(form.startTime.replace(" ", "T")).getTime() + duration.value * 60000)); } function syncDurationFromEnd() { if (!form.startTime || !form.endTime) return; const start = new Date(form.startTime.replace(" ", "T")); const end = new Date(form.endTime.replace(" ", "T")); if (end > start) duration.value = Math.round((end - start) / 60000); } function isSelected(questionId) { return form.questions.some((item) => item.questionId === questionId); } @@ -403,38 +403,48 @@ function toBoolean(value, fallback) { if (value === undefined || value === null display: flex; align-items: center; justify-content: center; - gap: var(--sp-4); + gap: var(--sp-3); margin-top: var(--sp-5); } .fixed-step-button { - min-width: 164px; - height: 40px; - padding: 0 var(--sp-5); - border: 0; + min-width: 148px; + height: 38px; + padding: 0 var(--sp-4); + border: 1px solid var(--border-color); border-radius: var(--r-control); - background: var(--edu-primary); - color: #ffffff; + background: #ffffff; + color: var(--text-body); + font: inherit; font-size: var(--fs-body); - font-weight: 600; + font-weight: 500; cursor: pointer; - transition: background 0.16s ease, transform 0.16s ease, box-shadow 0.16s ease; + transition: border-color 0.16s ease, background 0.16s ease, color 0.16s ease, box-shadow 0.16s ease; + + &:hover { + border-color: var(--edu-primary); + color: var(--edu-primary); + } - &:hover, &.is-active { - background: var(--edu-primary-deep); + border-color: var(--edu-primary); + background: var(--edu-primary); + color: #ffffff; box-shadow: 0 4px 10px rgba(25, 137, 204, 0.18); - transform: translateY(-1px); + + &:hover { color: #ffffff; } } &:focus-visible { outline: 2px solid var(--edu-primary); outline-offset: 2px; } } .fixed-step-arrow { - color: var(--text-strong); - font-size: 20px; - font-weight: 700; - letter-spacing: -3px; + display: inline-flex; + align-items: center; + color: var(--text-muted); + font-size: 16px; + + .el-icon { font-size: 16px; } } .form-row { diff --git a/src/views/teacherEnd/exam/index.vue b/src/views/teacherEnd/exam/index.vue index ac7123a..4518a7a 100644 --- a/src/views/teacherEnd/exam/index.vue +++ b/src/views/teacherEnd/exam/index.vue @@ -10,7 +10,7 @@
-

{{ row.name }}

起止时间:{{ formatTime(row.startTime) }} - {{ formatTime(row.endTime) }}

考试时长:{{ row.durationMinutes || durationBetween(row) }}分钟

+

{{ row.name }}

起止时间{{ formatTime(row.startTime) }} ~ {{ formatTime(row.endTime) }}

考试时长:{{ row.durationMinutes || durationBetween(row) }}分钟

{{ statusMeta[row.status]?.label || row.status }}
撤回 @@ -115,7 +115,7 @@ import { computed, onActivated, onMounted, reactive, ref } from "vue"; import { ElMessage, ElMessageBox } from "element-plus"; import { useRouter } from "vue-router"; -import { ArrowDown, Check, Close, DataAnalysis, Delete, Download, Edit, EditPen, FolderOpened, MagicStick, Paperclip, PictureFilled, Plus, RefreshLeft, Search, Upload } from "@element-plus/icons-vue"; +import { ArrowDown, Check, Clock, Close, DataAnalysis, Delete, Download, Edit, EditPen, FolderOpened, MagicStick, Paperclip, PictureFilled, Plus, RefreshLeft, Search, Upload } from "@element-plus/icons-vue"; import { saveAs } from "file-saver"; import * as examApi from "@/api/exam"; @@ -470,6 +470,33 @@ function durationBetween(row) { if (!row.startTime || !row.endTime) return 0; re color: var(--text-body); font-size: var(--fs-body); } + + .time-line { + display: inline-flex; + align-items: center; + gap: 6px; + margin: 4px 0; + padding: 4px 10px; + border-radius: 6px; + background: var(--edu-primary-soft); + + .el-icon { + font-size: 15px; + color: var(--edu-primary); + } + + .time-label { + color: var(--edu-primary-deep); + font-size: var(--fs-body); + } + + .time-value { + color: var(--text-strong); + font-size: var(--fs-body); + font-weight: 600; + font-variant-numeric: tabular-nums; + } + } } .assessment-status {