FinClip为企业提供小程序生态圈技术产品,开发者可在FinClip小程序开发帮助中心找到相关FinClip小程序指引

# 代理方法

小程序中部分业务是抽象定义的,这些抽象的业务通过接口的形式暴露给了外部,外部可以自行实现具体的业务逻辑。

函数签名如果是需要返回 boolean 类型表示是否需要阻止默认行为,如果需要阻止就返回 true,否则则返回 false

函数里的参数 appIdapiServer 均为触发的小程序的信息,可以根据这两个数据来对不同的小程序来执行不同的逻辑

# 1. CapsuleHandler

胶囊按钮的代理类

// namespace IFinProxyHandlerItem
class CapsuleHandler {
    /**
     * 点击关闭按钮时触发
     * @param appId 小程序 id
     * @param apiServer 小程序 apiServer
     * @returns 返回 true 表示宿主自行处理,返回 false 表示宿主处理完,仍执行后续的默认操作
     */
    public onCloseButtonClick(appId: string, apiServer: string): boolean 

    /**
     * 点击更多按钮时触发
     * @param appId 小程序 id
     * @param apiServer 小程序 apiServer
     * @returns 返回 true 表示宿主自行处理,返回 false 表示宿主处理完,仍执行后续的默认操作
     */
    public onMoreButtonClick(appId: string, apiServer: string): boolean
  }
已复制代码

示例代码:

import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk'
import { promptAction } from '@kit.ArkUI'

class CapsuleHandler extends IFinProxyHandlerItem.CapsuleHandler {
  onCloseButtonClick(appId: string, apiServer: string): boolean {
    this.promptMsg(`${appId},关闭按钮点击了`)
    return false
  }

  onMoreButtonClick(appId: string, apiServer: string): boolean {
    this.promptMsg(`${appId},更多按钮点击了`)
    return false
  }

  private promptMsg(message: string) {
    promptAction.showToast({
      message
    })
  }
}

FinAppProxyHandlerManager.capsuleHandler = new CapsuleHandler()
已复制代码
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class CapsuleHandler {  
  // 更多按钮的点击事件
  onMoreButtonClick?: (appId: string, apiServer: string) => boolean  
  // 关闭按钮的点击事件
  onCloseButtonClick?: (appId: string, apiServer: string) => boolean  
}
已复制代码

示例代码

client.proxyHandlerManager.capsuleHandler.onMoreButtonClick = (appId: string, apiServer: string) => {
	// do something
	return false // 不阻止默认行为
}
已复制代码

# 2. MoreMenuHandler

更多菜单的代理类

// namespace IFinProxyHandlerItem
class MoreMenuHandler {
    /**
     * 当收到小程序返回的数据时触发
     * @param contentInfo 小程序返回的信息
     * @returns
     */
    public onCustomMenuItemClickWithInfo(appId: string, apiServer: string,
      contentInfo: IFinAppProxy.IMoreMenuItemContentInfo)

    /**
     * 当更多按钮菜单点击时出发
     * @param type 菜单按钮的唯一标识
     * @param currentPath 小程序当前的页面路径
     * @returns
     */
    public onMoreMenuItemClick(appId: string, apiServer: string, type: string, currentPath: string): boolean | void

    /**
     * 获取更多菜单的按钮列表
     * @returns 更多菜单的按钮列表
     */
    public getMoreMenuItems(): IFinAppProxy.IMoreMenuItem[]
  }
已复制代码

示例代码:

import { FinAppProxyHandlerManager, IFinAppProxy, IFinProxyHandlerItem } from '@finclip/sdk'
import { promptAction } from '@kit.ArkUI'

class MoreMenuHandler extends IFinProxyHandlerItem.MoreMenuHandler {
  onCustomMenuItemClickWithInfo(appId: string, apiServer: string,
    contentInfo: IFinAppProxy.IMoreMenuItemContentInfo) {
    this.promptMsg(`小程序 ${apiServer} ${appId}${contentInfo.path} 页面的菜单 ${contentInfo.menuId} 被点击了,返回的数据为${JSON.stringify(contentInfo.params)}`)
  }

  onMoreMenuItemClick(appId: string, apiServer: string, menuItemId: string, currentPath: string): boolean | void {
    this.promptMsg(`小程序 ${apiServer} ${appId}${currentPath} 页面的菜单 ${menuItemId} 被点击了`)
  }

  getMoreMenuItems(): IFinAppProxy.IMoreMenuItem[] {
    return [
      {
        label: '自定义',
        icon: $r('app.media.app_icon'),
        menuItemId: 'custom',
        menuType: 'onMiniProgram'
      }
    ]
  }

  private promptMsg(message: string) {
    promptAction.showToast({
      message
    })
  }
}


FinAppProxyHandlerManager.moreMenuHandler = new MoreMenuHandler()

已复制代码
// 小程序端
Page({
    onCustomButtonHandler(){
        return {
            appInfo:'this is appInfo'
        }
      }
})
已复制代码
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class MoreMenuHandler { 
/**  
 * @param type 菜单按钮的唯一标识  
 * @param currentPath 小程序当前的页面路径  
 * @returns 
 * 
 */
  onMoreMenuItemClick?: (appId: string, apiServer: string, type: string, currentPath: string) => void  
/**  
* 当 onMiniProgram 类型的按钮点击时出发,携带小程序返回的信息
 * @param contentInfo 小程序返回的信息 
 * @returns 
 */
  onCustomMenuItemClickWithInfo?: (appId: string, apiServer: string,
  contentInfo: IFinAppProxy.IMoreMenuItemContentInfo) => void
/**  
 * @returns 自定义更多菜单的按钮  
 */
  getMoreMenuItems: () => IMoreMenuItem[]
}
已复制代码

示例代码

client.proxyHandlerManager.moreMenuHandler.onMoreMenuItemClick = (appId: string, apiServer: string, type: string, currentPath: string) => {
 // do something
}

