|
|
|
|
@ -134,23 +134,15 @@ function totalDraftText(): 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)
|
|
|
|
|
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
|
|
|
|
|
}
|
|
|
|
|
// 仅过滤非法字符;允许负号、大于 100、超过两位小数,由校验提示拦截
|
|
|
|
|
let cleaned = raw.replace(/[^\d.-]/g, '')
|
|
|
|
|
cleaned = cleaned.replace(/(?!^)-/g, '')
|
|
|
|
|
cleaned = cleaned.replace(/(\..*)\./g, '$1')
|
|
|
|
|
totalDraft.value = cleaned
|
|
|
|
|
},
|
|
|
|
|
})
|
|
|
|
|
@ -231,30 +223,22 @@ function getTotalError(): string | null {
|
|
|
|
|
if (scoreShowErrors.value || totalBlurred.value) return '请填写项目总分'
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
if (hasMoreThanTwoDecimals(raw)) return '最多保留两位小数'
|
|
|
|
|
if (raw === '-' || raw === '.' || raw === '-.') return '须为数字'
|
|
|
|
|
if (hasMoreThanTwoDecimals(raw)) return '项目总分最多保留两位小数'
|
|
|
|
|
const n = Number(raw)
|
|
|
|
|
if (!Number.isFinite(n)) return '须为数字'
|
|
|
|
|
if (n < 0) return '不能小于 0'
|
|
|
|
|
if (n > SCORE_MAX) return '不得超过 100 分'
|
|
|
|
|
if (n < 0) return '项目总分不能为负数'
|
|
|
|
|
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 === '' || hasMoreThanTwoDecimals(raw)) return false
|
|
|
|
|
const n = Number(raw)
|
|
|
|
|
return Number.isFinite(n) && n >= 0 && n <= SCORE_MAX
|
|
|
|
|
return getTotalError() === null && totalDraftText() !== ''
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
const scoreFormHint = computed(() => {
|
|
|
|
|
@ -387,9 +371,9 @@ async function downloadAttachment(f: FileItem) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function buildSubmitPayload(): Record<string, unknown> {
|
|
|
|
|
// 仅在 canSubmitScore 通过后调用;不做四舍五入/截断,原样提交校验过的数值
|
|
|
|
|
const n = Number(totalDraftText())
|
|
|
|
|
const score = Number.isFinite(n) ? Math.round(n * 100) / 100 : 0
|
|
|
|
|
return { line_total: score }
|
|
|
|
|
return { line_total: n }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function cleanupModalArtifacts(): void {
|
|
|
|
|
|