JSBridge 之 Web 端与 App 交互 - 鸿蒙 HarmonyOS Next

前端 JavaScript 与鸿蒙 Web 组件间相互通信的方式;

注:此案例采用两端间双向模式机制进行通信;

单向模式可参考 前端 Web 与原生应用端 WebView 通信交互

官方文档传送门如下:

WebCallApp

AppCallWeb

一.鸿蒙端

1.设置 WebCallApp 方法

import WebView from '@ohos.web.webview';
import business_error from '@ohos.base'
import { BusinessError } from '@kit.BasicServicesKit';

// 声明注册对象
@State WebCallAppMethod: WebCallAppClass = new WebCallAppClass(this.controller)


// WebCallApp 类方法
class WebCallAppClass {
  private webView: WebView.WebviewController;

  constructor(webView: WebviewController) {
    this.webView = webView
  }

  // 方式一: 方法无回调,通过交互协议 WebCallApp => AppCallWeb
  WebCallApp(value: string) {
    console.log('********* [交互] --- 协议 - HWebView')
    // 接收 Web 端数据源
    console.log(value)


    // 交互数据处理
    do something ...

    // 如果需要数据回调,则通过如下 callback 方法将处理后的数据回调至 Web 
    // 如果不需要数据回调,则执行其它业务逻辑即可,无需调用如下 callback 方法
    this.webView.runJavaScript(value)
  }

  // 方式二: 方法回调,WebCallApp 直接 return
  // WebCallApp(value: string): string { // 直接 return 方式
  //   console.log('[交互] --- WebCallApp - 测试 - HWebView')
  //   // 接收 Web 端数据源
  //   console.log(value)
  //
  //   // 交互数据处理
  //   do something ...
  //
  //   // callback
  //   return value
  // }

  WebTestAsync = async (value: string): Promise<string> => { // 测试 - 异步
    let userInfo: UserInfoModal = JSON.parse(userInfoData)
    userInfo.instituteId = instituteId
    userInfo.genderCode = genderCode
    let commandManager = CommandManager.shareInstance()
    let res = await commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
    let argsJson = JSON.stringify(res.args)
    console.log('[WebTestAsync]')
    console.log(argsJson)
    return argsJson
  }

  WebTest(value: string): Promise<string> { // 测试 - promise
    let p: Promise<string> = new Promise((resolve, reject) => {
      let userInfo: UserInfoModal = JSON.parse(userInfoData)
      userInfo.instituteId = instituteId
      userInfo.genderCode = genderCode
      let commandManager = CommandManager.shareInstance()
      let res = commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
      let argsJson = JSON.stringify(res.args)
      resolve('[Callback]:' + argsJson)
    })
    return p
  }
}

2.基于 @ohos.web.webview 初始化并配置

官方文档传送门

通过 javaScriptProxy 的代理方法与前端通信接发消息;

1).name 两端交互的协议 key

2).object 对应的是 WebCallApp 的交互接收方法

3).methodList 基于 object 的 WebCallApp 中交互具体实现的若干函数方法

4).controller 组件