client.proxyHandlerManager.moreMenuHandler.onCustomMenuItemClickWithInfo = (appId: string, apiServer: string,
  contentInfo: IFinAppProxy.IMoreMenuItemContentInfo) => {
    if(contentInfo.menuId === 'custom'){
      // do something
    }    
}

client.proxyHandlerManager.moreMenuHandler.getMoreMenuItems = () => {  
  return [  
    {  
      label: '自定义 Btn',  
      icon: $r('app.media.app_icon'),  
      menuItemId: 'custom'  
    }  
  ]  
}
已复制代码

IMoreMenuItem

属性 类型 是否必填 描述
label String 小程序 id
icon ResourceStr 菜单按钮的图片资源
disableIcon ResourceStr 菜单按钮禁用的图片资源
darkIcon ResourceStr 暗黑模式菜单按钮的图片资源
disableDarkIcon ResourceStr 暗黑模式菜单按钮禁用的图片资源
menuItemId String 菜单按钮的唯一标识,即 onMoreMenuItemClick 参数的 type
hover Boolean 是否开启 hover 效果
disable Boolean 是否禁用

IMoreMenuItemContentInfo

属性 类型 描述
title String 小程序名称
logo String 小程序 logo
description String 小程序描述
path String 当前页面路径
menuId String 菜单按钮的唯一标识
params IMoreMenuItemParams 由小程序返回的原始数据

IMoreMenuItemParams

属性 类型
title String
desc String
path String
appInfo object

# 3. PrivacyHandler

隐私协议的代理类

// namespace IFinProxyHandlerItem
export class PrivacyHandler {
  /**
   * @param scope 当前请求的权限,如果为空则是在关于页面显示的隐私协议
   * @returns IFinApplet.IPrivacy
   */
  public getPrivacyInfo(appId: string, apiServer: string, scope?: string): IFinApplet.IPrivacy | void
}
已复制代码

示例代码

import { FinAppProxyHandlerManager, IFinApplet, IFinProxyHandlerItem } from '@finclip/sdk'

class PrivacyHandler extends IFinProxyHandlerItem.PrivacyHandler {
  getPrivacyInfo(appId: string, apiServer: string, scope?: string): IFinApplet.IPrivacy | void {
    return {
      title: '自定义授权弹窗标题',
      docName: '自定义隐私协议文档名称',
      docUrl: 'https://www.finclip.com',
      copyWriting: '自定义授权弹窗文案,隐私协议文档名称为《自定义隐私协议文档名称》'
    }
  }
}


FinAppProxyHandlerManager.privacyHandler = new PrivacyHandler()
已复制代码
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class PrivacyHandler { 
/**  
 * @param scope 当前请求的权限,如果为空则是在关于页面显示的隐私协议
 * @returns IFinApplet.IPrivacy
 */
  getPrivacyInfo?: (appId: string, apiServer: string, scope?: string) => IFinApplet.IPrivacy
}
已复制代码

示例代码

client.proxyHandlerManager.privacyHandler.getPrivacyInfo = (appId: string, apiServer: string, scope?: string) => {  
  return {  
    title: '自定义授权弹窗标题',  
    docName: '自定义隐私协议文档名称',  
    docUrl: 'https://www.finclip.com',  
    copyWriting: '自定义授权弹窗文案,隐私协议文档名称为《自定义隐私协议文档名称》'  
  }  
}
已复制代码

IFinApplet.IPrivacy

属性 类型 是否必填 描述
title String 自定义隐私授权弹窗标题
copyWriting String 自定义隐私授权弹窗文案,内容包含 自定义隐私协议文档名称
docName String 自定义隐私协议文档名称
docUrl String 自定义隐私协议文档链接

# 4. ScopeHandler

自定义权限的代理类

// namespace IFinProxyHandlerItem
export class ScopeHandler {
    /**
     * 是否自定义小程序权限设置页
     */
    public isAppletCustomizeSettingPage: boolean = false

    /**
     * 自定义小程序权限设置页
     * @param applet
     * @param scopes
     * @param updateScopes 更新 scopes 的方法
     * @returns boolean 是否实现了代理,如果返回 false 表示未实现代理,会走默认的逻辑
     */
    public openSettingPage(applet: IFinApplet.IAppletInfo, scopes: IFinApplet.IScope[],
      updateScopes: (result: IFinAppProxy.ISettingListParams) => void): boolean

    /**
     * 注册自定义权限
     * @param appId
     * @param apiServer
     * @returns 自定义权限列表
     */
    public getCustomScopes(appId: string, apiServer: string): IFinApplet.IScope[]

    /**
     * 自定义弹窗,实现方式可以参考示例代码
     * @returns 弹窗 builder
     */
    public getCustomView(): (() => void) | void

    /**
     * @param api 当初小程序触发的 API 名称
     * @param context 当前 Ability 的 context
     * @param scopes 权限列表,包括内置权限和自定义权限
     * @param callback API 的回调函数,如果用户通过权限调用 callback 传 true,后续则会走自定义 API 的逻辑,否则则直接走 fail 逻辑
     * @returns boolean 是否实现了代理,如果返回 false 表示未实现代理,会走默认的权限逻辑
     */
    public onCustomApiInvoke(appId: string, apiServer: string, api: string, context: common.UIAbilityContext,
      scopes: IFinApplet.IScope[], callback: (res: boolean) => void): boolean
  }
已复制代码

示例代码

import { FinAppProxyHandlerManager, IFinApplet, IFinAppProxy, IFinProxyHandlerItem } from '@finclip/sdk'
import { promptAction } from '@kit.ArkUI'
import { ISettingRouterParams } from '../../pages/Setting'
import { getFinAppClient } from '../FinAppClient'
import { common } from '@kit.AbilityKit'

class ScopeHandler extends IFinProxyHandlerItem.ScopeHandler {
  isAppletCustomizeSettingPage = true

