feat: wire real upload progress and subtype mapping

This commit is contained in:
wren
2026-05-06 18:33:53 +08:00
parent 57c744eb17
commit 61bbf6907b
2 changed files with 265 additions and 186 deletions
+85 -20
View File
@@ -147,8 +147,10 @@ export interface DocumentType {
name: string;
code?: string;
entryModuleId?: number;
entryModuleName?: string | null;
isEnabled?: boolean;
ruleSetIds?: number[];
childDocumentTypeIds?: number[];
}
export interface DocumentSubtypeGroup {
@@ -233,6 +235,12 @@ export interface UploadResult {
error?: string;
}
export interface UploadProgressInfo {
loaded: number;
total: number;
percent: number;
}
// 旧接口上传响应(uploadContractTemplate / appendContractAttachments 仍在使用)
interface LegacyUploadResponse {
success: boolean;
@@ -684,6 +692,7 @@ export async function uploadDocumentToServer(
autoRun: boolean = true,
speed: string = "normal",
jwtToken?: string,
onProgress?: (progress: UploadProgressInfo) => void,
): Promise<{ data: UploadResult } | { error: string; status?: number; payload?: UploadErrorPayload }> {
try {
const formData = new FormData();
@@ -711,7 +720,23 @@ export async function uploadDocumentToServer(
headers["Authorization"] = `Bearer ${jwtToken}`;
}
const response = await axios.post(`${API_BASE_URL}/api/upload`, formData, { headers });
const response = await axios.post(`${API_BASE_URL}/api/upload`, formData, {
headers,
onUploadProgress: (event) => {
const fileBlob = formData.get("file");
const fallbackTotal = fileBlob instanceof Blob ? fileBlob.size : binaryData.byteLength;
const total = Number(event.total || fallbackTotal);
const loaded = Number(event.loaded || 0);
if (!total || !onProgress) {
return;
}
onProgress({
loaded,
total,
percent: Math.min(100, Math.max(0, Number(((loaded / total) * 100).toFixed(2)))),
});
},
});
const body = response.data;
// Result<DocumentUploadVO> envelope
@@ -826,8 +851,6 @@ export async function getDocumentTypes(token?: string): Promise<{data: DocumentT
const params: Record<string, string> = {};
if (selectedModuleId) {
params.entry_module_id = String(selectedModuleId);
} else if (documentTypeIds && documentTypeIds.length > 0) {
params.ids = documentTypeIds.join(",");
}
const headers: Record<string, string> = {};
@@ -835,18 +858,52 @@ export async function getDocumentTypes(token?: string): Promise<{data: DocumentT
headers["Authorization"] = `Bearer ${token}`;
}
const response = await axios.get(`${API_BASE_URL}/api/document-types`, { params, headers });
const [response, groupRoots] = await Promise.all([
axios.get(`${API_BASE_URL}/api/v3/document-type-roots`, { params, headers }),
fetchAllEvaluationPointGroupRoots(token),
]);
const body = response.data;
if (body?.data && Array.isArray(body.data)) {
const types: DocumentType[] = body.data.map((item: { id: number; name: string; code?: string; entryModuleId?: number; isEnabled?: boolean; ruleSetIds?: number[] }) => ({
id: item.id,
name: item.name,
code: item.code,
entryModuleId: item.entryModuleId,
isEnabled: item.isEnabled,
ruleSetIds: item.ruleSetIds,
}));
let types: DocumentType[] = body.data.map((item: {
id: number;
name: string;
code?: string;
entryModuleId?: number | null;
entryModuleName?: string | null;
isEnabled?: boolean;
ruleSetIds?: number[];
}) => {
const matchedRoot = groupRoots.find((root: any) => Number(root?.id || 0) === Number(item.id));
const childDocumentTypeIds = Array.isArray(matchedRoot?.children)
? Array.from(
new Set(
matchedRoot.children
.map((child: any) => Number(child?.document_type_id || 0))
.filter((childId: number) => childId > 0),
),
)
: [];
return {
id: item.id,
name: item.name,
code: item.code,
entryModuleId: item.entryModuleId ?? null,
entryModuleName: item.entryModuleName ?? null,
isEnabled: item.isEnabled,
ruleSetIds: item.ruleSetIds,
childDocumentTypeIds,
};
});
if (!selectedModuleId && documentTypeIds && documentTypeIds.length > 0) {
types = types.filter((item) =>
documentTypeIds.includes(item.id) ||
(item.childDocumentTypeIds || []).some((childId) => documentTypeIds.includes(childId)),
);
}
return { data: types };
}
return { error: body?.message || "获取文档类型失败", status: response.status };
@@ -919,18 +976,26 @@ async function fetchAllEvaluationPointGroupRoots(token?: string): Promise<any[]>
function collectSubtypeGroupsFromRoots(
roots: any[],
documentTypeId: number,
rootOrDocumentTypeId: number,
entryModuleId?: number | null,
): DocumentSubtypeGroup[] {
return dedupeSubtypeGroups(
roots.flatMap((root: any) => {
if (!Array.isArray(root?.children)) return [];
if (entryModuleId && Number(root?.entry_module_id || 0) !== Number(entryModuleId)) {
return [];
}
const scopedRoots = roots.filter((root: any) => {
if (entryModuleId && Number(root?.entry_module_id || 0) !== Number(entryModuleId)) {
return false;
}
return true;
});
const matchedRoot = scopedRoots.find((root: any) => Number(root?.id || 0) === Number(rootOrDocumentTypeId));
if (matchedRoot && Array.isArray(matchedRoot.children)) {
return dedupeSubtypeGroups(matchedRoot.children.map((child: any) => mapSubtypeChild(child, matchedRoot)));
}
return dedupeSubtypeGroups(
scopedRoots.flatMap((root: any) => {
if (!Array.isArray(root?.children)) return [];
return root.children
.filter((child: any) => Number(child?.document_type_id || 0) === Number(documentTypeId))
.filter((child: any) => Number(child?.document_type_id || 0) === Number(rootOrDocumentTypeId))
.map((child: any) => mapSubtypeChild(child, root));
}),
);