feat(P21PWA2-1,P21PWA2-2,P21PWA2-3): 深链路由守卫/离线审批队列/SW缓存策略升级

这个提交包含在:
2026-03-12 12:34:56 +08:00
父节点 102d3dcc2b
当前提交 9112383d23
共修改 10 个文件,包含 975 行新增和 163 行删除
+64 -1
查看文件
@@ -1,5 +1,17 @@
import { registerSW } from "virtual:pwa-register";
const MOBILE_APPROVAL_TASK_PATH = /^\/workflow\/tasks\/([^/]+)$/i;
const OFFLINE_ALLOWED_PATHS = ["/mobile/approvals"];
const RUNTIME_CACHE_NAMES = ["app-api-runtime"];
function canUseWindow() {
return typeof window !== "undefined";
}
function matchesMedia(query: string) {
return canUseWindow() && typeof window.matchMedia === "function" && window.matchMedia(query).matches;
}
export function registerPwaIfEnabled() {
if (import.meta.env.DEV) {
return;
@@ -12,9 +24,60 @@ export function registerPwaIfEnabled() {
registerSW({
immediate: true,
onOfflineReady() {
if (typeof window !== "undefined") {
if (canUseWindow()) {
window.dispatchEvent(new CustomEvent("hyb-oa:pwa-offline-ready"));
}
},
});
}
export function buildMobileApprovalsPath(taskId?: string | null) {
const normalizedTaskId = String(taskId ?? "").trim();
if (!normalizedTaskId) {
return "/mobile/approvals";
}
const params = new URLSearchParams({ taskId: normalizedTaskId });
return `/mobile/approvals?${params.toString()}`;
}
export function extractWorkflowTaskId(pathname: string) {
const match = pathname.match(MOBILE_APPROVAL_TASK_PATH);
return match?.[1] ?? null;
}
export function isOfflineSnapshotAllowedPath(pathname: string) {
if (OFFLINE_ALLOWED_PATHS.includes(pathname)) {
return true;
}
return Boolean(extractWorkflowTaskId(pathname));
}
export function shouldPreferMobileApprovalExperience() {
if (!canUseWindow()) {
return false;
}
const userAgent = window.navigator.userAgent.toLowerCase();
const isStandalone = matchesMedia("(display-mode: standalone)") || (window.navigator as Navigator & { standalone?: boolean }).standalone === true;
const isImContainer = /(wxwork|wecom|wechatwork|dingtalk|aliapp\(dingtalk\))/i.test(userAgent);
const isTouchSmallScreen = window.navigator.maxTouchPoints > 0 && matchesMedia("(max-width: 1024px)");
return isStandalone || isImContainer || isTouchSmallScreen;
}
export async function clearPwaRuntimeCaches() {
if (!canUseWindow() || !("caches" in window)) {
return;
}
await Promise.all(
RUNTIME_CACHE_NAMES.map(async (cacheName) => {
try {
await window.caches.delete(cacheName);
} catch {
}
}),
);
}