hejianhao
2026-08-24 ac95c75b74e4297730fbdb49ea0afef7c871d1d9
fix0806: 暂时使用web-view标签进入直播间
1个文件已修改
255 ■■■■■ 已修改文件
traditionalApp/pages/weizanLive/weizanLive.vue 255 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
traditionalApp/pages/weizanLive/weizanLive.vue
@@ -1,252 +1,61 @@
<template>
    <view class="weizan-live-container">
        <!-- 子 webview 由 plus.webview.create 命令式创建并浮在该 view 之上,无需在 template 中渲染 -->
    <view class="container">
        <web-view v-if="webViewUrl" class="webview-content" :src="webViewUrl"></web-view>
        <view v-else class="empty-box"><text class="empty-text">链接地址无效</text></view>
    </view>
</template>
<script setup>
import { ref } from 'vue'
import { onLoad, onUnload, onShow, onHide } from '@dcloudio/uni-app'
import { onLoad, onUnload } from '@dcloudio/uni-app'
import { clearWebViewData } from '@/utils/webview-cache.js'
import { registerWeizanPayBridge, unregisterWeizanPayBridge } from '@/utils/weizan-pay-bridge.js'
const WEIZAN_WEBVIEW_ID = 'weizanLiveWebview'
// 微赞识别易宝支付通道用的 UA 后缀(接入方案文档约定)。
// 必须完整携带以下标识,微赞 H5 才会把当前环境判定为 isVzanApp / 成功注册 AppChannel,
// 进而回调 window.android.jumpToMiniProgramPay:
//   webchatPay/wx...                → 易宝支付通道
//   InAppPay Vzan/miniAppPayment    → 微赞 APP 内小程序支付
//   vzanApp / zhiboApp              → 微赞 APP / 直播 APP 标识
const WEIZAN_UA_SUFFIX = 'webchatPay/wx074c1f0cf5a0ecf4 InAppPay Vzan/miniAppPayment vzanApp navigator.userAgent zhiboApp'
const webViewUrl = ref('')
const WEIXIN_APP_ID = 'wx074c1f0cf5a0ecf4'
let originalUserAgent = ''
let subWebview = null
let currentUrl = ''
let currentTitle = ''
const loadError = ref('')
/**
 * 解码 query 参数。
 */
function decodeQueryValue(value) {
    if (!value) {
        return ''
    }
    try {
        return decodeURIComponent(value)
    } catch (e) {
        return value || ''
    }
}
/**
 * 页面加载:读取 URL/Title,进入前清缓存,创建子 webview 并注册支付桥。
 */
onLoad((options) => {
    currentUrl = decodeQueryValue(options?.url)
    currentTitle = decodeQueryValue(options?.title)
    console.log('[weizanLive] onLoad, url=', currentUrl, 'title=', currentTitle)
    if (currentTitle) {
        uni.setNavigationBarTitle({ title: currentTitle })
    }
    const url = decodeValue(options?.url)
    const title = decodeValue(options?.title)
    if (title) uni.setNavigationBarTitle({ title })
    // #ifdef APP-PLUS
    // 进入前先清理上次缓存、cookie、登录态
    clearWebViewData()
    // 创建子 webview(注入 UA + 注册支付桥 + 加载 URL)
    createWeizanWebview(currentUrl)
    injectUserAgent()
    console.log('[weizanLive] 跳转前完整 UA:', plus.navigator.getUserAgent() || '')
    // #endif
    webViewUrl.value = url
})
/**
 * 页面卸载:反注册支付桥并关闭子 webview,避免残留。
 */
