feat: 管线四阶段进度上报 + 图片预览 + Vite 代理
后端: - pipeline.go: ProgressReporter 回调类型 + WithProgressReporter 注入 context - nodes.go: 各节点 pre/post handler 调用 reportProgress() 上报阶段进度 - generate.go: TaskResponse 新增 stage 字段,runPipelineBg 注入进度回调 前端: - vite.config.ts: 添加 /generation 代理到后端静态文件服务 - generation.ts: 轮询读取 stage 字段,暴露 stage/retryCount/rejectReason - GeneratePage.tsx: ProgressBar 接收真实管线阶段数据
This commit is contained in:
@@ -19,9 +19,12 @@ export default function GeneratePage() {
|
||||
const { style: projectStyle, loadProject } = useProjectStore()
|
||||
const {
|
||||
status,
|
||||
stage,
|
||||
progress,
|
||||
taskId,
|
||||
statusText,
|
||||
retryCount,
|
||||
rejectReason,
|
||||
submit,
|
||||
reset: resetGeneration,
|
||||
} = useGenerationStore()
|
||||
@@ -83,11 +86,11 @@ export default function GeneratePage() {
|
||||
) : (
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: 24 }}>
|
||||
<ProgressBar
|
||||
stage={null}
|
||||
stage={stage}
|
||||
progress={progress}
|
||||
status={status}
|
||||
retryCount={0}
|
||||
rejectReason={null}
|
||||
retryCount={retryCount}
|
||||
rejectReason={rejectReason}
|
||||
/>
|
||||
|
||||
{status === 'running' && (
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { create } from 'zustand'
|
||||
import type { Asset, GenerateRequest } from '../api/types'
|
||||
import type { Asset, GenerateRequest, PipelineStage } from '../api/types'
|
||||
import { submitGenerate, getTask, getAssets } from '../api/generate'
|
||||
|
||||
type Status = 'idle' | 'submitting' | 'running' | 'completed' | 'failed'
|
||||
@@ -7,9 +7,12 @@ type Status = 'idle' | 'submitting' | 'running' | 'completed' | 'failed'
|
||||
interface GenerationState {
|
||||
taskId: string | null
|
||||
projectId: string | null
|
||||
stage: PipelineStage | null
|
||||
progress: number
|
||||
status: Status
|
||||
statusText: string
|
||||
retryCount: number
|
||||
rejectReason: string | null
|
||||
assets: Asset[]
|
||||
error: string | null
|
||||
submit: (req: GenerateRequest) => Promise<void>
|
||||
@@ -25,18 +28,21 @@ function stopPolling() {
|
||||
}
|
||||
}
|
||||
|
||||
export const useGenerationStore = create<GenerationState>((set, get) => ({
|
||||
export const useGenerationStore = create<GenerationState>((set) => ({
|
||||
taskId: null,
|
||||
projectId: null,
|
||||
stage: null,
|
||||
progress: 0,
|
||||
status: 'idle',
|
||||
statusText: '',
|
||||
retryCount: 0,
|
||||
rejectReason: null,
|
||||
assets: [],
|
||||
error: null,
|
||||
|
||||
submit: async (req) => {
|
||||
stopPolling()
|
||||
set({ status: 'submitting', error: null, statusText: '提交中...' })
|
||||
set({ status: 'submitting', error: null, statusText: '提交中...', stage: null })
|
||||
try {
|
||||
const { taskId } = await submitGenerate(req)
|
||||
|
||||
@@ -44,24 +50,26 @@ export const useGenerationStore = create<GenerationState>((set, get) => ({
|
||||
taskId,
|
||||
projectId: req.projectId,
|
||||
status: 'running',
|
||||
progress: 10,
|
||||
progress: 5,
|
||||
stage: 'prompt_builder',
|
||||
statusText: '任务已提交,等待生成...',
|
||||
})
|
||||
|
||||
// 开始轮询进度
|
||||
pollTimer = setInterval(async () => {
|
||||
try {
|
||||
const task = await getTask(taskId)
|
||||
|
||||
set({
|
||||
progress: task.progress ?? get().progress,
|
||||
set((s) => ({
|
||||
stage: task.stage ?? s.stage,
|
||||
progress: task.progress ?? s.progress,
|
||||
retryCount: task.retryCount ?? s.retryCount,
|
||||
statusText:
|
||||
task.status === 'running'
|
||||
? '生成中...'
|
||||
? stageLabel(task.stage)
|
||||
: task.status === 'pending'
|
||||
? '排队中...'
|
||||
: task.status,
|
||||
})
|
||||
}))
|
||||
|
||||
if (task.status === 'completed') {
|
||||
stopPolling()
|
||||
@@ -71,6 +79,7 @@ export const useGenerationStore = create<GenerationState>((set, get) => ({
|
||||
set({
|
||||
status: 'completed',
|
||||
progress: 100,
|
||||
stage: 'format_adapter',
|
||||
statusText: '生成完成',
|
||||
assets,
|
||||
})
|
||||
@@ -85,7 +94,7 @@ export const useGenerationStore = create<GenerationState>((set, get) => ({
|
||||
} catch {
|
||||
// 网络错误不中断轮询
|
||||
}
|
||||
}, 2000)
|
||||
}, 1500)
|
||||
} catch (err) {
|
||||
stopPolling()
|
||||
set({ status: 'failed', error: (err as Error).message, statusText: '提交失败' })
|
||||
@@ -97,11 +106,24 @@ export const useGenerationStore = create<GenerationState>((set, get) => ({
|
||||
set({
|
||||
taskId: null,
|
||||
projectId: null,
|
||||
stage: null,
|
||||
progress: 0,
|
||||
status: 'idle',
|
||||
statusText: '',
|
||||
retryCount: 0,
|
||||
rejectReason: null,
|
||||
assets: [],
|
||||
error: null,
|
||||
})
|
||||
},
|
||||
}))
|
||||
|
||||
function stageLabel(stage?: string): string {
|
||||
switch (stage) {
|
||||
case 'prompt_builder': return '优化提示词...'
|
||||
case 'asset_generator': return '生成素材中...'
|
||||
case 'quality_supervisor': return '质检中...'
|
||||
case 'format_adapter': return '格式转换中...'
|
||||
default: return '生成中...'
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user