feat: 接入形态图库与 Seedream 生图操作

展示已有主图、候选图和失败记录,支持单图及批量生成、主图选择。
补充正式 ID 校验、费用确认、图片加载占位和回归测试。
依赖后端 eac8dc3 的项目形态图库只读接口。
This commit is contained in:
GouJ
2026-08-28 15:16:25 +08:00
parent bae69c3c62
commit 47443c0efe
26 changed files with 1338 additions and 25 deletions
+36
View File
@@ -3,6 +3,7 @@ import { ApiError, optionalResource, request } from './http'
import { projectsApi } from '../features/projects/api'
import { breakdownApi } from '../features/breakdown/api'
import { storyboardApi } from '../features/storyboard/api'
import { subjectImagesApi } from '../features/subject-images/api'
afterEach(() => {
vi.unstubAllGlobals()
@@ -10,6 +11,41 @@ afterEach(() => {
})
describe('后端 API 契约', () => {
it('图库读取与单图、批量和切换主图使用各自接口,默认显式 Seedream', async () => {
const fetcher = vi.fn<typeof fetch>().mockImplementation(async () => new Response('{"data":[]}'))
vi.stubGlobal('fetch', fetcher)
await subjectImagesApi.listForms('a/b')
await subjectImagesApi.listImages('form/id')
await subjectImagesApi.generate('form/id', {
provider: 'seedream',
setPrimary: false,
width: 2048,
height: 2048,
prompt: '本次提示词'
})
await subjectImagesApi.generateProject('a/b', { provider: 'seedream', concurrency: 2, force: false })
await subjectImagesApi.setPrimary('form/id', 'image/id')
expect(fetcher.mock.calls.map(([url, init]) => [url, init?.method])).toEqual([
['/api/projects/a%2Fb/subject-forms', 'GET'],
['/api/subject-forms/form%2Fid/images', 'GET'],
['/api/subject-forms/form%2Fid/images', 'POST'],
['/api/projects/a%2Fb/subject-images/generate', 'POST'],
['/api/subject-forms/form%2Fid/images/image%2Fid/primary', 'PUT']
])
expect(JSON.parse(fetcher.mock.calls[2]![1]!.body as string)).toEqual({
provider: 'seedream',
setPrimary: false,
width: 2048,
height: 2048,
prompt: '本次提示词'
})
expect(JSON.parse(fetcher.mock.calls[3]![1]!.body as string)).toEqual({
provider: 'seedream',
concurrency: 2,
force: false
})
expect(fetcher.mock.calls[4]![1]!.body).toBeUndefined()
})
it('分镜单集显式持久化,批量保持 force 和零次修复参数', async () => {
const fetcher = vi.fn<typeof fetch>().mockImplementation(async () => new Response('{"data":{}}'))
vi.stubGlobal('fetch', fetcher)