Files
gen2d/frontend/src/api/mock.ts
T

218 lines
4.9 KiB
TypeScript
Raw Normal View History

import type { Asset, PipelineProgress, PipelineStage, Project, Task } from './types'
const delay = (ms: number) => new Promise(resolve => setTimeout(resolve, ms))
const randomDelay = () => delay(300 + Math.random() * 500)
// Mock 数据
const MOCK_PROJECT: Project = {
id: 'proj-default',
name: '我的工程',
style: {
kvPairs: {
artStyle: 'pixel',
palette: 'warm',
lineWeight: 'thin',
scene: 'forest',
lighting: 'bright',
mood: 'cheerful',
},
},
createdAt: '2026-05-20T10:00:00Z',
}
const MOCK_TASKS: Task[] = [
{
id: 'task-001',
projectId: 'proj-default',
prompt: '一个拿剑的小人',
assetType: 'sprite',
status: 'completed',
progress: 100,
createdAt: '2026-05-24T09:00:00Z',
updatedAt: '2026-05-24T09:02:00Z',
},
{
id: 'task-002',
projectId: 'proj-default',
prompt: '森林背景',
assetType: 'background',
status: 'failed',
error: '生成超时',
createdAt: '2026-05-24T08:00:00Z',
updatedAt: '2026-05-24T08:10:00Z',
},
{
id: 'task-003',
projectId: 'proj-default',
prompt: '魔法药水瓶',
assetType: 'ui',
status: 'completed',
progress: 100,
createdAt: '2026-05-23T15:00:00Z',
updatedAt: '2026-05-23T15:03:00Z',
},
]
const MOCK_ASSETS: Asset[] = [
{
id: 'asset-001',
url: 'https://placehold.co/256x256/e94560/1a1a2e?text=Sprite',
format: 'png',
width: 256,
height: 256,
metadata: {
frameWidth: 64,
frameHeight: 64,
frameCount: 16,
directions: 4,
},
},
]
// Mock API 函数
export async function mockGetProject(_id: string): Promise<Project> {
await randomDelay()
return { ...MOCK_PROJECT }
}
export async function mockGetStyle(
_projectId: string
): Promise<Record<string, string>> {
await randomDelay()
return { ...MOCK_PROJECT.style.kvPairs }
}
export async function mockSaveStyle(
_projectId: string,
_kvPairs: Record<string, string>
): Promise<void> {
await randomDelay()
}
export async function mockGetTasks(_projectId: string): Promise<Task[]> {
await randomDelay()
return MOCK_TASKS.map(t => ({ ...t }))
}
export async function mockGetTask(taskId: string): Promise<Task> {
await randomDelay()
const task = MOCK_TASKS.find(t => t.id === taskId)
if (!task) throw new Error('任务不存在')
return { ...task }
}
export async function mockGetAssets(_taskId: string): Promise<Asset[]> {
await randomDelay()
return MOCK_ASSETS.map(a => ({ ...a }))
}
export async function mockSubmitGenerate(
_projectId: string,
prompt: string,
assetType: string
): Promise<string> {
await delay(500)
const taskId = `task-${Date.now()}`
MOCK_TASKS.unshift({
id: taskId,
projectId: _projectId,
prompt,
assetType,
status: 'pending',
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
})
return taskId
}
// 模拟 WebSocket 管线进度
const STAGES: PipelineStage[] = [
'prompt_builder',
'asset_generator',
'quality_supervisor',
'format_adapter',
]
const STAGE_LABELS: Record<PipelineStage, string> = {
prompt_builder: '提示词构建',
asset_generator: '素材生成',
quality_supervisor: '质量检查',
format_adapter: '格式转换',
}
export { STAGE_LABELS }
export function createMockWebSocket(
_taskId: string,
onProgress: (msg: PipelineProgress) => void,
onComplete: (assets: Asset[]) => void,
_onError?: (error: string) => void
): () => void {
let stageIndex = 0
let tick = 0
let retryCount = 0
const maxRetries = 1
let cancelled = false
const interval = setInterval(() => {
if (cancelled) return
const stage = STAGES[stageIndex]
const stageProgress = (tick % 3) * 50 // 0, 50, 100
// 质检阶段有概率重试
if (
stage === 'quality_supervisor' &&
stageProgress === 100 &&
retryCount < maxRetries &&
Math.random() < 0.3
) {
onProgress({
stage: 'quality_supervisor',
status: 'failed',
progress: 100,
retryCount: retryCount + 1,
rejectReason: '风格一致性不足',
})
retryCount++
stageIndex = 0
tick = 0
return
}
const isLastTickOfStage = stageProgress === 100
const isLastStage = stageIndex === STAGES.length - 1
if (isLastTickOfStage && isLastStage) {
// 管线完成
onProgress({
stage: 'format_adapter',
status: 'completed',
progress: 100,
result: { assets: MOCK_ASSETS },
})
onComplete(MOCK_ASSETS)
clearInterval(interval)
return
}
onProgress({
stage,
status: isLastTickOfStage ? 'completed' : 'running',
progress: stageProgress,
retryCount: retryCount > 0 ? retryCount : undefined,
})
tick++
if (isLastTickOfStage) {
stageIndex++
tick = 0
}
}, 1000)
return () => {
cancelled = true
clearInterval(interval)
}
}