From 55a7eb96dc4d669632e6bcf01f2315cde57f1a39 Mon Sep 17 00:00:00 2001 From: matevip Date: Sun, 19 Apr 2026 21:00:08 +0800 Subject: [PATCH] =?UTF-8?q?fix(wiki):=20JobStageBar=20terminal=20state=20?= =?UTF-8?q?=E2=80=94=20badge=20sync,=20pulse=20stop,=20line=20coloring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three bugs fixed: 1. Badge stays "processing" after job completes: pollJobs() never called fetchRawMaterials() when a job reached terminal status, so raw.processingStatus stayed processing in the store. Fix: detect terminal job status in pollJobs, trigger fetchRawMaterials to sync. 2. "Completed" dot pulses instead of solid: dotClass() treated completed the same as in-progress (target === cur → active). Fix: add isTerminal computed (includes completed), return done for all dots at or before the terminal position — no pulse animation. 3. Stage label stays orange at terminal: same cause — active class applied regardless of terminal state. Fix: use done class for terminal labels (green instead of orange). Also: v-if on JobStageBar now shows for terminal status jobs (not just stage !== queued), so completed/failed stage bars remain visible. --- .../src/views/Wiki/components/JobStageBar.vue | 17 +++++++++-- .../Wiki/components/RawMaterialPanel.vue | 28 +++++++++++++++---- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue b/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue index e1f3fd9e..be976b7e 100644 --- a/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue +++ b/mateclaw-ui/src/views/Wiki/components/JobStageBar.vue @@ -15,7 +15,7 @@
- + {{ t(`wiki.jobStage.${stage.key}`) }}
@@ -96,7 +96,12 @@ const currentStage = computed(() => { return stageMapping[raw] ?? raw }) -// True when backend status indicates a terminal failure (dots should not pulse) +// True when the job has reached any terminal state (dots should not pulse) +const isTerminal = computed(() => + props.status === 'completed' || props.status === 'failed' || props.status === 'partial' || props.status === 'cancelled' +) + +// True specifically for failure terminals (dots show red instead of done) const isTerminalFailure = computed(() => props.status === 'failed' || props.status === 'partial' || props.status === 'cancelled' ) @@ -117,11 +122,16 @@ function dotClass(key: string) { const target = stageIndex(key) if (cur < 0) return 'pending' // unknown stage → all pending if (isTerminalFailure.value) { - // Terminal failure: all dots before the failure point are done, the failure point is failed + // Terminal failure: dots before failure point are done, failure point is red if (target < cur) return 'done' if (target === cur) return 'failed' return 'pending' } + if (isTerminal.value) { + // Successful terminal (completed): all dots up to and including current are done (no pulse) + if (target <= cur) return 'done' + return 'pending' + } if (target < cur) return 'done' if (target === cur) return 'active' return 'pending' @@ -202,6 +212,7 @@ const elapsed = computed(() => { flex: 1; } .stage-label.active { color: var(--mc-primary); font-weight: 600; } +.stage-label.done { color: var(--mc-success, #5a8a5a); font-weight: 600; } .stage-info { display: flex; diff --git a/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue b/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue index c89fd288..e7ed2d1c 100644 --- a/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue +++ b/mateclaw-ui/src/views/Wiki/components/RawMaterialPanel.vue @@ -91,9 +91,9 @@ - + { const rawJobs = reactive>({}) let jobPoller: ReturnType | null = null +const TERMINAL_STATUSES = new Set(['completed', 'failed', 'partial', 'cancelled']) + async function pollJobs() { if (!store.currentKB) return + const kbId = store.currentKB.id const processingRaws = store.rawMaterials.filter( r => r.processingStatus === 'processing' || r.processingStatus === 'pending' ) + let anyTerminal = false for (const raw of processingRaws) { try { - const res: any = await wikiApi.getWikiJobs(store.currentKB.id, raw.id) + const res: any = await wikiApi.getWikiJobs(kbId, raw.id) const list = res.data || res || [] - if (list.length > 0) rawJobs[raw.id] = list[0] + if (list.length > 0) { + const job = list[0] + rawJobs[raw.id] = job + if (TERMINAL_STATUSES.has(job.status)) { + anyTerminal = true + } + } } catch { /* ignore */ } } - if (processingRaws.length > 0) { + // When any job reaches terminal, refresh raw materials to sync status badges + if (anyTerminal) { + await store.fetchRawMaterials(kbId) + } + // Continue polling while there are still processing/pending raws + const stillActive = store.rawMaterials.some( + r => r.processingStatus === 'processing' || r.processingStatus === 'pending' + ) + if (stillActive) { jobPoller = setTimeout(pollJobs, 3000) } }