  openSettingPage(applet: IFinApplet.IAppletInfo, scopes: IFinApplet.IScope[],
    updateScopes: (result: IFinAppProxy.ISettingListParams) => void): boolean {
    const param: ISettingRouterParams = {
      applet,
      scopes
    }
    // 以 Navigation 的方式初始化,使用 Navigation 跳转授权管理页
    getFinAppClient().getEntryInfo().routerState?.pushPath({
      name: 'Setting',
      param,
      onPop: (info) => {
        const param = info.result as ISettingRouterParams
        updateScopes({
          scopes: param.scopes,
          applet: param.applet
        })
      }
    })
    return true
  }

  getCustomScopes(appId: string, apiServer: string): IFinApplet.IScope[] {
    return [
      {
        scope: 'idCard',
        scopeName: '身份证',
        title: '获取身份证信息',
        desc: "获取您的身份证信息用于XXXXXX",
        scopeStatus: 0
      }
    ]
  }

  getCustomView(): (() => void) | void {
    return customView
  }

  onCustomApiInvoke(appId: string, apiServer: string, api: string, context: common.UIAbilityContext,
    scopes: IFinApplet.IScope[], callback: (res: boolean) => void): boolean {
    const scope = scopes.find(i => i.scope === 'idCard')
    if (scope?.scopeStatus === 0) {
      context.eventHub.emit('showCustomToast', api)
      context.eventHub.on('onShowCustomToast', (res: boolean) => {
        scope.scopeStatus = res ? 3 : 1
        callback(res)
        context.eventHub.off('onShowCustomToast')
      })
    } else {
      callback(true)
    }
    return true
  }

  private promptMsg(message: string) {
    promptAction.showToast({
      message
    })
  }
}

@Builder
function customView() {
  CustomViewComponent()
}

@Component
struct CustomViewComponent {
  @State show: boolean = false
  private context = getContext(this) as common.UIAbilityContext

  aboutToAppear() {
    this.context.eventHub.on('showCustomToast', () => {
      this.show = true
    })
  }

  handlePermission(res: boolean) {
    this.context.eventHub.emit('onShowCustomToast', res)
    this.show = false
  }

  @Builder
  toast() {
    Stack({ alignContent: Alignment.Bottom }) {
      Row() {
      }
      .height('100%')
      .width('100%')
      .backgroundColor('rgba(0,0,0,0.5)')

      Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {
        Row() {
          Button('拒绝').onClick(() => {
            this.handlePermission(false)
          })
          Button('同意').onClick(() => {
            this.handlePermission(true)
          })
        }
        .width('100%')
      }
      .padding({
        top: 24,
        left: 18,
        right: 18,
        bottom: 24
      })
      .width('100%')
      .height(241)
      .backgroundColor('#F5F6F6')
      .clip(true)
      .borderRadius({ topLeft: 16, topRight: 16 })
      .zIndex(10)
    }
  }

  build() {
    if (this.show) {
      this.toast()
    }
  }
}

FinAppProxyHandlerManager.scopeHandler = new ScopeHandler()
已复制代码
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class ScopeHandler {  
/**  
 * @param api 当初小程序触发的 API 名称  
 * @param context 当前 Ability 的 context * @param scopes 权限列表,包括内置权限和自定义权限  
 * @param callback API 的回调函数,如果用户通过权限调用 callback 传 true,后续则会走自定义 API 的逻辑,否则则直接走 fail 逻辑  
 * @returns 
 */
  onCustomApiInvoke?: (appId: string, apiServer: string, api: string, context: common.UIAbilityContext, scopes: IFinApplet.IScope[], callback: (res: boolean) => void) => void
  // 自定义弹窗,实现方式可以参考示例代码  
  getCustomView?: () => (() => void) | void 
  // 注册自定义权限
  getCustomScopes: (appId: string, apiServer: string) => IFinApplet.IScope[]
  // 是否自定义小程序权限设置页
  isAppletCustomizeSettingPage?: boolean
  // 自定义小程序权限设置页
  openSettingPage?: (applet: IFinApplet.IAppletInfo, scopes: IFinApplet.IScope[],
    updateScopes: (result: IFinAppProxy.ISettingListParams) => void) => void
}
已复制代码

示例代码

client.proxyHandlerManager.privacyHandler.getPrivacyInfo = (appId: string, apiServer: string, scope?: string) => {  
  return {  
    title: '自定义授权弹窗标题',  
    docName: '自定义隐私协议文档名称',  
    docUrl: 'https://www.finclip.com',  
    copyWriting: '自定义授权弹窗文案,隐私协议文档名称为《自定义隐私协议文档名称》'  
  }  
}
已复制代码

示例代码

4.1 自定义权限

client.proxyHandlerManager.scopeHandler.getCustomScopes = (appId: string, apiServer: string) => {  
  return [  
    {  
      scope: 'idCard',  
      scopeName: '身份证',  
      title: '获取身份证信息',  
      desc: "获取您的身份证信息用于XXXXXX",  
      scopeStatus: 0  
    }  
  ]  
}  

@Builder  
function customView() {  
  CustomViewComponent()  
}  
  
@Component  
struct CustomViewComponent {  
  @State show: boolean = false  
  private context = getContext(this) as common.UIAbilityContext  
  
  aboutToAppear() {  
    this.context.eventHub.on('showCustomToast', () => {  
      this.show = true  
    })  
  }  
  
  handlePermission(res: boolean) {  
    this.context.eventHub.emit('onShowCustomToast', res)  
    this.show = false  
  }  
  