Web({ src: this.url, controller: this.controller })
  .width('100%')
  .height('100%')
  .backgroundColor(Color.White)
  .multiWindowAccess(true)
  .javaScriptAccess(true)// 访问本地资源文件
  .imageAccess(true)// 权限状态开启
  .onlineImageAccess(true)// 权限状态开启
  .fileAccess(true)// 权限状态开启
  .domStorageAccess(true)// 数据存储权限
  .mediaPlayGestureAccess(true)// 媒体权限
  .mixedMode(MixedMode.All)// https 加载
  .layoutMode(WebLayoutMode.FIT_CONTENT)// 自适应布局
  .verticalScrollBarAccess(true)// 滚动条
  .horizontalScrollBarAccess(false)// 滚动条
  .cacheMode(CacheMode.Default)// 缓存
  .zoomAccess(false)// 禁止手势缩放
  .geolocationAccess(true)// 定位权限
  .onConsole((event) => {
    console.log('[交互] - onConsole')
    LogUtils.info(event?.message.getMessage())
    return false
  })
  .onPageBegin(() => { // 页面加载中
    console.log('[Web] - 页面加载中:', this.url)
  })
  .onPageEnd(() => {
    console.log('[Web] - 页面加载完成:', this.url)
    this.isLoading = false
  })
  .onErrorReceive((event) => { // 异常: 无网络,页面加载错误时
    if (event) {
      console.info('getErrorInfo:' + event.error.getErrorInfo());
      console.info('getErrorCode:' + event.error.getErrorCode());
      console.info('url:' + event.request.getRequestUrl());
      console.info('isMainFrame:' + event.request.isMainFrame());
      console.info('isRedirect:' + event.request.isRedirect());
      console.info('isRequestGesture:' + event.request.isRequestGesture());
      console.info('getRequestHeader_headerKey:' + event.request.getRequestHeader().toString());
      let result = event.request.getRequestHeader();
      console.info('The request header result size is ' + result.length);
      for (let i of result) {
        console.info('The request header key is : ' + i.headerKey + ', value is : ' + i.headerValue);
      }
    }
  })
  .onHttpErrorReceive((event) => { // 异常: 网页加载资源 Http code >= 400 时
    if (event) {
      console.info('url:' + event.request.getRequestUrl());
      console.info('isMainFrame:' + event.request.isMainFrame());
      console.info('isRedirect:' + event.request.isRedirect());
      console.info('isRequestGesture:' + event.request.isRequestGesture());
      console.info('getResponseData:' + event.response.getResponseData());
      console.info('getResponseEncoding:' + event.response.getResponseEncoding());
      console.info('getResponseMimeType:' + event.response.getResponseMimeType());
      console.info('getResponseCode:' + event.response.getResponseCode());
      console.info('getReasonMessage:' + event.response.getReasonMessage());
      let result = event.request.getRequestHeader();
      console.info('The request header result size is ' + result.length);
      for (let i of result) {
        console.info('The request header key is : ' + i.headerKey + ' , value is : ' + i.headerValue);
      }
      let resph = event.response.getResponseHeader();
      console.info('The response header result size is ' + resph.length);
      for (let i of resph) {
        console.info('The response header key is : ' + i.headerKey + ' , value is : ' + i.headerValue);
      }
    }
  })
  .onAlert((event) => { // 提示框处理相关
    AlertDialog.show({
      title: '温馨提示',
      message: event?.message,
      confirm: {
        value: '确认',
        action: () => {
          event?.result.handleConfirm()
        }
      },
      cancel: () => {
        event?.result.handleCancel()
      }
    })
    return true;
  })
  .onConfirm((event) => { // 提示框处理相关
    AlertDialog.show({
      title: '温馨提示',
      message: event?.message,
      confirm: {
        value: '确认',
        action: () => {
          event?.result.handleConfirm()
        }
      },
      cancel: () => {
        event?.result.handleCancel()
      }
    })
    return true
  })
  .onShowFileSelector((event) => { // 文件选择处理
    console.log('MyFileUploader onShowFileSelector invoked');
    const documentSelectOptions = new picker.PhotoSelectOptions();
    let uri: string | null = null;
    const documentViewPicker = new picker.PhotoViewPicker();
    documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => {
      uri = documentSelectResult[0];
      console.info('documentViewPicker.select to file succeed and uri is:' + uri);
      if (event) {
        event.result.handleFileList([uri]);
      }
    }).catch((err: BusinessError) => {
      console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
    })
    return true
  })
  .onLoadIntercept((event) => {
    if (event.data.isRedirect()) {
      console.log('[重定向]');
    }
    return false
  })
  .javaScriptProxy({
    // web call app, web 组件初始化调用
    // 对象注入 web
    object: this.WebCallAppMethod,
    name: 'WebCallAppHarmony', // AppCallWeb
    methodList: ['WebCallApp', 'WebTest', 'WebTestSync', 'WebTestAsync'],
    controller: this.controller
  })

3.完整案例

import HeaderComponent from "../../components/NavigationBar"
import WebView from '@ohos.web.webview';
import business_error from '@ohos.base'
import { BusinessError } from '@kit.BasicServicesKit';
import CommandManager from '../../expand/utility/CommandManager'
import AppCallWeb, { DataType } from '../../expand/utility/AppCallWeb'


// 路由取值
const routerParams = router.getParams() as RouterModel

// 用户信息相关
let userID: string = ''
let token: string = ''
let instituteId: string = ''
let cellphone: string = ''
let userInfoData: string = ''

@Entry
@Component
struct HWebView {
  controller: WebView.WebviewController = new WebView.WebviewController();
  
  // setCookies
  headers: Array<WebView.WebHeader> = [{ headerKey: 'author', headerValue: 'survivors' }];
  // 页面 loading
  isLoading: boolean = true
  // 声明注册对象
  @State WebCallAppMethod: WebCallAppClass = new WebCallAppClass(this.controller)
  // 标题
  @State title: string = ''
  // 导航栏是否显示(默认显示 true)
  @State navHidden: boolean = true
  // 链接
  @State url: string = 'https://blog.csdn.net/survivorsfyh'

