移动端 H5 界面与原生交互

1、介绍

原生和H5交互,主要是为了将原生中的用户信息、权限校验等信息传递给H5,不需要H5重复去登录,默前移动端用到webViewjavascripBridge这个混编框架,方便H5进行一些基础的原生操作。WebViewJavascriptBridge 是移动UIViewHtml交互通信的桥梁,用白话来说就是实现java(ios为oc)和js的互相调用的桥梁。替代了WebView的自带的JavascriptInterface的接口,使得我们的开发更加灵活和安全。

参考源码:源码地址

WebViewJavascriptBridgeGit 地址

全屏显示

  1. 微应用URL地址后面带上 关键字:

    http://10.62.16.95/demofront/web-js?isFallScreen=true
  2. JS代码中:

    created() {
       this.clickCallNative('full_screen', '1'); //隐藏标题,全面屏
       this.clickCallNative('capsule_mode', '1');//隐藏胶囊
     }

2、使用

2.1 H5集成桥接

  1. 下载参考源码:源码地址

  2. 导入bridge.js 文件,并引入到需要使用的界面;

  3. Bridge.callH5发消息给原生;

  4. Bridge.register:监听原生发送的消息;


2.2 H5发消息给原生

key:发送的类型;(3.0 key类型介绍 )

params:发送给原生的数据。

//import Bridge from "@/api/bridge.js";
Bridge.call(key, params, (data) => {
    //部分调用,可以直接监听到原生数据
     let json = JSON.parse(data);
     this.refresh_token = json.token;
});     

2.3 原生返回数据给H5

keys:需要监听的类型,数组;

type:原生发送时的类型,和监听类型一致;

data:原生发送的数据,字符串类型。

//import Bridge from "@/api/bridge.js";
initListener() {
      let keys = [
        "init_pai_data",
        "refresh_token",
        "selected_depart",
        "selected_person",
        "selected_park",
        "selected_card",
      ];
      Bridge.register(keys, (type, data, responseCallback) => {
        let json = JSON.parse(data);
        if (type == "init_pai_data") {
          this.msgJson = data;
          this.info = json;
        } else if (type == "selected_depart") {
          this.parseDepartData(json);
        } else if (type == "selected_person") {
          this.parsePersonData(json);
        } else if (type == "selected_park") {
          this.parseParkData(json);
        } else if (type == "selected_card") {
          this.selected_card = data;
        }
        if (responseCallback) responseCallback(data);
      });
    }


2.4 示例

示例1:支付组件

详细说明:链接

//import Bridge from "@/api/bridge.js";
let info = {
    suiteId: "1283242540845371392",
    parkId: this.$flyrise.get(this.$sp.parkId),
    bizType:"ordinary",//orderType 和 bizType,二选一
    orderType: "4",//充值订单
    orderVO: {
        amount: curMoney, //支付金额(分)
        goodsName: title, //支付标题
        payeeId: this.$flyrise.get(this.$sp.userId) //收款账户
    }
}
Bridge.call("open_pay", info, (data) => {
       let json = JSON.parse(data);
       this.res = data;
});

示例2:修改用户信息
//import Bridge from "@/api/bridge.js";
//统一字符串类型:用户头像、昵称、性别('1':男,'2':女)、邮箱
let params={
  userHead:"",
  nickName:"晓",
  sex:"2",
  email:"123@qq.com"
}

Bridge.call("update_user_info", params, (data) => {
       let json = JSON.parse(data);
       this.res = data;
       if(json.isUpdate){
            //成功
        }else{
            //失败
        }
});

示例3:刷新界面
//import Bridge from "@/api/bridge.js";
let params={
  refreshType:"my" //main:首页(第一个页面); my:我的(最后一个页面)
}

Bridge.call("refresh_view", params, (data) => {
       let json = JSON.parse(data);
       this.res = data;
});
//import Bridge from "@/api/bridge.js";
let params={
  refreshType:"my" //main:首页(第一个页面); my:我的(最后一个页面)
}

