mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-15 11:58:34 +08:00
fix(wiki): JobStageBar terminal state — badge sync, pulse stop, line coloring
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.
This commit is contained in:
parent
0301d5628e
commit
55a7eb96dc
@ -15,7 +15,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="stage-labels">
|
<div class="stage-labels">
|
||||||
<span v-for="stage in stages" :key="stage.key" class="stage-label" :class="{ active: stage.key === currentStage }">
|
<span v-for="stage in stages" :key="stage.key" class="stage-label" :class="{ active: stage.key === currentStage && !isTerminal, done: stage.key === currentStage && isTerminal }">
|
||||||
{{ t(`wiki.jobStage.${stage.key}`) }}
|
{{ t(`wiki.jobStage.${stage.key}`) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
@ -96,7 +96,12 @@ const currentStage = computed(() => {
|
|||||||
return stageMapping[raw] ?? raw
|
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(() =>
|
const isTerminalFailure = computed(() =>
|
||||||
props.status === 'failed' || props.status === 'partial' || props.status === 'cancelled'
|
props.status === 'failed' || props.status === 'partial' || props.status === 'cancelled'
|
||||||
)
|
)
|
||||||
@ -117,11 +122,16 @@ function dotClass(key: string) {
|
|||||||
const target = stageIndex(key)
|
const target = stageIndex(key)
|
||||||
if (cur < 0) return 'pending' // unknown stage → all pending
|
if (cur < 0) return 'pending' // unknown stage → all pending
|
||||||
if (isTerminalFailure.value) {
|
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 'done'
|
||||||
if (target === cur) return 'failed'
|
if (target === cur) return 'failed'
|
||||||
return 'pending'
|
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 'done'
|
||||||
if (target === cur) return 'active'
|
if (target === cur) return 'active'
|
||||||
return 'pending'
|
return 'pending'
|
||||||
@ -202,6 +212,7 @@ const elapsed = computed(() => {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
}
|
}
|
||||||
.stage-label.active { color: var(--mc-primary); font-weight: 600; }
|
.stage-label.active { color: var(--mc-primary); font-weight: 600; }
|
||||||
|
.stage-label.done { color: var(--mc-success, #5a8a5a); font-weight: 600; }
|
||||||
|
|
||||||
.stage-info {
|
.stage-info {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@ -91,9 +91,9 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- RFC-033: Job stage bar — only show when job has progressed past 'queued' -->
|
<!-- RFC-033: Job stage bar — show when job has progressed past 'queued' or reached terminal -->
|
||||||
<JobStageBar
|
<JobStageBar
|
||||||
v-if="rawJobs[raw.id] && rawJobs[raw.id].stage !== 'queued'"
|
v-if="rawJobs[raw.id] && (rawJobs[raw.id].stage !== 'queued' || rawJobs[raw.id].status !== 'queued')"
|
||||||
:stage="rawJobs[raw.id].stage"
|
:stage="rawJobs[raw.id].stage"
|
||||||
:status="rawJobs[raw.id].status"
|
:status="rawJobs[raw.id].status"
|
||||||
:current-model="rawJobs[raw.id].currentModelName ?? (rawJobs[raw.id].currentModelId ? `Model #${rawJobs[raw.id].currentModelId}` : undefined)"
|
:current-model="rawJobs[raw.id].currentModelName ?? (rawJobs[raw.id].currentModelId ? `Model #${rawJobs[raw.id].currentModelId}` : undefined)"
|
||||||
@ -288,19 +288,37 @@ onBeforeUnmount(() => {
|
|||||||
const rawJobs = reactive<Record<number, WikiProcessingJob>>({})
|
const rawJobs = reactive<Record<number, WikiProcessingJob>>({})
|
||||||
let jobPoller: ReturnType<typeof setTimeout> | null = null
|
let jobPoller: ReturnType<typeof setTimeout> | null = null
|
||||||
|
|
||||||
|
const TERMINAL_STATUSES = new Set(['completed', 'failed', 'partial', 'cancelled'])
|
||||||
|
|
||||||
async function pollJobs() {
|
async function pollJobs() {
|
||||||
if (!store.currentKB) return
|
if (!store.currentKB) return
|
||||||
|
const kbId = store.currentKB.id
|
||||||
const processingRaws = store.rawMaterials.filter(
|
const processingRaws = store.rawMaterials.filter(
|
||||||
r => r.processingStatus === 'processing' || r.processingStatus === 'pending'
|
r => r.processingStatus === 'processing' || r.processingStatus === 'pending'
|
||||||
)
|
)
|
||||||
|
let anyTerminal = false
|
||||||
for (const raw of processingRaws) {
|
for (const raw of processingRaws) {
|
||||||
try {
|
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 || []
|
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 */ }
|
} 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)
|
jobPoller = setTimeout(pollJobs, 3000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user