  aboutToAppear(): void { // 页面即将渲染
    console.log('[WebView] - 页面即将渲染');
    // 路由取值
    console.log(JSON.stringify(routerParams))
    const title = routerParams.title
    const navHidden = routerParams.navHidden
    const link = routerParams.link
    this.title = title ? title : ''
    this.navHidden = navHidden ? navHidden : false
    this.url = link ? link : ''

    this.webDebugMethod()
  }

  aboutToDisappear(): void { // 页面即将销毁
    console.log('[WebView] - 页面即将销毁');
  }

  onBackPress(): boolean | void { // Web 组件左滑手势返回处理
    if (this.controller.accessStep(-1)) {
      // 返回上一层级
      this.controller.backward()
      // 执行自定义逻辑
      return true
    } else { // 执行系统默认返回逻辑
      return false
    }
  }

  onPageShow(): void { // 页面进入前台
    console.log('[页面进入前台]');
    // get
    let value = WebView.WebCookieManager.fetchCookieSync(this.url);
    console.log('[cookie]: ' + value);
    // set
    WebView.WebCookieManager.configCookieSync('url', 'a=1,b=2,c=3');
  }

  onPageHide(): void { // 页面进入后台
    console.log('[页面进入后台]');
  }

  /****** 初始化 init 配置相关 ******/
  async initUserInfo() {
    console.log('[初始化] - 获取用户信息')
    userID = await StorageUtils.get('userID') as string
    token = await StorageUtils.get('token') as string
    instituteId = await StorageUtils.get('instituteId') as string
    cellphone = await StorageUtils.get('cellphone') as string
    userInfoData = await StorageUtils.get('UserInfoData') as string
  }


  webDebugMethod(): void {
    try {
      // 启用网页调试功能
      WebView.WebviewController.setWebDebuggingAccess(true);
    } catch (error) {
      let e: business_error.BusinessError = error as business_error.BusinessError;
      console.log(`[Web] ****** Error Code: ${e.code}, Message: ${e.message}`);
      // this.controller.refresh(); // 页面异常,刷新
    }
  }