Bridge.call("refresh_view", params, (data) => {
       let json = JSON.parse(data);
       this.res = data;
});

示例4:快捷支付
//import Bridge from "@/api/bridge.js";
let info = {
   bizOrderNo:"", //支付订单号,非必填
   payInfo:"", //支付订单,必填
   payMethod:"" //付款方式:02:微信;03:支付宝。必填 
}
Bridge.call("quick_pay", info, (data) => {
       let json = JSON.parse(data);
       this.res = data;
});

返回参数

// payStatus:订单交易状态,1成功,其它失败;
// bizOrderNo:支付订单号; 
// payMethod:付款方式。
json = {
    payStatus:"1",
    bizOrderNo:"109089878978",
    payMethod:"02"
}

示例5:获取定位
//import Bridge from "@/api/bridge.js";
Bridge.call("start_location", {}, (data) => {
       let json = JSON.parse(data);
       this.res = data;
});

返回参数

json = {
    "accuracy":30,
    "address":"广东省珠海市香洲区港湾大道1号靠近南方软件园",
    "city":"珠海市",
    "cityCode":"0756",
    "latitude":22.370807,
    "locationType":5,
    "longitude":113.572739
}

示例6:返回上一页

解决跳过支付空白页的事件

Bridge.call("back_page", {}, () => {
});

示例7:打开附件

打开附件

let data = {
    "type":"local", //不传:是用第三方应用打开; local:本地打开;
    "url":"" //可传完整地址或附近Id
}

Bridge.call("download_attach", data, () => {
});

3、参数介绍

3.1 关于token

token传递分为两种

  1. init_pai_data方式,在界面初始化成功后一次性传递所有用户信息,包含token;

  2. urlparams传递,传递的token字段名为st,并且token经过加密,需要解密后使用;

  3. 完整tokenBearer 9cece910-06b8-489e-a6ff-66e76af8a0c4

3.1.1 token解密方法一
//1. package.json 中引入中台小程序基础库
// "@flyriselink/pai-mp-core": "^1.0.1"
npm install @flyriselink/pai-mp-core
//2. main.js 中初始化
import flyriseCore from "@flyriselink/pai-mp-core";
Vue.use(flyriseCore)
//3. 解密后,手动拼装 `stHead`
 uni.$p.decodeToken(st)
//let token = stHead + uni.$p.decodeToken(st)

3.1.2 token解密方法二

完整tokenBearer 9cece910-06b8-489e-a6ff-66e76af8a0c4

//1. 导入第三方加解密库
npm install crypto-js
import CryptoJS from "crypto-js";
//2. 解密后,手动拼装 `stHead`
const decodeToken = (data) => {
    let reverse = data.split('').reverse().join('')
    return CryptoJS.enc.Base64.parse(reverse).toString(CryptoJS.enc.Utf8)
}
//let token = stHead + decodeToken(st) 

3.2 原生支持的类型

监听:被动接收原生返回的数据;

点击:主动调用原生的事件。

字段 返回类型 介绍 使用
init_pai_data json 原生默认传递的数据 监听
refresh_token json 刷新Token事件 监听、触发
selected_depart json 选择部门事件 监听、触发
selected_person json 选择人员事件 监听、触发
selected_park json 选择园区事件 监听、触发
selected_card json 选择名片事件 监听、触发
full_screen string 隐藏标题(0:显示,1:隐藏) 触发
finish_web_view string 关闭H5界面 触发
capsule_mode json|string 胶囊模式 (0:显示,1:灯光模式,2:暗黑模式,3:隐藏模式) 触发
open_pay string 调用凌云支付组件 监听、触发
init_param string 列表传参给详情页 监听
status_bar_mode string 1:下沉白色,2:下沉黑色,3:沉侵白色,4:沉侵暗黑 触发
open_micro string 打开微应用 触发
open_scan string 打开扫码 监听、触发
close_app string 关闭App(HOME键退出) 触发
open_native string 打开原生部分功能 触发
update_user_info string 修改用户信息 监听、触发
refresh_view string 刷新界面,main:首页;my:我都 触发
back_page string 返回上一页,其中解决支付跳过空白页事件 触发
download_attach string 附件下载并打开 触发

