feat(P21PWA2-1,P21PWA2-2,P21PWA2-3): 深链路由守卫/离线审批队列/SW缓存策略升级
这个提交包含在:
+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 {
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户