|
|
|
@ -50,34 +50,21 @@
|
|
|
|
<el-table-column prop="username" label="学号" align="center" />
|
|
|
|
<el-table-column prop="username" label="学号" align="center" />
|
|
|
|
<el-table-column prop="className" label="班级" align="center" />
|
|
|
|
<el-table-column prop="className" label="班级" align="center" />
|
|
|
|
<el-table-column prop="score" label="参考成绩" align="center" />
|
|
|
|
<el-table-column prop="score" label="参考成绩" align="center" />
|
|
|
|
<el-table-column label="老师评分" align="center" min-width="190">
|
|
|
|
<el-table-column label="老师评分" align="center" min-width="130">
|
|
|
|
<template #default="{ row }">
|
|
|
|
<template #default="{ row }">
|
|
|
|
<div v-if="canManageSelectedClass" class="teacher-score-editor">
|
|
|
|
<div class="teacher-score-readonly">
|
|
|
|
<el-input-number
|
|
|
|
|
|
|
|
v-model="row.teacherScoreDraft"
|
|
|
|
|
|
|
|
:min="0"
|
|
|
|
|
|
|
|
:max="100"
|
|
|
|
|
|
|
|
:precision="2"
|
|
|
|
|
|
|
|
:controls="false"
|
|
|
|
|
|
|
|
placeholder="0-100"
|
|
|
|
|
|
|
|
:aria-label="`给${row.name || row.username}评分`"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<el-button
|
|
|
|
|
|
|
|
type="primary"
|
|
|
|
|
|
|
|
size="small"
|
|
|
|
|
|
|
|
:loading="savingScoreUserId === row.userId"
|
|
|
|
|
|
|
|
@click="saveTeacherScore(row)"
|
|
|
|
|
|
|
|
>保存</el-button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div v-else class="teacher-score-readonly">
|
|
|
|
|
|
|
|
<strong>{{ formatTeacherScore(row.teacherScore) }}</strong>
|
|
|
|
<strong>{{ formatTeacherScore(row.teacherScore) }}</strong>
|
|
|
|
<small>只读</small>
|
|
|
|
<small>自动汇总</small>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column label="实训进度" align="center">
|
|
|
|
<el-table-column label="实训进度" align="center">
|
|
|
|
<template #default="{ row }">
|
|
|
|
<template #default="{ row }">
|
|
|
|
<span :title="trainingProgressTitle(row)">{{ formatTrainingProgress(row) }}</span>
|
|
|
|
<span :title="trainingProgressTitle(row)">
|
|
|
|
|
|
|
|
已完成 {{ row.completedTaskCount || 0 }}/{{ row.totalTaskCount || 0 }}
|
|
|
|
|
|
|
|
<small v-if="row.trainingProgress !== null && row.trainingProgress !== undefined">({{ formatTrainingProgress(row) }})</small>
|
|
|
|
|
|
|
|
<small v-else>(--)</small>
|
|
|
|
|
|
|
|
</span>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column label="成绩详情" align="center">
|
|
|
|
<el-table-column label="成绩详情" align="center">
|
|
|
|
@ -129,7 +116,7 @@
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-dialog>
|
|
|
|
</el-dialog>
|
|
|
|
<el-dialog v-model="scoreDetailsStart" title="成绩详情" width="50%" class="F_dialog teacher-dialog">
|
|
|
|
<el-dialog v-model="scoreDetailsStart" title="任务评阅" width="60%" class="F_dialog teacher-dialog">
|
|
|
|
<div class="score-detail-summary">
|
|
|
|
<div class="score-detail-summary">
|
|
|
|
<div class="score-detail-summary__item">
|
|
|
|
<div class="score-detail-summary__item">
|
|
|
|
<span>学生姓名</span>
|
|
|
|
<span>学生姓名</span>
|
|
|
|
@ -162,6 +149,23 @@
|
|
|
|
</template>
|
|
|
|
</template>
|
|
|
|
</el-table-column>
|
|
|
|
</el-table-column>
|
|
|
|
<el-table-column prop="aiAssessmentScore" label="AI评测成绩" align="center" />
|
|
|
|
<el-table-column prop="aiAssessmentScore" label="AI评测成绩" align="center" />
|
|
|
|
|
|
|
|
<el-table-column label="教师评分" align="center" min-width="190">
|
|
|
|
|
|
|
|
<template #default="{ row }">
|
|
|
|
|
|
|
|
<div v-if="canManageSelectedClass && canScoreTask(row)" class="task-score-editor">
|
|
|
|
|
|
|
|
<el-input-number
|
|
|
|
|
|
|
|
v-model="row.teacherScoreDraft"
|
|
|
|
|
|
|
|
:min="0"
|
|
|
|
|
|
|
|
:max="100"
|
|
|
|
|
|
|
|
:precision="2"
|
|
|
|
|
|
|
|
:controls="false"
|
|
|
|
|
|
|
|
placeholder="0-100"
|
|
|
|
|
|
|
|
:aria-label="`给${row.taskName}评分`"
|
|
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
<el-button type="primary" size="small" :loading="savingTaskId === row.taskId" @click="saveTaskTeacherScore(row)">保存</el-button>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<span v-else>{{ formatTeacherScore(row.teacherScore) }}</span>
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
</el-table-column>
|
|
|
|
</el-table>
|
|
|
|
</el-table>
|
|
|
|
</el-dialog>
|
|
|
|
</el-dialog>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
@ -174,7 +178,7 @@ const userStore = useUserStore();
|
|
|
|
const currentUserId = computed(() => String(userStore.userInfo.userId || ""));
|
|
|
|
const currentUserId = computed(() => String(userStore.userInfo.userId || ""));
|
|
|
|
const workingCapitalList = ref([]);
|
|
|
|
const workingCapitalList = ref([]);
|
|
|
|
const loading = ref(false);
|
|
|
|
const loading = ref(false);
|
|
|
|
const savingScoreUserId = ref("");
|
|
|
|
const savingTaskId = ref("");
|
|
|
|
const classList = ref([]);
|
|
|
|
const classList = ref([]);
|
|
|
|
const teachingClassList = computed(() => classList.value.filter((item) => item.classType === "TEACHING"));
|
|
|
|
const teachingClassList = computed(() => classList.value.filter((item) => item.classType === "TEACHING"));
|
|
|
|
const scoreParams = ref({
|
|
|
|
const scoreParams = ref({
|
|
|
|
@ -221,7 +225,7 @@ const formatScore = (score) => {
|
|
|
|
return Number.isFinite(value) ? value.toFixed(2).replace(/\.00$/, "") : "--";
|
|
|
|
return Number.isFinite(value) ? value.toFixed(2).replace(/\.00$/, "") : "--";
|
|
|
|
};
|
|
|
|
};
|
|
|
|
const formatTeacherScore = (score) => score === null || score === undefined || score === ""
|
|
|
|
const formatTeacherScore = (score) => score === null || score === undefined || score === ""
|
|
|
|
? "未评分"
|
|
|
|
? "待评阅"
|
|
|
|
: formatScore(score);
|
|
|
|
: formatScore(score);
|
|
|
|
const weightFields = ["foundationWeight", "marketInsightWeight", "planningDesignWeight", "developmentValidationWeight", "launchOperationWeight", "comprehensiveTrainingWeight"];
|
|
|
|
const weightFields = ["foundationWeight", "marketInsightWeight", "planningDesignWeight", "developmentValidationWeight", "launchOperationWeight", "comprehensiveTrainingWeight"];
|
|
|
|
const rules = Object.fromEntries(weightFields.map((field) => [field, [
|
|
|
|
const rules = Object.fromEntries(weightFields.map((field) => [field, [
|
|
|
|
@ -239,16 +243,11 @@ const getList = () => {
|
|
|
|
if (!scoreParams.value.classId) {
|
|
|
|
if (!scoreParams.value.classId) {
|
|
|
|
workingCapitalList.value = [];
|
|
|
|
workingCapitalList.value = [];
|
|
|
|
workingCapitalTotal.value = 0;
|
|
|
|
workingCapitalTotal.value = 0;
|
|
|
|
return;
|
|
|
|
return Promise.resolve();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
loading.value = true;
|
|
|
|
loading.value = true;
|
|
|
|
indexApi.getScoreList(buildScoreQuery()).then((res) => {
|
|
|
|
return indexApi.getScoreList(buildScoreQuery()).then((res) => {
|
|
|
|
workingCapitalList.value = (res.data.list || []).map((row) => ({
|
|
|
|
workingCapitalList.value = res.data.list || [];
|
|
|
|
...row,
|
|
|
|
|
|
|
|
teacherScoreDraft: row.teacherScore === null || row.teacherScore === undefined
|
|
|
|
|
|
|
|
? null
|
|
|
|
|
|
|
|
: Number(row.teacherScore),
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
workingCapitalTotal.value = res.data.total;
|
|
|
|
workingCapitalTotal.value = res.data.total;
|
|
|
|
}).finally(() => (loading.value = false));
|
|
|
|
}).finally(() => (loading.value = false));
|
|
|
|
};
|
|
|
|
};
|
|
|
|
@ -327,34 +326,48 @@ const getClassScoreDetail = (row) => {
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
scoreDetailStudent.value = {
|
|
|
|
scoreDetailStudent.value = {
|
|
|
|
|
|
|
|
userId: row.userId,
|
|
|
|
name: row.name,
|
|
|
|
name: row.name,
|
|
|
|
aiTotalScore: row.score,
|
|
|
|
aiTotalScore: row.score,
|
|
|
|
teacherScore: row.teacherScore,
|
|
|
|
teacherScore: row.teacherScore,
|
|
|
|
};
|
|
|
|
};
|
|
|
|
indexApi.getScoreDetail({ userId: row.userId, classId: scoreParams.value.classId }).then((res) => {
|
|
|
|
indexApi.getScoreDetail({ userId: row.userId, classId: scoreParams.value.classId }).then((res) => {
|
|
|
|
tableData.value = res.data;
|
|
|
|
tableData.value = (res.data || []).map((item) => ({
|
|
|
|
scoreDetailsStart.value = true;
|
|
|
|
...item,
|
|
|
|
});
|
|
|
|
teacherScoreDraft: item.teacherScore === null || item.teacherScore === undefined
|
|
|
|
|
|
|
|
? null
|
|
|
|
|
|
|
|
: Number(item.teacherScore),
|
|
|
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
scoreDetailsStart.value = true;
|
|
|
|
|
|
|
|
});
|
|
|
|
};
|
|
|
|
};
|
|
|
|
const saveTeacherScore = (row) => {
|
|
|
|
const canScoreTask = (row) => Boolean(row.submitted) || row.progressStatus === "COMPLETED";
|
|
|
|
|
|
|
|
const saveTaskTeacherScore = (row) => {
|
|
|
|
if (row.teacherScoreDraft === null || row.teacherScoreDraft === undefined || row.teacherScoreDraft === "") {
|
|
|
|
if (row.teacherScoreDraft === null || row.teacherScoreDraft === undefined || row.teacherScoreDraft === "") {
|
|
|
|
proxy.$message.error("请输入老师评分");
|
|
|
|
proxy.$message.error("请输入教师评分");
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
const score = Number(row.teacherScoreDraft);
|
|
|
|
const score = Number(row.teacherScoreDraft);
|
|
|
|
if (!Number.isFinite(score) || score < 0 || score > 100) {
|
|
|
|
if (!Number.isFinite(score) || score < 0 || score > 100) {
|
|
|
|
proxy.$message.error("老师评分必须为0至100之间的数值");
|
|
|
|
proxy.$message.error("教师评分必须为0至100之间的数值");
|
|
|
|
return;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
savingScoreUserId.value = row.userId;
|
|
|
|
savingTaskId.value = row.taskId;
|
|
|
|
indexApi.saveTeacherScore({
|
|
|
|
indexApi.saveTaskTeacherScore({
|
|
|
|
classId: scoreParams.value.classId,
|
|
|
|
classId: scoreParams.value.classId,
|
|
|
|
studentUserId: row.userId,
|
|
|
|
studentUserId: scoreDetailStudent.value.userId,
|
|
|
|
|
|
|
|
taskId: row.taskId,
|
|
|
|
teacherScore: score,
|
|
|
|
teacherScore: score,
|
|
|
|
}).then(() => {
|
|
|
|
}).then(() => {
|
|
|
|
row.teacherScore = score;
|
|
|
|
row.teacherScore = score;
|
|
|
|
proxy.$message.success("老师评分保存成功");
|
|
|
|
proxy.$message.success("任务评分保存成功");
|
|
|
|
}).finally(() => (savingScoreUserId.value = ""));
|
|
|
|
return getList();
|
|
|
|
|
|
|
|
}).then(() => {
|
|
|
|
|
|
|
|
const latest = workingCapitalList.value.find((item) => item.userId === scoreDetailStudent.value.userId);
|
|
|
|
|
|
|
|
if (latest) {
|
|
|
|
|
|
|
|
scoreDetailStudent.value.teacherScore = latest.teacherScore;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}).finally(() => (savingTaskId.value = ""));
|
|
|
|
};
|
|
|
|
};
|
|
|
|
// 更新成绩
|
|
|
|
// 更新成绩
|
|
|
|
const updateScore = () => {
|
|
|
|
const updateScore = () => {
|
|
|
|
@ -384,7 +397,7 @@ onMounted(() => {
|
|
|
|
background: #ffffff;
|
|
|
|
background: #ffffff;
|
|
|
|
height: calc(100vh - 70px);
|
|
|
|
height: calc(100vh - 70px);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
.teacher-score-editor {
|
|
|
|
.task-score-editor {
|
|
|
|
display: flex;
|
|
|
|
display: flex;
|
|
|
|
align-items: center;
|
|
|
|
align-items: center;
|
|
|
|
justify-content: center;
|
|
|
|
justify-content: center;
|
|
|
|
|