feat(desktop): 收口桌面工作台视觉与活动反馈
This commit is contained in:
@@ -154,6 +154,7 @@ import { isApiPermissionAllowed } from "../../utils/apiPermissionValue";
|
||||
import { TEXT } from "../../locales";
|
||||
import { clientRuntime } from "../../runtime";
|
||||
import { openFileWithFeedback, pickFilesWithFeedback, saveFileWithFeedback } from "../../utils/fileTaskFeedback";
|
||||
import { finishDesktopActivity, startDesktopActivity, updateDesktopActivity } from "../../session/desktopActivityCenter";
|
||||
|
||||
type AttachmentEntityGroup = {
|
||||
entityType: string;
|
||||
@@ -253,6 +254,7 @@ const tableProgress = computed(() => {
|
||||
return group ? progressMap[group.key] || 0 : 0;
|
||||
});
|
||||
const isImmediateUploading = computed(() => tableProgress.value > 0 && tableProgress.value < 100);
|
||||
const desktopActivityEnabled = () => nativeFiles;
|
||||
|
||||
const validateFile = (file: File) => {
|
||||
if (file.size > maxSize.value * 1024 * 1024) {
|
||||
@@ -293,14 +295,21 @@ const pendingSnapshot = () =>
|
||||
uploadGroups.value.flatMap((group) => (pendingMap[group.key] || []).map((item) => `${group.key}:${pendingFileKey(item.file)}`));
|
||||
const hasPendingUploads = computed(() => uploadGroups.value.some((group) => (pendingMap[group.key] || []).length > 0));
|
||||
|
||||
const uploadFile = async (group: UploadGroup, file: File, targetEntityId: string) => {
|
||||
const uploadFile = async (
|
||||
group: UploadGroup,
|
||||
file: File,
|
||||
targetEntityId: string,
|
||||
onProgress?: (progress: number) => void,
|
||||
) => {
|
||||
if (!props.studyId || !targetEntityId) return;
|
||||
if (!validateFile(file)) throw new Error("invalid file");
|
||||
progressMap[group.key] = 0;
|
||||
await uploadAttachment(props.studyId, group.entityType, targetEntityId, file, {
|
||||
onUploadProgress: (evt: ProgressEvent) => {
|
||||
if (evt.total) {
|
||||
progressMap[group.key] = Math.round((evt.loaded / evt.total) * 100);
|
||||
const progress = Math.round((evt.loaded / evt.total) * 100);
|
||||
progressMap[group.key] = progress;
|
||||
onProgress?.(progress);
|
||||
}
|
||||
},
|
||||
});
|
||||
@@ -313,6 +322,16 @@ const uploadPending = async (entityId?: string) => {
|
||||
}
|
||||
if (!targetEntityId) return;
|
||||
let hasFailure = false;
|
||||
const totalCount = uploadGroups.value.reduce((sum, group) => sum + (pendingMap[group.key] || []).length, 0);
|
||||
const activityId = desktopActivityEnabled() && totalCount > 0
|
||||
? startDesktopActivity({
|
||||
kind: "upload",
|
||||
title: "上传附件",
|
||||
detail: `正在上传 ${totalCount} 个附件`,
|
||||
progress: 0,
|
||||
})
|
||||
: "";
|
||||
let completedCount = 0;
|
||||
for (const group of uploadGroups.value) {
|
||||
const items = [...(pendingMap[group.key] || [])];
|
||||
const remainItems: PendingUploadItem[] = [];
|
||||
@@ -320,19 +339,36 @@ const uploadPending = async (entityId?: string) => {
|
||||
try {
|
||||
item.status = "uploading";
|
||||
item.error = "";
|
||||
await uploadFile(group, item.file, targetEntityId);
|
||||
await uploadFile(group, item.file, targetEntityId, (progress) => {
|
||||
if (!activityId || !totalCount) return;
|
||||
updateDesktopActivity(activityId, {
|
||||
detail: `正在上传 ${completedCount + 1}/${totalCount}`,
|
||||
progress: Math.min(99, Math.round(((completedCount + progress / 100) / totalCount) * 100)),
|
||||
});
|
||||
});
|
||||
} catch (e: any) {
|
||||
hasFailure = true;
|
||||
item.status = "failed";
|
||||
item.error = e?.response?.data?.message || TEXT.common.messages.uploadFailed;
|
||||
remainItems.push(item);
|
||||
} finally {
|
||||
completedCount += 1;
|
||||
if (activityId && totalCount) {
|
||||
updateDesktopActivity(activityId, {
|
||||
detail: `已处理 ${completedCount}/${totalCount}`,
|
||||
progress: Math.min(99, Math.round((completedCount / totalCount) * 100)),
|
||||
});
|
||||
}
|
||||
progressMap[group.key] = 0;
|
||||
}
|
||||
}
|
||||
pendingMap[group.key] = remainItems;
|
||||
}
|
||||
if (hasFailure) throw new Error(TEXT.common.messages.uploadFailed);
|
||||
if (hasFailure) {
|
||||
finishDesktopActivity(activityId, "failed", { detail: "部分附件上传失败" });
|
||||
throw new Error(TEXT.common.messages.uploadFailed);
|
||||
}
|
||||
finishDesktopActivity(activityId, "completed", { detail: "附件上传完成" });
|
||||
};
|
||||
|
||||
const uploadImmediate = async (options: any) => {
|
||||
@@ -340,11 +376,18 @@ const uploadImmediate = async (options: any) => {
|
||||
const group = tableUploadGroup.value;
|
||||
if (!file || !group || !props.entityId) return;
|
||||
if (!validateFile(file)) return;
|
||||
const activityId = desktopActivityEnabled()
|
||||
? startDesktopActivity({ kind: "upload", title: "上传附件", detail: "正在上传附件", progress: 0 })
|
||||
: "";
|
||||
try {
|
||||
await uploadFile(group, file, props.entityId);
|
||||
ElMessage.success(TEXT.common.messages.uploadSuccess);
|
||||
await uploadFile(group, file, props.entityId, (progress) => {
|
||||
updateDesktopActivity(activityId, { detail: "正在上传附件", progress });
|
||||
});
|
||||
finishDesktopActivity(activityId, "completed", { detail: "附件上传完成" });
|
||||
if (!desktopActivityEnabled()) ElMessage.success(TEXT.common.messages.uploadSuccess);
|
||||
load();
|
||||
} catch (e: any) {
|
||||
finishDesktopActivity(activityId, "failed", { detail: "附件上传失败" });
|
||||
ElMessage.error(e?.response?.data?.message || e?.message || TEXT.common.messages.uploadFailed);
|
||||
} finally {
|
||||
progressMap[group.key] = 0;
|
||||
@@ -399,25 +442,48 @@ const fetchAttachmentBlob = async (row: any): Promise<Blob> => {
|
||||
};
|
||||
|
||||
const download = async (row: any) => {
|
||||
const activityId = desktopActivityEnabled()
|
||||
? startDesktopActivity({ kind: "download", title: "下载附件", detail: "正在从服务器获取文件", progress: 20 })
|
||||
: "";
|
||||
try {
|
||||
const blob = await fetchAttachmentBlob(row);
|
||||
updateDesktopActivity(activityId, { detail: "等待选择保存位置", progress: 65 });
|
||||
await saveFileWithFeedback({
|
||||
suggestedName: row?.filename || "download",
|
||||
mimeType: row?.content_type,
|
||||
data: await fetchAttachmentBlob(row),
|
||||
data: blob,
|
||||
}, {
|
||||
activityId,
|
||||
title: "下载附件",
|
||||
pendingDetail: "等待选择保存位置",
|
||||
completedDetail: "附件已保存",
|
||||
cancelledDetail: "已取消保存附件",
|
||||
});
|
||||
} catch {
|
||||
finishDesktopActivity(activityId, "failed", { detail: "附件下载失败" });
|
||||
ElMessage.error(TEXT.common.messages.downloadFailed);
|
||||
}
|
||||
};
|
||||
|
||||
const openExternally = async (row: any) => {
|
||||
const activityId = desktopActivityEnabled()
|
||||
? startDesktopActivity({ kind: "open", title: "打开附件", detail: "正在从服务器获取文件", progress: 20 })
|
||||
: "";
|
||||
try {
|
||||
const blob = await fetchAttachmentBlob(row);
|
||||
updateDesktopActivity(activityId, { detail: "正在准备文件", progress: 65 });
|
||||
await openFileWithFeedback({
|
||||
suggestedName: row?.filename || "attachment",
|
||||
mimeType: row?.content_type,
|
||||
data: await fetchAttachmentBlob(row),
|
||||
data: blob,
|
||||
}, {
|
||||
activityId,
|
||||
title: "打开附件",
|
||||
pendingDetail: "正在准备文件",
|
||||
completedDetail: "已交给系统打开",
|
||||
});
|
||||
} catch {
|
||||
finishDesktopActivity(activityId, "failed", { detail: "附件打开失败" });
|
||||
ElMessage.error(TEXT.common.messages.previewNotSupported);
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user