diff --git a/web/src/admin/components/baseData/CategoriesSection.vue b/web/src/admin/components/baseData/CategoriesSection.vue index b99e11c..beccabf 100644 --- a/web/src/admin/components/baseData/CategoriesSection.vue +++ b/web/src/admin/components/baseData/CategoriesSection.vue @@ -15,11 +15,12 @@ const toast = useToastStore() interface CategoryRow { key: number name: string - multiplier: string + // type="number" 的 v-model 会被 Vue 自动转成 number(空值仍是 ''),这里如实标注 + multiplier: string | number gender: '' | Gender - ageMin: string - ageMax: string - sortOrder: string + ageMin: string | number + ageMax: string | number + sortOrder: string | number } interface RowError { @@ -97,6 +98,20 @@ function moveRow(index: number, delta: number): void { rows.value = next } +/** + * 年龄取值。 + * + * `type="number"` 的 v-model 会被 Vue 自动转成 number(空值仍是 ''),所以不能直接 `.trim()`: + * 填了数字后字段是 number,`.trim()` 抛 TypeError,点击处理器中断,表现为「点了没反应」。 + * 非法输入返回 NaN,交给 validate 标红。 + */ +function toAge(value: string | number): number | null { + const text = String(value).trim() + if (text === '') return null + const parsed = Number(text) + return Number.isFinite(parsed) ? parsed : Number.NaN +} + function validate(): boolean { const next: Record = {} const seen = new Set() @@ -112,8 +127,8 @@ function validate(): boolean { rowError.multiplier = '权重为 0–100 的正数' } - const ageMin = row.ageMin.trim() === '' ? null : Number(row.ageMin) - const ageMax = row.ageMax.trim() === '' ? null : Number(row.ageMax) + const ageMin = toAge(row.ageMin) + const ageMax = toAge(row.ageMax) if (ageMin !== null && (!Number.isInteger(ageMin) || ageMin < 0 || ageMin > 120)) rowError.age = '年龄为 0–120 的整数' else if (ageMax !== null && (!Number.isInteger(ageMax) || ageMax < 0 || ageMax > 120)) rowError.age = '年龄为 0–120 的整数' else if (ageMin !== null && ageMax !== null && ageMin > ageMax) rowError.age = '起始年龄不能大于结束年龄' @@ -129,8 +144,8 @@ function buildInputs(): EventCategoryInput[] { name: row.name.trim(), multiplier: Number(row.multiplier), gender: row.gender === '' ? null : row.gender, - ageMin: row.ageMin.trim() === '' ? null : Number(row.ageMin), - ageMax: row.ageMax.trim() === '' ? null : Number(row.ageMax), + ageMin: toAge(row.ageMin), + ageMax: toAge(row.ageMax), sortOrder: Number(row.sortOrder) || index + 1, })) } diff --git a/web/src/admin/pages/AdjustmentsPage.vue b/web/src/admin/pages/AdjustmentsPage.vue index d74605b..079db16 100644 --- a/web/src/admin/pages/AdjustmentsPage.vue +++ b/web/src/admin/pages/AdjustmentsPage.vue @@ -181,7 +181,7 @@ function openCreate(): void { function validate(): string | null { if (formTeamId.value === null) return '请选择单位' const delta = Number(formDelta.value) - if (formDelta.value.trim() === '' || !Number.isFinite(delta)) return '请填写分值' + if (String(formDelta.value).trim() === '' || !Number.isFinite(delta)) return '请填写分值' if (delta === 0) return '分值不能为 0' if (formReason.value.trim() === '') return '理由必填' return null