2026-05-21-00-05-04 导入进度与压缩包支持

This commit is contained in:
2026-05-21 00:24:29 +08:00
parent dcd6fe56c7
commit 14c8eb153d
9 changed files with 640 additions and 27 deletions

View File

@@ -5,6 +5,12 @@ export type SegmentationExportMode = 'combined' | 'separate';
export type ProjectAssetImportKind = 'dicom' | 'stl';
export type { SegmentationExportScope } from '../types';
export interface ProjectAssetImportProgress {
loaded: number;
total: number;
percent: number;
}
async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
const response = await fetch(path, {
headers: {
@@ -30,6 +36,59 @@ async function request<T>(path: string, options: RequestInit = {}): Promise<T> {
return response.json() as Promise<T>;
}
function parseXhrError(xhr: XMLHttpRequest) {
let message = `请求失败:${xhr.status}`;
try {
const data = JSON.parse(xhr.responseText);
if (typeof data?.message === 'string') {
message = data.message;
}
} catch {
if (xhr.responseText) {
message = xhr.responseText.slice(0, 240);
}
}
return message;
}
function uploadProjectAssetFiles(
projectId: string,
kind: ProjectAssetImportKind,
files: File[],
onProgress?: (progress: ProjectAssetImportProgress) => void,
) {
return new Promise<Project>((resolve, reject) => {
const formData = new FormData();
formData.append('kind', kind);
files.forEach((file) => {
formData.append('files', file, file.name);
});
const xhr = new XMLHttpRequest();
xhr.open('POST', `/api/projects/${projectId}/import-assets`);
xhr.upload.onprogress = (event) => {
const total = event.lengthComputable ? event.total : files.reduce((sum, file) => sum + file.size, 0);
const loaded = event.lengthComputable ? event.loaded : Math.min(total, files.reduce((sum, file) => sum + file.size, 0));
const percent = total > 0 ? Math.min(100, Math.round((loaded / total) * 100)) : 0;
onProgress?.({ loaded, total, percent });
};
xhr.onload = () => {
if (xhr.status >= 200 && xhr.status < 300) {
try {
resolve(JSON.parse(xhr.responseText) as Project);
} catch {
reject(new Error('导入响应解析失败'));
}
return;
}
reject(new Error(parseXhrError(xhr)));
};
xhr.onerror = () => reject(new Error('网络连接中断,导入失败'));
xhr.onabort = () => reject(new Error('导入已取消'));
xhr.send(formData);
});
}
export const api = {
getSession: () => request<SessionState>('/api/session'),
login: (account: string, password: string) =>
@@ -65,11 +124,12 @@ export const api = {
method: 'PATCH',
body: JSON.stringify({ modelPoses }),
}),
importProjectAssets: (projectId: string, kind: ProjectAssetImportKind, files: Array<{ name: string; data: string }>) =>
request<Project>(`/api/projects/${projectId}/import-assets`, {
method: 'POST',
body: JSON.stringify({ kind, files }),
}),
importProjectAssets: (
projectId: string,
kind: ProjectAssetImportKind,
files: File[],
onProgress?: (progress: ProjectAssetImportProgress) => void,
) => uploadProjectAssetFiles(projectId, kind, files, onProgress),
saveProjectSegmentationResult: (
projectId: string,
payload: {