Files
DashBoard/frontend/src/wip-shared/composables/useAutoRefresh.js
egg be22571421 feat(wip): preserve filters between Overview and Detail with thundering-herd fix
URL is now single source of truth for filter state (workorder, lotid,
package, type, status) across WIP Overview and Detail pages. Drill-down
carries all filters + status; back button dynamically reflects Detail
changes. Backend Detail API now supports pj_type filter parameter.

Harden concurrency: add pagehide abort for MPA navigation, double-check
locking on Redis JSON parse and snapshot build to prevent thread pool
saturation during rapid page switching. Fix watchdog setsid and PID
discovery. Fix test_realtime_equipment_cache RUNCARDLOTID field mismatch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-11 07:22:48 +08:00

111 lines
2.4 KiB
JavaScript

import { onBeforeUnmount, onMounted } from 'vue';
const DEFAULT_REFRESH_INTERVAL_MS = 10 * 60 * 1000;
export function useAutoRefresh({
onRefresh,
intervalMs = DEFAULT_REFRESH_INTERVAL_MS,
autoStart = true,
refreshOnVisible = true,
} = {}) {
let refreshTimer = null;
const controllers = new Map();
let pageHideHandler = null;
function stopAutoRefresh() {
if (refreshTimer) {
clearInterval(refreshTimer);
refreshTimer = null;
}
}
function startAutoRefresh() {
stopAutoRefresh();
refreshTimer = setInterval(() => {
if (!document.hidden) {
void onRefresh?.();
}
}, intervalMs);
}
function resetAutoRefresh() {
startAutoRefresh();
}
function createAbortSignal(key = 'default') {
const previous = controllers.get(key);
if (previous) {
previous.abort();
}
const controller = new AbortController();
controllers.set(key, controller);
return controller.signal;
}
function clearAbortController(key = 'default') {
const controller = controllers.get(key);
if (controller) {
controller.abort();
controllers.delete(key);
}
}
function abortAllRequests() {
controllers.forEach((controller) => {
controller.abort();
});
controllers.clear();
}
async function triggerRefresh({ force = false, resetTimer = false } = {}) {
if (!force && document.hidden) {
return;
}
if (resetTimer) {
resetAutoRefresh();
}
await onRefresh?.();
}
function handleVisibilityChange() {
if (!refreshOnVisible || document.hidden) {
return;
}
void triggerRefresh({ force: true, resetTimer: true });
}
onMounted(() => {
pageHideHandler = () => {
stopAutoRefresh();
abortAllRequests();
};
if (autoStart) {
startAutoRefresh();
}
document.addEventListener('visibilitychange', handleVisibilityChange);
window.addEventListener('pagehide', pageHideHandler);
});
onBeforeUnmount(() => {
stopAutoRefresh();
abortAllRequests();
document.removeEventListener('visibilitychange', handleVisibilityChange);
if (pageHideHandler) {
window.removeEventListener('pagehide', pageHideHandler);
pageHideHandler = null;
}
});
return {
startAutoRefresh,
stopAutoRefresh,
resetAutoRefresh,
createAbortSignal,
clearAbortController,
abortAllRequests,
triggerRefresh,
};
}