experience-webview.ts 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136
  1. import { type H5BridgeMessage, type H5ExperienceRequest } from '../../game/experience/h5Experience'
  2. type ExperienceWebViewPageData = {
  3. webViewSrc: string
  4. webViewReady: boolean
  5. loadErrorText: string
  6. }
  7. let currentRequest: H5ExperienceRequest | null = null
  8. let currentEventChannel: WechatMiniprogram.EventChannel | null = null
  9. let pageResolved = false
  10. function appendQueryParam(url: string, key: string, value: string): string {
  11. const separator = url.indexOf('?') >= 0 ? '&' : '?'
  12. return `${url}${separator}${key}=${encodeURIComponent(value)}`
  13. }
  14. function buildWebViewSrc(request: H5ExperienceRequest): string {
  15. let nextUrl = request.url
  16. nextUrl = appendQueryParam(nextUrl, 'cmrBridge', request.bridgeVersion)
  17. nextUrl = appendQueryParam(nextUrl, 'cmrKind', request.kind)
  18. return nextUrl
  19. }
  20. function emitFallbackAndClose() {
  21. if (!currentRequest || !currentEventChannel) {
  22. return
  23. }
  24. if (!pageResolved) {
  25. pageResolved = true
  26. currentEventChannel.emit('fallback', currentRequest.fallback)
  27. }
  28. wx.navigateBack({
  29. fail: () => {},
  30. })
  31. }
  32. function emitCloseAndBack(payload?: Record<string, unknown>) {
  33. if (currentEventChannel && !pageResolved) {
  34. pageResolved = true
  35. currentEventChannel.emit('close', payload || {})
  36. }
  37. wx.navigateBack({
  38. fail: () => {},
  39. })
  40. }
  41. Page<ExperienceWebViewPageData, WechatMiniprogram.IAnyObject>({
  42. data: {
  43. webViewSrc: '',
  44. webViewReady: false,
  45. loadErrorText: '',
  46. },
  47. onLoad() {
  48. pageResolved = false
  49. currentRequest = null
  50. currentEventChannel = null
  51. this.setData({
  52. webViewSrc: '',
  53. webViewReady: false,
  54. loadErrorText: '',
  55. })
  56. try {
  57. currentEventChannel = this.getOpenerEventChannel()
  58. } catch {
  59. currentEventChannel = null
  60. }
  61. if (!currentEventChannel) {
  62. return
  63. }
  64. currentEventChannel.on('init', (request: H5ExperienceRequest) => {
  65. currentRequest = request
  66. wx.setNavigationBarTitle({
  67. title: request.title || '内容体验',
  68. fail: () => {},
  69. })
  70. this.setData({
  71. webViewSrc: buildWebViewSrc(request),
  72. webViewReady: true,
  73. loadErrorText: '',
  74. })
  75. })
  76. },
  77. onUnload() {
  78. if (currentEventChannel && !pageResolved) {
  79. currentEventChannel.emit('close', {})
  80. }
  81. pageResolved = false
  82. currentRequest = null
  83. currentEventChannel = null
  84. },
  85. handleWebViewMessage(event: WechatMiniprogram.CustomEvent) {
  86. const dataList = event.detail && Array.isArray(event.detail.data)
  87. ? event.detail.data
  88. : []
  89. const rawMessage = dataList.length ? dataList[dataList.length - 1] : null
  90. if (!rawMessage || typeof rawMessage !== 'object') {
  91. return
  92. }
  93. const message = rawMessage as H5BridgeMessage
  94. const action = message.action || message.type || ''
  95. if (!action) {
  96. return
  97. }
  98. if (action === 'close') {
  99. emitCloseAndBack(message.payload)
  100. return
  101. }
  102. if (action === 'submitResult') {
  103. if (currentEventChannel) {
  104. currentEventChannel.emit('submitResult', message.payload || {})
  105. }
  106. return
  107. }
  108. if (action === 'fallback') {
  109. emitFallbackAndClose()
  110. }
  111. },
  112. handleWebViewError() {
  113. this.setData({
  114. loadErrorText: '页面打开失败,已回退原生内容',
  115. })
  116. emitFallbackAndClose()
  117. },
  118. })