3.3 init_pai_data

原生默认传递给 H5的用户信息数据

参数 类型 介绍
token string 用户唯一值(带前缀的)
userName string 用户名
name string 昵称、公司员工为真实姓名
phone string 手机号
userId string 用户 Id
entId string 企业 Id
entName string 企业名称
tokenValue string 唯一值(不带前缀的)
parkId: string 园区 Id
parkName string 园区名称

3.4 url参数控制

也可以在url上直接添加scr来达到全屏显示效果,可避免状态栏闪屏效果

https://www.baidu.com?scr=1
参数名 类型 描述
sta 1:下沉白色,2:下沉黑色,3:沉侵白色,4:沉侵暗黑 控制状态栏(默认:3)
cap 0:显示,1:灯光模式,2:暗黑模式,3:隐藏模式 控制胶囊(默认:0)
scr 0:显示,1:隐藏 隐藏标题栏(默认:0)
pro 0:显示,1:隐藏 隐藏进度条(默认:0)

3.5 打开微应用

open_micro ,参数中必须包含必填,选填为自定义参数,原生会回传给小程序:biz

参数名 类型 描述 是否必填
pageAppId string 页面唯一值,微应用AppCode
url string 路径
string

3.6 打开原生功能

open_native,参数为json格式

itemCode:需要开启的原生界面类型

url:打开微应用、web页面时需要用到的参数

appId:开启微应用时使用的页面标识

{
    itemCode:"",
    url:"",
    appId:""
}

itemCode参数说明

itemCode值 类型 描述
1001 String uniApp启动
1014 String 选择企业
1015 String 应用消息
1016 String 全文检索
1017 String 工作台设置界面
1018 String 工作台企业app,B和P端微应用
1019 String 工作台企业app,B端微应用
1020 String 工作台企业app,P端微应用
1021 String 工作台企业app,C端微应用
1030 String 通用扫码
1031 String 用户详情
1032 String 我的设置

4、H5 完整代码

下载完整代码

4.1 bridge.js代码

安卓和 iOS 通用的库,直接使用即可。

发送和监听的核心库,使用方式:

1、新建bridge.js,将代码考入;

2、引入到需要调用原生和监听原生的界面使用即可,详情参考完整的Vue代码。

call:给原生发送消息:

name:发送给原生的key,字符串类型,用来区分事件类型;

data:发送给原生的参数,用来传递数据;

callback:用来监听原生的回调。

register:接收原生发送的消息:

names:用来监听原生返回的key,数组类型,可以监听多个类型;

callback:监听原生返回的数据。

//判断机型
let u = navigator.userAgent;

function setupWebViewJavascriptBridge(callback) {
    //判断ios 还是Android
    if (/(iPhone|iPad|iPod|iOS)/i.test(u)) {
        console.log("-->>>调用-ios")
        connectWebViewJavascriptBridgeIOS(callback)
    } else {
        console.log("-->>>调用-安卓")
        connectWebViewJavascriptBridgeAndroid(callback)
    }
}

//苹果注册事件监听
function connectWebViewJavascriptBridgeIOS(callback) {
    if (window.WebViewJavascriptBridge) {
        return callback(window.WebViewJavascriptBridge)
    }
    if (window.WVJBCallbacks) {
        return window.WVJBCallbacks.push(callback)
    }
    window.WVJBCallbacks = [callback]
    let WVJBIframe = document.createElement('iframe')
    WVJBIframe.style.display = 'none'
    WVJBIframe.src = 'https://__bridge_loaded__'
    document.documentElement.appendChild(WVJBIframe)
    setTimeout(() => {
        document.documentElement.removeChild(WVJBIframe)
    }, 0)
}

