完善 AI 设计资产预览与视频首帧选择
需求:生成图片需要支持大图查看和本地下载;制作视频时需要从当前项目作品选择首帧,或上传本地图片。 实现:新增首帧选择弹窗、JPEG/PNG/WebP 有界上传、附件 Asset ID 透传、Main 到服务端 multipart 转发,并保留上传失败重试与私有图片下载链路。 验证:相关 57 项单测、TypeScript 类型检查、目标 ESLint 和 Vite 生产构建全部通过。
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import type {
|
||||
DesignAsset,
|
||||
DesignAssetUploadInput,
|
||||
DesignAssistantDeltaEvent,
|
||||
DesignBrief,
|
||||
DesignCapabilities,
|
||||
@@ -266,22 +267,26 @@ function mapTask(task: ServerTask): DesignGenerationTask {
|
||||
quoteId: task.quote_id,
|
||||
quotedDesignPoints: task.quoted_design_points,
|
||||
failureCode: task.failure_code,
|
||||
resultAssets: task.result_assets.map((asset) => ({
|
||||
assetId: asset.asset_id,
|
||||
workspaceId: task.workspace_id,
|
||||
mediaType: asset.media_type,
|
||||
mimeType: asset.mime_type,
|
||||
width: asset.width,
|
||||
height: asset.height,
|
||||
durationMilliseconds: asset.duration_milliseconds,
|
||||
createdAt: asset.created_at,
|
||||
contentPath: designAssetContentPath(task.workspace_id, asset.asset_id),
|
||||
})),
|
||||
resultAssets: task.result_assets.map((asset) => mapAsset(task.workspace_id, asset)),
|
||||
createdAt: task.created_at,
|
||||
updatedAt: task.updated_at,
|
||||
};
|
||||
}
|
||||
|
||||
function mapAsset(workspaceId: string, asset: ServerAsset): DesignAsset {
|
||||
return {
|
||||
assetId: asset.asset_id,
|
||||
workspaceId,
|
||||
mediaType: asset.media_type,
|
||||
mimeType: asset.mime_type,
|
||||
width: asset.width,
|
||||
height: asset.height,
|
||||
durationMilliseconds: asset.duration_milliseconds,
|
||||
createdAt: asset.created_at,
|
||||
contentPath: designAssetContentPath(workspaceId, asset.asset_id),
|
||||
};
|
||||
}
|
||||
|
||||
function createClientId(prefix: string): string {
|
||||
const id = globalThis.crypto?.randomUUID?.()
|
||||
?? `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
||||
@@ -913,6 +918,20 @@ export class WorksSquareDesignWorkspace implements DesignWorkspaceModule {
|
||||
return tasks.map(mapTask);
|
||||
}
|
||||
|
||||
async uploadAsset(input: DesignAssetUploadInput): Promise<DesignAsset> {
|
||||
const form = new FormData();
|
||||
form.set(
|
||||
'file',
|
||||
new Blob([Uint8Array.from(input.bytes)], { type: input.mimeType }),
|
||||
input.fileName,
|
||||
);
|
||||
const asset = await this.requestJson<ServerAsset>(
|
||||
`/api/design/workspaces/${encodeURIComponent(input.workspaceId)}/assets`,
|
||||
{ method: 'POST', body: form },
|
||||
);
|
||||
return mapAsset(input.workspaceId, asset);
|
||||
}
|
||||
|
||||
async openWorkspaceEvents(
|
||||
input: DesignWorkspaceEventSubscriptionInput,
|
||||
): Promise<DesignWorkspaceEventSubscription> {
|
||||
@@ -1141,7 +1160,9 @@ export class WorksSquareDesignWorkspace implements DesignWorkspaceModule {
|
||||
...init,
|
||||
headers: {
|
||||
Accept: 'application/json',
|
||||
...(init.body ? { 'Content-Type': 'application/json' } : {}),
|
||||
...(init.body && !(init.body instanceof FormData)
|
||||
? { 'Content-Type': 'application/json' }
|
||||
: {}),
|
||||
...(init.headers ?? {}),
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user