|
|
|
|
<template>
|
|
|
|
|
<main class="exam-admin app-container">
|
|
|
|
|
<header class="page-head">
|
|
|
|
|
<div><h1>综合考核</h1><p>维护题库并管理考试全流程</p></div>
|
|
|
|
|
<el-button v-if="activeTab === 'questions'" type="primary" :icon="Plus" @click="openQuestion()">新增题目</el-button>
|
|
|
|
|
<el-button v-else type="primary" :icon="Plus" @click="$router.push('/exam/manage/new')">新建考试</el-button>
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
|
|
<el-tabs v-model="activeTab" class="workspace-tabs">
|
|
|
|
|
<el-tab-pane label="题库管理" name="questions">
|
|
|
|
|
<section class="toolbar" aria-label="题库筛选">
|
|
|
|
|
<el-select v-model="questionQuery.type" placeholder="全部题型" clearable @change="loadQuestions">
|
|
|
|
|
<el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value" />
|
|
|
|
|
</el-select>
|
|
|
|
|
<el-input v-model="questionQuery.keyword" placeholder="搜索题干" clearable :prefix-icon="Search" @keyup.enter="loadQuestions" />
|
|
|
|
|
<el-button :icon="Search" @click="loadQuestions">查询</el-button>
|
|
|
|
|
<div class="toolbar-spacer" />
|
|
|
|
|
<el-button :icon="Download" @click="downloadTemplate">下载导入模板</el-button>
|
|
|
|
|
<el-upload :show-file-list="false" accept=".docx" :http-request="doImport">
|
|
|
|
|
<el-button :icon="Upload">Word 批量导入</el-button>
|
|
|
|
|
</el-upload>
|
|
|
|
|
</section>
|
|
|
|
|
<el-table v-loading="loading" :data="questions" class="data-table">
|
|
|
|
|
<el-table-column label="题型" width="110"><template #default="{ row }"><el-tag :type="typeTone[row.type]">{{ typeName[row.type] }}</el-tag></template></el-table-column>
|
|
|
|
|
<el-table-column prop="stem" label="题干" min-width="420" show-overflow-tooltip />
|
|
|
|
|
<el-table-column label="正确答案" min-width="150"><template #default="{ row }">{{ displayAnswer(row.answer) }}</template></el-table-column>
|
|
|
|
|
<el-table-column prop="updatedAt" label="更新时间" width="180"><template #default="{ row }">{{ formatTime(row.updatedAt) }}</template></el-table-column>
|
|
|
|
|
<el-table-column label="操作" width="150" fixed="right"><template #default="{ row }">
|
|
|
|
|
<el-button link type="primary" @click="openQuestion(row)">编辑</el-button>
|
|
|
|
|
<el-button link type="danger" @click="removeQuestion(row)">删除</el-button>
|
|
|
|
|
</template></el-table-column>
|
|
|
|
|
<template #empty><el-empty class="table-empty" :image-size="92" description="暂无题目" /></template>
|
|
|
|
|
</el-table>
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
|
|
|
|
|
<el-tab-pane label="考评管理" name="exams">
|
|
|
|
|
<section class="toolbar" aria-label="考试筛选">
|
|
|
|
|
<el-input v-model="examKeyword" placeholder="搜索考试名称" clearable :prefix-icon="Search" @keyup.enter="loadExams" />
|
|
|
|
|
<el-button :icon="Search" @click="loadExams">查询</el-button>
|
|
|
|
|
</section>
|
|
|
|
|
<el-table v-loading="loading" :data="exams" class="data-table">
|
|
|
|
|
<el-table-column prop="name" label="考试名称" min-width="210" />
|
|
|
|
|
<el-table-column label="考试时间" min-width="320"><template #default="{ row }">{{ formatTime(row.startTime) }} 至 {{ formatTime(row.endTime) }}</template></el-table-column>
|
|
|
|
|
<el-table-column label="试卷" width="150"><template #default="{ row }">{{ row.questionCount || 0 }} 题 / {{ row.totalScore || 0 }} 分</template></el-table-column>
|
|
|
|
|
<el-table-column label="班级" width="90"><template #default="{ row }">{{ row.classCount || 0 }} 个</template></el-table-column>
|
|
|
|
|
<el-table-column label="状态" width="110"><template #default="{ row }"><el-tag :type="statusMeta[row.status]?.tone">{{ statusMeta[row.status]?.label }}</el-tag></template></el-table-column>
|
|
|
|
|
<el-table-column label="操作" min-width="330" fixed="right"><template #default="{ row }">
|
|
|
|
|
<el-button v-if="['DRAFT','IN_PROGRESS'].includes(row.status)" link type="primary" @click="$router.push(`/exam/manage/${row.id}`)">编辑</el-button>
|
|
|
|
|
<el-button v-if="row.status === 'DRAFT'" link type="success" @click="publish(row)">发布</el-button>
|
|
|
|
|
<el-button v-if="row.status === 'NOT_STARTED'" link @click="withdraw(row)">撤回</el-button>
|
|
|
|
|
<el-button v-if="row.status === 'IN_PROGRESS'" link type="warning" @click="$router.push(`/exam/review/${row.id}`)">指定人回退</el-button>
|
|
|
|
|
<template v-if="row.status === 'ENDED'">
|
|
|
|
|
<el-button link type="primary" @click="$router.push(`/exam/review/${row.id}`)">评阅</el-button>
|
|
|
|
|
<el-button link type="primary" @click="$router.push(`/exam/analysis/${row.id}`)">分析</el-button>
|
|
|
|
|
<el-dropdown @command="(command) => handleExport(command, row)">
|
|
|
|
|
<el-button link type="primary">导出<el-icon class="el-icon--right"><ArrowDown /></el-icon></el-button>
|
|
|
|
|
<template #dropdown><el-dropdown-menu><el-dropdown-item command="paper">空白试卷</el-dropdown-item><el-dropdown-item command="scores">班级成绩</el-dropdown-item><el-dropdown-item command="submissions">全部答卷</el-dropdown-item></el-dropdown-menu></template>
|
|
|
|
|
</el-dropdown>
|
|
|
|
|
<el-button link type="danger" @click="removeExam(row)">删除</el-button>
|
|
|
|
|
</template>
|
|
|
|
|
</template></el-table-column>
|
|
|
|
|
<template #empty><el-empty class="table-empty" :image-size="92" description="暂无考试" /></template>
|
|
|
|
|
</el-table>
|
|
|
|
|
</el-tab-pane>
|
|
|
|
|
</el-tabs>
|
|
|
|
|
|
|
|
|
|
<el-dialog v-model="questionDialog" :title="questionForm.id ? '编辑题目' : '新增题目'" width="760px" class="teacher-dialog" destroy-on-close>
|
|
|
|
|
<el-form ref="questionFormRef" :model="questionForm" :rules="questionRules" label-width="92px">
|
|
|
|
|
<el-form-item label="题型" prop="type"><el-radio-group v-model="questionForm.type" @change="resetAnswer"><el-radio-button v-for="item in typeOptions" :key="item.value" :label="item.value">{{ item.label }}</el-radio-button></el-radio-group></el-form-item>
|
|
|
|
|
<el-form-item label="题干" prop="stem"><el-input v-model="questionForm.stem" type="textarea" :rows="4" maxlength="2000" show-word-limit /></el-form-item>
|
|
|
|
|
<el-form-item v-if="isChoice" label="选项">
|
|
|
|
|
<div class="option-editor">
|
|
|
|
|
<div v-for="(option, index) in questionForm.options" :key="option.label" class="option-row">
|
|
|
|
|
<el-checkbox v-if="questionForm.type === 'MULTIPLE'" :model-value="questionForm.answer.includes(option.label)" @change="toggleMultiple(option.label, $event)" />
|
|
|
|
|
<el-radio v-else v-model="singleAnswer" :label="option.label"><span class="option-label">{{ option.label }}</span></el-radio>
|
|
|
|
|
<el-input v-model="option.content" :placeholder="`选项 ${option.label}`" />
|
|
|
|
|
<el-button v-if="questionForm.options.length > 2" text :icon="Delete" aria-label="删除选项" @click="deleteOption(index)" />
|
|
|
|
|
</div>
|
|
|
|
|
<el-button v-if="questionForm.options.length < 8" text type="primary" :icon="Plus" @click="addOption">添加选项</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item v-else-if="questionForm.type === 'TRUE_FALSE'" label="正确答案"><el-radio-group v-model="questionForm.answer"><el-radio :label="true">正确</el-radio><el-radio :label="false">错误</el-radio></el-radio-group></el-form-item>
|
|
|
|
|
<el-form-item v-else label="参考答案" prop="answer"><el-input v-model="questionForm.answer" type="textarea" :rows="4" /></el-form-item>
|
|
|
|
|
<el-form-item v-if="questionForm.type === 'SHORT_ANSWER'" label="题目资料">
|
|
|
|
|
<el-upload :show-file-list="false" :http-request="uploadMaterial" accept=".pdf,.doc,.docx,.png,.jpg,.jpeg"><el-button :icon="Paperclip">上传资料</el-button></el-upload>
|
|
|
|
|
<span v-if="questionForm.attachmentName" class="file-name">{{ questionForm.attachmentName }}</span>
|
|
|
|
|
</el-form-item>
|
|
|
|
|
<el-form-item label="答案解析"><el-input v-model="questionForm.analysis" type="textarea" :rows="3" /></el-form-item>
|
|
|
|
|
</el-form>
|
|
|
|
|
<template #footer><el-button @click="questionDialog=false">取消</el-button><el-button type="primary" :loading="saving" @click="saveQuestion">保存</el-button></template>
|
|
|
|
|
</el-dialog>
|
|
|
|
|
</main>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
import { computed, onMounted, reactive, ref } from "vue";
|
|
|
|
|
import { useRouter } from "vue-router";
|
|
|
|
|
import { ElMessage, ElMessageBox } from "element-plus";
|
|
|
|
|
import { ArrowDown, Delete, Download, Paperclip, Plus, Search, Upload } from "@element-plus/icons-vue";
|
|
|
|
|
import { saveAs } from "file-saver";
|
|
|
|
|
import * as examApi from "@/api/exam";
|
|
|
|
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
const activeTab = ref("questions");
|
|
|
|
|
const loading = ref(false); const saving = ref(false); const questions = ref([]); const exams = ref([]); const examKeyword = ref("");
|
|
|
|
|
const questionDialog = ref(false); const questionFormRef = ref();
|
|
|
|
|
const typeOptions = [{ label: "单选题", value: "SINGLE" }, { label: "多选题", value: "MULTIPLE" }, { label: "判断题", value: "TRUE_FALSE" }, { label: "简答题", value: "SHORT_ANSWER" }];
|
|
|
|
|
const typeName = Object.fromEntries(typeOptions.map((item) => [item.value, item.label]));
|
|
|
|
|
const typeTone = { SINGLE: "", MULTIPLE: "success", TRUE_FALSE: "warning", SHORT_ANSWER: "info" };
|
|
|
|
|
const statusMeta = { DRAFT: { label: "未发布", tone: "info" }, NOT_STARTED: { label: "未开始", tone: "" }, IN_PROGRESS: { label: "进行中", tone: "success" }, ENDED: { label: "已结束", tone: "warning" } };
|
|
|
|
|
const questionQuery = reactive({ type: "", keyword: "" });
|
|
|
|
|
const emptyQuestion = () => ({ id: null, type: "SINGLE", stem: "", options: [{ label: "A", content: "" }, { label: "B", content: "" }, { label: "C", content: "" }, { label: "D", content: "" }], answer: [], analysis: "", attachmentName: "", attachmentUrl: "" });
|
|
|
|
|
const questionForm = reactive(emptyQuestion());
|
|
|
|
|
const questionRules = { type: [{ required: true, message: "请选择题型" }], stem: [{ required: true, message: "请输入题干" }], answer: [{ required: true, message: "请输入参考答案" }] };
|
|
|
|
|
const isChoice = computed(() => ["SINGLE", "MULTIPLE"].includes(questionForm.type));
|
|
|
|
|
const singleAnswer = computed({ get: () => questionForm.answer?.[0] || "", set: (value) => { questionForm.answer = value ? [value] : []; } });
|
|
|
|
|
|
|
|
|
|
onMounted(() => Promise.all([loadQuestions(), loadExams()]));
|
|
|
|
|
async function loadQuestions() { loading.value = true; try { questions.value = (await examApi.getQuestions(questionQuery)).data || []; } finally { loading.value = false; } }
|
|
|
|
|
async function loadExams() { loading.value = true; try { exams.value = (await examApi.getTeacherExams({ keyword: examKeyword.value })).data || []; } finally { loading.value = false; } }
|
|
|
|
|
function openQuestion(row) { Object.assign(questionForm, row ? JSON.parse(JSON.stringify(row)) : emptyQuestion()); if (!Array.isArray(questionForm.answer) && isChoice.value) questionForm.answer = []; questionDialog.value = true; }
|
|
|
|
|
function resetAnswer() { questionForm.answer = questionForm.type === "TRUE_FALSE" ? true : questionForm.type === "SHORT_ANSWER" ? "" : []; }
|
|
|
|
|
function toggleMultiple(label, checked) { questionForm.answer = checked ? [...new Set([...questionForm.answer, label])] : questionForm.answer.filter((item) => item !== label); }
|
|
|
|
|
function addOption() { questionForm.options.push({ label: String.fromCharCode(65 + questionForm.options.length), content: "" }); }
|
|
|
|
|
function deleteOption(index) { const [removed] = questionForm.options.splice(index, 1); questionForm.answer = questionForm.answer.filter((item) => item !== removed.label); }
|
|
|
|
|
async function saveQuestion() { await questionFormRef.value.validate(); if (isChoice.value && (!questionForm.answer.length || questionForm.options.some((item) => !item.content.trim()))) return ElMessage.warning("请补全选项并设置正确答案"); saving.value = true; try { questionForm.id ? await examApi.updateQuestion(questionForm.id, questionForm) : await examApi.createQuestion(questionForm); ElMessage.success("题目已保存"); questionDialog.value = false; loadQuestions(); } finally { saving.value = false; } }
|
|
|
|
|
async function removeQuestion(row) { await ElMessageBox.confirm("删除后无法恢复,确认删除该题目?", "删除题目", { type: "warning" }); await examApi.deleteQuestion(row.id); ElMessage.success("已删除"); loadQuestions(); }
|
|
|
|
|
async function uploadMaterial({ file }) { const form = new FormData(); form.append("file", file); const { data } = await examApi.uploadQuestionAttachment(form); questionForm.attachmentName = data.name; questionForm.attachmentUrl = data.url; ElMessage.success("资料已上传"); }
|
|
|
|
|
async function doImport({ file }) { const form = new FormData(); form.append("file", file); const { data } = await examApi.importQuestions(form); ElMessage.success(`成功导入 ${data.length} 道题`); loadQuestions(); }
|
|
|
|
|
async function downloadTemplate() { saveAs(await examApi.downloadQuestionTemplate(), "考试题库导入模板.docx"); }
|
|
|
|
|
async function publish(row) { await ElMessageBox.confirm(`确认发布“${row.name}”?发布后将在设定时间自动开始。`, "发布考试", { type: "warning" }); await examApi.publishExam(row.id); ElMessage.success("已发布"); loadExams(); }
|
|
|
|
|
async function withdraw(row) { await examApi.withdrawExam(row.id); ElMessage.success("已撤回"); loadExams(); }
|
|
|
|
|
async function removeExam(row) { await ElMessageBox.confirm(`确认永久删除“${row.name}”?仅在已导出全部答卷后允许删除。`, "删除考试", { type: "warning", confirmButtonText: "确认删除" }); await examApi.deleteExam(row.id); ElMessage.success("已删除"); loadExams(); }
|
|
|
|
|
async function handleExport(command, row) { const actions = { paper: [examApi.exportExamPaper, `${row.name}试卷.docx`], scores: [examApi.exportExamScores, `${row.name}班级成绩.xlsx`], submissions: [examApi.exportExamSubmissions, `${row.name}全部试卷.zip`] }; const [action, filename] = actions[command]; saveAs(await action(row.id), filename); if (command === "submissions") loadExams(); }
|
|
|
|
|
function displayAnswer(value) { if (Array.isArray(value)) return value.join("、"); if (value === true) return "正确"; if (value === false) return "错误"; return value || "-"; }
|
|
|
|
|
function formatTime(value) { return value ? new Date(value).toLocaleString("zh-CN", { hour12: false }).replaceAll("/", "-") : "-"; }
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
.exam-admin.app-container {
|
|
|
|
|
--exam-accent: #39c9ff;
|
|
|
|
|
--exam-text: #eaf8ff;
|
|
|
|
|
--exam-muted: #9fc2d8;
|
|
|
|
|
--exam-border: rgba(69, 185, 236, 0.3);
|
|
|
|
|
position: relative;
|
|
|
|
|
min-height: 0 !important;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
color: var(--exam-text);
|
|
|
|
|
background:
|
|
|
|
|
radial-gradient(circle at 88% 0%, rgba(27, 126, 194, 0.16), transparent 31%),
|
|
|
|
|
linear-gradient(145deg, rgba(6, 25, 40, 0.92), rgba(3, 13, 24, 0.9));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.exam-admin::before {
|
|
|
|
|
position: absolute;
|
|
|
|
|
top: 0;
|
|
|
|
|
right: 28px;
|
|
|
|
|
left: 28px;
|
|
|
|
|
height: 1px;
|
|
|
|
|
background: linear-gradient(90deg, transparent, rgba(79, 207, 255, 0.72), transparent);
|
|
|
|
|
content: "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-head {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
gap: 24px;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
padding: 4px 2px 8px;
|
|
|
|
|
|
|
|
|
|
h1 {
|
|
|
|
|
margin: 0;
|
|
|
|
|
color: #f4fbff;
|
|
|
|
|
font-size: 26px;
|
|
|
|
|
line-height: 1.3;
|
|
|
|
|
letter-spacing: 0.02em;
|
|
|
|
|
text-shadow: 0 0 18px rgba(58, 199, 255, 0.16);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p {
|
|
|
|
|
margin: 7px 0 0;
|
|
|
|
|
color: var(--exam-muted);
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.workspace-tabs {
|
|
|
|
|
--el-color-primary: var(--exam-accent);
|
|
|
|
|
|
|
|
|
|
:deep(.el-tabs__header) {
|
|
|
|
|
margin-bottom: 18px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.el-tabs__nav-wrap::after) {
|
|
|
|
|
height: 1px;
|
|
|
|
|
background-color: rgba(99, 187, 226, 0.26);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.el-tabs__item) {
|
|
|
|
|
height: 44px;
|
|
|
|
|
padding: 0 24px;
|
|
|
|
|
color: #aac9dc;
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.el-tabs__item:hover),
|
|
|
|
|
:deep(.el-tabs__item.is-active) {
|
|
|
|
|
color: #73ddff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.el-tabs__active-bar) {
|
|
|
|
|
height: 3px;
|
|
|
|
|
border-radius: 3px 3px 0 0;
|
|
|
|
|
background: linear-gradient(90deg, #27c9ff, #5be4ef);
|
|
|
|
|
box-shadow: 0 0 12px rgba(39, 201, 255, 0.45);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toolbar {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
margin-bottom: 16px;
|
|
|
|
|
padding: 14px;
|
|
|
|
|
border: 1px solid rgba(53, 162, 214, 0.25);
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
background: rgba(2, 18, 31, 0.6);
|
|
|
|
|
box-shadow: inset 0 1px 0 rgba(157, 224, 255, 0.04);
|
|
|
|
|
|
|
|
|
|
.el-select {
|
|
|
|
|
width: 160px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.el-input {
|
|
|
|
|
width: 280px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toolbar-spacer {
|
|
|
|
|
flex: 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.toolbar .el-input__wrapper),
|
|
|
|
|
:deep(.toolbar .el-select__wrapper) {
|
|
|
|
|
min-height: 38px;
|
|
|
|
|
border-color: rgba(71, 181, 229, 0.42);
|
|
|
|
|
background: rgba(3, 28, 45, 0.92);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.toolbar .el-input__inner),
|
|
|
|
|
:deep(.toolbar .el-select__selected-item),
|
|
|
|
|
:deep(.toolbar .el-select__placeholder) {
|
|
|
|
|
color: #e6f7ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.toolbar .el-input__inner::placeholder) {
|
|
|
|
|
color: #7fa5bc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.toolbar .el-button:not(.el-button--primary)) {
|
|
|
|
|
min-height: 38px;
|
|
|
|
|
color: #d8f2ff;
|
|
|
|
|
border-color: rgba(80, 181, 229, 0.46);
|
|
|
|
|
background: rgba(5, 32, 50, 0.88);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.toolbar .el-button:not(.el-button--primary):hover),
|
|
|
|
|
:deep(.toolbar .el-button:not(.el-button--primary):focus) {
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
border-color: rgba(91, 217, 255, 0.82);
|
|
|
|
|
background: rgba(10, 63, 91, 0.92);
|
|
|
|
|
box-shadow: 0 0 16px rgba(28, 174, 226, 0.14);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.data-table {
|
|
|
|
|
--el-table-border-color: rgba(59, 164, 213, 0.24);
|
|
|
|
|
--el-table-header-text-color: #e5f7ff;
|
|
|
|
|
--el-table-text-color: #cce7f6;
|
|
|
|
|
--el-table-row-hover-bg-color: rgba(16, 100, 143, 0.22);
|
|
|
|
|
width: 100%;
|
|
|
|
|
margin-top: 0 !important;
|
|
|
|
|
border: 1px solid rgba(59, 164, 213, 0.24);
|
|
|
|
|
border-radius: 10px;
|
|
|
|
|
background: rgba(2, 16, 27, 0.62);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.data-table th.el-table__cell) {
|
|
|
|
|
height: 50px;
|
|
|
|
|
padding: 0;
|
|
|
|
|
color: #e9f8ff !important;
|
|
|
|
|
background: linear-gradient(180deg, rgba(14, 77, 111, 0.98), rgba(8, 49, 75, 0.98)) !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.data-table td.el-table__cell) {
|
|
|
|
|
padding: 13px 0;
|
|
|
|
|
color: #cce7f6;
|
|
|
|
|
border-bottom-color: rgba(51, 143, 187, 0.18);
|
|
|
|
|
background: rgba(2, 17, 29, 0.74);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.data-table .el-table__empty-block) {
|
|
|
|
|
min-height: 238px;
|
|
|
|
|
background:
|
|
|
|
|
radial-gradient(circle at 50% 42%, rgba(37, 147, 196, 0.1), transparent 28%),
|
|
|
|
|
rgba(2, 15, 25, 0.5);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.table-empty) {
|
|
|
|
|
padding: 26px 0 30px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.table-empty .el-empty__description) {
|
|
|
|
|
margin-top: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.table-empty .el-empty__description p) {
|
|
|
|
|
color: #9fc4d9;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.option-editor {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.option-row {
|
|
|
|
|
display: grid;
|
|
|
|
|
grid-template-columns: 28px minmax(0, 1fr) 36px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 10px;
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.option-label {
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.file-name {
|
|
|
|
|
margin-left: 12px;
|
|
|
|
|
color: var(--exam-accent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .el-dialog__body) {
|
|
|
|
|
padding: 24px 28px 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .el-form-item) {
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .el-radio-button__inner) {
|
|
|
|
|
min-width: 92px;
|
|
|
|
|
padding: 10px 18px;
|
|
|
|
|
color: #cbe6f5;
|
|
|
|
|
border-color: rgba(80, 181, 229, 0.42);
|
|
|
|
|
background: rgba(4, 29, 46, 0.9);
|
|
|
|
|
box-shadow: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .el-radio-button:first-child .el-radio-button__inner) {
|
|
|
|
|
border-left-color: rgba(80, 181, 229, 0.42);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .el-radio-button__original-radio:checked + .el-radio-button__inner) {
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
border-color: #38c9ff;
|
|
|
|
|
background: linear-gradient(135deg, #078dce, #1557d6);
|
|
|
|
|
box-shadow: -1px 0 0 0 #38c9ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .el-radio__label),
|
|
|
|
|
:deep(.teacher-dialog .el-checkbox__label) {
|
|
|
|
|
color: #cbe6f5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .el-radio__input.is-checked + .el-radio__label),
|
|
|
|
|
:deep(.teacher-dialog .el-checkbox__input.is-checked + .el-checkbox__label) {
|
|
|
|
|
color: #e9f8ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .option-row) {
|
|
|
|
|
min-height: 48px;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
padding: 6px 10px;
|
|
|
|
|
border: 1px solid rgba(61, 171, 220, 0.2);
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
background: rgba(3, 24, 39, 0.68);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .option-label) {
|
|
|
|
|
color: #e7f7ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .option-row .el-button.is-text) {
|
|
|
|
|
color: #8edfff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .option-row .el-button.is-text:hover) {
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
background: rgba(19, 107, 145, 0.28);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .option-editor > .el-button.is-text) {
|
|
|
|
|
margin-top: 2px;
|
|
|
|
|
color: #77dcff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.teacher-dialog .el-dialog__footer) {
|
|
|
|
|
padding: 14px 28px 22px;
|
|
|
|
|
border-top: 1px solid rgba(52, 177, 238, 0.16);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 960px) {
|
|
|
|
|
.toolbar-spacer {
|
|
|
|
|
flex-basis: 100%;
|
|
|
|
|
height: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@media (max-width: 760px) {
|
|
|
|
|
.exam-admin.app-container {
|
|
|
|
|
padding: 16px 14px 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-head,
|
|
|
|
|
.toolbar {
|
|
|
|
|
align-items: stretch;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.page-head {
|
|
|
|
|
gap: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toolbar-spacer {
|
|
|
|
|
display: none;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.toolbar .el-input,
|
|
|
|
|
.toolbar .el-select {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.toolbar .el-button) {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|