//安卓注册事件监听
function connectWebViewJavascriptBridgeAndroid(callback) {
    if (window.WebViewJavascriptBridge) {
        callback(WebViewJavascriptBridge)
    } else {
        document.addEventListener(
            'WebViewJavascriptBridgeReady',
            function () {
                callback(WebViewJavascriptBridge)
            },
            false
        );
    }
}

export default {
    call(name, data, callback) {
        setupWebViewJavascriptBridge(function (bridge) {
            console.log("-->>>call-2:", name)
            bridge.callHandler(name, data, callback)
        })
    },
    register(names, callback) {
        setupWebViewJavascriptBridge(function (bridge) {
            if (!/(iPhone|iPad|iPod|iOS)/i.test(u)) {
                console.log("初始化-安卓")
                bridge.init(function (message, responseCallback) {
                    console.log("-->>>初始化-安卓-成功")
                    responseCallback("");
                });
            }
            names.forEach(item => {
                bridge.registerHandler(item, (data, responseCallback) => {
                    console.log("-->>>register:", item)
                    callback(item, data, responseCallback)
                });
            });
        })
    }
}

4.2 Vue界面代码

建议使用中台的前端默认模板来测试,使用的 UI库是有赞vant,前端工程默认包含。

vue-json-viewer:格式化显示json的一个库,需要开发者自己导入,只是显示数据用的库,非必要;

Bridge:封装的webViewjavascripBridge,也就是 bridge.js,监听和发数据的核心库,必须;

Html代码

<template>
  <div class="container">
    <van-cell-group title="接收数据">
      <json-viewer :value="info" :expand-depth="3" copyable sort></json-viewer>
    </van-cell-group>
    <div style="height: 16px"></div>
    <van-cell-group title="权限校验">
      <van-cell title="token值(带前缀的)" :value="info.token" />
      <van-cell title="token值" :value="info.tokenValue" />
    </van-cell-group>
    <div style="height: 16px"></div>
    <van-cell-group title="用户信息">
      <van-cell title="用户名" :value="info.userName" />
      <van-cell title="昵称" :value="info.name" />
      <van-cell title="手机号" :value="info.phone" />
      <van-cell title="用户 Id" :value="info.userId" />
    </van-cell-group>
    <div style="height: 16px"></div>
    <van-cell-group title="企业信息">
      <van-cell title="企业 Id" :value="info.entId" />
      <van-cell title="企业名称" :value="info.entName" />
    </van-cell-group>
    <div style="height: 16px"></div>
    <van-cell-group title="园区信息">
      <van-cell title="园区 Id" :value="info.parkId" />
      <van-cell
        title="园区名称"
        :value="info.parkName"
        is-link
        @click="clickCallNative('submit_from_text', info.parkName)"
      />
    </van-cell-group>

    <div style="height: 26px"></div>
    <van-cell-group title="原生交互">
      <van-cell
        title="刷新 token "
        :value="refresh_token"
        is-link
        @click="clickCallNative('refresh_token', refresh_token)"
      />

      <van-cell
        title="选择部门"
        :value="selected_depart"
        is-link
        @click="clickCallNative('selected_depart', selected_depart)"
      />
      <van-cell
        title="选择人员"
        :value="selected_person"
        is-link
        @click="clickCallNative('selected_person', selected_person)"
      />
      <van-cell
        title="选择园区"
        :value="selected_park"
        is-link
        @click="clickCallNative('selected_park', selected_park)"
      />
      <van-cell
        title="选择名片"
        :value="selected_card"
        is-link
        @click="clickCallNative('selected_card', selected_card)"
      />
      <van-cell
        title="全屏显示"
        value="灯光模式"
        icon="fire-o"
        is-link
        @click="clickCallNative('full_screen', '1')"
      />
      <van-cell
        title="全屏显示"
        value="暗黑模式"
        icon="fire"
        is-link
        @click="clickCallNative('full_screen', '2')"
      />
       <van-cell
        title="非全屏显示"
        icon="home-o"
        is-link
        @click="clickCallNative('full_screen', '0')"
      />
      <van-cell
        title="显示胶囊"
        value="灯光模式"
        icon="eye-o"
        is-link
        @click="clickCallNative('capsule_mode', capsuleList1)"
      />
      <van-cell
        title="显示胶囊"
        value="暗黑模式"
        icon="eye"
        is-link
        @click="clickCallNative('capsule_mode', capsuleList2)"
      />
      <van-cell
        title="隐藏胶囊"
        icon="closed-eye"
        is-link
        @click="clickCallNative('capsule_mode', '{\'type\':\'3\'}')"
      />
      <van-cell
        title="关闭界面"
        icon="close"
        is-link
        @click="clickCallNative('finish_web_view', '关闭界面')"
      />
    </van-cell-group>
    <div style="height: 60px"></div>
  </div>