  @Builder  
  toast() {  
    Stack({ alignContent: Alignment.Bottom }) {  
      Row() {  
      }      .height('100%')  
      .width('100%')  
      .backgroundColor('rgba(0,0,0,0.5)')  
  
      Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {  
        Row() {  
          Button('拒绝').onClick(() => {  
            this.handlePermission(false)  
          })  
          Button('同意').onClick(() => {  
            this.handlePermission(true)  
          })  
        }  
        .width('100%')  
      }  
      .padding({ top: 24, left: 18, right: 18, bottom: 24 })  
      .width('100%')  
      .height(241)  
      .backgroundColor('#F5F6F6')  
      .clip(true)  
      .borderRadius({ topLeft: 16, topRight: 16 })  
      .zIndex(10)  
    }  
  }  
  build() {  
    if (this.show) {  
      this.toast()  
    }  
  }}  
  
client.proxyHandlerManager.scopeHandler.getCustomView = () => customView
  
const onCustomApiInvoke = (appId: string, apiServer: string, api: String, context: common.UIAbilityContext, scopes: IFinApplet.IScope[], callback: (res: boolean) => void) => {  
  const scope = scopes.find(i => i.scope === 'idCard')  
  if (scope?.scopeStatus === 0) {  
    context.eventHub.emit('showCustomToast', api)  
    context.eventHub.on('onShowCustomToast', (res: boolean) => {  
      scope.scopeStatus = res ? 3 : 1  
      callback(res)  
      context.eventHub.off('onShowCustomToast')  
    })  
  } else {  
    callback(true)  
  }  
}
  
 
client.proxyHandlerManager.scopeHandler.onCustomApiInvoke = onCustomApiInvoke
已复制代码

4.2 自定义小程序权限设置页


interface ISettingRouterParams {
  applet: IFinApplet.IAppletInfo,
  scopes: IFinApplet.IScope[]
}
client.proxyHandlerManager.scopeHandler.isAppletCustomizeSettingPage = true
client.proxyHandlerManager.scopeHandler.openSettingPage =
  (applet: IFinApplet.IAppletInfo, scopes: IFinApplet.IScope[], updateScopes) => {
    const param: ISettingRouterParams = {
      applet,
      scopes
    }
    client.getEntryInfo().routerState?.pushPath({
      name: 'Setting',
      param,
      onPop: (info) => {
        const param = info.result as ISettingRouterParams
        // 更新权限
        updateScopes({
          scopes: param.scopes,
          applet: param.applet
        })
      }
    })
  }
已复制代码

IFinApplet.IScope

属性 类型 是否必填 描述
scope String 权限标识位
title String 权限标题
desc String 权限描述
scopeStatus EPermissionState 权限状态
scopeName String 设置页描述

EPermissionState

属性 描述
UNSET 0 未设置
DISALLOW 1 不允许
ALLOW_WHEN_USING 2 使用小程序时
ALLOW 3 允许

IFinApplet.IAppletInfo

属性 类型 是否必填 描述
appId string 小程序ID
name string 小程序名称
logo string 小程序logo地址

IFinAppProxy.ISettingListParams

属性 类型 是否必填 描述
applet IFinApplet.IAppletInfo 小程序信息
scopes IFinApplet.IScope[] 权限列表

# 5.GrayReleaseHandler

灰度发布相关的代理类

// namespace IFinProxyHandlerItem
export class GrayReleaseHandler {
    /**
     * 当获取小程序信息时会调用该方法,可以自定义灰度发布的数据,sdk 会将自定义数据和内置数据合并发送,当属性名相同时,使用自定义的数据
     * @param appId
     * @param apiServer
     * @returns 需要拼接到灰度发布配置的数据
     */
    public getGrayExtension(appId: string, apiServer: string): Record<string, string>
  }
已复制代码

示例代码

import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk'

class GrayReleaseHandler extends IFinProxyHandlerItem.GrayReleaseHandler {
  getGrayExtension(appId: string, apiServer: string): Record<string, string> {
    return {
      'customKey': 'customValue',
      'xUserId': '130000000'
    }
  }
}

FinAppProxyHandlerManager.grayReleaseHandler = new GrayReleaseHandler()
已复制代码
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class GrayReleaseHandler { 
/**  
 * 当获取小程序信息时会调用该方法,可以自定义灰度发布的数据,sdk 会将自定义数据和内置数据合并发送,当属性名相同时,使用自定义的数据
 */
  getGrayExtension?: ( appId: string, apiServer: string,) => Record<string, string>
}
已复制代码

示例代码:

client.proxyHandlerManager.grayReleaseHandler.getGrayExtension = ( appId: string, apiServer: string) => {  
  return {  
    customName: 'customName' 
  }  
}
已复制代码

# 6.AppletLifeCycleHandler

小程序生命周期相关

// namespace IFinProxyHandlerItem
export class AppletLifeCycleHandler {
    /**
     * 当小程序打开时触发。
     * @param appId 小程序的 ID。
     * @param apiServer 与小程序关联的 API 服务器。
     */
    public onOpen(appId: string, apiServer: string)

    /**
     * 当小程序初始化完成时触发。
     * @param appId 小程序的 ID。
     * @param apiServer 与小程序关联的 API 服务器。
     */
    public onInitCompletion(appId: string, apiServer: string)

    /**
     * 当小程序关闭时触发。
     * @param appId 小程序的 ID。
     * @param apiServer 与小程序关联的 API 服务器。
     */
    public onClose(appId: string, apiServer: string)

    /**
     * 当小程序发生错误时触发。
     * @param appId 小程序的 ID。
     * @param apiServer 与小程序关联的 API 服务器。
     * @param code 错误代码。
     * @param message 错误信息。
     */
    public onError(appId: string, apiServer: string, code: string, message: string)

    /**
     * 当小程序准备就绪时触发。
     * @param appId 小程序的 ID。
     * @param apiServer 与小程序关联的 API 服务器。
     */
    public onReady(appId: string, apiServer: string)

    /**
     * 当小程序显示时触发。
     * @param appId 小程序的 ID。
     * @param apiServer 与小程序关联的 API 服务器。
     */
    public onShow(appId: string, apiServer: string) 