  // 白屏情况可能是权限导致,把权限全部开启,例如本地存储或者 http & https
  build() {
    Column() {
      if (this.navHidden === true) {
        HeaderComponent({ title: this.title })
      }
      if (this.isLoading && !this.url) {
        HProgressHUD({ hudContent: '加载中~~~' })
      }
      if (this.url) {
        Web({ src: this.url, controller: this.controller })
          .width('100%')
          .height('100%')
          .backgroundColor(Color.White)
          .multiWindowAccess(true)
          .javaScriptAccess(true)// 访问本地资源文件
          .imageAccess(true)// 权限状态开启
          .onlineImageAccess(true)// 权限状态开启
          .fileAccess(true)// 权限状态开启
          .domStorageAccess(true)// 数据存储权限
          .mediaPlayGestureAccess(true)// 媒体权限
          .mixedMode(MixedMode.All)// https 加载
          .layoutMode(WebLayoutMode.FIT_CONTENT)// 自适应布局
          .verticalScrollBarAccess(true)// 滚动条
          .horizontalScrollBarAccess(false)// 滚动条
          .cacheMode(CacheMode.Default)// 缓存
          .zoomAccess(false)// 禁止手势缩放
          .geolocationAccess(true)// 定位权限
          .onConsole((event) => {
            console.log('[交互] - onConsole')
            LogUtils.info(event?.message.getMessage())
            return false
          })
          .onPageBegin(() => { // 页面加载中
            console.log('[Web] - 页面加载中:', this.url)
          })
          .onPageEnd(() => {
            console.log('[Web] - 页面加载完成:', this.url)
            this.isLoading = false
          })
          .onErrorReceive((event) => { // 异常: 无网络,页面加载错误时
            if (event) {
              console.info('getErrorInfo:' + event.error.getErrorInfo());
              console.info('getErrorCode:' + event.error.getErrorCode());
              console.info('url:' + event.request.getRequestUrl());
              console.info('isMainFrame:' + event.request.isMainFrame());
              console.info('isRedirect:' + event.request.isRedirect());
              console.info('isRequestGesture:' + event.request.isRequestGesture());
              console.info('getRequestHeader_headerKey:' + event.request.getRequestHeader().toString());
              let result = event.request.getRequestHeader();
              console.info('The request header result size is ' + result.length);
              for (let i of result) {
                console.info('The request header key is : ' + i.headerKey + ', value is : ' + i.headerValue);
              }
            }
          })
          .onHttpErrorReceive((event) => { // 异常: 网页加载资源 Http code >= 400 时
            if (event) {
              console.info('url:' + event.request.getRequestUrl());
              console.info('isMainFrame:' + event.request.isMainFrame());
              console.info('isRedirect:' + event.request.isRedirect());
              console.info('isRequestGesture:' + event.request.isRequestGesture());
              console.info('getResponseData:' + event.response.getResponseData());
              console.info('getResponseEncoding:' + event.response.getResponseEncoding());
              console.info('getResponseMimeType:' + event.response.getResponseMimeType());
              console.info('getResponseCode:' + event.response.getResponseCode());
              console.info('getReasonMessage:' + event.response.getReasonMessage());
              let result = event.request.getRequestHeader();
              console.info('The request header result size is ' + result.length);
              for (let i of result) {
                console.info('The request header key is : ' + i.headerKey + ' , value is : ' + i.headerValue);
              }
              let resph = event.response.getResponseHeader();
              console.info('The response header result size is ' + resph.length);
              for (let i of resph) {
                console.info('The response header key is : ' + i.headerKey + ' , value is : ' + i.headerValue);
              }
            }
          })
          .onAlert((event) => { // 提示框处理相关
            AlertDialog.show({
              title: '温馨提示',
              message: event?.message,
              confirm: {
                value: '确认',
                action: () => {
                  event?.result.handleConfirm()
                }
              },
              cancel: () => {
                event?.result.handleCancel()
              }
            })
            return true;
          })
          .onConfirm((event) => { // 提示框处理相关
            AlertDialog.show({
              title: '温馨提示',
              message: event?.message,
              confirm: {
                value: '确认',
                action: () => {
                  event?.result.handleConfirm()
                }
              },
              cancel: () => {
                event?.result.handleCancel()
              }
            })
            return true
          })
          .onShowFileSelector((event) => { // 文件选择处理
            console.log('MyFileUploader onShowFileSelector invoked');
            const documentSelectOptions = new picker.PhotoSelectOptions();
            let uri: string | null = null;
            const documentViewPicker = new picker.PhotoViewPicker();
            documentViewPicker.select(documentSelectOptions).then((documentSelectResult) => {
              uri = documentSelectResult[0];
              console.info('documentViewPicker.select to file succeed and uri is:' + uri);
              if (event) {
                event.result.handleFileList([uri]);
              }
            }).catch((err: BusinessError) => {
              console.error(`Invoke documentViewPicker.select failed, code is ${err.code}, message is ${err.message}`);
            })
            return true
          })
          .onLoadIntercept((event) => {
            if (event.data.isRedirect()) {
              console.log('[重定向]');
            }
            return false
          })
          .javaScriptProxy({
            // web call app, web 组件初始化调用
            // 对象注入 web
            object: this.WebCallAppMethod,
            name: 'WebCallAppHarmony', // AppCallWeb WebCallAppSsss  WebCallApp
            methodList: ['WebCallApp', 'WebTest', 'WebTestSync', 'WebTestAsync'],
            controller: this.controller
          })
      } else { // 加载异常,请重试

      }
    }.width('100%').height('100%')
  }
}

/**************************** Web JS Bridge ****************************/
class WebCallAppClass {
  private webView: WebView.WebviewController;

  constructor(webView: WebviewController) {
    this.webView = webView
  }

