fix(admin): 修复基础数据「运动员」页整块空白(TDZ 崩溃)
AthletesSection 里 filteredIds 在第 73 行引用了第 272 行才声明的 filtered, 而第 84 行的 watch(filteredIds, ...) 在注册时会同步求值一次源(Vue 行为), setup 阶段就访问到 TDZ 中的 filtered → ReferenceError: Cannot access 'filtered' before initialization → 整个分区渲染失败, 页面只剩标签栏、下面全白。自 631c8dd(上一轮)起 dev_0.1.6/0.1.7/0.1.8 均带此缺陷。 修法:把 filtered 的定义上移到 filteredIds 之前(只依赖三个筛选 ref,上移安全), 并加注释说明为什么不能挪回去。 验证:真 Chromium(Playwright)逐个打开 14 个管理端路由 + 7 个基础数据标签, console 错误与 pageerror 均为 0;「运动员」标签正常渲染出表格与空态。 版本 dev_0.1.8 → dev_0.1.9(热修,P2 批次另行发布)。
This commit is contained in:
1 parent
ab7662b7f1
commit
8a27f5a4fb
3 files changed
+24
-20
No files matched your search
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "scoreboard-console",
|
||||
"productName": "Scoreboard Console",
|
||||
"version": "0.1.8",
|
||||
"version": "0.1.9",
|
||||
"private": true,
|
||||
"author": "AskaEth",
|
||||
"description": "赛事积分榜 · 桌面宿主:守护 Go 服务端并把大屏投到扩展屏",
|
||||
|
||||
@@ -70,6 +70,28 @@ const batchPurgeOpen = ref(false)
|
||||
/** 彻底删除的预览(dryRun 响应):提交前把「会删掉什么、重算哪些小项」摆给操作员看。 */
|
||||
const bulkPurgePreview = ref<AthleteBatchResult | null>(null)
|
||||
|
||||
// filtered 必须声明在 filteredIds / watch 之前:
|
||||
// watch 注册时会同步求值一次源(Vue 的行为),若 filtered 还在 TDZ 里,
|
||||
// setup 会直接抛 "Cannot access 'filtered' before initialization",
|
||||
// 整个分区渲染不出来(页面只剩标签栏、下面全空白)。2026-09-11 实机复现。
|
||||
const filtered = computed(() => {
|
||||
const keyword = search.value.trim().toLowerCase()
|
||||
return data.athletes.value.filter((athlete) => {
|
||||
if (teamFilter.value !== 'all' && String(athlete.teamId) !== teamFilter.value) return false
|
||||
if (statusFilter.value === 'active' && !athlete.active) return false
|
||||
if (statusFilter.value === 'inactive' && athlete.active) return false
|
||||
if (keyword === '') return true
|
||||
const team = data.teamById(athlete.teamId)
|
||||
return (
|
||||
athlete.name.toLowerCase().includes(keyword) ||
|
||||
(athlete.employeeNo ?? '').toLowerCase().includes(keyword) ||
|
||||
(athlete.idCard ?? '').toLowerCase().includes(keyword) ||
|
||||
(athlete.bib ?? '').toLowerCase().includes(keyword) ||
|
||||
(team?.name ?? '').toLowerCase().includes(keyword)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
const filteredIds = computed(() => filtered.value.map((athlete) => athlete.id))
|
||||
const selectedCount = computed(() => selectedIds.value.size)
|
||||
const selectedAthletes = computed(() => data.athletes.value.filter((athlete) => selectedIds.value.has(athlete.id)))
|
||||
@@ -269,24 +291,6 @@ function toAge(value: string | number): number | null {
|
||||
return Number.isFinite(parsed) ? parsed : Number.NaN
|
||||
}
|
||||
|
||||
const filtered = computed(() => {
|
||||
const keyword = search.value.trim().toLowerCase()
|
||||
return data.athletes.value.filter((athlete) => {
|
||||
if (teamFilter.value !== 'all' && String(athlete.teamId) !== teamFilter.value) return false
|
||||
if (statusFilter.value === 'active' && !athlete.active) return false
|
||||
if (statusFilter.value === 'inactive' && athlete.active) return false
|
||||
if (keyword === '') return true
|
||||
const team = data.teamById(athlete.teamId)
|
||||
return (
|
||||
athlete.name.toLowerCase().includes(keyword) ||
|
||||
(athlete.employeeNo ?? '').toLowerCase().includes(keyword) ||
|
||||
(athlete.idCard ?? '').toLowerCase().includes(keyword) ||
|
||||
(athlete.bib ?? '').toLowerCase().includes(keyword) ||
|
||||
(team?.name ?? '').toLowerCase().includes(keyword)
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
const activeCount = computed(() => data.athletes.value.filter((athlete) => athlete.active).length)
|
||||
|
||||
function openCreate(): void {
|
||||
|
||||
Reference in new issue
Block a user