    /**
     * 当小程序隐藏时触发。
     * @param appId 小程序的 ID。
     * @param apiServer 与小程序关联的 API 服务器。
     */
    public onHide(appId: string, apiServer: string)

    /**
     * 当小程序销毁时触发。
     * @param appId 小程序的 ID。
     * @param apiServer 与小程序关联的 API 服务器。
     */
    public onDestroy(appId: string, apiServer: string)
  }
已复制代码

示例代码

import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk'
import { promptAction } from '@kit.ArkUI'

class AppletLifeCycleProxy extends IFinProxyHandlerItem.AppletLifeCycleHandler {
  onOpen(appId: string, apiServer: string) {
    this.promptMsg(`${appId} onOpen`)
  }

  onInitCompletion(appId: string, apiServer: string) {
    this.promptMsg(`${appId} onInitCompletion`)
  }

  onClose(appId: string, apiServer: string) {
    this.promptMsg(`${appId} onClose`)
  }

  onError(appId: string, apiServer: string, code: string, message: string) {
    this.promptMsg(`${appId} onError,code:${code},message:${message}`)
  }

  onReady(appId: string, apiServer: string) {
    this.promptMsg(`${appId} onReady`)
  }

  onShow(appId: string, apiServer: string) {
    this.promptMsg(`${appId} onShow`)
  }

  onHide(appId: string, apiServer: string) {
    this.promptMsg(`${appId} onHide`)
  }

  onDestroy(appId: string, apiServer: string) {
    this.promptMsg(`${appId} onDestroy`)
  }

  private promptMsg(message: string) {
    promptAction.showToast({
      message
    })
  }
}

FinAppProxyHandlerManager.appletLifeCycleHandler = new AppletLifeCycleProxy()
已复制代码
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class AppletLifeCycleHandler {
  onOpen?: (appId: string, apiServer: string) => void // 小程序打开
  onInitCompletion?: (appId: string, apiServer: string) => void // 小程序初始化完成
  onClose?: (appId: string, apiServer: string) => void // 小程序关闭
  onError?: (appId: string, apiServer: string, code: string, message: string) => void  // 小程序报错
  onReady?: (appId: string, apiServer: string) => void // 小程序页面首次渲染完成
  onDestroy?: (appId: string, apiServer: string) => void // 小程序销毁
}
已复制代码

# 7.CustomLayoutHandler

自定义布局相关的代理类

// namespace IFinProxyHandlerItem
export class CustomLayoutHandler {
    /**
     * 自定义分包下载错误页
     * @returns 自定义分包下载错误页 builder
     */
    public getDownloadSubpackageFileFailedLayout(): ((appId: string, apiServer: string, path: string,
      reload: () => void) => void) | void

    /**
     * 自定义加载错误页
     * @returns 自定义加载错误页 builder
     */
    public getLoadFailedLayout(): ((appId: string, apiServer: string, title: string, label: string) => void) | void

    /**
     * 当页面准备完成触发,该函数结束后销毁 loading 组件
     * @returns
     */
    public async onPageLoadingLayoutReady(): Promise<void>

    /**
     * 当小程序准备完成触发,该函数结束后销毁 loading 页
     * @returns
     */
    public async onLoadingLayoutReady(): Promise<void>

    /**
     * 自定义页面 loading 组件
     * @returns 自定义页面 loading 的 builder
     */
    public getPageCustomLoadingLayout(): ((appId: string, apiServer: string, path: string) => void) | void

    /**
     * 获取自定义 loading 页的布局方法
     * @returns 自定义 loading 页 builder
     */
    public getCustomLoadingLayout(): (() => void) | void

    /**
     * 获取自定义 Toast 方法
     * @returns 自定义 Toast builder
     */
    public getCustomToastLayout(): (() => void) | void

     /**
     * 获取自定义覆盖物 builder,会渲染在小程序页面上层
     * @returns 自定义覆盖物 builder
     */
    public getCustomOverlayLayout(): ((appId: string, apiServer: string) => void) | void
  }
已复制代码

示例代码

import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk'
import { CustomToast } from '../customLayout/customToast'
import { CustomOverlayLayout } from '../customLayout/customOverlayLayout'
import { DownloadSubpackageFileFailedLayout } from '../customLayout/DownloadSubpackageFileFailedLayout'
import { LoadFailedLayout } from '../customLayout/LoadFailedLayout'
import { CustomLoadingLayout, PageCustomLoadingLayout } from '../customLayout/LoadingLayout'

class CustomLayoutHandler extends IFinProxyHandlerItem.CustomLayoutHandler {
  /**
   * 自定义分包下载错误页
   * @returns 自定义分包下载错误页 builder
   */
  getDownloadSubpackageFileFailedLayout(): ((appId: string, apiServer: string, path: string,
    reload: () => void) => void) | void {
    return DownloadSubpackageFileFailedLayout
  }

  /**
   * 自定义加载错误页
   * @returns 自定义加载错误页 builder
   */
  getLoadFailedLayout(): ((appId: string, apiServer: string, title: string, label: string) => void) | void {
    return LoadFailedLayout
  }


  onPageLoadingLayoutReady(): Promise<void> {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve()
      }, 200)
    })
  }


  onLoadingLayoutReady(): Promise<void> {
    return new Promise((resolve) => {
      setTimeout(() => {
        resolve()
      }, 200)
    })
  }


  getPageCustomLoadingLayout(): ((appId: string, apiServer: string, path: string) => void) | void {
    return PageCustomLoadingLayout
  }

  getCustomLoadingLayout(): (() => void) | void {
    return CustomLoadingLayout
  }


  getCustomToastLayout(): (() => void) | void {
    return CustomToast
  }

   getCustomOverlayLayout(): ((appId: string, apiServer: string) => void) | void {
    return CustomOverlayLayout
  }
}