  // 方式一: 方法无回调,通过交互协议 WebCallApp => AppCallWeb
  WebCallApp(value: string) {
    console.log('********* [交互] --- 协议 - HWebView')
    console.log(value)

    // 解析交互协议
    let params: CommandModel = JSON.parse(value)
    let sn = params.sn
    let command = params.command
    let args: object = params.args
    console.info('********* ', command)
    console.info(sn)
    console.info(command)
    console.info(JSON.stringify(args))
    console.log('********* [交互] - 协议 *********')

    // 用户信息中添加机构
    let userInfo: UserInfoModal = JSON.parse(userInfoData)
    userInfo.instituteId = instituteId
    userInfo.genderCode = genderCode
    console.log('********* [用户信息]')
    console.log(JSON.stringify(userInfo))

    // 交互处理
    let commandManager = CommandManager.shareInstance()
    let res = commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
    let jsType = res.type
    let jsonRes = JSON.stringify(res)
    let resData: CommandModel = JSON.parse(jsonRes)
    let argsJson = JSON.stringify(res.args)
    console.log('[交互回调]:', argsJson)

    // App call web 数据格式处理
    let appCallWeb = AppCallWeb.shareInstance()
    let callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeNormal, sn, res, null)
    if (jsType == 'Json') {
      callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeJson, sn, res, null)
    } else if (jsType == 'Encode') {
      callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeJsonEncoded, sn, res, null)
    } else if (jsType == 'Basic') {
      callbacks = appCallWeb.callbackBasic(sn, res, null)
    }
    console.log('********* [callbacks]')
    console.log(callbacks)
    console.log('************')
    this.webView.runJavaScript(`Elf.AppCallWeb(${sn}, ${callbacks})`)
  }

  // 方式二: 方法回调,WebCallApp 直接 return
  // WebCallApp(value: string): string { // 直接 return 方式,New_20240703
  //   console.log('[交互] --- WebCallApp - 测试 - HWebView')
  //   console.log(value)
  //
  //   let params: CommandModel = JSON.parse(value)
  //   let command = params.command
  //   let args: object = params.args
  //   if (command === 'CmdOpenUrl') {
  //     let navigation: string = args['navigation']
  //     let name: string = args['name'] ? args['name'] : ''
  //     let staticState: string = args['static']
  //     let url: string = args['url']
  //     try {
  //       router.pushUrl({
  //         url: 'pages/component/HWebView',
  //         params: {
  //           title: name,
  //           navHidden: navigation === '1' ? true : false,
  //           link: url
  //         }
  //       })
  //     } catch (err) {
  //       console.error('[Router] - failed, code is ${(err as BusinessError).code}, message is ${(err as BusinessError).message}')
  //     }
  //     return ''
  //   } else {
  //     // 解析交互协议
  //     let params: CommandModel = JSON.parse(value)
  //     let sn = params.sn
  //     let command = params.command
  //     let args: object = params.args
  //     console.info(sn)
  //     console.info(command)
  //     console.info(JSON.stringify(args))
  //     // 用户信息中添加机构
  //     let userInfo: UserInfoModal = JSON.parse(userInfoData)
  //     userInfo.instituteId = instituteId
  //     userInfo.genderCode = genderCode
  //     console.log(JSON.stringify(userInfo))
  //     // 交互处理
  //     let commandManager = CommandManager.shareInstance()
  //     let res = commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
  //     let jsType = res.type
  //     let jsonRes = JSON.stringify(res)
  //     let resData: CommandModel = JSON.parse(jsonRes)
  //     console.log('[交互回调]:', JSON.stringify(res))
  //     let argsJson = JSON.stringify(res.args)
  //     // App call web 数据格式处理
  //     let appCallWeb = AppCallWeb.shareInstance()
  //     let callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeNormal, sn, res, null)
  //     if (jsType == 'Json') {
  //       callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeJson, sn, res, null)
  //     } else if (jsType == 'Encode') {
  //       callbacks = appCallWeb.callbackWithServiceResultSelect(DataType.DataTypeJsonEncoded, sn, res, null)
  //     } else if (jsType == 'Basic') {
  //       callbacks = appCallWeb.callbackBasic(sn, res, null)
  //     }
  //     console.log('[callbacks]')
  //     console.log(callbacks)
  //     console.log('************')
  //     return argsJson
  //   }
  // }


  WebTestAsync = async (value: string): Promise<string> => { // 测试 - 异步
    let userInfo: UserInfoModal = JSON.parse(userInfoData)
    userInfo.instituteId = instituteId
    userInfo.genderCode = genderCode
    let commandManager = CommandManager.shareInstance()
    let res = await commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
    let argsJson = JSON.stringify(res.args)
    console.log('[WebTestAsync]')
    console.log(argsJson)
    return argsJson
  }

  WebTest(value: string): Promise<string> { // 测试 - promise
    let p: Promise<string> = new Promise((resolve, reject) => {
      let userInfo: UserInfoModal = JSON.parse(userInfoData)
      userInfo.instituteId = instituteId
      userInfo.genderCode = genderCode
      let commandManager = CommandManager.shareInstance()
      let res = commandManager.webCallAppCommandWithScriptMessage(value, userInfo, this.webView)
      let argsJson = JSON.stringify(res.args)
      resolve('[Callback]:' + argsJson)
    })
    return p
  }
}

二.前端

基于 Vue 2 调试;

1.配置交互消息处理

/* eslint-disable */
import webApp from './index';
import { Toast } from "vant";

