更新记录

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 不触发?

  1. 确认 javaScriptEnabled 没有被显式设为 false
  2. JS Bridge 在 onLoadEnd 之后才注入,不要在 onLoadStart 里访问;
  3. 网页内推荐写法: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 而不是完整域名);
  • 跨域 / 重定向链中的中间页也会触发,必要时配合 labelonClose 中区分;
  • 若回跳 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

隐私、权限声明

1. 本插件需要申请的系统权限列表:

2. 本插件采集的数据、发送的服务器地址、以及数据用途说明:

插件不采集任何数据

3. 本插件是否包含广告,如包含需详细说明广告表达方式、展示频率: