JSBridge wrapper for Runner-base hybird app
Using npm:
$ npm install runner-bridge
Using yarn:
$ yarn add runner-bridge
Using pnpm:
$ pnpm add runner-bridge
- 获取设备信息
- 身份证扫描
- 图片选择
- 原生 Loading
- PDF 预览
- 跳转系统设置
- 第三方应用跳转/判断第三方应用是否安装
- 版本更新
- 退出应用
- 系统事件监听
- 网络状态变化
- 深色/浅色模式切换
- 字体大小设置
- Safe Area 变化(异形屏适配)
- 自定义事件监听
- 本地缓存
- 保存数据
- 获取数据
- 移除数据
- 清空数据
- 页面导航
- 设置系统状态栏
- 设置顶部标题栏
- 点击事件监听
- 页面跳转
- 页面返回
- 回到首页
- 设置首页tab
- 获取首页tab
- 社会分享
- 网络请求
- 人脸核验
- 一键登录
- 生物认证
- 剪切板
- 定位
import { installJSBridge } from "runner-bridge";
installJSBridge();
Code Example
import { getDeviceInfo } from "runner-bridge";
async function example() {
const result = await getDeviceInfo();
if (result.type === "success") {
console.info("Device Info", result.value);
}
}
请求参数: 无 返回值: Promise<Result<DeviceInfo | undefined>>
字段名称 | 类型 | 是否必选 | 描述 |
---|---|---|---|
platform | Platform | 是 | 平台类型(iOS,Android,等) |
deviceCode | string | 是 | 设备唯一标识,应用卸载后不更改 |
appUniqueIdentifier | string | 是 | 应用唯一标识符,应用卸载重装后发生更改 |
packageIdentifier | string | 是 | 应用包名 |
deviceType | DeviceType | 是 | 设备类型 |
deviceModel | string | 是 | 设备型号 |
deviceName | string | 是 | 设备名称 |
osVersion | string | 是 | 系统版本 |
appVersion | string | 是 | 应用版本 |
language | string | 是 | 国际化编码 |
screenInfo | ScreenInfo | 是 | 屏幕信息,宽度-width,高度-height |
打开身份证扫描页
关闭身份证扫描页面
Code Example
async function example() {
const frontResult = await startIDCardScanner({
scannerType: "front",
});
if (frontResult.type === "success" && frontResult.value) {
console.info("Front", frontResult.value);
finishIDCardScanner();
const backResult = await startIDCardScanner({
scannerType: "back",
});
if (backResult.type === "success" && backResult.value) {
console.info("Back", backResult.value);
} else {
console.info("身份证背面扫描失败");
}
} else {
console.info("身份证正面扫描失败");
}
}
请求参数: IDCardScannerParams
字段名称 | 类型 | 是否必选 | 描述 |
---|---|---|---|
message | string | 否 | 页面标题 |
scannerType | IDCardScannerType | 否 | 身份证扫描类型(front-正面,back-背面) |
返回值: Promise<Result<string | undefined>> 身份证图片 base64 编码
Code Example
async function example() {
const result = await showImagePicker({
minCount: 1,
maxCount: 9,
});
console.info("result", result);
}
请求参数: ImagePickerParams | undefined
字段名称 | 类型 | 是否必选 | 描述 |
---|---|---|---|
maxCount | number | 是 | 最大可选图片数量 |
minCount | number | 是 | 最少必选图片数量 |
返回值: Promise<Result<\ImagePickerResult | undefined>>
字段名称 | 类型 | 是否必选 | 描述 |
---|---|---|---|
images | ImageEntry[] | 是 | 图片列表 |
ImageEntry
字段名称 | 类型 | 是否必选 | 描述 |
---|---|---|---|
base64 | string | 是 | 图片 base64 编码 |
extension | string | 是 | 图片扩展名 |
- 通用 Loading 展示: show(type: LoadingType, params: LoadingParams): void
- type: LoadingType: Loading 类型("success" | "info" | "warning" | "danger" | "loading")
- params: LoadingParams: Loading 参数
- message: string | undefined: 文案提示
- config: LoadingConfig | undefined: Loading 配置
- duration: number | undefined: Loading 持续时间(单位:毫秒)
- 专用 Loading 展示
- 隐藏 Loading: dismissLoading()
Code Example
showSuccess("This loading will disappear in 1 seconds");
setTimeout(() => {
dismissLoading();
}, 1000);
- params: PDFPreviewParams
- url?: string | undefined; PDF 文件地址
- title?: string | undefined; 文档标题
- showBottomBtn?: boolean; 是否展示底部按钮,默认false
- seconds?: number;倒计时,秒数,默认5s
- disableText?: string; 不可用状态文案
- enableText?: string; 可用状态文案
Code Example
prewviewPDF({
title: "对账单11-01",
url: "https://www.w3.org/WAI/ER/tests/xhtml/testfiles/resources/pdf/dummy.pdf",
});
iOS: 打开系统设置 -> 应用设置页 Android: 打开系统设置
-
params: OpenUrlParams: 跳转参数
- url: string: 跳转地址
iOS 传应用注册的URL Scheme,如:
打开微信:wechat://path/to/scanQRCode
打开支付宝:alipay://mine/payToAndroid 传应用包名,如:
打开微信:"com.tencent.mm"
打开支付宝: "com.eg.android.AlipayGphone"
判断第三方应用是否安装
Code Example
const params = { url: "com.tencent.mm" };
const res = await canOpenUrl(params);
if (res) {
openUrl(params);
}
- params: MiniProgramReq: 跳转参数
- userName: string; 填小程序原始id
- path: string; 拉起小程序页面的可带参路径,不填默认拉起小程序首页,对于小游戏,可以只传入 query 部分,来实现传参效果,如:传入 "?foo=bar"
- miniprogramType: "TEST" | "PREVIEW" | "RELEASE"; 默认打开正式版,可选打开 PREVIEW-开发版 | TEST-体验版 | RELEASE-正式版
Code Example
openMiniProgram({
userName: "gh_d43f693ca31f",
path: "",
miniprogramType: "RELEASE"
});
有版本更新时,弹出对应弹框进行更新;没有更新时,toast提示“当前已为最新版本”
- code: number: 退出码
获取网络状态 ReachabilityStatus.(wifi|cellular|unavailable)
async function example() {
const result = await NativeBindings.instance().getReachbility();
console.info("result", result);
}
监听网络状态变化
- callback: ValueCallback<Result>: 网络状态变化回调
// 监听网络状态变化
const subscription = NativeBindings.instance().listenReachability(
(result) => {
console.info("result", result);
}
);
// 取消监听
subscription.cancel();
获取颜色模式 dark | light | unknown
async function example() {
const colorMode = await NativeBindings.instance().getColorMode();
console.info("colorMode", colorMode);
}
监听主题变化
// 监听主题设置变化
const subscription = NativeBindings.instance().listenColorMode((colorMode) => {
console.info("colorMode", colorMode);
});
// 取消监听
subscription.cancel();
获取字体大小设置 返回一个字体缩放因子,1.0 为正常大小,具体值由系统决定
async function example() {
const textScale = await NativeBindings.instance().getTextScale();
console.info("textScale", textScale);
}
监听字体大小变化
// 监听系统字体大小变化
const subscription = NativeBindings.instance().listenTextScale((textScale) => {
console.info("textScale", textScale);
});
// 取消监听
subscription.cancel();
获取系统 Safe Area
- top: number: 顶边距
- bottom: number: 底边距
- left: number: 左边距
- right: number: 右边距
async function example() {
const result = await NativeBindings.instance().getSafeAreaInsets();
console.info("result", result);
}
监听系统 Safe Area 变化
// 监听系统 Safe Area 变化
const subscription = NativeBindings.instance().listenSafeAreaInsets(
(safeAreaInsets) => {
console.info("safeAreaInsets", safeAreaInsets);
}
);
// 取消监听
subscription.cancel();
-
EventInfo
- type: "onNavigationClick" | "onShare" | "onPdfRead" | "onTabChange" | "onResume" | "onPause"; 事件类型
- "onNavigationClick" - 标题栏点击事件;
- "onShare" - 分享结果事件;
- "onPdfRead" - PDF阅读按钮点击事件;
- payload: EventInfoMap[K];
- PositionItem: 定义同上
- ShareResult: 分享结果
- PDFReadResult: PDF文件阅读结果
- TabChangeResult: tab切换结果
- type: "onNavigationClick" | "onShare" | "onPdfRead" | "onTabChange" | "onResume" | "onPause"; 事件类型
-
ShareResult
- result: "success" | "fail"; 分享结果
- platform?: "WEIXIN" | "WEIXIN_TIMELINE"; 分享平台
- errCode?: number; 错误码,详细信息见阿里云官网
- errMessage?: string; 错误信息
-
PDFReadResult
- isRead: boolean; 点击PDF确认阅读按钮
-
TabChangeResult
- tabIndex: number; 切换后的tabIndex
Code Example
// 自定义事件监听
const subscription = NativeBindings.instance().listenEvent(eventInfo => {
console.info("eventInfo", eventInfo);
});
// 取消监听
subscription.cancel();
保存数据
- key: 数据键值
- value: 数据值
// 缓存数据
putAppCache('token', 'xxxxxx')
根据key值获取本地缓存
- key: 数据键值
// 获取缓存数据
const token = getAppCache('token')
console.info("token", token);
根据key值移除本地缓存
- key: 数据键值
清除所有缓存数据
监听App缓存数据变化
Code Example
// 监听App缓存数据变化
const subscription = NativeBindings.instance().listenAppCache(
(cacheInfo) => {
console.info("cacheInfo", cacheInfo);
}
);
// 取消监听
subscription.cancel();
设置系统状态栏
- params: StatusBarParams
- isDarkFont?: boolean: 是否为深色字体
Code Example
// 设置系统状态栏
setStatusBar({ isDarkFont: true })
设置APP顶部标题栏
-
params: TitleBarParams
- title?: string | undefined: 标题
- titleColor?: string | undefined: 标题字体颜色
- subTitle?: string | undefined: 副标题
- subTitleColor?: string; 副标题字体颜色
- background?: string | undefined: 背景色,默认为#FFFFFF
- showBorder?: boolean; 是否展示边框,默认展示
- leftPosition?: Array
<PositionItem>
| undefined: 左侧位置,对象数组,默认为页面返回 - rightPosition?: Array
<PositionItem>
| undefined: 右侧位置,对象数组,默认不展示
-
position: PositionItem
- id?: string : 事件唯一标识
- type?: string | undefined: 操作类型,back - 页面返回 | share - 分享 | jump - 打开新页面 | video - 视频操作
- isWebBack?: boolean: 是否web内部返回,默认为false,容器端返回
- text?: string | undefined: 文案
- textColor?: string | undefined: 文案字体颜色
- url?: string | undefined: 跳转路径
- icon?: string | undefined: 图标,back - 返回 | share - 分享 | question - 问号 | video - 视频
- extra?: string | undefined: 其他参数
Code Example
// 设置页面标题栏
setTitleBar({
title: '基金详情',
subTitle: '003190',
background: '#D9242D',
leftPosition: [{ isWebBack: true }],
rightPosition: [
{
icon: 'share',
type: 'share',
},
{
icon: 'video',
type: 'jump',
url: 'https://www.baidu.com/',
}
]
});
APP页面跳转方法
- params: NavigationParams
- url?: string: 页面链接
- param?: PageParams: 页面参数
- titleBar?: TitleBarParams | undefined: 顶部标题栏配置,参数同上,为空不展示
- routerParams?: string | undefined: 路由参数
- isAutoBackItemEnable?: boolean 是否需要返回操作,默认为true
- popCount?: number; 返回页面数量
- callBack?: ResponseCallback<Result<{ isSuccess: boolean }>>: 跳转回调
Code Example
// 页面跳转
navigationTo({
url: 'https://www.baidu.com/',
param: { titleBar },
},
(res) => {
console.log("res", res);
});
APP页面返回方法
- params: BackParams
- popCount: number; 返回页面数量
- isWebBack?: boolean; 是否web内部返回,默认为false,容器端返回
Code Example
// 页面返回
navigationBack({ isWebBack: true });
回到首页
切换首页tab
获取首页当前tab
-
params: ShareParams
- platform: "WEIXIN" | "WEIXIN_TIMELINE";分享平台 WEIXIN - 微信好友,WEIXIN_TIMELINE - 微信朋友圈
- shareContent: ShareContent; 分享内容对象
-
ShareContent
- title?: string; 分享标题
- content?: string; 分享内容
- url?: string; 分享跳转的 URL
- imgUrl?: string; 分享图片地址,微信分享请保证图片小于32KB
- contentType?: "url" | "image"; 分享类型:"url" 为分享链接,"image" 为分享图片
Code Example
// 分享到微信好友
share({
platform: "WEIXIN",
shareContent: {
title: "share title",
content: "share content",
url: "https://www.cloud.alipay.com/products/MPAAS",
imgUrl: "https://gw.alipayobjects.com/zos/rmsportal/WqYuuhbhRSCdtsyNOKPv.png",
contentType: "url",
}
});
请求参数:
- params: RequestParams
- url: string; 请求地址
- method: "POST" | "GET"; 请求方法
- params: string; 请求参数, json字符串或加密字符串
- headers: { [s: string]: string }; 请求头
- isGM: boolean; 是否发送国密HTTPS请求
返回值:Promise<Result<unknown>>
- Result<T, E = unknown>
- { type: "success"; value: T } 成功结果
- { type: "failure"; error: E } 异常结果
Code Example
const headers = { "App-Code": "CJFLS", "Source-Channel": "ZX" };
const params = { serType: "home" };
const result = await request({
url: "https://wx.cjhxfund.com/dsapi/ds-business/app/getPrivacyAgreement",
method: "POST",
params: JSON.stringify(params),
headers,
isGM: false
});
console.log("result:", result);
请求参数:
- params: FaceVerifyParams
- faceId: string; 刷脸 ID 号,由合作方向人脸识别后台拉取获得
- orderNo: string; 订单号,合作方订单的唯一标识
- appId: string; 业务流程唯一标识,即 WBappid,可参考 获取 WBappid 指引在人脸核身控制台内申请
- apiVersion: string; 后台api接口版本号,默认填:1.0.0
- nonce: string; 满足接入要求的32位随机数,与服务端生成签名的随机数保持一致
- userId: string; 用户唯一标识, 由合作方自行定义
- sign: string; 签名信息,获取方式请参考 生成 SDK 接口调用步骤使用签名
- licence: string; 在人脸核身控制台申请的 SDKlicense
- videoUpload?: boolean; 是否需要录制上传视频,默认不需要
- videoCheck?: boolean; 是否对录制视频进行检查,默认不检查
- playVoice?: boolean; 设置是否打开语音提示,默认关闭
返回值:Promise<Result<string, WbFaceError | string>>
- WbFaceError
- domain: string; 错误发生的阶段,只有当 domain=WBFaceErrorDomainCompareServer 时表示用户完成了刷脸,可以通过接口去拉取刷脸结果。其他 domain 表示用户刷脸中途退出或命中了风控逻辑,后端无法查询到刷脸结果
- code: string; 错误码
- desc: string; 错误描述,如有需求,可以展示给用户
- reason: string; 错误信息内容,错误的详细实际原因,主要用于定位问题
请求参数:无
返回值:Promise<Result<WBFaceVerifyResult, WbFaceError | string>>
- WBFaceVerifyResult 人脸核验成功结果
- isSuccess: boolean; 人脸核身是否成功
- sign: string; 签名,供 App 校验人脸核身结果的安全性
- liveRate: string; 活体检测分数
- similarity: string; 人脸比对分数,“仅活体检测” 类型不提供此分数
- error: WbFaceError; 人脸核身错误,人脸核身成功时为空
SDK 预登陆,必须预登陆成功才能调用一键登录
唤起一键登录
请求参数: params: OauthParams
- protocols: { name: string; url: string; }[]; 授权页相关协议数组,最多传3个;
- name: string; 协议名称
- url: string; 协议链接
返回值:Promise<Result<OauthResult, string>>
OauthResult 授权结果
- operation: OperationType; 授权页进行的操作
- token: string; 登录凭证,登录成功时返回
- carrier: string; 运营商,移动:mobile, 联通:unicom,电信:telecom,登录成功时返回
OperationType 授权结束类型枚举,登录成功后由H5自行关闭授权页,其余操作类型基座自动关闭授权页
- LoginComplete:点击登录,授权成功
- PressBack:页面返回,点击页面图标或手势返回
- ChangeAccount:点击切换账号
- Password:点击密码登录
- VerifyCode:点击手机验证码登录
关闭登录授权页
Code Example
const params: OauthParams = {
protocols: [
{
name: "隐私政策",
url: "https://cj-samll-routine.oss-cn-shenzhen.aliyuncs.com/1058280196925185%E9%9A%90%E7%A7%81%E6%94%BF%E7%AD%96.pdf"
},
{
name: "个人信息使用授权书",
url: "https://cj-samll-routine.oss-cn-shenzhen.aliyuncs.com/1058280196925184个人信息使用授权书.pdf"
}
]
};
const result = await oauthLogin(params);
if (result.type === "success") {
const { value } = result;
switch (value.operation) {
case OperationType.LoginComplete:
console.log("token: ", value.token, "\n运营商:", value.carrier);
// TODO 登录业务逻辑
closeOauthPage();
break;
case OperationType.PressBack:
// TODO 授权页返回逻辑
break;
case OperationType.ChangeAccount:
// TODO 切换账号逻辑
break;
case OperationType.Password:
// TODO 跳转密码登录页
break;
case OperationType.VerifyCode:
// TODO 跳转手机验证码登录页
break;
default:
break;
}
} else {
console.log("授权失败:", result.error);
}
在进行指纹/人脸识别前,需要判断当前设备的识别功能是否可用
请求参数: 无
返回值: Promise<Result<BiometricStatus>>
- BiometricStatus,状态枚举,取值如下:
- Success:可以进行验证;
- NotEnrolled:没有录入指纹/人脸
- NotAvailable:设备不支持指纹/人脸
- PasscodeNotSet:没有设置设备密码
- BiometryLockout:超过重试限制,需要进行设备密码解锁后重新激活
- UserCancel:用户点击了取消按钮
- UserFallback:用户点击了输入密码按钮
- SystemCancel:系统强制取消
- BiometryChanged:系统指纹/人脸发生变更,请重新验证用户身份
获取当前设别支持的生物识别类型
请求参数:无
返回值:
- BiometricType 支持生物识别类型
- touchID:指纹
- faceID:人脸
- notSupport:不支持
开启生物认证
描述:业务系统验证用户身份后,调用此方法,开启指纹/人脸登录,系统验证成功后,生成公私钥对,返回公钥给服务器用于验签
请求参数:
- params: AuthParams; 用户签名参数
- algorithm: "rsa" | "sm2"; 签名算法
返回值: Promise<Result<string, AuthError | unknown>>
成功返回公钥 publicKey,失败返回错误信息
-
AuthError,错误信息
- errorCode?: BiometricError & BiometricStatus; 错误码
- errorMsg?: string; 错误信息
-
BiometricError,状态枚举,取值如下:
- AuthFailed:验证失败,指纹/人脸不匹配
- UserCancel:用户点击了取消按钮
- UserFallback:用户点击了输入密码按钮
- SystemCancel:系统强制取消
- Timeout:等待超时
- OtherError:其他原因,操作失败
生物认证
描述:指纹/人脸验证成功后,使用保存的私钥对用户参数进行签名,返回签名数据传给后端进行验签,确认用户身份
请求参数:
- params: AuthParams; 用户签名参数
- signedText: string; 签名文本
- algorithm: "rsa" | "sm2"; 签名算法
返回值: Promise<Result<string, AuthError | unknown>>
成功返回签名 signature,失败返回错误信息
SM2 验签调试地址: https://const.net.cn/tool/sm2/verify/
RSA 验签调试地址: https://8gwifi.org/RSAFunctionality?rsasignverifyfunctions=rsasignverifyfunctions&keysize=2048
RSA 算法前后端统一使用 SHA256withRSA 配置
设置剪切板内容
获取剪切板内容
清除剪切板内容
返回值: Promise<Result<LBSLocation, string>>
- LBSLocation,定位信息,属性如下:
- country: string; 国家
- province: string; 省
- city: string; 市
- district: string; 区
- street: string; 街道
- latitude: number; 纬度
- longitude: number; 经度