R20:Admin.vue —— 页面状态刷新修复 + 大题导出 Word + 评分一位小数
C 页面状态刷新(修复测试人员反馈的「切组 / 评分后需手动刷新」): - onQuestionTypeChange:移除 `bigQuestions.length === 0` 守卫(切组后旧数组仍非空 ⇒ 永不重新拉取,界面停留在上一组数据) - saveBigScores / saveExamScore:原来各只刷新一侧列表(评分后「查看」侧仍是旧值) - 收敛为唯一刷新入口 refreshQuestionData(),onMenuSelect / watch(questionGroup) / 评分收尾 / 切组 全部走它 - 新增 ensureBigData():按「数据属于哪个组」(bigDataGroup) 判定是否拉取 - fetchBigQuestions / fetchBigAnswers:固定发起时的组别,响应回来若组别已变则丢弃 (防切组瞬间旧响应串组显示) B 大题评分一位小数: - 3 处 el-input-number 加 :step="0.5" :precision="1" - 新增 fmtScore():整数不带小数点(27),非整数保留一位(27.4),空/非法回 '--' - bigScoreTotal 收敛到一位小数(防 0.1+0.2 浮点脏值显示) D 大题导出 Word: - 工具栏新增「导出选中Word(N)」「导出全部Word」 - 大题表加多选(reserve-selection)+ 选中态跟踪 - exportBigQuestions():走 safeDownload(已含 JSON 错误分支,避免把错误信封存成 .docx);过滤不属于当前组别的陈旧 ID 计划外修复: - exportBigQuestions 边界:选中 ID 全失效时原会因 qs 为空而静默退化成「导出全部」, 与用户意图相反;已改为提示而不导出 验证:VITE_EXIT=0(多次构建,Admin chunk hash 已更新)。 未 push:须先上线验证通过再单独授权。
This commit is contained in:
@@ -206,12 +206,7 @@
|
||||
<template v-if="activeQuestionType !== 'big'">
|
||||
<el-input v-model="questionSearch" placeholder="搜索题目内容" clearable size="small" class="search-input" @input="onQuestionSearchChange" />
|
||||
<el-button type="primary" @click="openQuestionDialog(null, activeQuestionType)">添加题目</el-button>
|
||||
<el-divider direction="vertical" />
|
||||
<span class="batch-score-label">批量设分:</span>
|
||||
<span class="batch-score-item">选择题<el-input-number v-model="batchChoiceScore" :min="1" :max="100" size="small" controls-position="right" /></span>
|
||||
<el-button size="small" v-guard="() => applyTypeScore('choice')">应用</el-button>
|
||||
<span class="batch-score-item">判断题<el-input-number v-model="batchJudgeScore" :min="1" :max="100" size="small" controls-position="right" /></span>
|
||||
<el-button size="small" v-guard="() => applyTypeScore('judge')">应用</el-button>
|
||||
<el-tag type="info" size="small" class="score-policy-tag">客观题分值口径:每小题固定 1 分</el-tag>
|
||||
<el-divider direction="vertical" />
|
||||
<el-button size="small" v-guard="() => downloadQuestionTpl()">下载Excel模板</el-button>
|
||||
<el-button size="small" type="success" v-guard="() => triggerExcelImport()">Excel批量导入</el-button>
|
||||
@@ -225,6 +220,14 @@
|
||||
<el-input v-model="bigSearch" placeholder="搜索大题标题/题干" clearable size="small" class="search-input" />
|
||||
<el-button type="primary" v-guard="() => openBigDialog(null)">新增大题</el-button>
|
||||
<el-button size="small" :loading="bigQuestionsLoading" @click="fetchBigQuestions">刷新</el-button>
|
||||
<el-button
|
||||
size="small"
|
||||
type="warning"
|
||||
:disabled="bigSelectedIds.length === 0"
|
||||
:loading="bigExporting"
|
||||
v-guard="() => exportBigQuestions(bigSelectedIds)"
|
||||
>导出选中Word({{ bigSelectedIds.length }})</el-button>
|
||||
<el-button size="small" type="success" :loading="bigExporting" v-guard="() => exportBigQuestions()">导出全部Word</el-button>
|
||||
<el-tag type="info" class="total-score-tag">
|
||||
共 {{ bigQuestions.length }} 道大题(每道 {{ bigSubCount }} 小问 × {{ bigScorePerSub }} 分 = {{ bigScorePerSub * bigSubCount }} 分)
|
||||
</el-tag>
|
||||
@@ -305,7 +308,17 @@
|
||||
</div>
|
||||
|
||||
<!-- 综合大题题库表格 -->
|
||||
<el-table v-if="activeQuestionType === 'big'" :data="pagedBigQuestions" stripe v-loading="bigQuestionsLoading" class="admin-table">
|
||||
<el-table
|
||||
v-if="activeQuestionType === 'big'"
|
||||
ref="bigTableRef"
|
||||
:data="pagedBigQuestions"
|
||||
row-key="id"
|
||||
stripe
|
||||
v-loading="bigQuestionsLoading"
|
||||
class="admin-table"
|
||||
@selection-change="onBigSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="45" align="center" :reserve-selection="true" />
|
||||
<el-table-column type="index" width="50" label="#" :index="(idx) => (bigPage - 1) * bigPageSize + idx + 1" align="center" />
|
||||
<el-table-column prop="title" label="大题标题" min-width="180" show-overflow-tooltip />
|
||||
<el-table-column label="题干(富文本,含表格)" min-width="260">
|
||||
@@ -396,7 +409,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column label="大题得分" width="110" align="center">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.bigScore !== null && row.bigScore !== undefined">{{ row.bigScore }} 分</span>
|
||||
<span v-if="row.bigScore !== null && row.bigScore !== undefined">{{ fmtScore(row.bigScore) }} 分</span>
|
||||
<span v-else style="color:#909399">未评分</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
@@ -432,7 +445,7 @@
|
||||
<el-input v-model="questionForm.content" type="textarea" :rows="3" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分值">
|
||||
<el-input-number v-model="questionForm.score" :min="1" :max="100" />
|
||||
<el-input-number v-model="questionForm.score" :min="1" :max="1" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="选项">
|
||||
<div class="choice-options">
|
||||
@@ -465,7 +478,7 @@
|
||||
<el-input v-model="questionForm.content" type="textarea" :rows="3" />
|
||||
</el-form-item>
|
||||
<el-form-item label="分值">
|
||||
<el-input-number v-model="questionForm.score" :min="1" :max="100" />
|
||||
<el-input-number v-model="questionForm.score" :min="1" :max="1" disabled />
|
||||
</el-form-item>
|
||||
<el-form-item label="正确答案">
|
||||
<el-radio-group v-model="questionForm.answer">
|
||||
@@ -596,6 +609,8 @@
|
||||
v-model="bigScoreForm[`score_${i}`]"
|
||||
:min="0"
|
||||
:max="bigScorePerSub"
|
||||
:step="0.5"
|
||||
:precision="1"
|
||||
size="small"
|
||||
controls-position="right"
|
||||
/>
|
||||
@@ -605,7 +620,7 @@
|
||||
|
||||
<div class="big-answer-total">
|
||||
本大题合计:
|
||||
<strong>{{ bigScoreTotal }}</strong> 分(满分 {{ bigScorePerSub * bigSubCount }} 分)
|
||||
<strong>{{ fmtScore(bigScoreTotal) }}</strong> 分(满分 {{ bigScorePerSub * bigSubCount }} 分)
|
||||
<span class="big-answer-tip">保存后将写入该考生「作品评分」,总分 = 客观题得分 + 作品评分</span>
|
||||
</div>
|
||||
</template>
|
||||
@@ -686,7 +701,7 @@
|
||||
<span>{{ examScoreDialogRow.examScore ?? 0 }} 分(系统自动评分,不可修改)</span>
|
||||
</el-form-item>
|
||||
<el-form-item label="大题分">
|
||||
<el-input-number v-model="examScoreDialogWord" :min="0" :max="100" />
|
||||
<el-input-number v-model="examScoreDialogWord" :min="0" :max="100" :step="0.5" :precision="1" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
@@ -776,7 +791,7 @@
|
||||
<el-button text type="primary" size="small" v-guard="() => downloadWork(row, f)">下载</el-button>
|
||||
<span class="score-inline">
|
||||
评分
|
||||
<el-input-number v-model="f._score" :min="0" :max="100" size="small" controls-position="right" class="score-input" />
|
||||
<el-input-number v-model="f._score" :min="0" :max="100" :step="0.5" :precision="1" size="small" controls-position="right" class="score-input" />
|
||||
</span>
|
||||
</div>
|
||||
<span v-if="!row.files || row.files.length === 0" style="color:#909399">未提交</span>
|
||||
@@ -1259,13 +1274,8 @@ function onMenuSelect(index) {
|
||||
if (index === 'dashboard') fetchStats()
|
||||
if (index === 'accounts') fetchAccounts()
|
||||
if (index === 'questions' && questionGroup.value) {
|
||||
fetchQuestions()
|
||||
fetchExamSubmissions()
|
||||
// 综合大题页签下同步拉取题库与作答查证数据
|
||||
if (activeQuestionType.value === 'big') {
|
||||
fetchBigQuestions()
|
||||
fetchBigAnswers()
|
||||
}
|
||||
// 单一刷新入口:题库 + 考生答题记录 +(大题库 / 大题作答)
|
||||
refreshQuestionData(true)
|
||||
}
|
||||
if (index === 'materials') fetchAdminMaterials()
|
||||
if (index === 'submissions' && submissionGroup.value) fetchSubmissions()
|
||||
@@ -1468,7 +1478,9 @@ const judgePageSize = ref(10)
|
||||
const questionDialogVisible = ref(false)
|
||||
const questionSaving = ref(false)
|
||||
const editingQuestion = ref(null)
|
||||
const questionForm = reactive({ group: 'zhongzhi', content: '', score: 5, optionA: '', optionB: '', optionC: '', optionD: '', answer: '', description: '' })
|
||||
/** 客观题(选择/判断)每小题固定分值 —— 与后端 service/admin.js 的 OBJECTIVE_SCORE_PER_QUESTION 同源 */
|
||||
const OBJECTIVE_SCORE = 1
|
||||
const questionForm = reactive({ group: 'zhongzhi', content: '', score: OBJECTIVE_SCORE, optionA: '', optionB: '', optionC: '', optionD: '', answer: '', description: '' })
|
||||
|
||||
const questionTypeLabel = computed(() => {
|
||||
const map = { choice: '选择题', judge: '判断题', big: '综合大题' }
|
||||
@@ -1583,12 +1595,24 @@ const bigRowScorePerSub = computed(() => {
|
||||
return v > 0 ? v : bigScorePerSub.value
|
||||
})
|
||||
|
||||
/**
|
||||
* 分数显示格式化(2026-09-23):人工评分允许小数点后一位。
|
||||
* 整数不带小数点(27 而非 27.0),非整数保留一位(27.4);空/非法值回 '--'。
|
||||
*/
|
||||
function fmtScore(v) {
|
||||
if (v === null || v === undefined || v === '') return '--'
|
||||
const n = Number(v)
|
||||
if (!Number.isFinite(n)) return '--'
|
||||
return Number.isInteger(n) ? String(n) : String(Math.round(n * 10) / 10)
|
||||
}
|
||||
|
||||
const bigScoreTotal = computed(() => {
|
||||
let sum = 0
|
||||
for (let i = 1; i <= bigSubCount.value; i++) {
|
||||
sum += Number(bigScoreForm[`score_${i}`]) || 0
|
||||
}
|
||||
return sum
|
||||
// 浮点求和收敛到一位小数,避免 0.1+0.2=0.30000000000000004 之类的脏值显示
|
||||
return Math.round(sum * 10) / 10
|
||||
})
|
||||
|
||||
/** 剥标签取纯文本(列表内搜索用,不用于渲染) */
|
||||
@@ -1611,10 +1635,13 @@ function formatDateTime(v) {
|
||||
}
|
||||
|
||||
async function fetchBigQuestions() {
|
||||
// 固定「发起请求时的组别」:切组瞬间的旧响应回来时不得写回,否则会串组显示别组题目
|
||||
const g = questionGroup.value
|
||||
bigQuestionsLoading.value = true
|
||||
try {
|
||||
const data = await adminApi.getBigQuestions(questionGroup.value)
|
||||
const data = await adminApi.getBigQuestions(g)
|
||||
const result = data?.data || data
|
||||
if (questionGroup.value !== g) return
|
||||
bigQuestions.value = result?.list || []
|
||||
if (Number(result?.subCount) > 0) bigSubCount.value = Number(result.subCount)
|
||||
if (Number(result?.scorePerSub) > 0) bigScorePerSub.value = Number(result.scorePerSub)
|
||||
@@ -1623,7 +1650,7 @@ async function fetchBigQuestions() {
|
||||
if (bigPage.value > maxPage) bigPage.value = maxPage
|
||||
} catch (e) {
|
||||
console.warn('获取综合大题失败', e)
|
||||
bigQuestions.value = []
|
||||
if (questionGroup.value === g) bigQuestions.value = []
|
||||
} finally {
|
||||
bigQuestionsLoading.value = false
|
||||
}
|
||||
@@ -1705,6 +1732,8 @@ async function deleteBigQuestion(row) {
|
||||
await adminApi.deleteBigQuestion(questionGroup.value, row.id)
|
||||
ElMessage.success('大题已删除')
|
||||
await fetchBigQuestions()
|
||||
// 已删除的行可能仍被勾选(reserve-selection 会保留),导出前清掉避免请求到已删 ID
|
||||
clearBigSelection()
|
||||
} catch (e) {
|
||||
// 409:该题已被抽取,服务端拒绝删除;给出可执行的处理建议
|
||||
ElMessage.error(e?.message || '删除失败')
|
||||
@@ -1719,16 +1748,20 @@ function openBigPreview(row) {
|
||||
}
|
||||
|
||||
async function fetchBigAnswers() {
|
||||
// 同 fetchBigQuestions:固定发起时的组别,防止切组竞态把别组数据写回
|
||||
const g = questionGroup.value
|
||||
bigAnswersLoading.value = true
|
||||
try {
|
||||
const data = await adminApi.getBigAnswers(questionGroup.value)
|
||||
const data = await adminApi.getBigAnswers(g)
|
||||
const result = data?.data || data
|
||||
if (questionGroup.value !== g) return
|
||||
bigAnswers.value = result?.list || []
|
||||
bigAnswersTotal.value = Number(result?.total) || bigAnswers.value.length
|
||||
bigAnswersDrawn.value = Number(result?.drawnCount) || 0
|
||||
if (Number(result?.subCount) > 0) bigSubCount.value = Number(result.subCount)
|
||||
} catch (e) {
|
||||
console.warn('获取大题作答失败', e)
|
||||
if (questionGroup.value !== g) return
|
||||
bigAnswers.value = []
|
||||
bigAnswersTotal.value = 0
|
||||
bigAnswersDrawn.value = 0
|
||||
@@ -1737,6 +1770,95 @@ async function fetchBigAnswers() {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 「大题库 / 大题作答」数据当前所属组别(2026-09-23)。
|
||||
*
|
||||
* 修复「切中职/高职后必须刷新页面才显示新数据」:
|
||||
* 原实现用 `bigQuestions.length === 0` 作为「要不要拉」的判据,但切组时
|
||||
* 上一组的数据仍在数组里(长度 ≠ 0)⇒ 永远不会重新拉取。
|
||||
* 改为按「数据属于哪个组」判定,而不是按「数组是否为空」。
|
||||
*/
|
||||
const bigDataGroup = ref('')
|
||||
|
||||
/** 保证大题库 + 大题作答与当前组别一致(同组且非强制时不重复请求) */
|
||||
async function ensureBigData(force = false) {
|
||||
const g = questionGroup.value
|
||||
if (!force && bigDataGroup.value === g && bigQuestions.value.length > 0) return
|
||||
await Promise.allSettled([fetchBigQuestions(), fetchBigAnswers()])
|
||||
if (questionGroup.value === g) bigDataGroup.value = g
|
||||
}
|
||||
|
||||
/**
|
||||
* 刷新「题目管理」菜单下与组别相关的全部数据(2026-09-23)。
|
||||
*
|
||||
* 原先各入口各刷一半(切组只刷题库、评分只刷大题作答),导致另一处仍是旧值,
|
||||
* 测试人员只能手动刷新整页。此处收敛为**唯一刷新入口**,避免再出现遗漏。
|
||||
*/
|
||||
async function refreshQuestionData(force = false) {
|
||||
await Promise.allSettled([
|
||||
fetchQuestions(),
|
||||
fetchExamSubmissions(),
|
||||
ensureBigData(force),
|
||||
])
|
||||
}
|
||||
|
||||
/** ==================== 大题导出 Word(2026-09-23) ==================== */
|
||||
|
||||
const bigTableRef = ref(null)
|
||||
/** 当前勾选的大题 ID(跨页保留;切组时必须清空 —— ID 是组内作用域) */
|
||||
const bigSelectedIds = ref([])
|
||||
const bigExporting = ref(false)
|
||||
|
||||
function onBigSelectionChange(rows) {
|
||||
bigSelectedIds.value = (rows || [])
|
||||
.map(r => Number(r && r.id))
|
||||
.filter(v => Number.isInteger(v) && v > 0)
|
||||
}
|
||||
|
||||
/** 清空勾选(同时清 el-table 内部状态,否则切组后旧勾选仍显示为选中) */
|
||||
function clearBigSelection() {
|
||||
const t = bigTableRef.value
|
||||
if (t && typeof t.clearSelection === 'function') t.clearSelection()
|
||||
bigSelectedIds.value = []
|
||||
}
|
||||
|
||||
/**
|
||||
* 导出大题为 Word。
|
||||
* · 不传 ids ⇒ 导出该组**全部**
|
||||
* · 传数组 ⇒ 只导出数组内、且确属当前组别的题目(陈旧 ID 被过滤);
|
||||
* 数组为空或全部失效 ⇒ 提示而不导出(⛔ 不可退化成「导出全部」)
|
||||
* · 1 道 ⇒ 直接 .docx;多道 ⇒ .zip(内含每道题一个 .docx)
|
||||
* 文件名由后端 Content-Disposition 下发(RFC5987 已由 koa 处理),此处只给兜底名。
|
||||
*/
|
||||
async function exportBigQuestions(ids) {
|
||||
const groupLabel = questionGroup.value === 'zhongzhi' ? '中职组' : '高职组'
|
||||
// 区分「导出全部」与「导出选中」:前者不传参,后者传数组。
|
||||
// 若不区分,则「选中的题全被过滤掉」时 qs 为空 ⇒ 静默退化成导出全部,与用户意图相反。
|
||||
const isAll = !Array.isArray(ids)
|
||||
// 兜底:把「不属于当前组别列表」的残留 ID 过滤掉(删除后前端的勾选可能滞后一拍)
|
||||
const available = new Set(bigQuestions.value.map(q => Number(q.id)))
|
||||
const idsArr = Array.isArray(ids) ? ids : []
|
||||
const list = idsArr
|
||||
.map(v => Number(v))
|
||||
.filter(v => Number.isInteger(v) && v > 0 && available.has(v))
|
||||
if (!isAll && list.length === 0) {
|
||||
ElMessage.warning('选中的题目已失效(可能已被删除),请刷新后重试')
|
||||
return
|
||||
}
|
||||
const qs = list.length ? `?ids=${list.join(',')}` : ''
|
||||
bigExporting.value = true
|
||||
try {
|
||||
await safeDownload(`/api/admin/big-questions/${questionGroup.value}/export${qs}`, {
|
||||
fallbackName: list.length === 1
|
||||
? `${groupLabel}_综合大题.docx`
|
||||
: `${groupLabel}_综合大题.zip`,
|
||||
onSuccess: name => ElMessage.success(`已开始下载:${name}`),
|
||||
})
|
||||
} finally {
|
||||
bigExporting.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 详情弹窗内第 i 小问的考生作答原文(空值统一为 '') */
|
||||
function bigAnswerTextOf(i) {
|
||||
const v = bigAnswerRow.value['answer' + i]
|
||||
@@ -1772,7 +1894,9 @@ async function saveBigScores() {
|
||||
})
|
||||
ElMessage.success(`评分已保存(大题合计 ${res?.total ?? bigScoreTotal.value} 分)`)
|
||||
bigAnswerDialogVisible.value = false
|
||||
await fetchBigAnswers()
|
||||
// 一次刷新全部相关列表:大题作答(含大题得分列)+ 考生答题记录(卷面分/大题分)。
|
||||
// 原实现只调 fetchBigAnswers(),切到「考生答题记录」仍是旧分数 ⇒ 测试人员只能刷整页。
|
||||
await refreshQuestionData(true)
|
||||
} catch (e) {
|
||||
ElMessage.error(e?.message || '评分保存失败')
|
||||
} finally {
|
||||
@@ -1781,23 +1905,17 @@ async function saveBigScores() {
|
||||
}
|
||||
|
||||
function onQuestionGroupChange() {
|
||||
// 仅重置分页与勾选;数据刷新统一交给 watch(questionGroup),避免两处各刷一半造成遗漏
|
||||
choicePage.value = 1
|
||||
judgePage.value = 1
|
||||
bigPage.value = 1
|
||||
fetchQuestions()
|
||||
if (activeQuestionType.value === 'big') {
|
||||
fetchBigQuestions()
|
||||
fetchBigAnswers()
|
||||
}
|
||||
if (activeMenu.value === 'questions') fetchExamSubmissions()
|
||||
clearBigSelection()
|
||||
}
|
||||
|
||||
function onQuestionTypeChange(name) {
|
||||
// 切到综合大题时按需拉取(切换 Tab 不重复请求)
|
||||
if (name === 'big') {
|
||||
if (bigQuestions.value.length === 0) fetchBigQuestions()
|
||||
if (bigAnswers.value.length === 0) fetchBigAnswers()
|
||||
}
|
||||
// 切到综合大题时按「该数据属于哪个组」判定是否拉取
|
||||
// (原按 bigQuestions.length === 0 判定:切组后旧数据仍在数组里 ⇒ 永不刷新)
|
||||
if (name === 'big') ensureBigData()
|
||||
}
|
||||
|
||||
function onQuestionSearchChange() {
|
||||
@@ -1826,7 +1944,7 @@ function openQuestionDialog(row = null, qType = activeQuestionType.value) {
|
||||
|
||||
if (row) {
|
||||
questionForm.group = questionGroup.value
|
||||
questionForm.score = row.score || 5
|
||||
questionForm.score = OBJECTIVE_SCORE
|
||||
|
||||
if (qType === 'choice') {
|
||||
questionForm.content = row.question || ''
|
||||
@@ -1843,7 +1961,7 @@ function openQuestionDialog(row = null, qType = activeQuestionType.value) {
|
||||
} else {
|
||||
questionForm.group = questionGroup.value
|
||||
questionForm.content = ''
|
||||
questionForm.score = 5
|
||||
questionForm.score = OBJECTIVE_SCORE
|
||||
questionForm.optionA = ''
|
||||
questionForm.optionB = ''
|
||||
questionForm.optionC = ''
|
||||
@@ -1922,9 +2040,8 @@ async function batchDeleteSelectedQuestions() {
|
||||
} catch (e) { /* 取消 */ }
|
||||
}
|
||||
|
||||
// ==================== 客观题批量设分 / Excel 导入 / 模板 ====================
|
||||
const batchChoiceScore = ref(5)
|
||||
const batchJudgeScore = ref(5)
|
||||
// ==================== 客观题 Excel 导入 / 模板 ====================
|
||||
// 注:原「批量设分」已移除 —— 客观题分值口径统一为 1 分,不再可设。
|
||||
const excelFileInput = ref(null)
|
||||
|
||||
// 客观题总分预览:当前组选择题 + 判断题分值之和
|
||||
@@ -1933,15 +2050,6 @@ const objectiveTotalScore = computed(() => {
|
||||
return sum(choiceQuestions.value) + sum(judgeQuestions.value)
|
||||
})
|
||||
|
||||
async function applyTypeScore(qType) {
|
||||
const score = qType === 'choice' ? batchChoiceScore.value : batchJudgeScore.value
|
||||
try {
|
||||
await adminApi.setQuestions(questionGroup.value, qType, { action: 'setTypeScore', score })
|
||||
ElMessage.success(`已将${qType === 'choice' ? '选择题' : '判断题'}统一设为 ${score} 分`)
|
||||
fetchQuestions()
|
||||
} catch (e) { ElMessage.error('批量设分失败') }
|
||||
}
|
||||
|
||||
function triggerExcelImport() {
|
||||
if (excelFileInput.value) excelFileInput.value.click()
|
||||
}
|
||||
@@ -2045,15 +2153,16 @@ async function saveExamScore() {
|
||||
})
|
||||
ElMessage.success('评分已保存')
|
||||
examScoreDialogVisible.value = false
|
||||
fetchExamSubmissions()
|
||||
// 同上:客观分 / 大题分同时影响两张表,必须一起刷新(原仅刷考生答题记录)
|
||||
await refreshQuestionData(true)
|
||||
} catch (e) { ElMessage.error('保存失败') } finally { examScoreSaving.value = false }
|
||||
}
|
||||
|
||||
// 监听问题组别切换时自动刷新答题记录
|
||||
// 组别切换 → 刷新该组全部相关数据(唯一刷新入口)
|
||||
// 2026-09-23 修复:原仅刷新 fetchExamSubmissions(),题库 / 大题目录 / 大题作答
|
||||
// 仍停留在上一组数据,导致测试人员必须手动刷新整页才能看到新组内容。
|
||||
watch(questionGroup, () => {
|
||||
if (activeMenu.value === 'questions') {
|
||||
fetchExamSubmissions()
|
||||
}
|
||||
if (activeMenu.value === 'questions') refreshQuestionData(true)
|
||||
})
|
||||
|
||||
// ==================== 3.5 备赛资料管理 ====================
|
||||
@@ -2889,8 +2998,7 @@ onMounted(() => {
|
||||
.search-input { width: 220px; }
|
||||
.filter-select { width: 140px; }
|
||||
|
||||
.batch-score-label { color: #606266; font-size: 13px; }
|
||||
.batch-score-item { display: inline-flex; align-items: center; gap: 4px; color: #606266; font-size: 13px; }
|
||||
.score-policy-tag { margin-left: 12px; }
|
||||
.avail-hint { margin-left: 8px; color: #909399; font-size: 12px; }
|
||||
.total-score-tag { margin-left: 4px; }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user