</template>

js代码

<script>
import Bridge from "@/api/bridge.js";
import JsonViewer from "vue-json-viewer";
export default {
  head: {
    meta: [
      { charset: "utf-8" },
      {
        name: "viewport",
        content:
          "width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no",
      },
    ],
  },
  components: { JsonViewer },
  data() {
    return {
      capsuleList1: {
        type: "1",
        list: [
          { title: "百度", url: "https://www.baidu.com/" },
          { title: "玩安卓", url: "https://wanandroid.com/index" },
        ],
      },
      capsuleList2: {
        type: "2",
        list: [
          { title: "百度", url: "https://www.baidu.com/" },
          { title: "玩安卓", url: "https://wanandroid.com/index" },
        ],
      },
      msgJson: {},
      refresh_token: "",
      selected_depart: "",
      selected_person: "",
      selected_park: "",
      selected_card: "",
      info: {
        token: "",
        userName: "",
        name: "",
        phone: "",
        userId: "",
        entId: "",
        entName: "",
        parkId: "",
        parkName: "",
        tokenValue: "",
      },
    };
  },
  created() {
    this.initListener();
    this.clickCallNative('full_screen', '0');
    this.clickCallNative('capsule_mode', '0');
  },
  methods: {
    initListener() {
      let keys = [
        "init_pai_data",
        "refresh_token",
        "selected_depart",
        "selected_person",
        "selected_park",
        "selected_card",
      ];
      Bridge.register(keys, (type, data, responseCallback) => {
        let json = JSON.parse(data);
        if (type == "init_pai_data") {
          this.msgJson = data;
          console.log("-->>>data:", data);
          this.info = json;
        } else if (type == "selected_depart") {
          this.parseDepartData(json);
        } else if (type == "selected_person") {
          this.parsePersonData(json);
        } else if (type == "selected_park") {
          this.parseParkData(json);
        } else if (type == "selected_card") {
          this.selected_card = data;
        }
        if (responseCallback) responseCallback(data);
      });
    },
    parseDepartData(json) {
      if (json && json.items) this.selected_depart = json.items[0].name;
    },
    parsePersonData(json) {
      if (json && json.items) this.selected_person = json.items[0].name;
    },
    parseParkData(item) {
      if (item) this.selected_park = item.parkName;
    },
    clickCallNative(key, params) {
      Bridge.call(key, params, (data) => {
        if (key == "refresh_token") {
          let json = JSON.parse(data);
          this.refresh_token = json.token;
        }
        console.log("-->>>返回数据:", data);
      });
    },
  },
};
</script>

css代码

<style>
.container {
  height: 750rpx;
  background-color: rgba(0, 0, 0, 0.02);
}
.response-content {
  width: 375rpx;
  margin: 16px;
  font-size: 16px;
  color: rgba(0, 0, 0, 0.65);
}
</style>
文档更新时间: 2022-11-10 09:51   作者:陈冕
#### 示例4:快捷支付