From 5a8e4198d84cb2745afcf151248b46159bf7534f Mon Sep 17 00:00:00 2001 From: lion <120344285@qq.com> Date: Fri, 7 Aug 2026 16:33:03 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AF=84=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ReviewerApplicationDetailView.vue | 47 ++++++++++++++----- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/src/views/reviewer/ReviewerApplicationDetailView.vue b/src/views/reviewer/ReviewerApplicationDetailView.vue index 9a0fbcd..d7cbdc0 100644 --- a/src/views/reviewer/ReviewerApplicationDetailView.vue +++ b/src/views/reviewer/ReviewerApplicationDetailView.vue @@ -131,13 +131,26 @@ function totalDraftText(): string { return String(totalDraft.value ?? '').trim() } -/** 文本输入绑定:过滤非法字符,始终写回 string */ +const SCORE_MAX = 100 +const SCORE_DECIMALS = 2 + +/** 文本输入绑定:仅 0~100,最多两位小数 */ const totalDraftModel = computed({ get: () => totalDraft.value, set: (v: string | number | null | undefined) => { const raw = v == null ? '' : String(v) - // 允许空、整数/小数、一位前导负号(校验时再拦负数) - const cleaned = raw.replace(/[^\d.-]/g, '').replace(/(?!^)-/g, '').replace(/(\..*)\./g, '$1') + let cleaned = raw.replace(/[^\d.]/g, '').replace(/(\..*)\./g, '$1') + const m = cleaned.match(/^(\d*)(\.\d{0,2})?/) + cleaned = m ? `${m[1] ?? ''}${m[2] ?? ''}` : '' + if (cleaned === '' || cleaned === '.') { + totalDraft.value = cleaned === '.' ? '0.' : '' + return + } + const n = Number(cleaned) + if (Number.isFinite(n) && n > SCORE_MAX) { + totalDraft.value = String(SCORE_MAX) + return + } totalDraft.value = cleaned }, }) @@ -163,7 +176,7 @@ function syncScoreDraft(): void { } else { const n = Number(raw) // Number('') === 0,空分必须保持空串,不能写成默认 0 - totalDraft.value = Number.isFinite(n) ? rtrimZeros(n.toFixed(4)) : '' + totalDraft.value = Number.isFinite(n) ? rtrimZeros(n.toFixed(SCORE_DECIMALS)) : '' } totalBlurred.value = false scoreShowErrors.value = false @@ -176,7 +189,11 @@ function rtrimZeros(s: string): string { } function formatMax(n: number): string { - return rtrimZeros(Number(n).toFixed(4)) + return rtrimZeros(Number(n).toFixed(SCORE_DECIMALS)) +} + +function hasMoreThanTwoDecimals(raw: string): boolean { + return /\.\d{3,}/.test(raw) } const scoringItems = computed(() => detail.value?.scoring_sheet?.items ?? []) @@ -214,23 +231,30 @@ function getTotalError(): string | null { if (scoreShowErrors.value || totalBlurred.value) return '请填写项目总分' return null } + if (hasMoreThanTwoDecimals(raw)) return '最多保留两位小数' const n = Number(raw) if (!Number.isFinite(n)) return '须为数字' if (n < 0) return '不能小于 0' - if (n > fullScore.value) return `不得超过 ${formatMax(fullScore.value)} 分` + if (n > SCORE_MAX) return '不得超过 100 分' return null } function onTotalBlur(): void { totalBlurred.value = true + const raw = totalDraftText() + if (raw === '' || raw.endsWith('.')) return + const n = Number(raw) + if (!Number.isFinite(n)) return + const clamped = Math.min(SCORE_MAX, Math.max(0, n)) + totalDraft.value = rtrimZeros(clamped.toFixed(SCORE_DECIMALS)) } const canSubmitScore = computed(() => { if (!detail.value?.scoring_allowed || !scoringItems.value.length) return false const raw = totalDraftText() - if (raw === '') return false + if (raw === '' || hasMoreThanTwoDecimals(raw)) return false const n = Number(raw) - return Number.isFinite(n) && n >= 0 && n <= fullScore.value + return Number.isFinite(n) && n >= 0 && n <= SCORE_MAX }) const scoreFormHint = computed(() => { @@ -364,7 +388,8 @@ async function downloadAttachment(f: FileItem) { function buildSubmitPayload(): Record { const n = Number(totalDraftText()) - return { line_total: Number.isFinite(n) ? n : 0 } + const score = Number.isFinite(n) ? Math.round(n * 100) / 100 : 0 + return { line_total: score } } function cleanupModalArtifacts(): void { @@ -571,7 +596,7 @@ async function submitReviewScore() { }} 最近保存:{{ detail.my_review_score.updated_at }} · 项目得分 - {{ rtrimZeros(Number(detail.my_review_score.line_total).toFixed(4)) }} 分 @@ -640,7 +665,7 @@ async function submitReviewScore() {