106 lines
3.5 KiB
TypeScript
106 lines
3.5 KiB
TypeScript
// 会会数字分身 H5 ↔ uniapp 原生壳 桥接工具
|
||
// 协议详见 uniapp-avatar/README.md
|
||
//
|
||
// uni-webview bridge is bundled by main.ts; no external CDN is required.
|
||
|
||
const BRIDGE_HANDLER = '__uniBridgeHandle__'
|
||
const nativeMessageHandlers = new Set<(message: any) => void>()
|
||
|
||
export interface UniLaunchParams {
|
||
token?: string
|
||
userId?: string
|
||
nickname?: string
|
||
avatar?: string
|
||
ts?: string
|
||
}
|
||
|
||
const PARAM_KEYS: (keyof UniLaunchParams)[] = ['token', 'userId', 'nickname', 'avatar', 'ts']
|
||
|
||
function readParams(search: string, target: UniLaunchParams): void {
|
||
const sp = new URLSearchParams(search)
|
||
for (const key of PARAM_KEYS) {
|
||
const value = sp.get(key)
|
||
if (value) target[key] = value
|
||
}
|
||
}
|
||
|
||
// 是否运行在 uniapp web-view 环境中
|
||
export function isInUniWebView(): boolean {
|
||
const runtime = window as any
|
||
const userAgent = navigator.userAgent
|
||
const isDCloudApp = Boolean(
|
||
runtime.plus ||
|
||
runtime.__dcloud_weex_postMessage ||
|
||
runtime.__dcloud_weex_ ||
|
||
/uni-app|Html5Plus/i.test(userAgent)
|
||
)
|
||
const isMiniProgram = Boolean(
|
||
(/miniProgram/i.test(userAgent) && (runtime.wx?.miniProgram || runtime.qq?.miniProgram)) ||
|
||
runtime.my ||
|
||
runtime.swan?.webView ||
|
||
runtime.tt?.miniProgram
|
||
)
|
||
return Boolean(runtime.uni?.webView && (isDCloudApp || isMiniProgram))
|
||
}
|
||
|
||
// 解析 web-view 加载 URL 时原生注入的参数(token / 会会用户)
|
||
export function getLaunchParams(): UniLaunchParams {
|
||
const params: UniLaunchParams = {}
|
||
readParams(window.location.search, params)
|
||
const hashQueryIndex = window.location.hash.indexOf('?')
|
||
if (hashQueryIndex >= 0) {
|
||
readParams(window.location.hash.slice(hashQueryIndex + 1), params)
|
||
}
|
||
return params
|
||
}
|
||
|
||
// Remove the one-time login credential before any route is rendered or logged.
|
||
export function stripLaunchToken(): void {
|
||
const url = new URL(window.location.href)
|
||
url.searchParams.delete('token')
|
||
|
||
const hash = url.hash.slice(1)
|
||
const queryIndex = hash.indexOf('?')
|
||
if (queryIndex >= 0) {
|
||
const path = hash.slice(0, queryIndex)
|
||
const hashParams = new URLSearchParams(hash.slice(queryIndex + 1))
|
||
hashParams.delete('token')
|
||
const query = hashParams.toString()
|
||
url.hash = `${path}${query ? `?${query}` : ''}`
|
||
}
|
||
|
||
window.history.replaceState(window.history.state, '', `${url.pathname}${url.search}${url.hash}`)
|
||
}
|
||
|
||
// H5 → 原生:发送事件(需引入 uniapp web-view bridge)
|
||
export function postToNative(message: Record<string, any>): boolean {
|
||
if (!isInUniWebView()) return false
|
||
;(window as any).uni.webView.postMessage({ data: message })
|
||
return true
|
||
}
|
||
|
||
// 原生 → H5:注册消息处理(原生通过 web-view.evalJS 调用 window.__uniBridgeHandle__)
|
||
export function onNativeMessage(handler: (message: any) => void): () => void {
|
||
nativeMessageHandlers.add(handler)
|
||
;(window as any)[BRIDGE_HANDLER] = (message: any) => {
|
||
for (const listener of nativeMessageHandlers) {
|
||
try {
|
||
listener(message)
|
||
} catch (e) {
|
||
console.error('[uniBridge] handler error', e)
|
||
}
|
||
}
|
||
}
|
||
return () => nativeMessageHandlers.delete(handler)
|
||
}
|
||
|
||
// 便捷事件
|
||
export const UniEvents = {
|
||
ready: () => postToNative({ type: 'ready' }),
|
||
needLogin: () => postToNative({ type: 'needLogin' }),
|
||
setTitle: (title: string) => postToNative({ type: 'setTitle', title }),
|
||
navigate: (path: string) => postToNative({ type: 'navigate', path }),
|
||
payment: (payment: Record<string, any>) => postToNative({ type: 'payment', payment }),
|
||
back: () => postToNative({ type: 'back' })
|
||
}
|