feat: 添加通用webview预览组件并优化入口
新增通用webview预览工具函数和组件,支持动态URL和标题参数传递。移除未使用的NutUI组件类型声明,在首页添加模拟webview测试入口,并优化测试中心描述文案。 - 新增webview.js工具模块,包含URL构建和参数解析函数 - 重构webview-preview页面,支持动态URL和错误状态处理 - 在首页添加模拟webview测试入口,便于验证承载能力 - 移除components.d.ts中未使用的NutUI组件类型声明 - 优化pay-test页面,使用工具函数构建webview预览URL
Showing
5 changed files
with
125 additions
and
7 deletions
| ... | @@ -9,9 +9,6 @@ declare module 'vue' { | ... | @@ -9,9 +9,6 @@ declare module 'vue' { |
| 9 | export interface GlobalComponents { | 9 | export interface GlobalComponents { |
| 10 | AppTabbar: typeof import('./src/components/AppTabbar.vue')['default'] | 10 | AppTabbar: typeof import('./src/components/AppTabbar.vue')['default'] |
| 11 | IndexNav: typeof import('./src/components/indexNav.vue')['default'] | 11 | IndexNav: typeof import('./src/components/indexNav.vue')['default'] |
| 12 | - NutCell: typeof import('@nutui/nutui-taro')['Cell'] | ||
| 13 | - NutCellGroup: typeof import('@nutui/nutui-taro')['CellGroup'] | ||
| 14 | - NutIcon: typeof import('@nutui/nutui-taro')['Icon'] | ||
| 15 | NutTabbar: typeof import('@nutui/nutui-taro')['Tabbar'] | 12 | NutTabbar: typeof import('@nutui/nutui-taro')['Tabbar'] |
| 16 | NutTabbarItem: typeof import('@nutui/nutui-taro')['TabbarItem'] | 13 | NutTabbarItem: typeof import('@nutui/nutui-taro')['TabbarItem'] |
| 17 | Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] | 14 | Picker: typeof import('./src/components/time-picker-data/picker.vue')['default'] | ... | ... |
| ... | @@ -42,10 +42,18 @@ | ... | @@ -42,10 +42,18 @@ |
| 42 | <button class="primary-btn" @tap="goToMapGuide">打开地图导览</button> | 42 | <button class="primary-btn" @tap="goToMapGuide">打开地图导览</button> |
| 43 | </view> | 43 | </view> |
| 44 | 44 | ||
| 45 | + <view class="webview-entry-card"> | ||
| 46 | + <text class="section-label">模拟 WebView</text> | ||
| 47 | + <text class="webview-entry-desc"> | ||
| 48 | + 这里提供一个首页直达的测试 WebView 入口,用于验证通用 WebView 承载能力。 | ||
| 49 | + </text> | ||
| 50 | + <button class="outline-btn" @tap="goToMockWebview">打开模拟 WebView</button> | ||
| 51 | + </view> | ||
| 52 | + | ||
| 45 | <view class="test-entry-card"> | 53 | <view class="test-entry-card"> |
| 46 | <text class="section-label">测试入口</text> | 54 | <text class="section-label">测试入口</text> |
| 47 | <text class="test-entry-desc"> | 55 | <text class="test-entry-desc"> |
| 48 | - 支付测试与 WebView 预览已移入测试中心,首页只保留统一入口。 | 56 | + 支付测试等调试能力仍统一收口到测试中心,方便集中联调。 |
| 49 | </text> | 57 | </text> |
| 50 | <button class="outline-btn" @tap="goToTestCenter">进入测试中心</button> | 58 | <button class="outline-btn" @tap="goToTestCenter">进入测试中心</button> |
| 51 | </view> | 59 | </view> |
| ... | @@ -60,6 +68,10 @@ import { ref } from 'vue' | ... | @@ -60,6 +68,10 @@ import { ref } from 'vue' |
| 60 | import Taro, { useDidShow } from '@tarojs/taro' | 68 | import Taro, { useDidShow } from '@tarojs/taro' |
| 61 | import AppTabbar from '@/components/AppTabbar.vue' | 69 | import AppTabbar from '@/components/AppTabbar.vue' |
| 62 | import { hasAuth } from '@/utils/authRedirect' | 70 | import { hasAuth } from '@/utils/authRedirect' |
| 71 | +import { buildWebviewPreviewUrl } from '@/utils/webview' | ||
| 72 | + | ||
| 73 | +const mock_webview_url = 'https://oa-dev.onwall.cn/f/futian_home/?f=f&p=futian_list' | ||
| 74 | +const mock_webview_title = '福田首页' | ||
| 63 | 75 | ||
| 64 | const isAuthed = ref(false) | 76 | const isAuthed = ref(false) |
| 65 | 77 | ||
| ... | @@ -73,6 +85,12 @@ const goToMapGuide = () => { | ... | @@ -73,6 +85,12 @@ const goToMapGuide = () => { |
| 73 | }) | 85 | }) |
| 74 | } | 86 | } |
| 75 | 87 | ||
| 88 | +const goToMockWebview = () => { | ||
| 89 | + Taro.navigateTo({ | ||
| 90 | + url: buildWebviewPreviewUrl(mock_webview_url, mock_webview_title), | ||
| 91 | + }) | ||
| 92 | +} | ||
| 93 | + | ||
| 76 | const goToTestCenter = () => { | 94 | const goToTestCenter = () => { |
| 77 | Taro.navigateTo({ | 95 | Taro.navigateTo({ |
| 78 | url: '/pages/pay-test/index', | 96 | url: '/pages/pay-test/index', |
| ... | @@ -100,6 +118,7 @@ useDidShow(() => { | ... | @@ -100,6 +118,7 @@ useDidShow(() => { |
| 100 | .status-card, | 118 | .status-card, |
| 101 | .overview-card, | 119 | .overview-card, |
| 102 | .map-entry-card, | 120 | .map-entry-card, |
| 121 | + .webview-entry-card, | ||
| 103 | .test-entry-card { | 122 | .test-entry-card { |
| 104 | background: rgba(255, 255, 255, 0.94); | 123 | background: rgba(255, 255, 255, 0.94); |
| 105 | border: 2rpx solid rgba(166, 121, 57, 0.08); | 124 | border: 2rpx solid rgba(166, 121, 57, 0.08); |
| ... | @@ -154,6 +173,7 @@ useDidShow(() => { | ... | @@ -154,6 +173,7 @@ useDidShow(() => { |
| 154 | .status-text, | 173 | .status-text, |
| 155 | .card-desc, | 174 | .card-desc, |
| 156 | .map-entry-desc, | 175 | .map-entry-desc, |
| 176 | + .webview-entry-desc, | ||
| 157 | .test-entry-desc { | 177 | .test-entry-desc { |
| 158 | display: block; | 178 | display: block; |
| 159 | margin-top: 12rpx; | 179 | margin-top: 12rpx; |
| ... | @@ -200,6 +220,7 @@ useDidShow(() => { | ... | @@ -200,6 +220,7 @@ useDidShow(() => { |
| 200 | } | 220 | } |
| 201 | 221 | ||
| 202 | .map-entry-card, | 222 | .map-entry-card, |
| 223 | + .webview-entry-card, | ||
| 203 | .test-entry-card { | 224 | .test-entry-card { |
| 204 | margin-top: 24rpx; | 225 | margin-top: 24rpx; |
| 205 | padding: 32rpx; | 226 | padding: 32rpx; | ... | ... |
| ... | @@ -67,6 +67,10 @@ | ... | @@ -67,6 +67,10 @@ |
| 67 | import { ref, watch } from 'vue' | 67 | import { ref, watch } from 'vue' |
| 68 | import Taro, { useDidShow, useLoad } from '@tarojs/taro' | 68 | import Taro, { useDidShow, useLoad } from '@tarojs/taro' |
| 69 | import { useWechatMiniPay } from '@/composables/useWechatMiniPay' | 69 | import { useWechatMiniPay } from '@/composables/useWechatMiniPay' |
| 70 | +import { buildWebviewPreviewUrl } from '@/utils/webview' | ||
| 71 | + | ||
| 72 | +const pay_bridge_preview_url = 'https://oa-dev.onwall.cn/f/map/#/weapp-pay-bridge' | ||
| 73 | +const pay_bridge_preview_title = '支付桥预览' | ||
| 70 | 74 | ||
| 71 | const order_id = ref('') | 75 | const order_id = ref('') |
| 72 | const should_auto_pay = ref(false) | 76 | const should_auto_pay = ref(false) |
| ... | @@ -105,7 +109,7 @@ const handleRefreshAuth = async () => { | ... | @@ -105,7 +109,7 @@ const handleRefreshAuth = async () => { |
| 105 | 109 | ||
| 106 | const goToWebviewPreview = () => { | 110 | const goToWebviewPreview = () => { |
| 107 | Taro.navigateTo({ | 111 | Taro.navigateTo({ |
| 108 | - url: '/pages/webview-preview/index', | 112 | + url: buildWebviewPreviewUrl(pay_bridge_preview_url, pay_bridge_preview_title), |
| 109 | }) | 113 | }) |
| 110 | } | 114 | } |
| 111 | 115 | ... | ... |
| 1 | <template> | 1 | <template> |
| 2 | - <web-view :src="preview_url" /> | 2 | + <web-view v-if="preview_url" :src="preview_url" /> |
| 3 | + <view v-else class="webview-preview-page"> | ||
| 4 | + <view class="empty-card"> | ||
| 5 | + <text class="empty-title">缺少预览地址</text> | ||
| 6 | + <text class="empty-desc"> | ||
| 7 | + 当前页面没有收到可用的 URL,请从业务入口重新进入。 | ||
| 8 | + </text> | ||
| 9 | + </view> | ||
| 10 | + </view> | ||
| 3 | </template> | 11 | </template> |
| 4 | 12 | ||
| 5 | <script setup> | 13 | <script setup> |
| 6 | -const preview_url = 'https://oa-dev.onwall.cn/f/map/#/weapp-pay-bridge' | 14 | +import { ref } from 'vue' |
| 15 | +import Taro, { useLoad } from '@tarojs/taro' | ||
| 16 | +import { parseWebviewRouteParam, parseWebviewRouteUrl } from '@/utils/webview' | ||
| 17 | + | ||
| 18 | +const preview_url = ref('') | ||
| 19 | +const default_page_title = 'WebView 预览' | ||
| 20 | + | ||
| 21 | +useLoad((options) => { | ||
| 22 | + preview_url.value = parseWebviewRouteUrl(options?.url) | ||
| 23 | + const page_title = parseWebviewRouteParam(options?.title) || default_page_title | ||
| 24 | + | ||
| 25 | + Taro.setNavigationBarTitle({ | ||
| 26 | + title: page_title, | ||
| 27 | + }) | ||
| 28 | + | ||
| 29 | + if (!preview_url.value) { | ||
| 30 | + Taro.showToast({ | ||
| 31 | + title: '缺少预览地址', | ||
| 32 | + icon: 'none', | ||
| 33 | + }) | ||
| 34 | + } | ||
| 35 | +}) | ||
| 7 | </script> | 36 | </script> |
| 37 | + | ||
| 38 | +<style lang="less"> | ||
| 39 | +.webview-preview-page { | ||
| 40 | + min-height: 100vh; | ||
| 41 | + padding: 32rpx 24rpx; | ||
| 42 | + box-sizing: border-box; | ||
| 43 | + background: | ||
| 44 | + radial-gradient(circle at top right, rgba(166, 121, 57, 0.16), transparent 30%), | ||
| 45 | + linear-gradient(180deg, #fffaf3 0%, #f6f7fb 100%); | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +.empty-card { | ||
| 49 | + background: rgba(255, 255, 255, 0.94); | ||
| 50 | + border: 2rpx solid rgba(166, 121, 57, 0.08); | ||
| 51 | + border-radius: 28rpx; | ||
| 52 | + padding: 32rpx; | ||
| 53 | + box-sizing: border-box; | ||
| 54 | + box-shadow: 0 20rpx 60rpx rgba(15, 23, 42, 0.06); | ||
| 55 | +} | ||
| 56 | + | ||
| 57 | +.empty-title { | ||
| 58 | + display: block; | ||
| 59 | + font-size: 36rpx; | ||
| 60 | + font-weight: 700; | ||
| 61 | + color: #111827; | ||
| 62 | +} | ||
| 63 | + | ||
| 64 | +.empty-desc { | ||
| 65 | + display: block; | ||
| 66 | + margin-top: 16rpx; | ||
| 67 | + font-size: 26rpx; | ||
| 68 | + line-height: 1.7; | ||
| 69 | + color: #6b7280; | ||
| 70 | +} | ||
| 71 | +</style> | ... | ... |
src/utils/webview.js
0 → 100644
| 1 | +export const buildWebviewPreviewUrl = (target_url = '', page_title = '') => { | ||
| 2 | + const normalized_url = String(target_url || '').trim() | ||
| 3 | + const normalized_title = String(page_title || '').trim() | ||
| 4 | + | ||
| 5 | + if (!normalized_url) { | ||
| 6 | + return '/pages/webview-preview/index' | ||
| 7 | + } | ||
| 8 | + | ||
| 9 | + const query_list = [`url=${encodeURIComponent(normalized_url)}`] | ||
| 10 | + | ||
| 11 | + if (normalized_title) { | ||
| 12 | + query_list.push(`title=${encodeURIComponent(normalized_title)}`) | ||
| 13 | + } | ||
| 14 | + | ||
| 15 | + return `/pages/webview-preview/index?${query_list.join('&')}` | ||
| 16 | +} | ||
| 17 | + | ||
| 18 | +export const parseWebviewRouteParam = (route_value = '') => { | ||
| 19 | + const normalized_route_value = String(route_value || '').trim() | ||
| 20 | + | ||
| 21 | + if (!normalized_route_value) { | ||
| 22 | + return '' | ||
| 23 | + } | ||
| 24 | + | ||
| 25 | + try { | ||
| 26 | + return decodeURIComponent(normalized_route_value) | ||
| 27 | + } catch (error) { | ||
| 28 | + return normalized_route_value | ||
| 29 | + } | ||
| 30 | +} | ||
| 31 | + | ||
| 32 | +export const parseWebviewRouteUrl = (route_url = '') => parseWebviewRouteParam(route_url) |
-
Please register or login to post a comment