2026-05-21-00-05-04 导入进度与压缩包支持
This commit is contained in:
@@ -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: {
|
||||
|
||||
Reference in New Issue
Block a user