refactor(admin): 评分入口收敛 + 大题小问得分可见(R30)
- 文案:「考生大题作答查证」提示由「作品评分」改为「大题评分」
(保存后将写入该考生「大题评分」,总分 = 客观题得分 + 大题评分)
- 删除「考生答题记录」的评分操作:操作列(评分按钮)+ 评分弹窗 +
examScoreDialog* / openExamScoreDialog / saveExamScore + .exam-score-form 样式
⇒ 大题评分统一由「考生大题作答查证」按小问给分,word_score 不再有两条口径
⛔ 刻意保留:adminApi.setScores(作品管理 batchSaveWorkScores 仍在用)
与后端 PUT /api/admin/scores/:group(10+ 门禁脚本依赖:verify_full_e2e /
exam_regression_test / verify_large_scale_17 / verify_agg_key_mismatch 等)
- 新增「小问得分」列(「大题得分」之后):后端 listAnswersForAdmin 本就返回
score1/2/3 ⇒ 纯前端改动、未动后端。新增 subScoresOf / hasSubScores / fmtSubScores;
hasSubScores 用于区分「未评分」与「三问都给 0 分」(二者不可都显示成 0)
验证:
- TDZ 顺序扫描:src 38 文件 + dist 打包产物 26 文件 全 PASS
- 真 chromium(playwright)ui-runtime-check.mjs:16/16 断言全过 / 致命错误 0 / 告警 0
(含 expectText "10 / 8 / 9" 直证小问得分列渲染;DOM dump 证实答题记录表头
已无「操作」列、大题查证表头含「小问得分」)
- dist 重建 built in 13.00s / VITE_EXIT=0;产物「作品评分」= 0,「小问得分」「大题评分」在
This commit is contained in:
@@ -828,11 +828,6 @@
|
|||||||
<span style="margin-left:10px;color:#1a3a5c;font-weight:600">总分:{{ row.totalScore ?? 0 }}</span>
|
<span style="margin-left:10px;color:#1a3a5c;font-weight:600">总分:{{ row.totalScore ?? 0 }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="操作" width="100" align="center">
|
|
||||||
<template #default="{ row }">
|
|
||||||
<el-button text type="warning" size="small" @click="openExamScoreDialog(row)">评分</el-button>
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column prop="submitTime" label="提交时间" width="160" align="center">
|
<el-table-column prop="submitTime" label="提交时间" width="160" align="center">
|
||||||
<template #default="{ row }">{{ row.submitTime ? fmtDate(row.submitTime) : '--' }}</template>
|
<template #default="{ row }">{{ row.submitTime ? fmtDate(row.submitTime) : '--' }}</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
@@ -848,29 +843,12 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 考生大题评分弹窗(客观分只读,大题得分可改) -->
|
<!-- R30:原「考生答题记录 · 评分」弹窗已删除。
|
||||||
<!-- R29:标签改「左对齐 + 定宽 120px」,修复「客观分(卷面)」被折行成
|
原因:大题评分统一在下方「考生大题作答查证」按小问给分
|
||||||
「客观分(卷」/「面)」造成的文字错位(测试人员反馈)。 -->
|
(PUT /api/admin/big-answers/:group/:uid/scores),
|
||||||
<el-dialog v-model="examScoreDialogVisible" title="评分" width="520px">
|
避免同一份 word_score 存在两条入口、出现口径不一致。
|
||||||
<el-form
|
⛔ 后端 PUT /api/admin/scores/:group 与 adminApi.setScores 均**保留**:
|
||||||
v-if="examScoreDialogRow"
|
前者被 10+ 个门禁脚本依赖,后者作品管理页(本科/教师)仍在使用。 -->
|
||||||
class="exam-score-form"
|
|
||||||
label-width="120px"
|
|
||||||
label-position="left"
|
|
||||||
>
|
|
||||||
<el-form-item label="选手">{{ examScoreDialogRow.userName }}</el-form-item>
|
|
||||||
<el-form-item label="客观分(卷面)">
|
|
||||||
<span class="exam-score-readonly">{{ examScoreDialogRow.examScore ?? 0 }} 分(系统自动评分,不可修改)</span>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="大题分">
|
|
||||||
<el-input-number v-model="examScoreDialogWord" :min="0" :max="100" :step="0.25" :precision="2" />
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
<template #footer>
|
|
||||||
<el-button @click="examScoreDialogVisible = false">取消</el-button>
|
|
||||||
<el-button type="primary" :loading="examScoreSaving" v-guard="() => saveExamScore()">保存评分</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- ========== 考生大题作答查证 ========== -->
|
<!-- ========== 考生大题作答查证 ========== -->
|
||||||
<el-divider />
|
<el-divider />
|
||||||
@@ -943,6 +921,14 @@
|
|||||||
<span v-else style="color:#909399">未评分</span>
|
<span v-else style="color:#909399">未评分</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
|
<!-- R30:三小问得分逐问展示(数据源 listAnswersForAdmin 的 score1/2/3,
|
||||||
|
故本列不需要任何后端改动)。按 bigSubCount 顺序以「/」分隔。 -->
|
||||||
|
<el-table-column label="小问得分" width="150" align="center">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span v-if="hasSubScores(row)" class="big-sub-scores">{{ fmtSubScores(row) }}</span>
|
||||||
|
<span v-else style="color:#909399">未评分</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
<el-table-column label="提交时间" width="170">
|
<el-table-column label="提交时间" width="170">
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<span v-if="row.submitTime">{{ formatDateTime(row.submitTime) }}</span>
|
<span v-if="row.submitTime">{{ formatDateTime(row.submitTime) }}</span>
|
||||||
@@ -1031,7 +1017,7 @@
|
|||||||
<div class="big-answer-total">
|
<div class="big-answer-total">
|
||||||
本大题合计:
|
本大题合计:
|
||||||
<strong>{{ fmtScore(bigScoreTotal) }}</strong> 分(满分 {{ bigScorePerSub * bigSubCount }} 分)
|
<strong>{{ fmtScore(bigScoreTotal) }}</strong> 分(满分 {{ bigScorePerSub * bigSubCount }} 分)
|
||||||
<span class="big-answer-tip">保存后将写入该考生「作品评分」,总分 = 客观题得分 + 作品评分</span>
|
<span class="big-answer-tip">保存后将写入该考生「大题评分」,总分 = 客观题得分 + 大题评分</span>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<el-alert v-else type="info" :closable="false" show-icon title="该考生尚未抽取综合大题(可能未进入考试)" />
|
<el-alert v-else type="info" :closable="false" show-icon title="该考生尚未抽取综合大题(可能未进入考试)" />
|
||||||
@@ -1921,6 +1907,31 @@ function fmtScore(v) {
|
|||||||
return Number.isInteger(n) ? String(n) : String(Math.round(n * 100) / 100)
|
return Number.isInteger(n) ? String(n) : String(Math.round(n * 100) / 100)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* R30:该考生各小问得分数组(按 bigSubCount 顺序)。
|
||||||
|
*
|
||||||
|
* 数据源 = 后端 listAnswersForAdmin 返回的 score1 / score2 / score3(未评分时为 null)。
|
||||||
|
* ⇒ 仅是「把已有数据显示出来」,**不需要任何后端改动**。
|
||||||
|
*/
|
||||||
|
function subScoresOf(row) {
|
||||||
|
const out = []
|
||||||
|
for (let i = 1; i <= bigSubCount.value; i++) out.push(row ? row[`score${i}`] : null)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否已有任一小问得分。
|
||||||
|
* 用于区分「尚未评分」(false) 与「三小问都给 0 分」(true) —— 二者不能都显示成 0 分。
|
||||||
|
*/
|
||||||
|
function hasSubScores(row) {
|
||||||
|
return subScoresOf(row).some(v => v !== null && v !== undefined && v !== '')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 「8 / 9.5 / 9.5」形式的小问得分展示(某一小问未给分则该项显示 '--') */
|
||||||
|
function fmtSubScores(row) {
|
||||||
|
return subScoresOf(row).map(v => fmtScore(v)).join(' / ')
|
||||||
|
}
|
||||||
|
|
||||||
const bigScoreTotal = computed(() => {
|
const bigScoreTotal = computed(() => {
|
||||||
let sum = 0
|
let sum = 0
|
||||||
for (let i = 1; i <= bigSubCount.value; i++) {
|
for (let i = 1; i <= bigSubCount.value; i++) {
|
||||||
@@ -2517,35 +2528,12 @@ function onExamSubsFilterChange() {
|
|||||||
examSubmissionsPage.value = 1
|
examSubmissionsPage.value = 1
|
||||||
}
|
}
|
||||||
|
|
||||||
// 考生答题评分弹窗
|
// R30:原「考生答题评分弹窗」的 state 与 openExamScoreDialog / saveExamScore 已整体移除。
|
||||||
const examScoreDialogVisible = ref(false)
|
// 大题评分改由「考生大题作答查证」按小问给分(adminApi.setBigAnswerScores
|
||||||
const examScoreDialogRow = ref(null)
|
// → service.bigQuestion.setSubScores → service.admin.updateScores),
|
||||||
const examScoreDialogWord = ref(0)
|
// 使 word_score 的**写入入口收敛为唯一一条**,不再出现两个口径。
|
||||||
const examScoreSaving = ref(false)
|
// ⚠️ adminApi.setScores 本身**保留**:作品管理页 batchSaveWorkScores(本科/教师)仍在使用;
|
||||||
function openExamScoreDialog(row) {
|
// 后端 PUT /api/admin/scores/:group 亦须保留(10+ 个门禁脚本依赖)。
|
||||||
examScoreDialogRow.value = row
|
|
||||||
examScoreDialogWord.value = row.wordScore ?? 0
|
|
||||||
examScoreDialogVisible.value = true
|
|
||||||
}
|
|
||||||
async function saveExamScore() {
|
|
||||||
if (!examScoreDialogRow.value) return
|
|
||||||
examScoreSaving.value = true
|
|
||||||
try {
|
|
||||||
// R29:组别跟随「成绩管理」页
|
|
||||||
await adminApi.setScores(scoreExportGroup.value, {
|
|
||||||
scores: [{
|
|
||||||
contestantId: examScoreDialogRow.value.contestantId,
|
|
||||||
// 客观分回传弹窗所见值,避免后端按答题记录重算导致与所见不一致
|
|
||||||
examScore: Number(examScoreDialogRow.value.examScore ?? 0),
|
|
||||||
wordScore: Number(examScoreDialogWord.value),
|
|
||||||
}],
|
|
||||||
})
|
|
||||||
ElMessage.success('评分已保存')
|
|
||||||
examScoreDialogVisible.value = false
|
|
||||||
// 客观分 / 大题分同时影响成绩明细表与考生答题记录,必须一起刷新
|
|
||||||
await refreshScorePageData(true)
|
|
||||||
} catch (e) { ElMessage.error('保存失败') } finally { examScoreSaving.value = false }
|
|
||||||
}
|
|
||||||
|
|
||||||
// 组别切换 → 刷新该组全部相关数据(唯一刷新入口)
|
// 组别切换 → 刷新该组全部相关数据(唯一刷新入口)
|
||||||
// 2026-09-23 修复:原仅刷新 fetchExamSubmissions(),题库 / 大题目录 / 大题作答
|
// 2026-09-23 修复:原仅刷新 fetchExamSubmissions(),题库 / 大题目录 / 大题作答
|
||||||
@@ -3550,14 +3538,7 @@ onMounted(() => {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* R29:评分弹窗标签左对齐且禁止折行(「客观分(卷面)」曾折行成两行导致错位) */
|
/* R30:`.exam-score-form` 相关样式已随「考生答题记录 · 评分」弹窗一并删除 */
|
||||||
.exam-score-form .el-form-item__label {
|
|
||||||
text-align: left;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
.exam-score-form .exam-score-readonly {
|
|
||||||
color: #606266;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-input { width: 220px; }
|
.search-input { width: 220px; }
|
||||||
.filter-select { width: 140px; }
|
.filter-select { width: 140px; }
|
||||||
@@ -4119,6 +4100,13 @@ onMounted(() => {
|
|||||||
color: #909399;
|
color: #909399;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* R30:「考生大题作答查证」列表的「小问得分」列(如 8 / 9.5 / 9.5)。
|
||||||
|
使用等宽数字,避免切组/刷新时列宽抖动。 */
|
||||||
|
.big-sub-scores {
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
/* ==================== 评分输入 ==================== */
|
/* ==================== 评分输入 ==================== */
|
||||||
.score-input {
|
.score-input {
|
||||||
width: 80px;
|
width: 80px;
|
||||||
|
|||||||
Reference in New Issue
Block a user