新增图片画布项目页
新增 /project 项目页和我的页项目入口 补齐图片画布工程列表、重命名和删除 API 支持 /editor/canvas 按 projectid 加载指定工程 更新图片画布文档、TRACKING 和对应测试
This commit is contained in:
@@ -3,9 +3,13 @@ import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import {
|
||||
createEditorProject,
|
||||
createEditorProjectResource,
|
||||
deleteEditorProject,
|
||||
editEditorImage,
|
||||
generateEditorImage,
|
||||
listEditorProjects,
|
||||
loadEditorProject,
|
||||
loadOrCreateRecentEditorProject,
|
||||
renameEditorProject,
|
||||
saveEditorProjectLayout,
|
||||
} from './editorProjectClient';
|
||||
|
||||
@@ -139,6 +143,121 @@ describe('editorProjectClient', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('lists editor projects from the project API', async () => {
|
||||
requestJsonMock.mockResolvedValueOnce({
|
||||
projects: [
|
||||
{
|
||||
projectId: 'editor-project-1',
|
||||
title: '角色设定板',
|
||||
canvas: {
|
||||
canvasId: 'editor-project-1:canvas:default',
|
||||
projectId: 'editor-project-1',
|
||||
title: '默认画布',
|
||||
viewport: { x: 0, y: 0, scale: 1 },
|
||||
layers: [],
|
||||
updatedAt: '2026-06-12T00:00:00.000Z',
|
||||
},
|
||||
viewport: { x: 0, y: 0, scale: 1 },
|
||||
layers: [],
|
||||
resources: [],
|
||||
updatedAt: '2026-06-12T00:00:00.000Z',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const projects = await listEditorProjects();
|
||||
|
||||
expect(projects).toHaveLength(1);
|
||||
expect(requestJsonMock).toHaveBeenCalledWith(
|
||||
'/api/editor/projects',
|
||||
{ method: 'GET' },
|
||||
'读取图片画布工程列表失败',
|
||||
expect.objectContaining({
|
||||
clearAuthOnUnauthorized: false,
|
||||
notifyAuthStateChange: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('loads an explicit project by id', async () => {
|
||||
requestJsonMock.mockResolvedValueOnce({
|
||||
project: {
|
||||
projectId: 'editor-project-1',
|
||||
title: '角色设定板',
|
||||
canvas: {
|
||||
canvasId: 'editor-project-1:canvas:default',
|
||||
projectId: 'editor-project-1',
|
||||
title: '默认画布',
|
||||
viewport: { x: 8, y: 9, scale: 1.5 },
|
||||
layers: [],
|
||||
updatedAt: '2026-06-12T00:00:00.000Z',
|
||||
},
|
||||
viewport: { x: 8, y: 9, scale: 1.5 },
|
||||
layers: [],
|
||||
resources: [],
|
||||
updatedAt: '2026-06-12T00:00:00.000Z',
|
||||
},
|
||||
});
|
||||
|
||||
const project = await loadEditorProject('editor-project-1');
|
||||
|
||||
expect(project.viewport.scale).toBe(1.5);
|
||||
expect(requestJsonMock).toHaveBeenCalledWith(
|
||||
'/api/editor/projects/editor-project-1',
|
||||
{ method: 'GET' },
|
||||
'读取图片画布工程失败',
|
||||
expect.objectContaining({
|
||||
clearAuthOnUnauthorized: false,
|
||||
notifyAuthStateChange: false,
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('renames and deletes an editor project', async () => {
|
||||
requestJsonMock
|
||||
.mockResolvedValueOnce({
|
||||
project: {
|
||||
projectId: 'editor-project-1',
|
||||
title: '新标题',
|
||||
canvas: {
|
||||
canvasId: 'editor-project-1:canvas:default',
|
||||
projectId: 'editor-project-1',
|
||||
title: '默认画布',
|
||||
viewport: { x: 0, y: 0, scale: 1 },
|
||||
layers: [],
|
||||
updatedAt: '2026-06-12T00:00:00.000Z',
|
||||
},
|
||||
viewport: { x: 0, y: 0, scale: 1 },
|
||||
layers: [],
|
||||
resources: [],
|
||||
updatedAt: '2026-06-12T00:00:00.000Z',
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({ deletedProjectId: 'editor-project-1' });
|
||||
|
||||
await renameEditorProject('editor-project-1', '新标题');
|
||||
await deleteEditorProject('editor-project-1');
|
||||
|
||||
expect(requestJsonMock).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'/api/editor/projects/editor-project-1/metadata',
|
||||
expect.objectContaining({
|
||||
method: 'PATCH',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ title: '新标题' }),
|
||||
}),
|
||||
'重命名图片画布工程失败',
|
||||
expect.any(Object),
|
||||
);
|
||||
expect(requestJsonMock).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'/api/editor/projects/editor-project-1',
|
||||
{ method: 'DELETE' },
|
||||
'删除图片画布工程失败',
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it('creates a resource with upload or generated metadata', async () => {
|
||||
requestJsonMock.mockResolvedValueOnce({
|
||||
resource: {
|
||||
|
||||
@@ -131,6 +131,16 @@ function jsonRequest(method: 'POST' | 'PATCH', body: Record<string, unknown>) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function listEditorProjects() {
|
||||
const response = await requestJson<{ projects: EditorProjectSnapshot[] }>(
|
||||
EDITOR_PROJECT_API_BASE,
|
||||
{ method: 'GET' },
|
||||
'读取图片画布工程列表失败',
|
||||
EDITOR_PROJECT_REQUEST_OPTIONS,
|
||||
);
|
||||
return response.projects;
|
||||
}
|
||||
|
||||
export async function loadRecentEditorProject() {
|
||||
return requestJson<EditorProjectRecentResponse>(
|
||||
`${EDITOR_PROJECT_API_BASE}/recent`,
|
||||
@@ -158,6 +168,36 @@ export async function loadOrCreateRecentEditorProject() {
|
||||
return createEditorProject({ title: DEFAULT_PROJECT_TITLE });
|
||||
}
|
||||
|
||||
export async function loadEditorProject(projectId: string) {
|
||||
const response = await requestJson<EditorProjectResponse>(
|
||||
`${EDITOR_PROJECT_API_BASE}/${encodeURIComponent(projectId)}`,
|
||||
{ method: 'GET' },
|
||||
'读取图片画布工程失败',
|
||||
EDITOR_PROJECT_REQUEST_OPTIONS,
|
||||
);
|
||||
return response.project;
|
||||
}
|
||||
|
||||
export async function renameEditorProject(projectId: string, title: string) {
|
||||
const response = await requestJson<EditorProjectResponse>(
|
||||
`${EDITOR_PROJECT_API_BASE}/${encodeURIComponent(projectId)}/metadata`,
|
||||
jsonRequest('PATCH', { title }),
|
||||
'重命名图片画布工程失败',
|
||||
EDITOR_PROJECT_REQUEST_OPTIONS,
|
||||
);
|
||||
return response.project;
|
||||
}
|
||||
|
||||
export async function deleteEditorProject(projectId: string) {
|
||||
const response = await requestJson<{ deletedProjectId: string }>(
|
||||
`${EDITOR_PROJECT_API_BASE}/${encodeURIComponent(projectId)}`,
|
||||
{ method: 'DELETE' },
|
||||
'删除图片画布工程失败',
|
||||
EDITOR_PROJECT_REQUEST_OPTIONS,
|
||||
);
|
||||
return response.deletedProjectId;
|
||||
}
|
||||
|
||||
export async function saveEditorProjectLayout(
|
||||
projectId: string,
|
||||
input: EditorProjectLayoutSaveInput,
|
||||
|
||||
Reference in New Issue
Block a user