FinAppProxyHandlerManager.customLayoutHandler = new CustomLayoutHandler()
已复制代码
// DownloadSubpackageFileFailedLayout.ets
@Builder
export function DownloadSubpackageFileFailedLayout(appId: string, apiServer: string, path: string,
  reload: () => void) {
  CustomViewComponent({
    appId,
    apiServer,
    path,
    reload
  })
}

@Component
struct CustomViewComponent {
  @Prop appId: string
  @Prop apiServer: string
  @Prop path: string
  reload?: () => void

  build() {
    Flex({
      alignItems: ItemAlign.Center,
      justifyContent: FlexAlign.Center,
      direction: FlexDirection.Column
    }) {
      Column() {
        Text('appId: ' + this.appId)
        Text('apiServer: ' + this.apiServer)
        Text('path: ' + this.path)
        Button('重新加载').onClick(() => {
          this.reload?.()
        })
      }
    }
    .backgroundColor(Color.White)
    .width('100%')
    .height('100%')
  }
}
已复制代码
// LoadFailedLayout.ets
@Builder
export function LoadFailedLayout(appId: string, apiServer: string, title: string, label: string) {
  CustomViewComponent({
    appId,
    apiServer,
    title,
    label
  })
}

@Component
struct CustomViewComponent {
  @Prop appId: string
  @Prop apiServer: string
  @Prop title: string
  @Prop label: string

  build() {
    Flex({
      alignItems: ItemAlign.Center,
      justifyContent: FlexAlign.Center,
      direction: FlexDirection.Column
    }) {
      Column() {
        Text('appId: ' + this.appId)
        Text('apiServer: ' + this.apiServer)
        Text('title: ' + this.title)
        Text('label: ' + this.label)
      }
    }
    .backgroundColor(Color.White)
    .width('100%')
    .height('100%')
  }
}
已复制代码
// LoadingLayout.ets
@Builder
export function PageCustomLoadingLayout(appId: string, apiServer: string, path: string) {
  CustomViewComponent({
    appId,
    apiServer,
    path,
  })
}

@Builder
export function CustomLoadingLayout() {
  CustomViewComponent()
}

@Component
struct CustomViewComponent {
  @Prop appId: string
  @Prop apiServer: string
  @Prop path: string

  build() {
    Flex({
      alignItems: ItemAlign.Center,
      justifyContent: FlexAlign.Center,
      direction: FlexDirection.Column
    }) {
      Column() {
        if (this.path) {
          Text('页面自定义 loading')
          Text('appId: ' + this.appId)
          Text('apiServer: ' + this.apiServer)
          Text('path: ' + this.path)
        } else {
          Text('自定义 loading 页')
        }
      }
    }
    .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM])
    .backgroundColor(this.path ? Color.Red : Color.Green)
    .width('100%')
    .height('100%')
  }
}
已复制代码
// CustomToast.ets
import { IFinAppProxy } from '@finclip/sdk';

@Builder
export function CustomToast() {
  CustomViewComponent()
}

@Component
struct CustomViewComponent {
  @Consume toastInfo: IFinAppProxy.ICustomToastLayoutParams

  build() {
    this.toastWithMask()
  }

  @Builder
  toastImage() {
    if (this.toastInfo.image) {
      Image(this.toastInfo.image)
        .width(40)
        .height(40)
    } else if (this.toastInfo.icon) {
      if (this.toastInfo.icon === 'loading') {
        Text('loading 图案')
      }
      if (this.toastInfo.icon === 'success') {
        Text('success 图案')
      }
      if (this.toastInfo.icon === 'error') {
        Text('error 图案')
      }
    }
  }

  @Builder
  toast() {
    if (this.toastInfo.icon === 'none') {
      Column() {
        Text(this.toastInfo.title)
          .fontSize(14)
          .fontColor('rgba(255, 255, 255, 0.9)')
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .maxLines(2)
          .textAlign(TextAlign.Center)
      }
      .constraintSize({
        maxWidth: 200,
        minHeight: 48
      })
      .padding({
        left: 12,
        right: 12,
        top: 14,
        bottom: 14
      })
      // 加一个占位空间,让垂直居中稍微靠上
      .margin({
        bottom: 120
      })
      .backgroundColor('rgba(0, 0, 0, 0.7)')
      .align(Alignment.Center)
      .justifyContent(FlexAlign.Center)
      .opacity(1)
      .borderRadius(10)
      .clip(true)
    } else {
      Column() {
        this.toastImage()
        Text(this.toastInfo.title)
          .fontSize(14)
          .fontColor('rgba(255, 255, 255, 0.9)')
          .margin({ top: 12 })
          .textOverflow({ overflow: TextOverflow.Ellipsis })
          .maxLines(2)
          .textAlign(TextAlign.Center)
      }
      .width(130)
      .height(130)
      .padding({
        left: 14,
        right: 14
      })
      // 加一个占位空间,让垂直居中稍微靠上
      .margin({
        bottom: 120
      })
      .backgroundColor('rgba(0, 0, 0, 0.7)')
      .align(Alignment.Center)
      .justifyContent(FlexAlign.Center)
      .opacity(1)
      .borderRadius(10)
      .clip(true)
    }
  }

  @Builder
  toastWithMask() {
    Flex({
      direction: FlexDirection.Column,
      justifyContent: FlexAlign.Center,
      alignItems: ItemAlign.Center
    }) {
      this.toast()
    }
    .width('100%')
    .height('100%')
    .backgroundColor('transparent')
    // 稍微小于 modal 和 actionsheet
    .zIndex(110)
    .position({
      top: 0,
      left: 0
    })
    .hitTestBehavior(this.toastInfo.mask ? HitTestMode.Default : HitTestMode.Transparent)
  }

}
已复制代码
// customOverlayLayout.ets