onUnload(() => {
    console.log('[weizanLive] onUnload')
    // #ifdef APP-PLUS
    if (subWebview) {
        try {
            unregisterWeizanPayBridge(subWebview)
        } catch (e) {
            console.warn('[weizanLive] unregisterWeizanPayBridge fail:', e)
        }
        try {
            subWebview.close()
        } catch (e) {
            console.warn('[weizanLive] subWebview.close fail:', e)
        }
        subWebview = null
    if (originalUserAgent) {
        try { plus.navigator.setUserAgent(originalUserAgent, false) } catch (e) {}
        originalUserAgent = ''
    }
    // #endif
})
onShow(() => {
function injectUserAgent() {
    // #ifdef APP-PLUS
    if (subWebview) {
        try {
            subWebview.show()
        } catch (e) {}
        const baseUA = plus.navigator.getUserAgent() || ''
        if (baseUA.indexOf('webchatPay/') >= 0) {
            originalUserAgent = baseUA.replace(/\s*webchatPay\/\S+/g, '')
            return
    }
    // #endif
})
onHide(() => {
    // #ifdef APP-PLUS
    if (subWebview) {
        try {
            subWebview.hide()
        } catch (e) {}
    }
    // #endif
})
/**
 * 创建子 webview:先创建空白实例 → 注入 UA → 注册支付桥 → 加载真实 URL → show。
 * 这个顺序保证 H5 加载完成时 UA 已带上微赞标识、window.android.jumpToMiniProgramPay 已可用。
 */
function createWeizanWebview(targetUrl) {
    // #ifdef APP-PLUS
    const platform = String(uni.getSystemInfoSync().platform || '').toLowerCase()
    console.log('[weizanLive] createWeizanWebview, platform=', platform, 'url=', targetUrl)
    try {
        // 1. 创建空白 webview(不立即加载 URL)
        subWebview = plus.webview.create('', WEIZAN_WEBVIEW_ID, {
            top: '0px',
            bottom: '0px',
            background: '#FFFFFF',
            // 让子 webview 的 progress 进度条颜色与 APP 一致
            progress: {
                color: '#FF3333'
            }
        })
        console.log('[weizanLive] plus.webview.create 完成, subWebview=', subWebview ? '[Object]' : 'null')
        // 2. 注入 UA(让微赞 H5 识别为易宝支付通道)
        injectUserAgent(subWebview, platform)
        // 3. 注册支付 JSBridge(在 loadURL 之前注册,保证 H5 加载完成时钩子已可用)
        registerWeizanPayBridge(subWebview)
        // 4. 加载真实 URL
        if (targetUrl) {
            subWebview.loadURL(targetUrl)
            console.log('[weizanLive] loadURL 调用完成:', targetUrl)
        } else {
            loadError.value = '直播间地址为空'
        }
        // 5. 显示子 webview
        subWebview.show()
        console.log('[weizanLive] subWebview.show() 完成')
    } catch (e) {
        console.error('[weizanLive] createWeizanWebview 失败:', e)
        loadError.value = '直播间加载失败'
    }
        originalUserAgent = baseUA
        plus.navigator.setUserAgent(`${baseUA} webchatPay/${WEIXIN_APP_ID}`.trim(), false)
    } catch (e) { console.error('[weizanLive] UA 注入失败:', e) }
    // #endif
}
/**
 * 给子 webview 注入微赞支付 UA 后缀。
 * - Android:取底层原生 WebView 后反射 getSettings().setUserAgentString
 * - iOS:反射 WKWebView setValue:forKey:@"customUserAgent"
 */
function injectUserAgent(webview, platform) {
    // #ifdef APP-PLUS
    if (platform === 'android') {
        try {
            // plus.webview.create 返回的是 JS 包装对象,不能直接反射,
            // 必须先取底层原生 WebView(android.webkit.WebView)再取 settings。
            const nativeWebView = getAndroidNativeWebView(webview)
            const settings = safeAndroidInvoke(nativeWebView, 'getSettings')
            if (settings) {
                const originalUA = safeAndroidInvoke(settings, 'getUserAgentString')
                const customUA = `${originalUA} ${WEIZAN_UA_SUFFIX}`
                safeAndroidInvoke(settings, 'setUserAgentString', customUA)
                console.log('[weizanLive] Android UA 注入完成:', customUA)
            } else {
                console.warn('[weizanLive] Android 未取到原生 WebView 或 getSettings 返回空,UA 未注入')
            }
        } catch (e) {
            console.error('[weizanLive] Android UA 注入失败:', e)
        }
    } else if (platform === 'ios') {
        try {
            // plus.webview 实例 → valueForKey:@"webView" 拿底层 WKWebView
            const wkWebView = safeIosInvoke(webview, 'valueForKey:', 'webView') || webview
            // 读取原 UA
            const originalUA = safeIosInvoke(wkWebView, 'valueForKey:', 'userAgent') || ''
            const customUA = `${originalUA} ${WEIZAN_UA_SUFFIX}`
            // WKWebView.customUserAgent 是可写属性,反射设置
            safeIosInvoke(wkWebView, 'setValue:forKey:', customUA, 'customUserAgent')
            console.log('[weizanLive] iOS UA 注入完成:', customUA)
        } catch (e) {
            console.error('[weizanLive] iOS UA 注入失败:', e)
        }
    }
    // #endif
}
/**
 * 取 plus.webview 实例底层的原生 android.webkit.WebView。
 * 优先用官方 nativeInstanceObject(),失败再退回 obtainWebview() 等兼容方式。
 */
function getAndroidNativeWebView(webview) {
    if (!webview) {
        return null
    }
    // 1. 官方 API:WebviewObject.nativeInstanceObject() 返回原生 WebView
    if (typeof webview.nativeInstanceObject === 'function') {
        try {
            const nativeObj = webview.nativeInstanceObject()
            if (nativeObj) {
                return nativeObj
            }
        } catch (e) {
            console.warn('[weizanLive] nativeInstanceObject 调用失败:', e && e.message)
        }
    }
    // 2. 兼容:部分运行时通过 obtainWebview 取原生 WebView
    if (typeof webview.obtainWebview === 'function') {
        try {
            const obtained = webview.obtainWebview()
            if (obtained) {
                return obtained
            }
        } catch (e) {}
    }
    return null
}
/**
 * 安全调用 plus.android.invoke,失败返回 null。
 */
function safeAndroidInvoke(obj, methodName, ...args) {
    try {
        return plus.android.invoke(obj, methodName, ...args)
    } catch (e) {
        console.warn('[weizanLive] android invoke fail:', methodName, e && e.message)
        return null
    }
}
/**
 * 安全调用 plus.ios.invoke,失败返回 null。
 */
function safeIosInvoke(obj, selector, ...args) {
    try {
        return plus.ios.invoke(obj, selector, ...args)
    } catch (e) {
        console.warn('[weizanLive] ios invoke fail:', selector, e && e.message)
        return null
    }
function decodeValue(value) {
    if (typeof value !== 'string' || !value.trim()) return ''
    try { return decodeURIComponent(value) } catch (e) { return value.trim() }
}
</script>
<style>
.weizan-live-container {
    width: 100%;
    height: 100vh;
    background-color: #FFFFFF;
}
<style lang="scss" scoped>
.container { width: 100vw; min-height: 100vh; background: #fff; }
.webview-content { width: 100%; height: 100vh; }
.empty-box { min-height: 100vh; display: flex; align-items: center; justify-content: center; }
.empty-text { font-size: 28rpx; color: #999; }
</style>