diff --git a/web/package.json b/web/package.json index c6c21cd..71fb60e 100644 --- a/web/package.json +++ b/web/package.json @@ -1,7 +1,7 @@ { "name": "web", "private": true, - "version": "2.3.0-rc3", + "version": "2.3.0-rc4", "type": "module", "scripts": { "dev": "vite", diff --git a/web/src/views/OtherExamsView.vue b/web/src/views/OtherExamsView.vue index 021f118..2e41e11 100644 --- a/web/src/views/OtherExamsView.vue +++ b/web/src/views/OtherExamsView.vue @@ -16,6 +16,10 @@ const mine = reactive({ best: [] as any[], history: [] as any[] }) const dialog = ref(false) const saving = ref(false) const form = reactive({ examCode: '', name: '', organizer: '', examDate: new Date().toISOString().slice(0, 10), metricKind: 3, maxScore: 100, levelOptions: '' }) +const selectedMetricKind = computed(() => Number(selected.value?.metricKind ?? 0)) +const isScoreMetric = computed(() => selectedMetricKind.value === 3) +const isLevelMetric = computed(() => selectedMetricKind.value === 2) +const isPassMetric = computed(() => selectedMetricKind.value === 1) async function load() { loading.value = true @@ -27,6 +31,8 @@ async function load() { async function openBatch(row: any) { try { const data = (await http.get(`/other-exams/batches/${row.id}`)).data + const metricKind = Number(data.batch.metricKind ?? 3) + data.batch.metricKind = [1, 2, 3].includes(metricKind) ? metricKind : 3 selected.value = data.batch results.value = data.results.map((item: any) => ({ ...item, score: item.score ?? null, level: item.level ?? '', notes: item.notes ?? '' })) } catch (error) { ElMessage.error(apiErrorMessage(error)) } @@ -43,7 +49,7 @@ async function lookupStudent(row: ResultRow | any) { async function saveResults() { saving.value = true try { - await http.put(`/other-exams/batches/${selected.value.id}/results`, { results: results.value.map(row => ({ studentNumber: row.studentNumber, score: selected.value.metricKind === 3 ? row.score : null, level: selected.value.metricKind === 2 ? row.level : null, isPassed: selected.value.metricKind === 1 ? row.isPassed : null, notes: row.notes })) }) + await http.put(`/other-exams/batches/${selected.value.id}/results`, { results: results.value.map(row => ({ studentNumber: row.studentNumber, score: isScoreMetric.value ? row.score : null, level: isLevelMetric.value ? row.level : null, isPassed: isPassMetric.value ? row.isPassed : null, notes: row.notes })) }) ElMessage.success('成绩已保存,参加次数由系统自动计算'); await openBatch(selected.value); await load() } catch (error) { ElMessage.error(apiErrorMessage(error)) } finally { saving.value = false } } @@ -71,7 +77,7 @@ async function importExcel(event: Event) { try { await http.post(`/other-exams/batches/${selected.value.id}/import`, data, { headers: { 'Content-Type': 'multipart/form-data' } }); ElMessage.success('其他考试成绩导入成功'); await openBatch(selected.value); await load() } catch (error) { ElMessage.error(apiErrorMessage(error)) } } -function metricName(kind: number) { return kind === 1 ? '合格制' : kind === 2 ? '等级制' : '分数制' } +function metricName(kind: number | string) { const value = Number(kind); return value === 1 ? '合格制' : value === 2 ? '等级制' : '分数制' } function displayResult(row: any) { return row.metricKind === 3 ? `${row.score} / ${row.maxScore}` : row.metricKind === 2 ? row.level : row.isPassed ? '合格' : '不合格' } onMounted(load) @@ -96,4 +102,7 @@ onMounted(load) .exam-hero { display:flex; justify-content:space-between; align-items:end; gap:24px; padding:26px 0 24px; border-bottom:1px solid var(--line); margin-bottom:20px; } .kicker { color:var(--teal); font-size:11px; letter-spacing:.17em; font-weight:800; }.exam-hero h1,.board-title h2,.panel-heading h2,.editor-heading h2 { color:var(--ink); margin:7px 0; letter-spacing:-.025em; }.exam-hero h1 { font-size:32px; }.exam-hero p,.editor-heading p { color:var(--muted); margin:0; line-height:1.7; }.result-board,.batch-panel,.editor-panel { background:#fff; border:1px solid var(--line); border-radius:14px; box-shadow:0 12px 30px rgba(32,61,73,.06); margin-bottom:18px; overflow:hidden; }.board-title,.panel-heading { display:flex; justify-content:space-between; align-items:center; padding:20px 22px 14px; }.board-title h2,.panel-heading h2,.editor-heading h2 { font-size:20px; }.board-note,.panel-heading>span { color:var(--muted); font-size:13px; }.history-board { opacity:.96; }.best-value { color:var(--navy); font-variant-numeric:tabular-nums; }.editor-heading { display:flex; justify-content:space-between; gap:20px; align-items:center; padding:20px 22px 14px; }.editor-actions { display:flex; flex-wrap:wrap; gap:8px; justify-content:flex-end; }.upload-button { display:inline-flex; align-items:center; gap:5px; border:1px solid #dcdfe6; border-radius:4px; padding:8px 14px; color:#606266; cursor:pointer; font-size:14px; }.upload-button:hover { color:var(--navy); border-color:var(--navy); }.upload-button input { display:none; }.editor-hint { margin:0 22px 14px; padding:11px 14px; border-left:3px solid #d4a72c; background:#fff9e8; color:#786223; font-size:13px; }.auto-attempt { display:inline-flex; align-items:center; padding:4px 8px; border-radius:20px; background:#edf6f5; color:var(--teal); font-size:12px; }.unresolved { color:#c27b18; }.score-table :deep(.el-input-number) { width:125px; }.score-table :deep(.el-table__cell) { padding:12px 0; } @media (max-width:760px) { .exam-hero,.editor-heading,.board-title,.panel-heading { display:block; }.exam-hero .el-button { margin-top:16px; }.editor-actions { justify-content:flex-start; margin-top:16px; }.board-note,.panel-heading>span { display:block; margin-top:5px; }.editor-hint { margin-left:14px; margin-right:14px; } } +.editor-heading > div:first-child { min-width: 0; } +.editor-actions { flex: 0 0 auto; } +.upload-button { flex: 0 0 auto; min-width: 112px; white-space: nowrap; justify-content: center; line-height: 1.4; }