feat(*): 创作工作台 - 完善媒体上传预览与连线交互并修复任务章节及日志筛选
This commit is contained in:
@@ -567,7 +567,9 @@ export function updateAdminDrawingEngineSetting(input: Pick<CanvasDrawingEngineS
|
||||
return request<{ setting: CanvasDrawingEngineSetting }>(api.patch("/admin/settings/drawing-engine", input));
|
||||
}
|
||||
|
||||
export function listAdminApiLogs(params: AdminListParams = {}) {
|
||||
export type AdminApiLogParams = AdminListParams & { recordType?: "request" | "download" | "all" };
|
||||
|
||||
export function listAdminApiLogs(params: AdminApiLogParams = {}) {
|
||||
return request<{ logs: ApiCallLog[]; total: number; page: number; limit: number }>(api.get("/admin/api-logs", { params }));
|
||||
}
|
||||
|
||||
@@ -579,7 +581,7 @@ export function queryAdminApiLogTask(id: string) {
|
||||
return request<AdminProviderTaskQueryResult>(api.post(`/admin/api-logs/${encodeURIComponent(id)}/query-task`));
|
||||
}
|
||||
|
||||
export async function exportAdminApiLogs(params: AdminListParams & { ids?: string[] } = {}) {
|
||||
export async function exportAdminApiLogs(params: AdminApiLogParams & { ids?: string[] } = {}) {
|
||||
const response = await api.get<Blob>("/admin/api-logs-export.csv", { params: { ...params, ids: params.ids?.join(",") }, responseType: "blob" });
|
||||
return response.data;
|
||||
}
|
||||
|
||||
@@ -137,7 +137,12 @@ export async function uploadResourceFile(
|
||||
if (meta?.height) formData.append("height", String(Math.round(meta.height)));
|
||||
if (meta?.durationMs) formData.append("durationMs", String(Math.round(meta.durationMs)));
|
||||
try {
|
||||
const data = await request<{ resource: RemoteResource }>(api.post("/resources", formData, uploadRequestConfig(meta?.idempotencyKey)));
|
||||
const data = await request<{ resource: RemoteResource }>(api.post("/resources", formData, {
|
||||
...uploadRequestConfig(meta?.idempotencyKey),
|
||||
onUploadProgress: onProgress ? ({ loaded, total }) => {
|
||||
if (total && total > 0) onProgress(Math.min(file.size, file.size * loaded / total), file.size);
|
||||
} : undefined,
|
||||
}));
|
||||
resourceCache.set(resourceCacheKey(data.resource.id), data.resource);
|
||||
return data.resource;
|
||||
} catch (error) {
|
||||
@@ -170,7 +175,10 @@ async function runChunkedUpload(file: Blob, name: string, kind: "image" | "video
|
||||
const data = new FormData();
|
||||
data.append("chunk", blob);
|
||||
// raw 二进制直传,与后端按裸 body 逐片落盘对齐(勿设手动 Content-Type,让 axios 处理)。
|
||||
await request<{ index: number }>(api.put(`/resources/uploads/${encodeURIComponent(session.uploadId)}/chunks/${index}`, blob, { headers: { "Content-Type": "application/octet-stream" } }));
|
||||
await request<{ index: number }>(api.put(`/resources/uploads/${encodeURIComponent(session.uploadId)}/chunks/${index}`, blob, {
|
||||
headers: { "Content-Type": "application/octet-stream" },
|
||||
onUploadProgress: onProgress ? ({ loaded }) => onProgress(start + Math.min(loaded, blob.size), file.size) : undefined,
|
||||
}));
|
||||
onProgress?.(Math.min(end, file.size), file.size);
|
||||
}
|
||||
const complete = await request<{ resource: RemoteResource }>(api.post(`/resources/uploads/${encodeURIComponent(session.uploadId)}/complete`));
|
||||
|
||||
@@ -12,7 +12,7 @@ export type UploadedFile = { url: string; storageKey: string; bytes: number; mim
|
||||
const store = localforage.createInstance({ name: "infinite-canvas", storeName: "media_files" });
|
||||
const objectUrls = new Map<string, string>();
|
||||
|
||||
export async function uploadMediaFile(input: string | Blob, prefix = "file"): Promise<UploadedFile> {
|
||||
export async function uploadMediaFile(input: string | Blob, prefix = "file", onProgress?: (uploadedBytes: number, totalBytes: number) => void): Promise<UploadedFile> {
|
||||
// 直传和失败后的本地同步必须复用同一上传身份,避免响应丢失后创建第二个对象。
|
||||
const storageKey = `${prefix}:${getActiveUserScope()}:${nanoid()}`;
|
||||
const blob = typeof input === "string" ? await (await fetch(input)).blob() : input;
|
||||
@@ -31,7 +31,7 @@ export async function uploadMediaFile(input: string | Blob, prefix = "file"): Pr
|
||||
const poster = captured?.poster ? await uploadImage(captured.poster).catch(() => undefined) : undefined;
|
||||
try {
|
||||
const kind = blob.type.startsWith("video/") ? "video" : blob.type.startsWith("audio/") ? "audio" : "file";
|
||||
const resource = await uploadResourceFile(blob, kind, { ...meta, fileName: input instanceof File ? input.name : undefined, idempotencyKey: storageKey });
|
||||
const resource = await uploadResourceFile(blob, kind, { ...meta, fileName: input instanceof File ? input.name : undefined, idempotencyKey: storageKey }, onProgress);
|
||||
await primeResourceBlobCache(resourceStorageKey(resource.id), blob).catch(() => "");
|
||||
URL.revokeObjectURL(previewUrl);
|
||||
return { url: resource.publicUrl || resourceFileUrl(resource.id), storageKey: resourceStorageKey(resource.id), bytes: resource.size || blob.size, mimeType: resource.mimeType || blob.type || "application/octet-stream", width: resource.width || meta.width, height: resource.height || meta.height, durationMs: resource.durationMs || meta.durationMs, hasAudio: meta.hasAudio, preview: poster };
|
||||
|
||||
@@ -18,7 +18,7 @@ export type UploadedImage = {
|
||||
const store = localforage.createInstance({ name: "infinite-canvas", storeName: "image_files" });
|
||||
const objectUrls = new Map<string, string>();
|
||||
|
||||
export async function uploadImage(input: string | Blob): Promise<UploadedImage> {
|
||||
export async function uploadImage(input: string | Blob, onProgress?: (uploadedBytes: number, totalBytes: number) => void): Promise<UploadedImage> {
|
||||
// 同一个逻辑上传在直传失败后会退回 IndexedDB,并由云端数据同步再次提交。
|
||||
// 提前生成本地 key,确保两条路径向后端发送相同的幂等标识。
|
||||
const storageKey = `image:${getActiveUserScope()}:${nanoid()}`;
|
||||
@@ -41,7 +41,7 @@ export async function uploadImage(input: string | Blob): Promise<UploadedImage>
|
||||
const previewUrl = URL.createObjectURL(blob);
|
||||
const meta = await readImageMeta(previewUrl);
|
||||
try {
|
||||
const resource = await uploadResourceFile(blob, "image", { width: meta.width, height: meta.height, fileName: input instanceof File ? input.name : undefined, idempotencyKey: storageKey });
|
||||
const resource = await uploadResourceFile(blob, "image", { width: meta.width, height: meta.height, fileName: input instanceof File ? input.name : undefined, idempotencyKey: storageKey }, onProgress);
|
||||
await primeResourceBlobCache(resourceStorageKey(resource.id), blob).catch(() => "");
|
||||
URL.revokeObjectURL(previewUrl);
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user