feat(desktop): 稳定桌面端界面与文件操作反馈

- 重构 DesktopPreferences 为分栏式设置面板,整合连接、外观、通知、更新与诊断信息分区,并补充过渡动效与暗色主题样式
- DesktopLayout 侧边栏导航分组支持展开折叠,调整管理/项目区块顺序并统一图标与标题
- 新增 fileTaskFeedback 工具,统一 pickFiles/saveFile/openFile 的成功/取消提示,替换审计导出、权限日志、附件、文档、线程、项目配置等处的直接调用
- desktopUpdateManager 暴露更新状态快照与状态变更监听,区分检查中、安装中、已推迟、失败等状态
- DesktopServerSettings 增加连接诊断信息(检查时间、健康地址、耗时、HTTP 状态)
- unified-page.css 与 ProjectMilestones 引入 CSS 变量以适配暗色主题
- WebLayout 将服务器设置入口改为打开系统偏好面板,管理菜单中邮件服务归入系统设置分组
- ProfileSettings 移除已迁入偏好面板的桌面端专属区块
- 补充 Layout.desktop 布局与偏好面板契约测试
This commit is contained in:
Cheng Zhou
2026-07-01 17:04:23 +08:00
parent 593c60c782
commit 8c8327df92
19 changed files with 2098 additions and 612 deletions
@@ -378,8 +378,8 @@ 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";
import { onDesktopRefreshCurrentView } from "../../composables/useDesktopRefresh";
import { openFileWithFeedback, pickFilesWithFeedback, saveFileWithFeedback } from "../../utils/fileTaskFeedback";
const route = useRoute();
const auth = useAuthStore();
@@ -524,7 +524,7 @@ const uploadDirtyGuard = useDrawerDirtyGuard(() => ({
}));
const triggerFileInput = async () => {
const [file] = await pickFiles({
const [file] = await pickFilesWithFeedback({
multiple: false,
accept: ["pdf", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "txt", "png", "jpg", "jpeg"],
title: "选择文档版本",
@@ -813,7 +813,7 @@ 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 });
await saveFile({ suggestedName: filename, mimeType: contentType, data: blob });
await saveFileWithFeedback({ suggestedName: filename, mimeType: contentType, data: blob });
} catch (e: any) { ElMessage.error(e?.response?.data?.message || TEXT.common.messages.downloadFailed); }
};
@@ -822,7 +822,7 @@ const openVersion = async (version: DocumentVersion) => {
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({
await openFileWithFeedback({
suggestedName: filename,
mimeType: contentType,
data: new Blob([response.data], { type: contentType }),