更新记录
1.0.0(2026-04-30)
- dh-webview-sheet 首发版本
- 三种弹窗模式:
bottom底部 Sheet、center居中弹窗、fullscreen全屏 - URL 拦截规则
interceptRules:支持close/block/allow三种动作,命中后自动关闭并透传 status - JS 双向通信:网页通过
window.dhWebSheet.postMessage()上报,原生通过evaluateJS/postMessageToWeb下发 - 加载进度条、圆角、拖拽手柄、下拉关闭、点遮罩关闭、自定义 UA 等完整 WebView 容器能力
平台兼容性
uni-app(4.0)
| Vue2 | Vue3 | Chrome | Safari | app-vue | app-nvue | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 |
|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | 5.0 | 1.0.0 | 12 | 1.0.0 | - |
| 微信小程序 | 支付宝小程序 | 抖音小程序 | 百度小程序 | 快手小程序 | 京东小程序 | 鸿蒙元服务 | QQ小程序 | 飞书小程序 | 小红书小程序 | 快应用-华为 | 快应用-联盟 |
|---|---|---|---|---|---|---|---|---|---|---|---|
| - | - | - | - | - | - | - | - | - | - | - | - |
uni-app x(5.0)
| Chrome | Safari | Android | Android插件版本 | iOS | iOS插件版本 | 鸿蒙 | 微信小程序 |
|---|---|---|---|---|---|---|---|
| - | - | 5.0 | 1.0.0 | 12 | 1.0.0 | - | - |
其他
| 多语言 | 暗黑模式 | 宽屏模式 |
|---|---|---|
| √ | √ | √ |
dh-webview-sheet
通用 WebView 弹窗插件,支持底部 Sheet、居中弹窗、全屏三种模式。内置 URL 拦截规则、JS 双向通信、加载进度条、多实例管理、圆角与拖拽手柄等特性,功能与各主流移动框架的 WebView/Sheet 组件对齐。
典型适用场景
- 支付收银台:Stripe Checkout、Airwallex、PayPal、Adyen、PingPong、连连等海外收单页 —— 用
interceptRules监听成功/取消回跳 URL,自动关闭并回调结果 - 第三方授权:Google / Apple / Facebook / Twitter / GitHub / 飞书 / 钉钉 OAuth、SAML SSO —— 居中弹窗 + JS Bridge 接管 token
- 活动 / 营销页:H5 抽奖、签到、问卷、邀请页 —— 底部 Sheet 浮层,不离开主流程
- 客服 / IM:环信、网易云信、Zendesk、Intercom 的网页客服窗口
- 协议 / 隐私政策:Markdown / HTML 渲染的隐私协议、用户协议、版本说明
- 本地 H5 资源:
file://加载本地 HTML 报表、图表、富文本预览 - 小程序内嵌 H5 模拟:在原生 App 里嵌入 H5 模块,与原生页面共存
- OAuth Web 提币 / KYC 流程:钱包绑定、币安/欧易等交易所 KYC 流程
- 任何需要"打开网页 → 拿到结果 → 自动关闭"的轻量浮层场景
快速开始
最简调用只需 url + onClose,其他参数均有合理默认值。
// #ifdef APP-PLUS
import { openWebSheet } from '@/uni_modules/dh-webview-sheet';
openWebSheet({
url: 'https://example.com/pay',
onClose: (r) => console.log(r.status, r.label),
});
// #endif
带 URL 拦截 + 头部标题的典型支付收银台调用:
// #ifdef APP-PLUS
import { openWebSheet } from '@/uni_modules/dh-webview-sheet';
const id = openWebSheet({
url: 'https://example.com/pay', // 必填,支持 https / http / file://
title: 'Checkout', // 头部标题,传 '' 隐藏整个头部
heightRatio: 0.85, // bottom / center 高度比例
// 命中规则后自动关闭弹窗,并把 status 透传到 onClose
interceptRules: [
{ pattern: '/pay/success', label: 'success', action: 'close', status: 'success' },
{ pattern: '/pay/cancel', label: 'cancel', action: 'close', status: 'cancel' },
],
onClose: (r) => console.log(r.status, r.label, r.message),
});
// 拿到 id 后可主动操作
// closeWebSheet(id)
// evaluateJS(id, 'document.title', (r) => console.log(r))
// postMessageToWeb(id, JSON.stringify({ token: 'abc' }))
// #endif
完整参数(位置、外观、进度条、JS Bridge、事件回调等)见下方 options 完整参数。
API
openWebSheet(options): string
打开 WebView 弹窗,返回实例 ID(用于后续操作)。
closeWebSheet(instanceId: string): void
主动关闭指定实例,触发 onClose(label: 'programmatic_close')。
evaluateJS(instanceId, script, callback): void
在指定 WebView 中执行 JavaScript,结果通过 callback 返回。
evaluateJS(id, 'document.title', (result) => {
console.log('页面标题:', result);
});
postMessageToWeb(instanceId, data): void
向指定 WebView 网页发送消息,网页通过监听 window message 事件接收。
postMessageToWeb(id, JSON.stringify({ type: 'token', value: 'abc123' }));
网页端接收:
window.addEventListener('message', (e) => {
const data = JSON.parse(e.data);
});
options 完整参数
位置与尺寸
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
url |
string |
必传 | 要加载的 URL,支持 https:// / http:// / file://(本地 HTML) |
position |
string |
'bottom' |
'bottom' | 'center' | 'fullscreen' |
heightRatio |
number |
0.85 |
高度比例 0.5~1.0(bottom / center 生效) |
widthRatio |
number |
0.9 |
宽度比例 0.5~1.0(center 生效) |
外观
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
cornerRadius |
number |
20 |
圆角半径 |
backgroundColor |
string |
'#FFFFFF' |
背景色 |
dimAmount |
number |
0.5 |
遮罩透明度 0~1 |
showGrabber |
boolean |
true |
顶部拖拽手柄(仅 bottom)。iOS 上显示 grabber 即允许下拉关闭(与 grabber 的标准 UX 语义一致);想锁定 sheet 必须 showGrabber=false + cancelable=false,配合 showClose=true 留下唯一关闭路径 |
animated |
boolean |
true |
开关弹出动画 |
cancelable |
boolean |
false |
center 模式:点 dim 遮罩可关闭;fullscreen 模式(iOS):屏幕左侧边缘下滑可关闭;bottom 模式(iOS):与 showGrabber 任一为 true 即可下拉关闭;Android:影响系统返回键 + 点遮罩外 |
头部栏
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
title |
string |
'' |
传空字符串则隐藏整个头部 |
titleColor |
string |
'#1A1A1A' |
标题颜色 |
showClose |
boolean |
true |
显示关闭按钮 |
closeButtonColor |
string |
'#888888' |
关闭按钮颜色 |
headerBackground |
string |
同 backgroundColor |
头部背景色 |
进度条
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
showProgress |
boolean |
true |
显示加载进度条 |
progressColor |
string |
'#635BFF' |
进度条颜色 |
WebView 行为
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
userAgent |
string |
'' |
自定义 User-Agent |
javaScriptEnabled |
boolean |
true |
是否允许 JavaScript |
URL 拦截规则 interceptRules
监听 WebView 内部的 URL 变化,命中规则时按 action 决定是关闭弹窗、阻止导航还是放行。常用于支付/授权回跳页的结果识别。
type WebViewInterceptRule = {
pattern: string; // URL 中包含此字符串时触发
label: string; // 触发标识,在 onClose result.label 中返回
action: 'close' | 'block' | 'allow';
status?: 'success' | 'cancel' | 'error'; // action='close' 时上报的状态
message?: string; // action='close' 时附带的消息
};
| action | 行为 |
|---|---|
close |
拦截导航,关闭弹窗,触发 onClose |
block |
拦截导航,不关闭,不回调 |
allow |
放行(默认行为,可用于记录日志) |
事件回调
| 回调 | 签名 | 说明 |
|---|---|---|
onClose |
(result: WebViewSheetResult) => void |
弹窗关闭,含触发原因 |
onLoadStart |
(url: string) => void |
页面开始加载 |
onLoadEnd |
(url: string) => void |
页面加载完成 |
onLoadError |
(url: string, errMsg: string) => void |
页面加载失败 |
onProgress |
(progress: number) => void |
加载进度 0~100 |
onNavigate |
(url: string) => void |
每次 URL 变化 |
onMessage |
(data: string) => void |
网页通过 window.dhWebSheet.postMessage() 发来的消息 |
WebViewSheetResult
type WebViewSheetResult = {
label: string; // 触发来源:规则 label 或 'user_closed'、'user_dismissed'、'programmatic_close'
status: 'success' | 'cancel' | 'error';
message: string;
};
JS Bridge(网页 → 原生)
页面加载完成后,插件自动向网页注入 window.dhWebSheet 对象,调用后触发 onMessage:
// 网页内调用
window.dhWebSheet.postMessage('hello');
window.dhWebSheet.postMessage(JSON.stringify({ event: 'close' }));
完整示例
Stripe 支付
// #ifdef APP-PLUS
import { openWebSheet } from '@/uni_modules/dh-webview-sheet';
// #endif
function stripePay(orderId, sessionUrl, successUrl, cancelUrl) {
return new Promise((resolve) => {
// #ifdef APP-PLUS
openWebSheet({
url: sessionUrl,
title: 'Stripe',
heightRatio: 0.85,
interceptRules: [
{ pattern: successUrl, label: 'success', action: 'close', status: 'success' },
{ pattern: cancelUrl, label: 'cancel', action: 'close', status: 'cancel' },
],
onClose(result) {
if (result.status === 'success') resolve({ status: 'success', orderId });
else resolve({ status: result.status, orderId, message: result.message });
},
});
// #endif
});
}
居中 OAuth 弹窗(含 JS 通信)
// #ifdef APP-PLUS
import { openWebSheet, evaluateJS, closeWebSheet } from '@/uni_modules/dh-webview-sheet';
// #endif
// #ifdef APP-PLUS
const id = openWebSheet({
url: 'https://example.com/oauth',
position: 'center',
heightRatio: 0.7,
widthRatio: 0.92,
title: '授权登录',
cancelable: true,
onMessage(data) {
const payload = JSON.parse(data);
if (payload.type === 'token') {
closeWebSheet(id);
handleToken(payload.value);
}
},
onClose(result) {
console.log('关闭:', result.label);
},
});
// #endif
全屏隐私协议(锁定 + 仅留关闭按钮)
// #ifdef APP-PLUS
openWebSheet({
url: 'https://example.com/privacy',
position: 'fullscreen',
title: '隐私协议',
showClose: true,
showGrabber: false, // 锁定:不允许下拉关闭
cancelable: false, // 锁定:屏蔽边缘手势 / 系统返回
onClose: (r) => console.log('用户已阅读完毕', r.label),
});
// #endif
加载本地 HTML(file://)
// #ifdef APP-PLUS
import { openWebSheet, postMessageToWeb } from '@/uni_modules/dh-webview-sheet';
const id = openWebSheet({
url: 'file:///android_asset/apps/__UNI__XXX/www/static/html/report.html', // Android
// url: 'file:///var/mobile/.../static/html/report.html', // iOS
title: '本地报表',
: () => {
// 页面加载完成后向网页推数据
postMessageToWeb(id, JSON.stringify({ chartData: [1, 2, 3] }));
},
});
// #endif
客服 / IM 浮层(半屏,可拖拽缩放)
// #ifdef APP-PLUS
openWebSheet({
url: 'https://kf.example.com?uid=123',
title: '在线客服',
heightRatio: 0.75,
showGrabber: true,
cancelable: true, // 用户可点遮罩或下拉关闭
onClose: (r) => console.log('关闭客服:', r.label),
});
// #endif
常见问题(FAQ)
Q1:iOS 下拉关闭不生效?
检查 showGrabber 是否为 true。iOS 上 grabber 显示即允许下拉关闭(这是 grabber 的 UX 标准语义);想完全锁定必须 showGrabber=false + cancelable=false,并保留 showClose=true 作为唯一关闭路径。
Q2:网页里 window.dhWebSheet 不存在 / onMessage 不触发?
- 确认
javaScriptEnabled没有被显式设为false; - JS Bridge 在
onLoadEnd之后才注入,不要在onLoadStart里访问; - 网页内推荐写法:
if (window.dhWebSheet) window.dhWebSheet.postMessage(data)。
Q3:file:// 加载失败 / 白屏?
- Android:插件已默认开启
allowFileAccess/allowFileAccessFromFileURLs,路径要正确(推荐plus.io.convertLocalFileSystemURL或_doc/、_www/之类的 5+ Runtime 路径); - iOS:路径必须是 App 沙盒内的真实文件,插件内部会用
loadFileURL(_:allowingReadAccessTo:)加载并授予父目录访问权限。
Q4:interceptRules 命中不到回跳 URL?
- 用的是包含匹配(
url.contains(pattern)),建议传唯一路径片段(如/pay/success而不是完整域名); - 跨域 / 重定向链中的中间页也会触发,必要时配合
label在onClose中区分; - 若回跳 URL 是
myapp://...之类的自定义 scheme,直接拦截即可,不用走系统 schemeHandler。
Q5:多实例叠加显示?
连续调用 openWebSheet 会按顺序叠加(栈式),每个实例返回独立的 instanceId,分别用 closeWebSheet(id) 关闭。
Q6:iOS 15 / 14 上效果与 16+ 不同?
- iOS 16+:使用
UISheetPresentationController自定义 detent,高度比例精确; - iOS 15:降级到 large detent,
heightRatio不生效但功能完整; - iOS 14 及以下:未做兼容,建议提示用户升级。
注意事项
- 仅支持 APP 端,请用
#ifdef APP-PLUS包裹所有调用 interceptRules使用包含匹配(url.contains(pattern)),建议使用唯一路径片段- 多个实例可同时存在,用返回的
instanceId区分 javaScriptEnabled: false时,JS Bridge 和evaluateJS不生效position='center'时建议同时设置cancelable: true让用户可点遮罩关闭- 想锁定 sheet 不让用户关闭:
showGrabber=false+cancelable=false+showClose=true

收藏人数:
购买源码授权版(
试用
赞赏(0)
下载 200
赞赏 5
下载 11762163
赞赏 1911
赞赏
京公网安备:11010802035340号