Date.prototype.format = function(format) {
  var o = {
      "M+": this.getMonth() + 1, //month
      "d+": this.getDate(), //day
      "h+": this.getHours(), //hour
      "m+": this.getMinutes(), //minute
      "s+": this.getSeconds(), //second
      "q+": Math.floor((this.getMonth() + 3) / 3), //quarter
      "S": this.getMilliseconds() //millisecond
  };
  if (/(y+)/.test(format)) {
      format = format.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length));
  }
  for (var k in o) {
      if (new RegExp("(" + k + ")").test(format)) {
          format = format.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ("00" + o[k]).substr(("" + o[k]).length));
      }
  }
  return format;
};
var AppCallBacks = {},//动态数据流水列表
    AppCommendBackHandlers = [],//APP后退监听事件列表
    AppCommendRefreshHandlers = [],//刷新监听事件列表
    APPCommendReBackHandlers = [],//监听重新回到页面通知
    AppCommendNetworkHandlers = [],//监听网络链接状态
    AppCommendAppStartingHandlers = [],//监听APP进入后台运行
    AppCommendAppReactivateHandlers = [],//监听APP重新进入前台运行
    AppCommendKeyboardBounceUp = [],
    AppHarmonyHandlers = {}; // 基于 Harmony 适配;
//监听移动端事件
var AppMsgHandlers = {
  //页面加载完成(TODO plan)
  //"MsgOnReady":function(msg){},
  // 移动端软键盘事件触发
  "MsgSoftKeyboardBounceUp": function (data) {
    if (AppCommendKeyboardBounceUp.length > 0) {
      AppCommendKeyboardBounceUp.forEach(item => {
        item.call(window,data);
      });
    }
  },
  //刷新数据通知(Realized)
  "MsgRefresh": function (data) {
    //data.type 0 刷新书架和已获得,1只刷新书架,2,只刷新已获得
    if (AppCommendRefreshHandlers.length > 0) {
      AppCommendRefreshHandlers.forEach(item => {
        item.call(window,data);
      });
    }
  },
  //后退事件(Realized)
  "MsgGoBack": function (data) {
    webApp.APPCommendBack(data);
  },
  //重新返回到界面(Realized)
  "MsgReBack": function () {
    if (APPCommendReBackHandlers && APPCommendReBackHandlers.length > 0) {
      APPCommendReBackHandlers.forEach(item => {
        item.call(this);
      });
    }
  },
  //网络监测
  "MsgNetworkState": function (result) {
    if (AppCommendNetworkHandlers && AppCommendNetworkHandlers.length > 0) {
      AppCommendNetworkHandlers.forEach(item => {
        item.call(this, result);
      });
    }
  },
  //App 后台运行
  "MsgAppStarting": function (result) {
    if (AppCommendAppStartingHandlers && AppCommendAppStartingHandlers.length > 0) {
      AppCommendAppStartingHandlers.forEach(item => {
        item.call(this, result);
      });
    }
  },
  //App重新激活
  "MsgAppReactivate": function (result) {
    if (AppCommendAppReactivateHandlers && AppCommendAppReactivateHandlers.length > 0) {
      AppCommendAppReactivateHandlers.forEach(item => {
        item.call(this, result);
      });
    }
  },
};

/*
 *
 * app对接
 * 移动端种植Elf对象
 * window => Elf
 *
 */
(function () {
  if (!window.applicationCache&&typeof(Worker)=='undefined') {
    alert("E001-检测到您的环境不支持HTML5,程序终止运行!");//不支持HTML5
    return;
  }

  // iOS & Android
  var global = window;//create a pointer to the window root
  if (typeof Elf === 'undefined') {
    global.Elf = {};//create elf root if not existed
  }
  Elf.global = global;//add a pointer to window

  if (typeof WebCallApp == 'undefined') {
    global.WebCallApp = {
      WebCallApp: (args) => {},
      // WebCallAppHarmony: (args) => {}
    };
  }

  // Harmony
  if (typeof WebCallAppHarmony == 'undefined') {
    global.WebCallAppHarmony = {
      WebCallAppHarmony: (args) => {},
    };
  }

  // 测试
  if (typeof WebCallAppTest == 'undefined') {
    global.WebCallAppTest = {
      WebCallAppTest: (args) => {},
    }
  }

  // global.WebCallApp.Elf = Elf;
  WebCallApp.global = global;
  WebCallAppHarmony.global = global;
  WebCallAppTest.global = global;
})();


