export const MP_WEBVIEW_PATH_PREFIX = '/m/' export const MP_PLAN_LIST_PATH = '/m/pl/plan' export const MINI_WORKBENCH_PATH = '/pages/workbench/workbench' /** H5 iframe / uni 壳页约定:内嵌页请求返回工作台 */ export const MP_WEBVIEW_GO_WORKBENCH = 'MP_WEBVIEW_GO_WORKBENCH' /** H5 iframe:内嵌页 Clipboard API 被拦截时,请壳页代为写入剪贴板 */ export const MP_WEBVIEW_COPY_TEXT = 'MP_WEBVIEW_COPY_TEXT' const MP_RETURN_ORIGIN_KEY = 'mp_webview_return_origin' /** MpWebviewShell 换票 / 就绪引导中:业务请求若 401 不得触发登出(与换票竞态) */ let mpWebviewAuthBootDepth = 0 export function beginMpWebviewAuthBoot(): void { mpWebviewAuthBootDepth += 1 } export function endMpWebviewAuthBoot(): void { mpWebviewAuthBootDepth = Math.max(0, mpWebviewAuthBootDepth - 1) } export function isMpWebviewAuthBooting(): boolean { return mpWebviewAuthBootDepth > 0 } export function isMpWebviewPath(path: string): boolean { return (path || '').startsWith(MP_WEBVIEW_PATH_PREFIX) } type WxMiniProgramApi = { reLaunch?: (opt: { url: string; fail?: () => void }) => void navigateBack?: (opt?: { delta?: number; fail?: () => void }) => void getEnv?: (cb: (res: { miniprogram?: boolean }) => void) => void } export function getWxMiniProgram(): WxMiniProgramApi | null { if (typeof window === 'undefined') return null const wxObj = (window as unknown as { wx?: { miniProgram?: unknown } }).wx const mini = wxObj?.miniProgram as WxMiniProgramApi | undefined return mini || null } /** 真机 web-view 内才为 true;勿信任 jweixin 在普通浏览器里挂上的空壳 API */ export function isInMiniProgramWebView(): boolean { if (typeof window === 'undefined') return false const w = window as unknown as { __wxjs_environment?: string } if (w.__wxjs_environment === 'miniprogram') return true const ua = typeof navigator !== 'undefined' ? navigator.userAgent || '' : '' return /miniProgram/i.test(ua) && /MicroMessenger/i.test(ua) } /** 记住小程序壳 origin,供 H5 iframe 顶栏返回工作台(跨域无法读 parent.location) */ export function rememberMpReturnOrigin(origin?: string): void { if (typeof sessionStorage === 'undefined') return const value = String(origin || '').trim().replace(/\/$/, '') if (!value || !/^https?:\/\//i.test(value)) return try { sessionStorage.setItem(MP_RETURN_ORIGIN_KEY, value) } catch { /* ignore quota */ } } function readMpReturnOrigin(): string { if (typeof sessionStorage === 'undefined') return '' try { return String(sessionStorage.getItem(MP_RETURN_ORIGIN_KEY) || '').trim() } catch { return '' } } function postGoWorkbenchToHost(): boolean { if (typeof window === 'undefined') return false const payload = { type: MP_WEBVIEW_GO_WORKBENCH } let posted = false try { if (window.parent && window.parent !== window) { window.parent.postMessage(payload, '*') posted = true } } catch { /* ignore */ } try { if (window.top && window.top !== window && window.top !== window.parent) { window.top.postMessage(payload, '*') posted = true } } catch { /* ignore */ } return posted } function navigateTopToMiniWorkbench(): boolean { const origin = readMpReturnOrigin() if (!origin || typeof window === 'undefined') return false try { const target = `${origin}${MINI_WORKBENCH_PATH}` if (window.top && window.top !== window) { window.top.location.href = target return true } window.location.href = target return true } catch { return false } } /** 列表顶栏「返回」:回到小程序工作台(web-view 多用 redirectTo 打开,须 reLaunch) */ export function goToMiniWorkbench(): void { const mini = getWxMiniProgram() if (isInMiniProgramWebView() && mini?.reLaunch) { mini.reLaunch({ url: MINI_WORKBENCH_PATH }) return } // uni-app H5:web-view 多为 iframe,由壳页监听 postMessage;再兜底顶层跳转 postGoWorkbenchToHost() navigateTopToMiniWorkbench() } export function redirectToMiniLogin(): void { const mini = getWxMiniProgram() if (isInMiniProgramWebView() && mini?.reLaunch) { mini.reLaunch({ url: '/pages/login/login' }) return } postGoWorkbenchToHost() // 非小程序:只展示空态,禁止跳管理端登录 } export function stripMpTicketFromUrl( _path: string, query: Record ): Record { const next = { ...query } delete next.mpTicket delete next.mpReturnOrigin return next }