feat(desktop): implement phase 2 native capabilities
Client Quality Gates / Shared client and Web (push) Has been cancelled
Client Quality Gates / macOS Desktop (push) Has been cancelled

This commit is contained in:
Cheng Zhou
2026-06-30 21:21:55 +08:00
parent 7c721d4e5c
commit 628ff8828b
64 changed files with 3516 additions and 222 deletions
+29 -13
View File
@@ -123,6 +123,9 @@
<el-button link type="primary" size="small" @click="downloadVersion(row)" v-if="canReadDocument">
{{ TEXT.modules.fileVersionManagement.actions.download }}
</el-button>
<el-button link type="primary" size="small" @click="openVersion(row)" v-if="canReadDocument">
打开
</el-button>
<el-button v-if="canDeleteDocument" link type="danger" size="small" @click="confirmDeleteVersion(row)">
{{ TEXT.common.actions.delete }}
</el-button>
@@ -262,7 +265,6 @@
上传文件
</div>
<div class="upload-zone" :class="{ 'has-file': uploadFile }" @click="triggerFileInput">
<input type="file" ref="fileInputRef" @change="onFileChange" class="file-input-hidden" accept=".pdf,.doc,.docx,.xls,.xlsx,.ppt,.pptx,.txt,.png,.jpg" />
<template v-if="!uploadFile">
<div class="upload-zone-icon">
<svg width="32" height="32" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="17 8 12 3 7 8"/><line x1="12" y1="3" x2="12" y2="15"/></svg>
@@ -370,6 +372,7 @@ import { useRoleTemplateMeta } from "../../composables/useRoleTemplateMeta";
import { usePermission } from "../../utils/permission";
import StateError from "../../components/StateError.vue";
import StateLoading from "../../components/StateLoading.vue";
import { openFile, pickFiles, saveFile } from "../../runtime";
const route = useRoute();
const auth = useAuthStore();
@@ -494,7 +497,6 @@ const editorDirtyGuard = useDrawerDirtyGuard(() => editorForm);
const uploadVisible = ref(false);
const uploading = ref(false);
const uploadFormRef = ref<FormInstance>();
const fileInputRef = ref<HTMLInputElement | null>(null);
const uploadForm = reactive({
version_no: "",
version_date: "",
@@ -513,8 +515,15 @@ const uploadDirtyGuard = useDrawerDirtyGuard(() => ({
: null,
}));
const triggerFileInput = () => { fileInputRef.value?.click(); };
const removeFile = () => { uploadFile.value = null; if (fileInputRef.value) fileInputRef.value.value = ""; };
const triggerFileInput = async () => {
const [file] = await pickFiles({
multiple: false,
accept: ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "png", "jpg", "jpeg"],
title: "选择文档版本",
});
if (file) uploadFile.value = file;
};
const removeFile = () => { uploadFile.value = null; };
const distributeVisible = ref(false);
const distributing = ref(false);
@@ -706,11 +715,6 @@ const openUpload = () => {
uploadVisible.value = true;
};
const onFileChange = (event: Event) => {
const target = event.target as HTMLInputElement;
if (target.files && target.files[0]) uploadFile.value = target.files[0];
};
const submitUpload = async () => {
if (!canUpdateDocument.value) { ElMessage.warning("权限不足"); return; }
if (!uploadFormRef.value) return;
@@ -797,13 +801,25 @@ const downloadVersion = async (version: DocumentVersion) => {
const contentType = response.headers?.["content-type"] || "application/octet-stream";
const filename = getFilename(response.headers?.["content-disposition"]) || `document-${version.version_no || version.id}.bin`;
const blob = new Blob([response.data], { type: contentType });
const url = window.URL.createObjectURL(blob);
const link = document.createElement("a"); link.href = url; link.download = filename;
document.body.appendChild(link); link.click(); link.remove();
window.URL.revokeObjectURL(url);
await saveFile({ suggestedName: filename, mimeType: contentType, data: blob });
} catch (e: any) { ElMessage.error(e?.response?.data?.message || TEXT.common.messages.downloadFailed); }
};
const openVersion = async (version: DocumentVersion) => {
try {
const response = await downloadDocumentVersion(version.id);
const contentType = response.headers?.["content-type"] || "application/octet-stream";
const filename = getFilename(response.headers?.["content-disposition"]) || `document-${version.version_no || version.id}.bin`;
await openFile({
suggestedName: filename,
mimeType: contentType,
data: new Blob([response.data], { type: contentType }),
});
} catch (e: any) {
ElMessage.error(e?.response?.data?.message || TEXT.common.messages.downloadFailed);
}
};
const confirmDeleteVersion = async (version: DocumentVersion) => {
if (!version?.id) return;
if (!canDeleteDocument.value) { ElMessage.warning("权限不足"); return; }