if (typeof Elf != "undefined") {
  Elf.AppCallWeb = (sn, result) => {
    console.log('[鸿蒙] - 测试');
    console.log('sn:' + sn);
    console.log('res:' + JSON.stringify(result));

    if (navigator.userAgent.toLowerCase().indexOf('openharmony') !== -1) { // Harmony
      AppCallBacks[sn].callback.call(AppCallBacks[sn].context,result);
    } else { // iOS & Android
      if (result && typeof result == "string") {
        result = decodeURIComponent(result.replace(/\+/g,'%20'));
        try {
          result = JSON.parse(result);//解决空格变成+的问题
        } catch (error) {
          AppCallBacks[sn].callback.call(AppCallBacks[sn].context,result);
          return;
        }
        if (result.sn) {
          AppCallBacks[sn].callback.call(AppCallBacks[sn].context,result.QrCode);
          return;
        }
      }
      if (AppCallBacks[sn]) {
        if (JSON.parse(result.opFlag)) {
          //执行对应回调
          AppCallBacks[sn].callback.call(AppCallBacks[sn].context,(typeof result.serviceResult == "string") ? JSON.parse(result.serviceResult) : result.serviceResult);
        } else {
          //接口调用返回失败信息,统一处理错误消息
          Toast(result.errorMessage ? result.errorMessage : "服务器异常!");
        }
        //调用完成删除对象
        delete AppCallBacks[sn];
      } else if (AppMsgHandlers[sn] && typeof AppMsgHandlers[sn] == "function") {
        //处理消息通知
        AppMsgHandlers[sn].call(window,result);
      }
    }
  };
}
export {
  AppCallBacks,
  AppCommendBackHandlers,
  AppCommendAppStartingHandlers,
  APPCommendReBackHandlers,
  AppCommendKeyboardBounceUp,
  AppMsgHandlers,
  AppHarmonyHandlers
}

2.设置 WebCallApp 方法

/* eslint-disable */
import { Toast } from "vant";
import { AppCallBacks, AppCommendBackHandlers, AppHarmonyHandlers } from './AppMsgHandlers'

