perf(desktop): lazy-load renderer routes and pause polling when hidden

Drop the eager Antd global import in favour of unplugin-vue-components
on-demand resolution, async route components, defineAsyncComponent for
the shell/login split, and a dynamic Modal import inside
showClientActionError. Pair that with a visibility-aware runtime poller
that stops the 15s snapshot refresh when the window is hidden and kicks
off an immediate refresh on re-show. Refresh DesktopShell nav with
inline icons and a calmer active/hover treatment, and land
ActionButton/DisclosurePanel/PaginationBar primitives for upcoming
views.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-20 11:08:09 +08:00
parent ef868f81a1
commit 73dfbbe8b5
12 changed files with 579 additions and 53 deletions
@@ -2,12 +2,19 @@ import { computed, onMounted, onUnmounted, readonly, shallowRef } from "vue";
import type { DesktopRuntimeSnapshot } from "../types";
const RUNTIME_POLL_INTERVAL_MS = 15_000;
const snapshot = shallowRef<DesktopRuntimeSnapshot | null>(null);
const loading = shallowRef(false);
const error = shallowRef<string | null>(null);
let intervalHandle: ReturnType<typeof setInterval> | null = null;
let subscribers = 0;
let visibilityListenerBound = false;
function isPageVisible(): boolean {
return typeof document === "undefined" || document.visibilityState === "visible";
}
async function refreshRuntimeSnapshot() {
loading.value = true;
@@ -47,18 +54,8 @@ async function refreshAccounts() {
}
}
function startPolling() {
if (intervalHandle) {
return;
}
intervalHandle = setInterval(() => {
void refreshRuntimeSnapshot();
}, 15_000);
}
function stopPolling() {
if (subscribers > 0 || !intervalHandle) {
if (!intervalHandle) {
return;
}
@@ -66,18 +63,66 @@ function stopPolling() {
intervalHandle = null;
}
function syncPollingState() {
if (subscribers === 0 || !isPageVisible()) {
stopPolling();
return;
}
if (intervalHandle) {
return;
}
intervalHandle = setInterval(() => {
void refreshRuntimeSnapshot();
}, RUNTIME_POLL_INTERVAL_MS);
}
function handleVisibilityChange() {
if (isPageVisible()) {
void refreshRuntimeSnapshot();
}
syncPollingState();
}
function bindVisibilityListener() {
if (visibilityListenerBound || typeof document === "undefined") {
return;
}
document.addEventListener("visibilitychange", handleVisibilityChange);
visibilityListenerBound = true;
}
function unbindVisibilityListener() {
if (!visibilityListenerBound || typeof document === "undefined") {
return;
}
document.removeEventListener("visibilitychange", handleVisibilityChange);
visibilityListenerBound = false;
}
export function useDesktopRuntime() {
onMounted(() => {
subscribers += 1;
bindVisibilityListener();
if (!snapshot.value && !loading.value) {
void refreshRuntimeSnapshot();
}
startPolling();
syncPollingState();
});
onUnmounted(() => {
subscribers = Math.max(0, subscribers - 1);
stopPolling();
if (subscribers === 0) {
stopPolling();
unbindVisibilityListener();
return;
}
syncPollingState();
});
return {