refactor(monitor): ♻️ 优化代码
This commit is contained in:
parent
0b2c1ca83f
commit
cc30c088c4
|
|
@ -2,9 +2,9 @@
|
|||
"permissions": {
|
||||
"defaultMode": "acceptEdits",
|
||||
"allow": [
|
||||
"Read(./)",
|
||||
"Edit(./)",
|
||||
"Write(./)",
|
||||
"Read",
|
||||
"Edit",
|
||||
"Write",
|
||||
|
||||
"Bash(pnpm build *)",
|
||||
"Bash(pnpm install:*)",
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
"version:major": "node scripts/bump-version.mjs major",
|
||||
"publish": "node scripts/publish.mjs",
|
||||
"deploy": "npm run tauri-build && npm run publish",
|
||||
"type-check": "vue-tsc --noEmit",
|
||||
"lint": "eslint src",
|
||||
"lint:fix": "eslint src --fix",
|
||||
"commit": "git-cz",
|
||||
|
|
|
|||
|
|
@ -14,17 +14,17 @@ import MonitorControl from "@/components/MonitorControl.vue";
|
|||
import SettingsPanel from "@/components/SettingsPanel.vue";
|
||||
import StatusHeader from "@/components/StatusHeader.vue";
|
||||
import UpdateDialog from "@/components/UpdateDialog.vue";
|
||||
import { useUpdater } from "@/composables/useUpdater";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { useLogStore } from "@/stores/log";
|
||||
import { useMonitorStore } from "@/stores/monitor";
|
||||
import { useNetworkStore } from "@/stores/network";
|
||||
import { useUpdaterStore } from "@/stores/updater";
|
||||
|
||||
const activeTab = ref("monitor");
|
||||
const store = useMonitorStore();
|
||||
const appStore = useAppStore();
|
||||
const logStore = useLogStore();
|
||||
const updater = useUpdater();
|
||||
const updater = useUpdaterStore();
|
||||
const networkStore = useNetworkStore();
|
||||
|
||||
/** 通知动作事件的取消监听函数 */
|
||||
|
|
@ -88,7 +88,7 @@ onMounted(async () => {
|
|||
|
||||
getVersion()
|
||||
.then((v) => {
|
||||
updater.currentVersion.value = v;
|
||||
updater.currentVersion = v;
|
||||
})
|
||||
.catch(() => {});
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,11 @@ import { CloudSyncOutlined } from "@ant-design/icons-vue";
|
|||
import dayjs, { type Dayjs } from "dayjs";
|
||||
import { debounce } from "lodash-es";
|
||||
import { computed, watch } from "vue";
|
||||
import { useUpdater } from "@/composables/useUpdater";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { useUpdaterStore } from "@/stores/updater";
|
||||
|
||||
const appStore = useAppStore();
|
||||
const { currentVersion, isCheckingUpdates, isUpdating, checkForUpdates } = useUpdater();
|
||||
const { currentVersion, isCheckingUpdates, isUpdating, checkForUpdates } = useUpdaterStore();
|
||||
|
||||
// ===== 检查间隔:内部用秒,TimePicker 用 Dayjs =====
|
||||
const intervalTime = computed<Dayjs>({
|
||||
|
|
|
|||
|
|
@ -1,22 +1,28 @@
|
|||
<script setup lang="ts">
|
||||
import { storeToRefs } from "pinia";
|
||||
import { computed } from "vue";
|
||||
import { useUpdater } from "@/composables/useUpdater";
|
||||
import { useUpdaterStore } from "@/stores/updater";
|
||||
|
||||
const { showUpdateDialog, updateInfo, updateProgress, isUpdating, confirmUpdate, dismissUpdate } = useUpdater();
|
||||
const store = useUpdaterStore();
|
||||
const { showUpdateDialog, updateInfo, updateProgress, isUpdating } = storeToRefs(store);
|
||||
const { confirmUpdate, dismissUpdate } = store;
|
||||
|
||||
const progressPercent = computed(() => {
|
||||
if (!updateProgress.value || !updateProgress.value.total) return 0;
|
||||
return Math.round((updateProgress.value.downloaded / updateProgress.value.total) * 100);
|
||||
const progress = updateProgress.value;
|
||||
if (!progress || !progress.total) return 0;
|
||||
return Math.round((progress.downloaded / progress.total) * 100);
|
||||
});
|
||||
|
||||
const downloadedMB = computed(() => {
|
||||
if (!updateProgress.value) return "0";
|
||||
return (updateProgress.value.downloaded / 1024 / 1024).toFixed(1);
|
||||
const progress = updateProgress.value;
|
||||
if (!progress) return "0";
|
||||
return (progress.downloaded / 1024 / 1024).toFixed(1);
|
||||
});
|
||||
|
||||
const totalMB = computed(() => {
|
||||
if (!updateProgress.value || !updateProgress.value.total) return "0";
|
||||
return (updateProgress.value.total / 1024 / 1024).toFixed(1);
|
||||
const progress = updateProgress.value;
|
||||
if (!progress || !progress.total) return "0";
|
||||
return (progress.total / 1024 / 1024).toFixed(1);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
import { storeToRefs } from "pinia";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { useLogStore } from "@/stores/log";
|
||||
import { getErrorMessage } from "@/utils/common";
|
||||
|
||||
/** 通知 Composable */
|
||||
export function useNotification() {
|
||||
|
|
@ -44,8 +45,8 @@ export function useNotification() {
|
|||
await invoke("send_clickable_notification", { title, body });
|
||||
playNotificationSound();
|
||||
addLog("INFO", `通知: ${title} - ${body}`, "NOTIFICATION");
|
||||
} catch (e: any) {
|
||||
addLog("ERROR", `发送通知失败: ${e}`, "NOTIFICATION");
|
||||
} catch (e: unknown) {
|
||||
addLog("ERROR", `发送通知失败: ${getErrorMessage(e)}`, "NOTIFICATION");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import { type Ref } from "vue";
|
|||
import { CRM_URL } from "@/constants/app";
|
||||
import { useLogStore } from "@/stores/log";
|
||||
import { type TicketCounts } from "@/stores/monitor";
|
||||
import { getErrorMessage } from "@/utils/common";
|
||||
|
||||
/**
|
||||
* @param isMonitoring 是否正在监测
|
||||
|
|
@ -161,8 +162,8 @@ export function useTray(
|
|||
const icon = await Image.new(await makeTrayIconRGBA(...color), 32, 32);
|
||||
await trayIconInstance.setIcon(icon);
|
||||
await trayIconInstance.setTooltip(tooltip);
|
||||
} catch (e: any) {
|
||||
addLog("WARN", `更新托盘图标失败: ${e.message || e}`, "SYSTEM");
|
||||
} catch (e: unknown) {
|
||||
addLog("WARN", `更新托盘图标失败: ${getErrorMessage(e)}`, "SYSTEM");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -228,8 +229,8 @@ export function useTray(
|
|||
try {
|
||||
const menu = await buildTrayMenu();
|
||||
await trayIconInstance.setMenu(menu);
|
||||
} catch (e: any) {
|
||||
addLog("WARN", `更新托盘菜单失败: ${e.message || e}`, "SYSTEM");
|
||||
} catch (e: unknown) {
|
||||
addLog("WARN", `更新托盘菜单失败: ${getErrorMessage(e)}`, "SYSTEM");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -264,8 +265,8 @@ export function useTray(
|
|||
},
|
||||
});
|
||||
addLog("INFO", "系统托盘创建成功", "SYSTEM");
|
||||
} catch (e: any) {
|
||||
addLog("ERROR", `系统托盘创建失败: ${e.message || e}`, "SYSTEM");
|
||||
} catch (e: unknown) {
|
||||
addLog("ERROR", `系统托盘创建失败: ${getErrorMessage(e)}`, "SYSTEM");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
import { defineStore, acceptHMRUpdate } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import { useLogStore } from "@/stores/log";
|
||||
import { getErrorMessage } from "@/utils/common";
|
||||
|
||||
export const useAppStore = defineStore("app", () => {
|
||||
const message = ref(""); // 界面状态提示消息
|
||||
|
|
@ -45,8 +46,8 @@ export const useAppStore = defineStore("app", () => {
|
|||
config.value = { ...config.value, ...data };
|
||||
addLog("INFO", "已加载保存的配置", "CONFIG");
|
||||
}
|
||||
} catch (e: any) {
|
||||
addLog("ERROR", `加载配置失败: ${e.message || e}`, "CONFIG");
|
||||
} catch (e: unknown) {
|
||||
addLog("ERROR", `加载配置失败: ${getErrorMessage(e)}`, "CONFIG");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -71,8 +72,8 @@ export const useAppStore = defineStore("app", () => {
|
|||
`自启动设置: ${config.value.auto_start ? "启用" : "禁用"}`,
|
||||
"CONFIG",
|
||||
);
|
||||
} catch (e: any) {
|
||||
addLog("ERROR", `自启动设置失败: ${e.message || e}`, "CONFIG");
|
||||
} catch (e: unknown) {
|
||||
addLog("ERROR", `自启动设置失败: ${getErrorMessage(e)}`, "CONFIG");
|
||||
}
|
||||
saveConfigToStorage();
|
||||
setMessage("配置更新成功");
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { useTray } from "@/composables/useTray";
|
|||
import { LOGIN_URL, CHECK_URL, WORK_ORDER_URL } from "@/constants/api";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { useLogStore } from "@/stores/log";
|
||||
import { formatTime } from "@/utils/common";
|
||||
import { formatTime, getErrorMessage } from "@/utils/common";
|
||||
|
||||
export interface TicketCounts {
|
||||
pending: number;
|
||||
|
|
@ -73,7 +73,7 @@ export const useMonitorStore = defineStore("monitor", () => {
|
|||
});
|
||||
|
||||
const setCookie = resp.headers.get("set-cookie") || "";
|
||||
const match = setCookie.match(/ASP\.NET_SessionId=([^;]+)/);
|
||||
const match = /ASP\.NET_SessionId=([^;]+)/.exec(setCookie);
|
||||
if (match) sessionId = match[1];
|
||||
addLog("DEBUG", `SessionId: ${sessionId || "(未提取)"}`, "LOGIN");
|
||||
|
||||
|
|
@ -91,8 +91,8 @@ export const useMonitorStore = defineStore("monitor", () => {
|
|||
addLog("ERROR", `登录失败: ${data.msg}`, "LOGIN");
|
||||
return false;
|
||||
}
|
||||
} catch (e: any) {
|
||||
addLog("ERROR", `登录请求失败: ${e.message || e}`, "LOGIN");
|
||||
} catch (e: unknown) {
|
||||
addLog("ERROR", `登录请求失败: ${getErrorMessage(e)}`, "LOGIN");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -123,65 +123,94 @@ export const useMonitorStore = defineStore("monitor", () => {
|
|||
addLog("WARN", `待审核 API 返回异常: ${result.msg}`, "SCHEDULER");
|
||||
return 0;
|
||||
}
|
||||
} catch (e: any) {
|
||||
addLog("ERROR", `待审核 API 检查失败: ${e.message || e}`, "SCHEDULER");
|
||||
} catch (e: unknown) {
|
||||
addLog(
|
||||
"ERROR",
|
||||
`待审核 API 检查失败: ${getErrorMessage(e)}`,
|
||||
"SCHEDULER",
|
||||
);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
/** 发送工单状态检查请求 */
|
||||
async function performStatusCheckRequest() {
|
||||
const body: Record<string, string> = {};
|
||||
if (userGid) body.UserGID = userGid;
|
||||
const resp = await fetch(CHECK_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Cookie: `ASP.NET_SessionId=${sessionId}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return await resp.json();
|
||||
}
|
||||
|
||||
/** 处理检查到的工单数据并更新状态 */
|
||||
async function updateTicketCountsAndLog(data: any) {
|
||||
const pending = data.PendingCount || 0;
|
||||
const stayclose = data.StaycloseCount || 0;
|
||||
const confirm = data.ConfirmCount || 0;
|
||||
const workOrderCount = await apiCheckWorkOrders();
|
||||
|
||||
const counts = {
|
||||
pending,
|
||||
stayclose,
|
||||
confirm,
|
||||
workOrderCount,
|
||||
lastCheck: formatTime(new Date()),
|
||||
};
|
||||
ticketCounts.value = counts;
|
||||
|
||||
addLog(
|
||||
"INFO",
|
||||
`检查完成 - 待处理: ${pending}, 待关闭: ${stayclose}, 待确认: ${confirm}, 待审核: ${workOrderCount}`,
|
||||
"SCHEDULER",
|
||||
);
|
||||
return counts;
|
||||
}
|
||||
|
||||
/** 发送工单提醒通知 */
|
||||
async function sendTicketNotifications(counts: TicketCounts) {
|
||||
const msgs: string[] = [];
|
||||
if (counts.pending > 0) msgs.push(`待处理工单: ${counts.pending}`);
|
||||
if (counts.stayclose > 0) msgs.push(`待关闭工单: ${counts.stayclose}`);
|
||||
if (counts.confirm > 0) msgs.push(`待确认工单: ${counts.confirm}`);
|
||||
if (counts.workOrderCount > 0)
|
||||
msgs.push(`待审核: ${counts.workOrderCount}`);
|
||||
|
||||
if (msgs.length > 0) {
|
||||
await notify("工单提醒", msgs.join(","));
|
||||
}
|
||||
}
|
||||
|
||||
/** 拉取工单状态计数,触发通知;isRetry=true 时不再递归重登 */
|
||||
async function apiCheckStatus(isRetry = false): Promise<void> {
|
||||
// 1. 预检查:如果会话失效且是密码模式,尝试重登
|
||||
if (!userGid && loginMode.value === "password") {
|
||||
addLog("WARN", "会话已失效,尝试重新登录", "SCHEDULER");
|
||||
if (!isRetry) await tryReLogin();
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const body: Record<string, string> = {};
|
||||
if (userGid) body.UserGID = userGid;
|
||||
const resp = await fetch(CHECK_URL, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Cookie: `ASP.NET_SessionId=${sessionId}`,
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const result = await resp.json();
|
||||
const result = await performStatusCheckRequest();
|
||||
if (result.success && result.data) {
|
||||
const d = result.data;
|
||||
const pending = d.PendingCount || 0;
|
||||
const stayclose = d.StaycloseCount || 0;
|
||||
const confirm = d.ConfirmCount || 0;
|
||||
const workOrderCount = await apiCheckWorkOrders();
|
||||
ticketCounts.value = {
|
||||
pending,
|
||||
stayclose,
|
||||
confirm,
|
||||
workOrderCount,
|
||||
lastCheck: formatTime(new Date()),
|
||||
};
|
||||
addLog(
|
||||
"INFO",
|
||||
`检查完成 - 待处理: ${pending}, 待关闭: ${stayclose}, 待确认: ${confirm}, 待审核: ${workOrderCount}`,
|
||||
"SCHEDULER",
|
||||
);
|
||||
if (pending > 0 || stayclose > 0 || confirm > 0 || workOrderCount > 0) {
|
||||
const msgs: string[] = [];
|
||||
if (pending > 0) msgs.push(`待处理工单: ${pending}`);
|
||||
if (stayclose > 0) msgs.push(`待关闭工单: ${stayclose}`);
|
||||
if (confirm > 0) msgs.push(`待确认工单: ${confirm}`);
|
||||
if (workOrderCount > 0) msgs.push(`待审核: ${workOrderCount}`);
|
||||
await notify("工单提醒", msgs.join(","));
|
||||
}
|
||||
// 2. 处理成功检查
|
||||
const counts = await updateTicketCountsAndLog(result.data);
|
||||
await sendTicketNotifications(counts);
|
||||
} else {
|
||||
// 3. 处理业务失败
|
||||
addLog("WARN", `API 返回异常: ${result.msg}`, "SCHEDULER");
|
||||
if (!isRetry && isSessionExpiredMsg(result.msg)) {
|
||||
await tryReLogin();
|
||||
}
|
||||
}
|
||||
} catch (e: any) {
|
||||
addLog("ERROR", `API 检查失败: ${e.message || e}`, "SCHEDULER");
|
||||
} catch (e: unknown) {
|
||||
// 4. 处理网络/系统异常
|
||||
addLog("ERROR", `API 检查失败: ${getErrorMessage(e)}`, "SCHEDULER");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -242,8 +271,8 @@ export const useMonitorStore = defineStore("monitor", () => {
|
|||
tokenSessionId.value = data.tokenSessionId || "";
|
||||
addLog("INFO", "已加载保存的凭据", "CONFIG");
|
||||
}
|
||||
} catch (e: any) {
|
||||
addLog("ERROR", `加载凭据失败: ${e.message || e}`, "CONFIG");
|
||||
} catch (e: unknown) {
|
||||
addLog("ERROR", `加载凭据失败: ${getErrorMessage(e)}`, "CONFIG");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -266,8 +295,8 @@ export const useMonitorStore = defineStore("monitor", () => {
|
|||
localStorage.removeItem("crm_credentials");
|
||||
addLog("INFO", "已清除本地凭据", "CONFIG");
|
||||
}
|
||||
} catch (e: any) {
|
||||
addLog("ERROR", `保存凭据失败: ${e.message || e}`, "CONFIG");
|
||||
} catch (e: unknown) {
|
||||
addLog("ERROR", `保存凭据失败: ${getErrorMessage(e)}`, "CONFIG");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,42 +1,42 @@
|
|||
import { relaunch } from "@tauri-apps/plugin-process";
|
||||
import { check, type Update } from "@tauri-apps/plugin-updater";
|
||||
import { acceptHMRUpdate, defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import { useLogStore } from "@/stores/log";
|
||||
import { getErrorMessage } from "@/utils/common";
|
||||
|
||||
// ===== 模块级单例状态 =====
|
||||
/** 是否显示更新对话框 */
|
||||
const showUpdateDialog = ref(false);
|
||||
/** 新版本信息 */
|
||||
const updateInfo = ref<{
|
||||
version: string;
|
||||
notes: string;
|
||||
date: string;
|
||||
} | null>(null);
|
||||
/** 下载进度 */
|
||||
const updateProgress = ref<{ total: number; downloaded: number } | null>(null);
|
||||
/** 是否正在更新中 */
|
||||
const isUpdating = ref(false);
|
||||
/** 当前应用版本号 */
|
||||
const currentVersion = ref("");
|
||||
/** 是否正在检查更新 */
|
||||
const isCheckingUpdates = ref(false);
|
||||
|
||||
/** 待安装的更新对象 */
|
||||
let pendingUpdate: Update | null = null;
|
||||
|
||||
/**
|
||||
* 版本更新 Composable(单例模式,模块级状态共享)
|
||||
*/
|
||||
export function useUpdater() {
|
||||
export const useUpdaterStore = defineStore("updater", () => {
|
||||
const { addLog } = useLogStore();
|
||||
|
||||
/** 是否显示更新对话框 */
|
||||
const showUpdateDialog = ref(false);
|
||||
/** 新版本信息 */
|
||||
const updateInfo = ref<{
|
||||
version: string;
|
||||
notes: string;
|
||||
date: string;
|
||||
} | null>(null);
|
||||
/** 下载进度 */
|
||||
const updateProgress = ref<{ total: number; downloaded: number } | null>(
|
||||
null,
|
||||
);
|
||||
/** 是否正在更新中 */
|
||||
const isUpdating = ref(false);
|
||||
/** 当前应用版本号 */
|
||||
const currentVersion = ref("");
|
||||
/** 是否正在检查更新 */
|
||||
const isCheckingUpdates = ref(false);
|
||||
|
||||
/** 待安装的更新对象 */
|
||||
let pendingUpdate: Update | null = null;
|
||||
|
||||
/** 检查是否有可用的应用更新,发现新版本时展示更新对话框 */
|
||||
async function checkForUpdates() {
|
||||
if (isCheckingUpdates.value || isUpdating.value) return;
|
||||
isCheckingUpdates.value = true;
|
||||
try {
|
||||
const update = await check();
|
||||
if (update?.available) {
|
||||
if (update) {
|
||||
pendingUpdate = update;
|
||||
updateInfo.value = {
|
||||
version: update.version,
|
||||
|
|
@ -48,8 +48,8 @@ export function useUpdater() {
|
|||
} else {
|
||||
addLog("INFO", "当前已是最新版本", "UPDATER");
|
||||
}
|
||||
} catch (e: any) {
|
||||
addLog("WARN", `检查更新失败: ${e.message || e}`, "UPDATER");
|
||||
} catch (e: unknown) {
|
||||
addLog("WARN", `检查更新失败: ${getErrorMessage(e)}`, "UPDATER");
|
||||
} finally {
|
||||
isCheckingUpdates.value = false;
|
||||
}
|
||||
|
|
@ -76,8 +76,8 @@ export function useUpdater() {
|
|||
}
|
||||
});
|
||||
await relaunch();
|
||||
} catch (e: any) {
|
||||
addLog("ERROR", `更新安装失败: ${e.message || e}`, "UPDATER");
|
||||
} catch (e: unknown) {
|
||||
addLog("ERROR", `更新安装失败: ${getErrorMessage(e)}`, "UPDATER");
|
||||
isUpdating.value = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -98,4 +98,8 @@ export function useUpdater() {
|
|||
confirmUpdate,
|
||||
dismissUpdate,
|
||||
};
|
||||
});
|
||||
|
||||
if (import.meta.hot) {
|
||||
import.meta.hot.accept(acceptHMRUpdate(useUpdaterStore, import.meta.hot));
|
||||
}
|
||||
|
|
@ -1,5 +1,10 @@
|
|||
import dayjs from "dayjs";
|
||||
|
||||
/** 从 unknown 类型的 catch 错误中提取可读的错误信息 */
|
||||
export function getErrorMessage(e: unknown): string {
|
||||
return e instanceof Error ? e.message : String(e);
|
||||
}
|
||||
|
||||
/** 将 Date 对象格式化为 'YYYY-MM-DD HH:mm:ss' 字符串 */
|
||||
export function formatTime(date: Date): string {
|
||||
return dayjs(date).format("YYYY-MM-DD HH:mm:ss");
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ interface ImportMeta {
|
|||
}
|
||||
|
||||
declare module "*.vue" {
|
||||
import type { DefineComponent } from "vue";
|
||||
import type { Component } from "vue";
|
||||
|
||||
const component: DefineComponent<{}, {}, any>;
|
||||
const component: Component;
|
||||
export default component;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue