| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369 |
- <!-- 支付跳转中间页(完整兼容版) -->
- <template>
- <view>
- <!-- 自定义导航栏 -->
- <view class="pay-webview-nav" :style="{ height: (statusBarH + 44) + 'px' }">
- <view class="nav-bar" :style="{ height: (statusBarH + 44) + 'px', paddingTop: statusBarH + 'px' }">
- <!-- 返回按钮 -->
- <view class="nav-action" @tap="goBack">
- <text class="wlIcon wlIcon-fanhui1"></text>
- </view>
- <!-- 标题 -->
- <view class="nav-title">支付中</view>
- <!-- 首页按钮 -->
- <view class="nav-action" @tap="goHome">
- <text class="wlIcon wlIcon-zhuye"></text>
- <text class="nav-home-text">88live</text>
- </view>
- </view>
- </view>
- <view class="edgeInsetTop"></view>
- <!--
- TNG 等收银台:App 内 WebView 可能因 UA/存储与系统浏览器不同出现白屏。
- 本页在 App 端对 tngdigital 域名会在赋值 payUrl 前临时设置更接近 Chrome/Safari 的 UA,并在 onUnload 尝试恢复。
- -->
- <web-view v-if="payUrl" :webview-styles="webviewStyles" :src="payUrl" @message="onMessage" @error="onWebViewError"
- @load="onWebViewLoad"></web-view>
- <view v-if="payUrl" class="open-browser-bar">
- <view class="open-browser-btn" @tap="openInBrowser">
- <text class="open-browser-text">用瀏覽器打開</text>
- </view>
- </view>
- </view>
- </template>
- <script>
- export default {
- data() {
- return {
- payUrl: '',
- order_id: '',
- order_type: '',
- successUrl: '',
- hasClosedByPayPush: false,
- leavingToSuccess: false,
- statusBarH: 0,
- navTop: 0,
- webViewHeight: 600,
- /** App 端为 TNG 临时改 UA 前备份 */
- _beforePayWebviewUA: '',
- _tngUaReplaced: false,
- webviewStyles: {
- progress: {
- color: '#ff6b35'
- }
- }
- }
- },
- onLoad(option) {
- const sys = uni.getSystemInfoSync()
- this.statusBarH = sys.statusBarHeight || 0
- const navBarInner = uni.upx2px(88)
- this.navTop = this.statusBarH + navBarInner
- this.webViewHeight = (sys.windowHeight || 667) - this.navTop
- const rawUrl = option.url || ''
- // #ifdef APP-PLUS
- if (rawUrl && this.isTngCashierUrl(rawUrl)) {
- this.applyTngAppUserAgent()
- }
- // #endif
- this.payUrl = rawUrl
- if (option.order_id) this.order_id = option.order_id
- if (option.order_type) this.order_type = option.order_type
- if (option.success_url) this.successUrl = decodeURIComponent(option.success_url)
- this.webviewStyles = {
- progress: {
- color: '#ff6b35'
- },
- top: `${this.navTop}px`,
- height: `${this.webViewHeight}px`
- }
- uni.$on('onPayeMessage', this.onPayeMessage)
- },
- onReady() {
- // #ifdef APP-PLUS
- if (this.payUrl) {
- this.adjustAppWebviewLayout()
- if (this.isTngCashierUrl(this.payUrl)) {
- this.patchSubWebviewForTng()
- }
- }
- // #endif
- },
- onBackPress() {
- if (!this.leavingToSuccess) {
- uni.setStorageSync('pay_webview_return', JSON.stringify({
- type: 'back',
- ts: Date.now(),
- order_id: this.order_id,
- order_type: this.order_type
- }))
- }
- return false
- },
- onUnload() {
- uni.$off('onPayeMessage', this.onPayeMessage)
- // #ifdef APP-PLUS
- this.restoreAppUserAgent()
- // #endif
- },
- onShow() {
- this.checkPaymentStatus()
- },
- methods: {
- isTngCashierUrl(url) {
- if (!url || typeof url !== 'string') return false
- const u = url.toLowerCase()
- return u.indexOf('tngdigital.com') >= 0
- },
- /** App:整页级 UA(影响当前页内子 web-view 首包请求) */
- applyTngAppUserAgent() {
- try {
- if (typeof plus === 'undefined' || !plus.navigator) return
- if (typeof plus.navigator.getUserAgent === 'function') {
- this._beforePayWebviewUA = plus.navigator.getUserAgent()
- }
- const sys = uni.getSystemInfoSync()
- const androidChrome =
- 'Mozilla/5.0 (Linux; Android 12) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.144 Mobile Safari/537.36'
- const iosSafari =
- 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1'
- plus.navigator.setUserAgent(sys.platform === 'ios' ? iosSafari : androidChrome)
- this._tngUaReplaced = true
- } catch (e) {
- console.warn('[pay-webview] applyTngAppUserAgent', e)
- }
- },
- restoreAppUserAgent() {
- if (!this._tngUaReplaced) return
- try {
- if (typeof plus !== 'undefined' && plus.navigator && this._beforePayWebviewUA) {
- plus.navigator.setUserAgent(this._beforePayWebviewUA)
- }
- } catch (e) {
- console.warn('[pay-webview] restoreAppUserAgent', e)
- }
- this._tngUaReplaced = false
- this._beforePayWebviewUA = ''
- },
- /** 延迟获取子 web-view,尝试 setStyle(兼容性因基座版本而异) */
- patchSubWebviewForTng() {
- setTimeout(() => {
- try {
- const pages = getCurrentPages()
- const page = pages[pages.length - 1]
- if (!page || typeof page.$getAppWebview !== 'function') return
- const cw = page.$getAppWebview()
- const children = cw.children && cw.children()
- if (!children || !children.length) return
- const wv = children[0]
- if (wv && typeof wv.setStyle === 'function') {
- wv.setStyle({ hardwareAccelerated: true })
- }
- const sys = uni.getSystemInfoSync()
- const androidChrome =
- 'Mozilla/5.0 (Linux; Android 12) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.6099.144 Mobile Safari/537.36'
- const iosSafari =
- 'Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1'
- const ua = sys.platform === 'ios' ? iosSafari : androidChrome
- if (wv && typeof wv.setUserAgent === 'function') {
- wv.setUserAgent(ua)
- } else if (wv && typeof wv.setStyle === 'function') {
- wv.setStyle({ userAgent: ua })
- }
- } catch (e) {
- console.warn('[pay-webview] patchSubWebviewForTng', e)
- }
- }, 300)
- },
- /** 调整 App 端原生 webview 布局,使其不遮挡自定义导航栏 */
- adjustAppWebviewLayout(retry = 0) {
- // #ifdef APP-PLUS
- const pages = getCurrentPages()
- const page = pages[pages.length - 1]
- if (!page || typeof page.$getAppWebview !== 'function') return
- const apply = () => {
- const currentWebview = page.$getAppWebview()
- const children = currentWebview.children()
- if (!children || !children.length) {
- if (retry < 10) {
- setTimeout(() => this.adjustAppWebviewLayout(retry + 1), 200)
- }
- return
- }
- const wv = children[0]
- wv.setStyle({
- top: `${this.navTop}px`,
- height: `${this.webViewHeight}px`,
- bottom: 'auto'
- })
- }
- setTimeout(apply, retry === 0 ? 300 : 0)
- // #endif
- },
- onPayeMessage(msg) {
- const isPay =
- (msg && msg.type === 'pay') ||
- (msg && msg.message && msg.message.type === 'pay')
- if (!isPay) return
- if (this.hasClosedByPayPush) return
- this.hasClosedByPayPush = true
- this.paySuccess()
- },
- onMessage(e) {
- console.log('[pay-webview] 收到 webview 消息:', e.detail.data)
- if (e.detail.data && e.detail.data[0] && e.detail.data[0].type === 'payment_success') {
- this.paySuccess()
- }
- },
- onWebViewError(e) {
- console.warn('[pay-webview] webview 加载失败:', e)
- uni.showModal({
- title: '加載失敗',
- content: '支付頁面無法在應用內加載,是否用瀏覽器打開?',
- confirmText: '用瀏覽器打開',
- cancelText: '取消',
- success: (res) => {
- if (res.confirm) this.openInBrowser()
- }
- })
- },
- onWebViewLoad() { },
- openInBrowser() {
- if (!this.payUrl) return
- // #ifdef H5
- window.open(this.payUrl, '_blank')
- // #endif
- // #ifdef APP-PLUS
- plus.runtime.openURL(this.payUrl, (err) => {
- if (err) {
- uni.showToast({ title: '打開失敗', icon: 'none' })
- }
- })
- // #endif
- // #ifndef H5 || APP-PLUS
- if (typeof plus !== 'undefined' && plus.runtime && plus.runtime.openURL) {
- plus.runtime.openURL(this.payUrl)
- } else {
- uni.showModal({
- content: '請復製鏈接到瀏覽器打開:\n' + this.payUrl,
- showCancel: false
- })
- }
- // #endif
- },
- checkPaymentStatus() { },
- paySuccess() {
- this.leavingToSuccess = true
- uni.showToast({ title: '支付成功', icon: 'success' })
- setTimeout(() => {
- uni.navigateBack()
- }, 1500)
- },
- // 返回上一页
- goBack() {
- uni.navigateBack()
- },
- // 返回首页
- goHome() {
- uni.switchTab({ url: '/pages/index/index' })
- }
- }
- }
- </script>
- <style>
- page {
- background-color: #fff;
- }
- /* 自定义导航栏 */
- .pay-webview-nav {
- position: relative;
- z-index: 999;
- background-color: #fff;
- border-bottom: 1rpx solid #eee;
- }
- .nav-bar {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 20rpx;
- box-sizing: border-box;
- position: relative;
- }
- .nav-action {
- width: 100rpx;
- height: 44px;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 48rpx;
- color: #333;
- }
- .nav-home {
- width: auto;
- padding: 0 10rpx;
- }
- .nav-home-text {
- font-size: 28rpx;
- color: #333;
- margin-left: 8rpx;
- font-weight: 500;
- }
- .nav-title {
- flex: 1;
- text-align: center;
- font-size: 32rpx;
- font-weight: 600;
- color: #333;
- }
- .edgeInsetTop {
- height: var(--status-bar-height);
- }
- .open-browser-bar {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- padding: 20rpx 30rpx;
- padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
- background: linear-gradient(to top, rgba(255, 255, 255, 0.98), transparent);
- z-index: 999;
- }
- .open-browser-btn {
- background: #ff6b35;
- color: #fff;
- text-align: center;
- padding: 24rpx;
- border-radius: 12rpx;
- }
- .open-browser-text {
- font-size: 28rpx;
- color: #fff;
- }
- </style>
|