vue+postMessage实现与贩售机交互

776 阅读3分钟

公司需求配合自动贩售机售卖化妆品小样,主要功能用户扫码登录小程序,获取兑换码,输入兑换码取货,管理人员操作贩售机控制界面进行货品数量设置。因为贩售机取货功能是由内置app控制的,它提供给前端一个取货sdk,需要前端设置一个html文件存储到app对应的文件中。但是都2021年了,不想完全使用原生开发界面,所以最后技术方案为,使用一个html文件,在该文件中调取sdk,通过iframe引入Vue写的功能页面,并使用PostMessage进入通信。

一、页面效果图

二、功能实现

1.1  html如何调用贩售机内置app提供的sdk

 function spitGoodsByChannelPosition(row, col) { 
     console.log('位置', row, col) 
     // 掉货
     window.WebViewJavascriptBridge.callHandler(   
       'spitGoodsByChannelPosition', {
          'row': parseInt(row),'col': parseInt(col)},
             function(responseData) { 
               // console.log(responseData)  
             } 
         );     
   }

 function connectWebViewJavascriptBridge(callback) {  
     if (window.WebViewJavascriptBridge) {        
      callback(WebViewJavascriptBridge)     
     } else {  
          document.addEventListener(     
         'WebViewJavascriptBridgeReady',function() {    
            callback(WebViewJavascriptBridge)  
         },false);
     }
  }
 connectWebViewJavascriptBridge(function(bridge) {  
        bridge.init(function(message, responseCallback) {  
          if (responseCallback) {      
             responseCallback(data);  
          }       
 });       
 bridge.registerHandler("receiveCommandFromSerialPort", function(data, responseCallback) {   
         appendLog("receiveCommandFromSerialPort: " + data)    
        if (responseCallback) {          
        var responseData = "Javascript Says Right back aka!";     
           responseCallback(responseData);            
        }      
    });    
    bridge.registerHandler("spitGoodsResult", function(data, responseCallback) {
    // 这这个回调函数里可以拿到贩售机取货的结果返回值,进行后续与h5页面的通信,或者其他功能。。。
};
};
};

1.2 html中利用iframe引入vue h5链接地址

 <div class="vending-machine-container">  
  <iframe src="vue h5的链接地址" id="myframe" frameborder="0" width="100%"></iframe> 
 </div>

1.3 html父页面、vue h5子页面互相通信

这里简单介绍下iframe + postMessage跨域通信

web是构建在同源策略基础之上,同源策略是浏览器的行为,是为了保护本地数据不被javascript代码获取回来的数据污染,因此拦截的是客户端发出的请求回来的数据接收,即请求发送了,服务器响应了,但是无法被浏览器接收,所以我们可以通过iframe来实现不同域之间互相请求资源。

(1)父页面向子页面传递数据

 // 给子页面传递code var myframe = document.getElementById('myframe'); 
// 获取iframe的ID myframe.contentWindow.postMessage({ code: dataCode.errorCode }, '*');

(2)父页面接口来自子页面的数据

    window.addEventListener("message", function(event) {        
         productCallParams = event.data; 
         console.log('event', event.data) 
         if (event.data) {      
           var row = event.data.position_x;    
           var col = event.data.position_y;        
           spitGoodsByChannelPosition(row, col);    
         }    
    }, false);

1.4 vue h5子页面(获取兑换码、后台管理)

此处代码就省略啦,一些大家都懂的交互代码。。。

重点说下sign的生成,后端同学为了防止多次重复提交,所以要求每个接口传递前端生成的sign值!

export function getSign(params, kAppSecret) {  
  let content;
  let Base64 = require('js-base64').Base64;
  if (typeof params == "string") {    
    content = params  
  } else if (typeof params == "object") { 
     var arr = [];    
     for (var i in params) {     
      arr.push(i + "=" + params[i]); 
  }      
    content = arr.join("&")
  }
  let urlStr = content.split("&").sort().join("&"); 
  let hash = CryptoJS.HmacSHA256(urlStr, kAppSecret) 
  return Base64.encode(hash)
}

总的来说分为:

参与计算的
    json数据
        {
            "key1":value1, # value的类型必需是数字、布尔、浮点或者字符串之一
            "key2":value2,
        }
    秘钥 xxxx
    
第1步 将json数据结构的key按照ascii码从小到大排序,拼接成一个字符串
第2步 将字符串运行 hmac sha256算法,盐使用秘钥xxxx
第3步 将上一步得到的摘要base64编码,计算完成

坑:这里引入这些加密包的时候,由于贩售机最开始提供的app是安卓6.0系统,没法识别由npm安装的cryptojs包,所以改为直接把代码down下来放在util函数中,费了点儿功夫。

1.5 使用内网穿透工具花生壳实时预览最新测试功能

背景:贩售机内嵌index.html中通过iframe引入的vue h5地址如果做到实时更新,必须把代码发到测试或者正式上面,很麻烦,所以引入花生壳工具进行内网穿透,实时预览最新代码。

(1)没有设置自己的电脑IP,得到一个专有访问域名

(2)然后在vue项目中更改vue.config.js文件

设置publicPath为'./',disableHostCheck为true,重新启动Vue项目就可以了。

以上就是本功能的部分重点难点,仅此记录下。