Compare commits

..
Author SHA1 Message Date
weichen 278b54cf9d Add render/fetch lifecycle logs to cockpit (temporary diagnostics)
Why: scenario 1 hangs at 加载中 with the fetch resolved but the DOM stuck;
lifecycle logs (instance id, effect start, resolve, setProjects, render
state) will locate where the update is lost.

Evidence: tsc green (SELF).

Impact: debug-only logs; removed after diagnosis.
2026-10-17 01:57:02 +00:00
3 changed files with 6 additions and 24 deletions
-16
View File
@@ -1,16 +0,0 @@
import { test } from '@playwright/test'
test('probe10', async ({ page }) => {
const logs: string[] = []
page.on('console', m => { const t = m.text(); if (t.includes('[cockpit-dbg]')) logs.push(t) })
page.on('pageerror', e => logs.push('PAGEERR: ' + e.message.slice(0, 150)))
await page.goto('http://127.0.0.1:4173/')
await page.getByPlaceholder('由管理员分配').fill('e2e-auditor')
await page.getByPlaceholder('初始密码或常用密码').fill('E2eAudit#2026b')
await page.getByRole('button', { name: '登录', exact: true }).click()
await page.waitForTimeout(4000)
await page.locator('.tsb-tool', { hasText: '文档驾驶舱' }).click()
await page.waitForTimeout(12000)
const sel = await page.locator('.cockpit-project').count()
console.log('FINAL-SELECT:', sel)
console.log('LOGS:\n' + logs.join('\n'))
})
-4
View File
@@ -46,12 +46,8 @@ async function openReaderOnDoc(page: Page, title: string) {
test.describe.serial('I4 文档驾驶舱组合验收', () => {
// 场景 1:首次进入项目——统计带、结构树、全景可见(DV-R01/R02)。
test('1 首屏:统计、树、全景可见且统计可判读', async ({ page }) => {
const dbg: string[] = []
page.on('console', m => { const t = m.text(); if (t.includes('[cockpit]') || t.includes('cockpit-dbg')) dbg.push(t.slice(0, 200)) })
page.on('pageerror', e => dbg.push('PAGEERR: ' + e.message.slice(0, 200)))
await loginOwner(page)
await openCockpit(page)
test.info().annotations.push({ type: 'debug', description: dbg.join(' | ') || '(no cockpit console)' })
// placeholder
await expect(page.locator('.cockpit-stat', { hasText: '完成度' })).toBeVisible()
await expect(page.locator('.cockpit-stat', { hasText: '文档' })).toBeVisible()
+6 -4
View File
@@ -29,6 +29,7 @@ interface Selection { ref: CockpitNodeRef; parentDocId?: string }
export function DocumentCockpit() {
const identity = useStore(() => store.getIdentityEpoch())
const instanceId = useRef(Math.random().toString(36).slice(2, 8))
const [projects, setProjects] = useState<ProjectView[] | null>(null)
const [pid, setPid] = useState('')
const [filter, setFilter] = useState('')
@@ -63,16 +64,16 @@ export function DocumentCockpit() {
// 项目清单(rdm.project.list:成员可见项目,canAudit 与否不影响文档读取入口)。
useEffect(() => {
let c = false
console.log('[cockpit-dbg] effect start identity=', identity)
console.log('[cockpit-dbg] EFFECT start inst=' + instanceId.current, 'identity=', identity)
setProjects(null); setPid(''); setOverview(null); setSelected(null); setFilter(''); setError('')
api.rdmCommand<{ projects?: ProjectView[] } | ProjectView[]>('rdm.project.list')
.then(list => {
const rows = Array.isArray(list) ? list : (list?.projects ?? [])
console.log('[cockpit-dbg] resolved cancelled=', c, 'rows=', rows?.length)
const rows = Array.isArray(list) ? list : ((list as any)?.projects ?? [])
console.log('[cockpit-dbg] FETCH resolved inst=' + instanceId.current, 'cancelled=', c, 'rows=', rows.length)
if (!c) return
// rdm.project.list 返回 { projects: [...] };容忍裸数组形态。
setProjects(rows)
console.log('[cockpit-dbg] setProjects done')
console.log('[cockpit-dbg] setProjects done inst=' + instanceId.current, 'rows=', rows.length)
})
.catch(e => { if (!c) setError(e.message) })
return () => { c = true }
@@ -119,6 +120,7 @@ export function DocumentCockpit() {
.catch(() => { /* 行内重试入口承担 */ })
}, [pid, filter])
console.log('[cockpit-dbg] RENDER inst=' + instanceId.current, 'identity=', identity, 'projects=', projects === null ? 'null' : projects.length, 'error=', error)
if (error) return <CockpitErrorState raw={error} />
if (!projects) return <div className="empty-note">加载中…</div>
if (projects.length === 0) return <EmptyState icon="folder">暂无可见研发项目——项目成员身份由项目负责人授予</EmptyState>