import { promptAction, router } from '@kit.ArkUI' import { webview } from '@kit.ArkWeb' import { bundleManager, common } from '@kit.AbilityKit' import { BusinessError } from '@kit.BasicServicesKit' import { WebViewJavascriptBridge, WVJBResponseCallback } from '@yue/webview_javascript_bridge' import util from '@kit.ArkTS'; @Component struct PrivacyPolicyPage { @Require fromText: string @State targetUrl: string = '' @State title: string = '' // 是否有进度条 hasProgressBar: boolean = false // 网络加载进度 @State progressNum: number = 0 // 网络是否在加载中 @State loading: boolean = true // 是否显示 navBarShow: boolean = true // 进入页面时,状态条颜色 onPageShowBarColor: StatusBarColor = StatusBarColor.Black // 隐藏页面时,状态条颜色 onPageHideBarColor: StatusBarColor = StatusBarColor.Black // 顶部安全高度 safeTop: number = AppStorage.get('safeTop') as number // 底部安全高度 safeBottom: number = AppStorage.get('safeBottom') as number controller: webview.WebviewController = new webview.WebviewController() historyCurrIndex: number = 0 // WebViewJavascriptBridge 桥接 private bridge: WebViewJavascriptBridge | undefined; private context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext; // 是否安装支付宝应用 private canOpenAlipays = false; // 是否安装微信应用 private canOpenWechat = false; // 是否已拉起微信支付 private isWeixinPay = false; // 支付订单页url,如果成功拉起微信支付以后取消会回到app,此时需要跳到订单页 private orderUrl: string = ''; // 监听网页加载是否出错 @State loadError: boolean = false; // 点击返回键需要直接退出web组件的url集合 private excludedUrlList: string[] = [ 'https://app.sightcloud.cn/storage/#/package', 'https://app.aikancloud.com/storage/#/packageOrder', 'https://wx.88iot.net' ]; aboutToAppear(): void { this.onPageShow() } onPageShow(): void { // this.statusBarColorSelect(this.onPageShowBarColor) // 判断是否安装支付宝 try { this.canOpenAlipays = bundleManager.canOpenLink('alipays://'); this.canOpenWechat = bundleManager.canOpenLink('weixin://'); } catch (err) { let message = (err as BusinessError).message; console.log('canOpenLink call error:', message); } // 判断是否是跳转到微信以后再返回app,若是,则跳转到订单页,若没有拿到订单url,直接返回上一页 if (this.isWeixinPay) { this.isWeixinPay = false if (this.orderUrl) { // 清跳转订单页 this.controller.loadUrl(this.orderUrl) } else { this.backMethod() } } } onPageHide(): void { this.statusBarColorSelect(this.onPageHideBarColor) } // 状态条颜色选取函数 statusBarColorSelect(color: StatusBarColor) { switch (color) { case StatusBarColor.White: // themeManager.settingStatusBarWhite() break case StatusBarColor.Black: // themeManager.settingStatusBarBlack() break default: // themeManager.settingStatusBarBlack() break } } // 页面自行处理返回逻辑,不进行页面路由 onBackPress() { this.backMethod() return true } backMethod() { if (this.loadError) { router.back() return } const url = this.controller.getUrl() if (this.excludedUrlList.some(item => url.startsWith(item))) { router.back() return } if (this.historyCurrIndex > 0) { this.controller.backward() } else { router.back() } } build() { NavDestination() { Column() { if (this.navBarShow) { // PrivacyPolicyPageTitle({ title: this.title }) } Stack({ alignContent: Alignment.Top }) { if (this.hasProgressBar && this.loading && !this.loadError) { this.progress() } if (this.loadError) { Column() { Text('出错啦!点击空白处刷新') .fontColor('#686868') .fontSize(20) } // .width(StyleConstants.FULL_PARENT) // .height(StyleConstants.FULL_PARENT) .justifyContent(FlexAlign.Center) .onClick(() => { this.loadError = false }) } else { Scroll() { Web({ src: this.fromText, controller: this.controller }) .width('100%') .height('100%') .geolocationAccess(false) .domStorageAccess(true) .mixedMode(MixedMode.Compatible) .fileAccess(true) .multiWindowAccess(true) .onControllerAttached(() => { this.setupWebViewJavascriptBridge(); }) .onProgressChange((date) => { if (date) { //记录加载进度 this.progressNum = date.newProgress if (this.progressNum == 100) { //加载完成进度条消失 animateTo({ duration: 800, delay: 300 }, () => { this.loading = false }) } } }) .onRefreshAccessedHistory((event) => { //H5页面间的跳转 // if (this.controller.getTitle().length < 5) { // this.title = this.controller.getTitle() // } if (event) { const history = this.controller.getBackForwardEntries() this.historyCurrIndex = history.currentIndex } }) .onPageBegin((event) => { this.progressNum = 0 this.loading = true }) .onLoadIntercept((event) => { const isPayment = this.handlePaymentIntercept(event) return isPayment }) .onPageEnd((event) => { // this.controller.runJavaScript('document.forms[0].submit()') if (this.orderUrl && event.url == this.orderUrl) { this.controller.clearHistory() } }) .onErrorReceive((event) => { if (event.request.getRequestUrl() == this.controller.getUrl()) { console.error(`WebView load error: ${event.request.getRequestUrl()}, code: ${event.error.getErrorCode()}, message: ${event.error.getErrorInfo()}`); this.loadError = true; // 显示错误页面 } }) } .margin({ bottom: this.navBarShow ? this.safeBottom : 0 }) } } } .width('100%') .height('100%') .padding({ top: this.navBarShow ? this.safeTop : 0, bottom: this.navBarShow ? this.safeBottom : 0 }) } .hideTitleBar(true) } // 处理支付相关的拦截逻辑 private handlePaymentIntercept(event: OnLoadInterceptEvent) { let data = event.data; let url = data.getRequestUrl(); console.log(`alipay: url: ${url}`); // 点击商城回退时会触发该跳转,直接退出就好,不加载 if (url.includes('js_native://close')) { router.back() return true } // 拉起支付宝支付 if (url.startsWith('alipays://platformapi/startApp')) { if (this.canOpenAlipays) { try { this.context.openLink(url).then(() => { console.info('open alipays success.'); }).catch((err: BusinessError) => { console.error(`open alipays failed. Code is ${err.code}, message is ${err.message}`); promptAction.showToast({ message: '打开支付宝失败' }) }) } catch (paramError) { console.error(`Failed to start alipays. Code is ${paramError.code}, message is ${paramError.message}`) promptAction.showToast({ message: '打开支付宝失败,请稍后重试' }) } } else { promptAction.showToast({ message: '请先安装支付宝客户端' }) } } // 拉起微信支付链接,提前将支付成功微信支付跳转回app的订单页url保存,取消时同样需要跳转 if (url.startsWith('https://wx.tenpay.com')) { // 提取 redirect_url 参数 const redirectUrl = this.getRedirectUrlFromTenpay(url); if (redirectUrl) { console.info('提取到重定向链接:', redirectUrl); // 可以选择保存该链接用于后续操作 this.orderUrl = redirectUrl; } return false; } // 拉起微信支付链接 if (url.startsWith('weixin://')) { if (this.canOpenWechat) { try { this.context.openLink(url).then(() => { // 拉起微信支付成功 this.isWeixinPay = true console.info('open weixin success.'); }).catch((err: BusinessError) => { console.error(`open weixin failed. Code is ${err.code}, message is ${err.message}`); promptAction.showToast({ message: '打开微信失败' }) }) } catch (paramError) { console.error(`Failed to start weixin. Code is ${paramError.code}, message is ${paramError.message}`) promptAction.showToast({ message: '打开微信失败,请稍后重试' }) } return false } else { promptAction.showToast({ message: '请先安装微信客户端'}) return true } } return false } // 从微信支付链接中提取 redirect_url 参数 private getRedirectUrlFromTenpay(url: string): string | null { try { // 使用正则提取 redirect_url 参数 const regex = /[?&]redirect_url=([^&]+)/; const match = url.match(regex); if (match && match[1]) { let decodedUrl = decodeURIComponent(match[1]); return decodedUrl; } } catch (e) { console.error('解析 redirect_url 失败:', e); } return null; } @Builder progress() { Progress({ value: this.progressNum, total: 100, type: ProgressType.Linear }) .color('#17ab19') .zIndex(1) } /** * 设置 jsBridge */ private setupWebViewJavascriptBridge() { // 开启调试,控制台打印信息,默认无调试 WebViewJavascriptBridge.enableLogging(); // 创建 WebViewJavascriptBridge this.bridge = new WebViewJavascriptBridge(this.controller); this.registerHandlers(this.bridge) } // 注册原生处理函数 (这里需要知道H5端的方法名) private registerHandlers(bridge: WebViewJavascriptBridge) { bridge.registerHandler('backToNative', (data: object, responseCallback: WVJBResponseCallback) => { this.backMethod(); responseCallback(null); }); } } export enum StatusBarColor { White = 'White', Black = 'Black', } @Builder function PrivacyPolicyBuilder(_: string, fromText: string) { PrivacyPolicyPage({fromText}) }