feat(): 优化状态栏

This commit is contained in:
tsl
2026-03-10 15:20:06 +08:00
parent da677a033e
commit 17618ec3a0
11 changed files with 135 additions and 52 deletions
+13 -3
View File
@@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from "vue";
import { ref, computed, onMounted, onUnmounted } from "vue";
import {
SettingOutlined,
DashboardOutlined,
@@ -12,13 +12,23 @@ import LoginForm from "./components/LoginForm.vue";
import MonitorControl from "./components/MonitorControl.vue";
import SettingsPanel from "./components/SettingsPanel.vue";
import LogViewer from "./components/LogViewer.vue";
import AppStatusBar from "./components/AppStatusBar.vue";
import AppStatusBar, { type StatusBarItem } from "./components/AppStatusBar.vue";
import CloseDialog from "./components/CloseDialog.vue";
import { useMonitorStore } from "./stores/monitor";
const activeTab = ref("monitor");
const store = useMonitorStore();
const statusItems = computed<StatusBarItem[]>(() => {
const items: StatusBarItem[] = [];
if (store.isPostponed) {
items.push({ key: "postponed", type: "warning", content: store.message });
} else if (store.message) {
items.push({ key: "message", type: "info", content: store.message });
}
return items;
});
onMounted(() => store.initialize());
onUnmounted(() => store.cleanup());
</script>
@@ -61,7 +71,7 @@ onUnmounted(() => store.cleanup());
</a-tabs>
</a-layout-content>
<AppStatusBar />
<AppStatusBar :items="statusItems" />
</a-layout>
<CloseDialog />
+31 -16
View File
@@ -1,22 +1,43 @@
<script setup lang="ts">
import {
ExclamationCircleOutlined,
InfoCircleOutlined,
ExclamationCircleOutlined,
CheckCircleOutlined,
CloseCircleOutlined,
} from "@ant-design/icons-vue";
import { useMonitorStore } from "../stores/monitor";
const store = useMonitorStore();
export interface StatusBarItem {
key: string;
content: string;
type?: "info" | "warning" | "success" | "error";
}
defineProps<{ items?: StatusBarItem[] }>();
const colorMap: Record<string, string> = {
info: "rgba(255,255,255,0.85)",
warning: "#ffe58f",
success: "#95de64",
error: "#ff7875",
};
const iconMap: Record<string, unknown> = {
info: InfoCircleOutlined,
warning: ExclamationCircleOutlined,
success: CheckCircleOutlined,
error: CloseCircleOutlined,
};
</script>
<template>
<div class="app-statusbar">
<span v-if="store.isPostponed" class="statusbar-item statusbar-item--warning">
<ExclamationCircleOutlined /> 监控已推迟1小时后自动恢复
</span>
<span v-if="store.isPostponed && store.message" class="statusbar-sep" />
<span v-if="store.message" class="statusbar-item statusbar-item--info">
<InfoCircleOutlined /> {{ store.message }}
</span>
<template v-for="(item, index) in items" :key="item.key">
<span v-if="index > 0" class="statusbar-sep" />
<span class="statusbar-item" :style="{ color: colorMap[item.type ?? 'info'] }">
<component :is="iconMap[item.type ?? 'info']" />
{{ item.content }}
</span>
</template>
</div>
</template>
@@ -38,12 +59,6 @@ const store = useMonitorStore();
padding: 0 10px;
height: 100%;
}
.statusbar-item--warning {
color: #ffe58f;
}
.statusbar-item--info {
color: rgba(255, 255, 255, 0.85);
}
.statusbar-sep {
width: 1px;
height: 14px;
+28 -5
View File
@@ -1,18 +1,33 @@
<script setup lang="ts">
import { computed } from "vue";
import dayjs, { type Dayjs } from "dayjs";
import { useMonitorStore } from "../stores/monitor";
const store = useMonitorStore();
// ===== 检查间隔:内部用秒,TimePicker 用 Dayjs =====
const intervalTime = computed<Dayjs>({
get() {
const t = store.config.check_interval;
return dayjs().hour(Math.floor(t / 3600)).minute(Math.floor((t % 3600) / 60)).second(t % 60).millisecond(0);
},
set(val: Dayjs | null) {
if (!val) return;
store.config.check_interval = Math.max(10, val.hour() * 3600 + val.minute() * 60 + val.second());
},
});
</script>
<template>
<a-form layout="vertical">
<a-form-item label="检查间隔(秒)">
<a-input-number
v-model:value="store.config.check_interval"
:min="10"
:max="3600"
<a-form-item label="检查间隔">
<a-time-picker
v-model:value="intervalTime"
format="HH:mm:ss"
:allow-clear="false"
style="width: 100%"
/>
<div class="form-hint">最短 10 当前 {{ store.config.check_interval }} </div>
</a-form-item>
<a-form-item label="网络超时(秒)">
<a-input-number
@@ -50,6 +65,14 @@ const store = useMonitorStore();
</a-form-item>
</a-col>
</a-row>
<a-form-item label="点击「去处理」后暂停时长(分钟)">
<a-input-number
v-model:value="store.config.handle_pause_minutes"
:min="1"
:max="120"
style="width: 100%"
/>
</a-form-item>
<a-form-item label="关闭窗口行为">
<a-radio-group v-model:value="store.config.close_action">
<a-radio value="ask">每次询问</a-radio>
+23 -2
View File
@@ -68,6 +68,7 @@ export const useMonitorStore = defineStore("monitor", () => {
notification_sound: true,
network_timeout: 30,
close_action: "ask" as "ask" | "minimize" | "close",
handle_pause_minutes: 20,
});
const showCloseDialog = ref(false);
@@ -233,8 +234,9 @@ export const useMonitorStore = defineStore("monitor", () => {
async function tryReLogin(): Promise<void> {
if (loginMode.value === "token") {
addLog("WARN", "Token 模式下无法自动重登录,请更换新的 Token", "SESSION");
message.value = "Token 可能已过期,请更换新的 Token";
addLog("ERROR", "Token 已失效,监控已停止,请更换新的 Token 后重新开始", "SESSION");
message.value = "Token 已失效,监控已停止";
stopMonitoring();
return;
}
if (!username.value || !password.value) {
@@ -453,6 +455,24 @@ export const useMonitorStore = defineStore("monitor", () => {
}, 60 * 60 * 1000);
}
function pauseForHandle() {
if (!isMonitoring.value) return;
if (monitorTimer) { clearInterval(monitorTimer); monitorTimer = null; }
if (postponeTimer) { clearTimeout(postponeTimer); }
isPostponed.value = true;
const minutes = config.value.handle_pause_minutes;
addLog("INFO", `点击"去处理",监控已暂停 ${minutes} 分钟`, "MONITOR");
message.value = `监控已暂停 ${minutes} 分钟,将在 ${minutes} 分钟后自动恢复`;
postponeTimer = setTimeout(() => {
isPostponed.value = false;
postponeTimer = null;
apiCheckStatus();
monitorTimer = setInterval(() => apiCheckStatus(), config.value.check_interval * 1000);
addLog("INFO", "暂停结束,监控已自动恢复", "MONITOR");
message.value = "暂停结束,监控已自动恢复";
}, minutes * 60 * 1000);
}
async function manualCheck() {
if (!isLoggedIn.value) { message.value = "请先开始监控(登录)"; return; }
isLoading.value = true;
@@ -564,6 +584,7 @@ export const useMonitorStore = defineStore("monitor", () => {
} catch { /* ignore */ }
unlistenNotification = await listen<string>("notification-action", (event) => {
if (event.payload === "postpone_1h") postponeMonitoring();
else if (event.payload === "go_handle") pauseForHandle();
});
unlistenClose = await listen<void>("close-requested", async () => {
const action = config.value.close_action;