@Builder
export function CustomOverlayLayout(appId: string, apiServer: string) {
  Column() {
    Text('CustomLayout')
    Text(appId)
    Text(apiServer)
  }
}
已复制代码
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class CustomLayoutHandler {
  // 获取自定义 loading 页的布局方法
  getCustomLoadingLayout?: () => (() => void) | void
  // 自定义页面 loading 组件
  getPageCustomLoadingLayout?: () => ((appId: string, apiServer: string, path: string) => void) | void
  // 当小程序准备完成触发,该函数结束后销毁 loading 页
  onLoadingLayoutReady: () => Promise<void> = async () => {}
  // 当页面准备完成触发,该函数结束后销毁 loading 组件
  onPageLoadingLayoutReady: () => Promise<void> = async () => {}
  // 自定义分包下载错误页
  getDownloadSubpackageFileFailedLayout?: () => ((appId: string, apiServer: string, path: string, reload: () => void) => void) | void
   // 自定义加载错误页
  getLoadFailedLayout?: () => ((appId: string, apiServer: string, title: string, label: string) => void) | void
}
已复制代码

示例代码1(小程序 loading)

client.proxyHandlerManager.customLayoutHandler.getCustomLoadingLayout = () => customLoading
 
 // 如果小程序启动依赖某些异步数据才渲染,比如请求等,这时候小程序可能还是处于无数据的状态,可以通过延迟调用 resolve 的方式来延缓 loading 页销毁时机
client.proxyHandlerManager.customLayoutHandler.onLoadingLayoutReady = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
       resolve()
    }, 200)
   })
}
 
 
@Builder
function customLoading() {
  CustomViewComponent()
}

// 该组件会渲染在整个 loading 页面
@Component
struct CustomViewComponent {
  @Consume layoutParams: IFinAppProxy.ICustomLoadingLayoutParams

  build() {
    Column() {
      Image(this.layoutParams.logo)
      Text(this.layoutParams.name)
    }
  }
}
已复制代码

示例代码2(页面 loading)

client.proxyHandlerManager.customLayoutHandler.getPageCustomLoadingLayout = () => customLoading
 
 // 如果小程序启动依赖某些异步数据才渲染,比如请求等,这时候小程序页面可能还是处于无数据的状态,可以通过延迟调用 resolve 的方式来延缓 loading 页销毁时机
client.proxyHandlerManager.customLayoutHandler.onPageLoadingLayoutReady = () => {
  return new Promise((resolve) => {
    setTimeout(() => {
       resolve()
    }, 200)
   })
}
 
 // 这里会传入小程序的页面的信息,开发者可以根据 path 来自定义不同的 loading 组件
@Builder
function customLoading(appId: string, apiServer: string, path: string) {
  CustomViewComponent({ appId, apiServer, path })
}

@Component
struct CustomViewComponent {
  @Prop appId: string
  @Prop apiServer: string
  @Prop path: string

  build() {
    Flex({
      alignItems: ItemAlign.Center,
      justifyContent: FlexAlign.Center,
      direction: FlexDirection.Column
    }) {
      Column() {
        Text('path: ' + this.path)
      }
    }
    .backgroundColor(Color.White)
    .width('100%')
    .height('100%')
  }
}
已复制代码

示例代码3(自定义分包下载错误页)

client.proxyHandlerManager.customLayoutHandler.getDownloadSubpackageFileFailedLayout = () => customLayout
 
 // 这里会传入小程序的页面的信息
@Builder
function customLayout(appId: string, apiServer: string, path: string, reload: () => void) {
  CustomViewComponent({ appId, apiServer, path, reload })
}

@Component
struct CustomViewComponent {
  @Prop appId: string
  @Prop apiServer: string
  @Prop path: string
  reload:() => void // 重新加载
  
  build() {
    Flex({
      alignItems: ItemAlign.Center,
      justifyContent: FlexAlign.Center,
      direction: FlexDirection.Column
    }) {
      Column() {
        Text('path: ' + this.path)
      }.onClick(()=>{
          this.reload?.()
      })
    }
    .backgroundColor(Color.White)
    .width('100%')
    .height('100%')
  }
}
已复制代码

示例代码4(自定义加载错误页)

client.proxyHandlerManager.customLayoutHandler.getLoadFailedLayout = () => customLayout
 
 // 这里会传入小程序的页面的信息
@Builder
function customLayout(appId: string, apiServer: string, title: string, label: string) {
  CustomViewComponent({ appId,
    apiServer,
    title,
    label
  })
}

@Component
struct CustomViewComponent {
  @Prop appId: string
  @Prop apiServer: string
  @Prop title: string
  @Prop label: string

  build() {
    Flex({
      alignItems: ItemAlign.Center,
      justifyContent: FlexAlign.Center,
      direction: FlexDirection.Column
    }) {
      Column() {
        Text('appletName: ' + this.title)
        Text('label: ' + this.label)
        Text('apiServer: ' + this.apiServer)
        Text('appId: ' + this.appId)
      }
    }
    .backgroundColor(Color.Green)
  }
}
已复制代码

IFinAppProxy.ICustomLoadingLayoutParams

属性 类型 描述
name String 小程序名称,当未获取到小程序详情时,默认为空
logo ResourceStr 小程序图标,当未获取到小程序详情或者小程序详情中的小程序图标字段为空会返回一个默认的图标,开发者可以自行选择是否使用
appId String 小程序 appId,当未获取到小程序详情时,默认为空
apiServer String 小程序 apiServer,当未获取到小程序详情时,默认为空

# 8.ButtonOpenTypeHandler

小程序中的 button 有一系列的open-type事件,有部分行为需要App来实现,所以也会通过代理方法来触发对应的事件

// namespace IFinProxyHandlerItem
export class ButtonOpenTypeHandler {
    /**
     * 调用获取用户信息的Api(getUserProfile) 时,会触发该代理方法。
     * @param appId 小程序 id
     * @param apiServer 小程序 apiServer
     * @returns Promise<IFinApplet.IUserProfile | void>
     *
     */
    public async getUserProfile(appId: string, apiServer: string): Promise<IFinApplet.IUserProfile | void>

