|
|
|
@ -0,0 +1,295 @@
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
|
|
|
<div class="task-review" v-loading="loading">
|
|
|
|
|
|
|
|
<aside class="task-review__sidebar">
|
|
|
|
|
|
|
|
<el-form label-position="top" class="task-review__filters">
|
|
|
|
|
|
|
|
<el-form-item label="实训项目">
|
|
|
|
|
|
|
|
<el-select v-model="filters.projectName" placeholder="请选择实训项目" clearable @change="onProjectChange">
|
|
|
|
|
|
|
|
<el-option v-for="project in review.projects" :key="project" :label="project" :value="project" />
|
|
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-form-item label="实训任务">
|
|
|
|
|
|
|
|
<el-select v-model="filters.taskId" placeholder="请选择实训任务" :disabled="!filters.classId" @change="loadReview">
|
|
|
|
|
|
|
|
<el-option v-for="task in review.tasks" :key="task.taskId" :label="task.taskName" :value="task.taskId" />
|
|
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
<el-form-item label="教学班级">
|
|
|
|
|
|
|
|
<el-select v-model="filters.classId" placeholder="请选择教学班" @change="onClassChange">
|
|
|
|
|
|
|
|
<el-option v-for="item in teachingClasses" :key="item.schoolClassId" :label="item.className" :value="item.schoolClassId" />
|
|
|
|
|
|
|
|
</el-select>
|
|
|
|
|
|
|
|
</el-form-item>
|
|
|
|
|
|
|
|
</el-form>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="task-review__list-head">
|
|
|
|
|
|
|
|
<span>学生列表({{ review.students.length }})</span>
|
|
|
|
|
|
|
|
<el-button type="primary" size="small" :disabled="!canBatchScore" @click="batchDialogVisible = true">批量打分</el-button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<el-table
|
|
|
|
|
|
|
|
ref="studentTableRef"
|
|
|
|
|
|
|
|
:data="review.students"
|
|
|
|
|
|
|
|
border
|
|
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
|
|
row-key="studentUserId"
|
|
|
|
|
|
|
|
highlight-current-row
|
|
|
|
|
|
|
|
:row-class-name="studentRowClass"
|
|
|
|
|
|
|
|
@selection-change="onSelectionChange"
|
|
|
|
|
|
|
|
@row-click="selectStudent"
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
<el-table-column type="selection" width="42" :selectable="canSelectStudent" />
|
|
|
|
|
|
|
|
<el-table-column prop="name" label="姓名" min-width="84" show-overflow-tooltip />
|
|
|
|
|
|
|
|
<el-table-column prop="username" label="学号" min-width="92" show-overflow-tooltip />
|
|
|
|
|
|
|
|
<el-table-column label="AI评分" width="72" align="center">
|
|
|
|
|
|
|
|
<template #default="{ row }">{{ formatScore(row.aiAssessmentScore) }}</template>
|
|
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
<el-table-column label="老师评分" min-width="112" align="center">
|
|
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
|
|
<el-input-number
|
|
|
|
|
|
|
|
v-if="canManageClass && row.scorable"
|
|
|
|
|
|
|
|
v-model="row.teacherScoreDraft"
|
|
|
|
|
|
|
|
class="task-review__score-input"
|
|
|
|
|
|
|
|
:min="0"
|
|
|
|
|
|
|
|
:max="100"
|
|
|
|
|
|
|
|
:precision="0"
|
|
|
|
|
|
|
|
:controls="false"
|
|
|
|
|
|
|
|
placeholder="评分"
|
|
|
|
|
|
|
|
@change="saveSingleScore(row)"
|
|
|
|
|
|
|
|
@click.stop
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<el-tag v-else-if="!row.scorable" size="small" type="info">未完成</el-tag>
|
|
|
|
|
|
|
|
<span v-else>{{ formatScore(row.teacherScore) }}</span>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</el-table-column>
|
|
|
|
|
|
|
|
</el-table>
|
|
|
|
|
|
|
|
<p v-if="!canManageClass && filters.classId" class="task-review__readonly-tip">仅班级创建教师可评分</p>
|
|
|
|
|
|
|
|
</aside>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<section class="task-review__content">
|
|
|
|
|
|
|
|
<template v-if="selectedStudent && review.selectedTask">
|
|
|
|
|
|
|
|
<header class="task-review__student-head">
|
|
|
|
|
|
|
|
<div>
|
|
|
|
|
|
|
|
<h2>{{ selectedStudent.name || selectedStudent.username }}</h2>
|
|
|
|
|
|
|
|
<p>{{ review.selectedTask.projectName }} · {{ review.selectedTask.taskName }}</p>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="task-review__scores">
|
|
|
|
|
|
|
|
<span>AI评分 <strong>{{ formatScore(selectedStudent.aiAssessmentScore) }}</strong></span>
|
|
|
|
|
|
|
|
<span>老师评分 <strong>{{ formatScore(selectedStudent.teacherScore) }}</strong></span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</header>
|
|
|
|
|
|
|
|
<el-steps :active="completedStepCount" finish-status="success" align-center class="task-review__steps">
|
|
|
|
|
|
|
|
<el-step v-for="(step, index) in taskSteps" :key="`${index}-${step}`" :title="step" />
|
|
|
|
|
|
|
|
</el-steps>
|
|
|
|
|
|
|
|
<el-empty v-if="!hasAnswer" description="该学生尚未保存任务内容" :image-size="104" />
|
|
|
|
|
|
|
|
<el-collapse v-else v-model="openSteps" class="task-review__answers">
|
|
|
|
|
|
|
|
<el-collapse-item v-for="(step, index) in answerSteps" :key="step.name" :name="step.name">
|
|
|
|
|
|
|
|
<template #title>第{{ index + 1 }}步:{{ step.name }}</template>
|
|
|
|
|
|
|
|
<pre>{{ formatAnswer(step.value) }}</pre>
|
|
|
|
|
|
|
|
</el-collapse-item>
|
|
|
|
|
|
|
|
</el-collapse>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
<el-empty v-else description="请选择教学班和实训任务后查看学生任务步骤" :image-size="128" />
|
|
|
|
|
|
|
|
</section>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<el-dialog v-model="batchDialogVisible" title="批量打分" width="420px" append-to-body @closed="batchScore = null">
|
|
|
|
|
|
|
|
<p class="task-review__batch-copy">请给该实训任务下已选中的学生批量打分</p>
|
|
|
|
|
|
|
|
<el-input-number v-model="batchScore" :min="0" :max="100" :precision="0" :controls="false" class="task-review__batch-input" placeholder="请输入0-100分" />
|
|
|
|
|
|
|
|
<template #footer>
|
|
|
|
|
|
|
|
<el-button @click="batchDialogVisible = false">取消</el-button>
|
|
|
|
|
|
|
|
<el-button type="primary" :loading="savingBatch" @click="saveBatchScore">确定</el-button>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</el-dialog>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script setup>
|
|
|
|
|
|
|
|
import { ElMessage } from "element-plus";
|
|
|
|
|
|
|
|
import * as teacherApi from "@/api/teacher";
|
|
|
|
|
|
|
|
import useUserStore from "@/store/modules/user";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const props = defineProps({
|
|
|
|
|
|
|
|
teachingClassList: { type: Array, default: () => [] },
|
|
|
|
|
|
|
|
initialClassId: { type: String, default: "" },
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const userStore = useUserStore();
|
|
|
|
|
|
|
|
const fallbackTeachingClassList = ref([]);
|
|
|
|
|
|
|
|
const teachingClasses = computed(() => props.teachingClassList.length
|
|
|
|
|
|
|
|
? props.teachingClassList
|
|
|
|
|
|
|
|
: fallbackTeachingClassList.value);
|
|
|
|
|
|
|
|
const filters = ref({ classId: "", projectName: "", taskId: "" });
|
|
|
|
|
|
|
|
const review = ref({ projects: [], tasks: [], selectedTask: null, students: [] });
|
|
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
|
|
|
const savingBatch = ref(false);
|
|
|
|
|
|
|
|
const batchDialogVisible = ref(false);
|
|
|
|
|
|
|
|
const batchScore = ref(null);
|
|
|
|
|
|
|
|
const selectedStudents = ref([]);
|
|
|
|
|
|
|
|
const selectedStudentId = ref("");
|
|
|
|
|
|
|
|
const studentTableRef = ref();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const canManageClass = computed(() => {
|
|
|
|
|
|
|
|
const currentClass = teachingClasses.value.find((item) => item.schoolClassId === filters.value.classId);
|
|
|
|
|
|
|
|
return Boolean(currentClass) && String(currentClass.createdBy || "") === String(userStore.userInfo.userId || "");
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
const selectedStudent = computed(() => review.value.students.find((row) => row.studentUserId === selectedStudentId.value) || review.value.students[0] || null);
|
|
|
|
|
|
|
|
const canBatchScore = computed(() => canManageClass.value && selectedStudents.value.length > 0 && Boolean(filters.value.taskId));
|
|
|
|
|
|
|
|
const taskSteps = computed(() => parseSteps(review.value.selectedTask?.steps));
|
|
|
|
|
|
|
|
const answerSteps = computed(() => {
|
|
|
|
|
|
|
|
if (!selectedStudent.value) return [];
|
|
|
|
|
|
|
|
const values = [selectedStudent.value.step1Answer, selectedStudent.value.step2Answer, selectedStudent.value.step3Answer, selectedStudent.value.step4Answer];
|
|
|
|
|
|
|
|
const names = taskSteps.value.length ? taskSteps.value : values.map((_, index) => `任务步骤${index + 1}`);
|
|
|
|
|
|
|
|
return values.map((value, index) => ({ name: names[index] || `任务步骤${index + 1}`, value })).filter((step) => step.value);
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
const hasAnswer = computed(() => answerSteps.value.length > 0 || Boolean(selectedStudent.value?.dynamicStepAnswers));
|
|
|
|
|
|
|
|
const openSteps = ref([]);
|
|
|
|
|
|
|
|
const completedStepCount = computed(() => Math.min(answerSteps.value.length, taskSteps.value.length || 4));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
watch(() => props.initialClassId, (classId) => {
|
|
|
|
|
|
|
|
if (!filters.value.classId && classId) {
|
|
|
|
|
|
|
|
filters.value.classId = classId;
|
|
|
|
|
|
|
|
loadReview();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}, { immediate: true });
|
|
|
|
|
|
|
|
watch(teachingClasses, (classes) => {
|
|
|
|
|
|
|
|
if (!filters.value.classId && classes.length) {
|
|
|
|
|
|
|
|
filters.value.classId = props.initialClassId || classes[0].schoolClassId;
|
|
|
|
|
|
|
|
loadReview();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}, { immediate: true });
|
|
|
|
|
|
|
|
watch(selectedStudent, (student) => {
|
|
|
|
|
|
|
|
if (student) openSteps.value = answerSteps.value.map((step) => step.name);
|
|
|
|
|
|
|
|
}, { flush: "post" });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function parseSteps(rawSteps) {
|
|
|
|
|
|
|
|
if (!rawSteps) return ["任务步骤1", "任务步骤2", "任务步骤3", "任务步骤4"];
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const parsed = JSON.parse(rawSteps);
|
|
|
|
|
|
|
|
if (Array.isArray(parsed)) return parsed.map((step, index) => typeof step === "string" ? step : step?.name || step?.title || `任务步骤${index + 1}`);
|
|
|
|
|
|
|
|
} catch (_) {
|
|
|
|
|
|
|
|
// 任务配置不是 JSON 时,保留一个可读的步骤标题。
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
return ["任务步骤1", "任务步骤2", "任务步骤3", "任务步骤4"];
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function formatScore(score) {
|
|
|
|
|
|
|
|
return score === null || score === undefined || score === "" ? "--" : score;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function formatAnswer(value) {
|
|
|
|
|
|
|
|
if (!value) return "未填写";
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
return JSON.stringify(JSON.parse(value), null, 2);
|
|
|
|
|
|
|
|
} catch (_) {
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function normalizeReview(data) {
|
|
|
|
|
|
|
|
const students = (data?.students || []).map((row) => ({ ...row, teacherScoreDraft: row.teacherScore ?? null }));
|
|
|
|
|
|
|
|
review.value = { projects: data?.projects || [], tasks: data?.tasks || [], selectedTask: data?.selectedTask || null, students };
|
|
|
|
|
|
|
|
const selectedTask = review.value.selectedTask;
|
|
|
|
|
|
|
|
filters.value.taskId = selectedTask?.taskId || "";
|
|
|
|
|
|
|
|
if (selectedTask && !filters.value.projectName) filters.value.projectName = selectedTask.projectName || "";
|
|
|
|
|
|
|
|
if (!students.some((row) => row.studentUserId === selectedStudentId.value)) selectedStudentId.value = students[0]?.studentUserId || "";
|
|
|
|
|
|
|
|
selectedStudents.value = [];
|
|
|
|
|
|
|
|
studentTableRef.value?.clearSelection();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
async function loadReview() {
|
|
|
|
|
|
|
|
if (!filters.value.classId) return;
|
|
|
|
|
|
|
|
loading.value = true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
const res = await teacherApi.getTaskReview({ ...filters.value });
|
|
|
|
|
|
|
|
normalizeReview(res.data);
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
loading.value = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
async function loadTeachingClasses() {
|
|
|
|
|
|
|
|
if (props.teachingClassList.length) return;
|
|
|
|
|
|
|
|
const res = await teacherApi.getAccessibleTeachingClasses();
|
|
|
|
|
|
|
|
fallbackTeachingClassList.value = (res.data || []).filter((item) => item.classType === "TEACHING");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function onClassChange() {
|
|
|
|
|
|
|
|
filters.value.projectName = "";
|
|
|
|
|
|
|
|
filters.value.taskId = "";
|
|
|
|
|
|
|
|
loadReview();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function onProjectChange() {
|
|
|
|
|
|
|
|
filters.value.taskId = "";
|
|
|
|
|
|
|
|
loadReview();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function onSelectionChange(rows) {
|
|
|
|
|
|
|
|
selectedStudents.value = rows;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function canSelectStudent(row) {
|
|
|
|
|
|
|
|
return canManageClass.value && Boolean(row.scorable);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function selectStudent(row) {
|
|
|
|
|
|
|
|
selectedStudentId.value = row.studentUserId;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
function studentRowClass({ row }) {
|
|
|
|
|
|
|
|
return row.studentUserId === selectedStudentId.value ? "task-review__selected-row" : "";
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
async function saveSingleScore(row) {
|
|
|
|
|
|
|
|
const score = row.teacherScoreDraft;
|
|
|
|
|
|
|
|
if (!Number.isInteger(score) || score < 0 || score > 100) {
|
|
|
|
|
|
|
|
ElMessage.error("教师评分必须为0至100之间的整数");
|
|
|
|
|
|
|
|
row.teacherScoreDraft = row.teacherScore ?? null;
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
await teacherApi.saveTaskTeacherScore({ classId: filters.value.classId, studentUserId: row.studentUserId, taskId: filters.value.taskId, teacherScore: score });
|
|
|
|
|
|
|
|
ElMessage.success("任务评分保存成功");
|
|
|
|
|
|
|
|
await loadReview();
|
|
|
|
|
|
|
|
} catch (_) {
|
|
|
|
|
|
|
|
row.teacherScoreDraft = row.teacherScore ?? null;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
async function saveBatchScore() {
|
|
|
|
|
|
|
|
if (!Number.isInteger(batchScore.value) || batchScore.value < 0 || batchScore.value > 100) {
|
|
|
|
|
|
|
|
ElMessage.error("请输入0至100之间的整数分数");
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
savingBatch.value = true;
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
|
|
await teacherApi.batchSaveTaskTeacherScore({
|
|
|
|
|
|
|
|
classId: filters.value.classId,
|
|
|
|
|
|
|
|
taskId: filters.value.taskId,
|
|
|
|
|
|
|
|
studentUserIds: selectedStudents.value.map((row) => row.studentUserId),
|
|
|
|
|
|
|
|
teacherScore: batchScore.value,
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
ElMessage.success("批量任务评分保存成功");
|
|
|
|
|
|
|
|
batchDialogVisible.value = false;
|
|
|
|
|
|
|
|
await loadReview();
|
|
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
|
|
savingBatch.value = false;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
onMounted(loadTeachingClasses);
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="scss" scoped>
|
|
|
|
|
|
|
|
.task-review { display: grid; grid-template-columns: minmax(430px, 40%) minmax(0, 1fr); min-height: calc(100vh - 142px); border: 1px solid rgba(39, 172, 238, 0.28); border-radius: 14px; overflow: hidden; background: linear-gradient(135deg, rgba(6, 23, 36, 0.84), rgba(4, 12, 23, 0.78)); box-shadow: 0 18px 48px rgba(0, 5, 16, 0.34), inset 0 1px 0 rgba(153, 220, 255, 0.08); }
|
|
|
|
|
|
|
|
.task-review__sidebar { padding: 20px; border-right: 1px solid rgba(54, 159, 212, 0.25); overflow: auto; background: rgba(2, 18, 31, 0.48); }
|
|
|
|
|
|
|
|
.task-review__filters { display: grid; grid-template-columns: repeat(3, minmax(112px, 1fr)); gap: 12px; padding: 14px; border: 1px solid rgba(44, 169, 226, 0.28); border-radius: 10px; background: rgba(2, 18, 31, 0.64); }
|
|
|
|
|
|
|
|
.task-review__filters :deep(.el-form-item) { margin-bottom: 0; }
|
|
|
|
|
|
|
|
.task-review__filters :deep(.el-select) { width: 100%; }
|
|
|
|
|
|
|
|
.task-review__list-head { display: flex; align-items: center; justify-content: space-between; margin: 18px 0 4px; color: #dff6ff; font-size: 16px; font-weight: 700; }
|
|
|
|
|
|
|
|
.task-review__score-input { width: 88px; }
|
|
|
|
|
|
|
|
.task-review__readonly-tip { margin: 12px 0 0; color: #9abed8; font-size: 12px; }
|
|
|
|
|
|
|
|
.task-review__content { min-width: 0; padding: 28px 34px; background: linear-gradient(135deg, rgba(5, 25, 40, 0.58), rgba(3, 13, 24, 0.38)); }
|
|
|
|
|
|
|
|
.task-review__student-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; padding: 0 0 20px; border-bottom: 1px solid rgba(54, 159, 212, 0.26); }
|
|
|
|
|
|
|
|
.task-review__student-head h2 { margin: 0; color: #eef9ff; font-size: 24px; line-height: 1.25; }
|
|
|
|
|
|
|
|
.task-review__student-head p { margin: 8px 0 0; color: #9abed8; }
|
|
|
|
|
|
|
|
.task-review__scores { display: flex; gap: 10px; color: #b9d9eb; font-size: 13px; white-space: nowrap; }
|
|
|
|
|
|
|
|
.task-review__scores span { padding: 7px 10px; border: 1px solid rgba(50, 166, 223, 0.28); border-radius: 999px; background: rgba(2, 26, 43, 0.68); }
|
|
|
|
|
|
|
|
.task-review__scores strong { margin-left: 5px; color: #77d9ff; font-size: 17px; }
|
|
|
|
|
|
|
|
.task-review__steps { margin: 34px 0 26px; padding: 0 8px 8px; }
|
|
|
|
|
|
|
|
.task-review__steps :deep(.el-step__icon) { border-color: rgba(112, 204, 248, 0.68); background: #071b2a; }
|
|
|
|
|
|
|
|
.task-review__steps :deep(.el-step__head.is-success), .task-review__steps :deep(.el-step__title.is-success) { color: #75d7ff; border-color: #75d7ff; }
|
|
|
|
|
|
|
|
.task-review__steps :deep(.el-step__line) { background-color: rgba(93, 176, 218, 0.42); }
|
|
|
|
|
|
|
|
.task-review__steps :deep(.el-step__title) { min-height: 42px; padding: 0 7px; color: #bcd9e9; font-size: 14px; line-height: 1.45; white-space: normal; }
|
|
|
|
|
|
|
|
.task-review__answers { border-color: rgba(54, 159, 212, 0.24); }
|
|
|
|
|
|
|
|
.task-review__answers :deep(.el-collapse-item__header) { height: auto; min-height: 48px; padding: 10px 14px; color: #dff6ff; border-bottom-color: rgba(54, 159, 212, 0.22); background: rgba(4, 28, 45, 0.7); line-height: 1.5; }
|
|
|
|
|
|
|
|
.task-review__answers :deep(.el-collapse-item__wrap) { border-bottom-color: rgba(54, 159, 212, 0.22); background: rgba(2, 15, 26, 0.66); }
|
|
|
|
|
|
|
|
.task-review__answers :deep(pre) { margin: 0; padding: 16px; overflow: auto; border-radius: 0; background: transparent; color: #cce9f8; font-family: Consolas, Monaco, monospace; font-size: 13px; line-height: 1.65; white-space: pre-wrap; }
|
|
|
|
|
|
|
|
.task-review__batch-copy { margin: 0 0 18px; color: #dff6ff; }
|
|
|
|
|
|
|
|
.task-review__batch-input { width: 100%; }
|
|
|
|
|
|
|
|
:deep(.task-review__selected-row > td) { background: rgba(0, 111, 177, 0.26) !important; }
|
|
|
|
|
|
|
|
@media (max-width: 1180px) { .task-review { grid-template-columns: 1fr; } .task-review__sidebar { border-right: 0; border-bottom: 1px solid rgba(54, 159, 212, 0.25); } }
|
|
|
|
|
|
|
|
@media (max-width: 720px) { .task-review__filters { grid-template-columns: 1fr; } .task-review__content { padding: 22px 18px; } .task-review__student-head { flex-direction: column; } .task-review__scores { flex-wrap: wrap; white-space: normal; } }
|
|
|
|
|
|
|
|
</style>
|