export default {
  /***********************************************************
   处理App发送的后退命令
   ***********************************************************/
  APPCommendBack() {
    if (AppCommendBackHandlers.length > 0) {
      if (typeof AppCommendBackHandlers[ AppCommendBackHandlers.length - 1 ] == "function") {
        AppCommendBackHandlers[ AppCommendBackHandlers.length - 1 ].call(window);
      }
    } else {
      if (this.isInApp()) {
        if (!this.isEmptyObject(AppCallBacks)) {
          //Elf.components.toast({text:""});
        } else {
          this.WebCallApp("CmdGoBack");
        }
      } else {
        history.back(-1);
      }
    }
  },
  WebTestAsync(command, args, callback, context) { // 测试
    console.log('WebTestAsync');
    console.log(command);
    console.log(args);
  },
  WebTestSync(command, args, callback, context) { // 测试
    console.log('WebTestSync');
    console.log(command);
    console.log(args);
  },
  WebCallApp(command, args, callback, context) {
    /**
     * 交互
     *
     * 协议:command
     * 入参:args
     * 回调:callback
     * 参数:context
     * */
    if (typeof Elf.AppCallWeb != "undefined") {
      context = context || window;//默认为window对象
      args = args || {};
      let sn = null;
      //IOS调用相机--sn特殊处理
      if (command == "callCamera") {
        sn = "examCamera";
      } else {
        sn = this.getSerialNumber();//请求App统一加水单号
      }
      console.log('first:', sn);
      let params = {
        args : args,
        command : command,
        sn : sn,
      };
      //绑定回调函数
      if (callback) {
        AppCallBacks[ sn ] = {
          callback : callback,
          context : context
        };
      }
      if (window.webkit && window.webkit.messageHandlers) { // iOS
        params.sn = sn;
        window.webkit.messageHandlers[ "WebCallApp" ].postMessage(JSON.stringify(params));
      } else if (Elf.WebCallApp) { // Android
        params.sn = sn;
        Elf.WebCallApp(JSON.stringify(params));
      } else if (this.isInCef()) { // PC
        params.sn = sn;
        Elf.WebCallCef(JSON.stringify(params));
      } else if (this.isInHarmonyOS()) { // HarmonyOS Next
        // 方式一: WebCallApp => AppCallWeb 交互通信方式
        params.sn = sn;
        WebCallAppHarmony[ 'WebCallApp' ](JSON.stringify(params));
        // 方式三: callback 延迟回调
        // console.log('sn - 0:', sn);
        // let timer = setTimeout(() => {
        //   console.log('sn - 1:', sn);
        //   if (callback) {
        //     console.log('sn - 2:', sn);
        //     callback(AppHarmonyHandlers.result);
        //     clearTimeout(timer);
        //   }
        // }, 500);

        // let result = WebCallApp[ 'WebCallApp' ](JSON.stringify(params)); // old
        // 方式二: WebCallApp 直接交互
        // let result = WebCallAppHarmony[ 'WebCallApp' ](JSON.stringify(params)); // 适配考试
        // console.log(result);
        // callback
        // if (result && typeof result == 'string') {
        //   try {
        //     result = JSON.parse(result);
        //   } catch (error) {
        //     console.log(JSON.parse(result));
        //     AppCallBacks[ sn ].callback.call(AppCallBacks[ sn ].context, result);
        //     return
        //   }
        //   if (result.sn) {
        //     AppCallBacks[ sn ].callback.call(AppCallBacks[ sn ].context, result);
        //     return;
        //   }
        // }
      } else {
        //直连接口服务器
        // if (Config.services[command]) {
        //   args.token = User.token;
        //   if (command == "UserLogin") {
        //     args.token = -1;
        //   }
        //   var request = {
        //     terminalType: "A",
        //     deviceType: "1",
        //     serviceModule: Config.services[command].sm,
        //     serviceNumber: Config.services[command].sn,
        //     token: command == "UserLogin" ? "-1" : User.token,
        //     args: args
        //   };
        //   options.data = request;
        //   options.method = 'post';
        //   options.url = process.env.root;
        //   options.dataType = 'json';
        //   axios(options).then(res => {
        //     Elf.AppCallWeb(sn,data);
        //     Toast.clear();
        //   }).catch(
        //     error => {
        //       var errorData = { opFlag: false,errorMessage: "总线错误" };
        //       Elf.AppCallWeb(sn,JSON.stringify(errorData),"JSON");
        //     }
        //   );
        // }
      }
    }
  },
  AppCommendTostInfo(result) {
    if (result.msg) {
      Toast(result.msg);
    }
  },
  /***********************************************************
   App Call Web 入口方法,
   All of app to web use that
   ***********************************************************/
  isInApp() {
    if (typeof Elf.AppCallWeb != "undefined") {
      return !!((window.webkit && window.webkit.messageHandlers) || typeof Elf.WebCallApp == "function" || typeof Elf.WebCallCef == "function" || typeof Elf.getTestDataAsync == "function");
      // || typeof Elf.WebCallAppHarmony == "function"
    }
  },
  isInIOS() {
    return window.webkit && window.webkit.messageHandlers;
  },
  isInAndroid() {
    if (typeof Elf.AppCallWeb != "undefined") {
      return typeof Elf.WebCallApp == "function";
    }
  },
  isInHarmonyOS() {
    if (navigator.userAgent.toLowerCase().indexOf('openharmony') !== -1) {
      return true;
    } else {
      return false;
    }
  },
  isInCef() {
    if (typeof Elf.AppCallWeb != "undefined") {
      return typeof Elf.WebCallCef == "function";
    }
  },
  isEmptyObject(obj) {
    let name;
    for (name in
      obj) {
      return false;
    }
    return true;
  },
  UUID(len, radix) {
    var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split('');
    var uuid = [],
      i;
    radix = radix || chars.length;
    if (len) {
      for (i = 0;
        i < len;
        i++) {
        uuid[ i ] = chars[ 0 | Math.random() * radix ];
      }
    } else {
      var r;
      uuid[ 8 ] = uuid[ 13 ] = uuid[ 18 ] = uuid[ 23 ] = '-';
      uuid[ 14 ] = '4';
      for (i = 0;
        i < 36;
        i++) {
        if (!uuid[ i ]) {
          r = 0 | Math.random() * 16;
          uuid[ i ] = chars[ (i == 19) ? (r & 0x3) | 0x8 : r ];
        }
      }
    }
    return uuid.join('');
  },
  getSerialNumber() {
    // var uuid = this.UUID(3, 8);
    // return new Date().format("yyyyMMddhhmmssS") + uuid; // after
    return Math.random().toString(); // new
  },
}

3.全局设置

跟目录 main.js 中配置为全局

import webApp from '../src/expand/webApp/index';
import { AppCommendBackHandlers } from "./expand/webApp/AppMsgHandlers";

Vue.prototype.webApp = webApp;
Vue.prototype.WebBack = AppCommendBackHandlers;

4.交互调用

this.webApp.WebCallApp('GetAPPDetail', {'abc': '123'}, (res) => { // 测试: 鸿蒙 jsbridge 交互
  console.log('[交互] - 回调');
  console.log(res);
});

以上便是此次分享的全部内容,希望能对大家有所帮助!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值