    /**
     * 调用获取用户信息API(getUserInfo) 或者 点击open-type 属性为 getUserInfo 的 Button 时触发该代理事件
     * @param appId 小程序 id
     * @param apiServer 小程序 apiServer
     * @returns Promise<IFinApplet.IUserInfo | void>
     *
     */
    public async getUserInfo(appId: string, apiServer: string): Promise<IFinApplet.IUserInfo | void>
  }
已复制代码

示例代码

import { FinAppProxyHandlerManager, IFinApplet, IFinProxyHandlerItem } from '@finclip/sdk'

class ButtonOpenTypeHandler extends IFinProxyHandlerItem.ButtonOpenTypeHandler {
  async getUserProfile(appId: string, apiServer: string): Promise<IFinApplet.IUserProfile | void> {
    return {
      nickName: "昵称",
      avatarUrl: "头像地址",
      gender: "性别",
      province: "省份",
      city: "城市",
      country: "国家",
    }
  }

  async getUserInfo(appId: string, apiServer: string): Promise<IFinApplet.IUserInfo | void> {
    return {
      nickName: "昵称",
      avatarUrl: "头像地址",
      gender: "性别",
      province: "省份",
      city: "城市",
      country: "国家",
    }
  }
}

FinAppProxyHandlerManager.buttonOpenTypeHandler = new ButtonOpenTypeHandler()
已复制代码
1.1.1 之前的实现方式,1.1.1 之后不推荐使用
class ButtonOpenTypeHandler {
  /**  
 * 调用获取用户信息API(getUserInfo) 或者 点击open-type属性为getUserInfo的Button时触发该代理事件
 * @param appId 小程序 id  
 * @param apiServer 小程序 apiServer
 * @returns Promise<IFinApplet.IUserInfo | void>
 * 
 */
  getUserInfo?: (appId: string, apiServer: string) => Promise<IFinApplet.IUserInfo | void>
  /**  
 * 调用获取用户信息的Api(getUserProfile) 时,会触发该代理方法。
 * @param appId 小程序 id  
 * @param apiServer 小程序 apiServer
 * @returns Promise<IFinApplet.IUserProfile | void>
 * 
 */
  getUserProfile?: (appId: string, apiServer: string) => Promise<IFinApplet.IUserProfile | void>
}
已复制代码

示例代码:

client.proxyHandlerManager.buttonOpenTypeHandler.getUserInfo = async ( appId: string, apiServer: string) => {
  return  {
    nickName: "昵称",
    avatarUrl: "头像地址",
    gender: "性别",
    province: "省份",
    city:"城市",
    country: "国家",
 }
}

client.proxyHandlerManager.buttonOpenTypeHandler.getUserProfile = async ( appId: string, apiServer: string) => {  
  return  {
    nickName: "昵称",
    avatarUrl: "头像地址",
    gender: "性别",
    province: "省份",
    city:"城市",
    country: "国家",
 }
}
已复制代码

IFinApplet.IUserInfo

属性 类型
nickName String
avatarUrl String
gender String
province String
city String
country String

IFinApplet.IUserProfile

type IUserInfo =  string | number | boolean | null | Serializable[] | { [key: string]: Serializable }
已复制代码

# 9.onAppBackPressHandler

侧滑代理

// namespace IFinProxyHandlerItem
export class onAppBackPressHandler {
  /**
   * 当用户侧滑时触发
   * @param appId 小程序 id
   * @param apiServer 小程序 apiServer
   * @returns 返回 true 表示宿主自行处理,返回 false 表示宿主处理完,仍执行后续的默认操作
   */
  public onBackPress(appId: string, apiServer: string): boolean | void
}
已复制代码

示例代码

import { FinAppProxyHandlerManager, IFinProxyHandlerItem } from '@finclip/sdk'
import { promptAction } from '@kit.ArkUI'

class onAppBackPressHandler extends IFinProxyHandlerItem.onAppBackPressHandler {
  onBackPress(appId: string, apiServer: string) {
    // 仅对小程序生效
    // 返回 true 表示宿主自行处理
    // 返回 false 表示宿主处理完,仍执行后续的默认操作
    this.promptMsg(`触发了侧滑代理`)
    return false
  }

  private promptMsg(message: string) {
    promptAction.showToast({
      message
    })
  }
}


FinAppProxyHandlerManager.onAppBackPressHandler = new onAppBackPressHandler()
已复制代码

# 10. LogHandler

日志的代理类

// namespace IFinProxyHandlerItem
export class LogHandler {
  /**
   * 是否开启控制台输出,默认 false
   */
  public isConsoleLog: boolean = false;
  /**
   * 是否写入文件,默认 true
   */
  public writeFile: boolean = true;
  /**
   * 设置日志文件保留时间,单位为秒(默认10天,至少为1天)
   */
  public logFileAliveDuration: number = 60 * 60 * 24 * 10
  /**
   * 日志输出的完整的沙箱路径
   * 默认路径 /data/app/el2/100/base/包名/haps/entry/files/Applet/logs
   */
  public logDir?: string

  /**
   * 触发 log 时会执行该方法,如果想自行处理日志文件,可以将 writeFile 设置为 false,然后在该方法处理日志内容
   */
  public onLog(level: IFinAppConfig.ILogLevel, log: string)
}
已复制代码

示例代码

import { FinAppProxyHandlerManager, IFinAppConfig, IFinProxyHandlerItem } from '@finclip/sdk';

class LogHandler extends IFinProxyHandlerItem.LogHandler {
  isConsoleLog: boolean = true;
  writeFile: boolean = true;

  onLog(level: IFinAppConfig.ILogLevel, log: string): void {
    console.log(`level:${level},log:${log}`)
  }
}

FinAppProxyHandlerManager.logHandler = new LogHandler()
已复制代码
© FinClip with ❤ , Since 2017

❤️Support for FinClip

We need your support, please click the button below to help us add Github star