## magic-applet

`magic-applet`是用于统一特斯联微前端移动端应用在小程序和在 FutureAPP 中的 API 。

`magic-applet`是基于`uni-app`，使用`typescript`编写，编译为`ESM`规范的 JS 产物的 npm 包，支持在 js 或者 ts 项目中使用。

推荐在 typescript 项目中使用，可以获得更加友好的代码提示。

**项目基于`compositionAPI`编写，请保证你的 vue 版本大于等于`2.7`！**

**项目基于`compositionAPI`编写，请保证你的 vue 版本大于等于`2.7`！**

**项目基于`compositionAPI`编写，请保证你的 vue 版本大于等于`2.7`！**

## 背景

移动端产品有同时在微信小程序和 FutureApp 上架的需求。

而传统的小程序开发发版较为麻烦，因此采用在小程序（主应用）中通过`web-view`嵌入`h5`的子应用，在 FutureApp 集成`uni-app SDK`，嵌入`app-plus`子应用包的微前端的方式。

uni-app 虽然统一了大多数的 API，但`h5`和`app-plus`中依然有部分 API 无法统一，例如`h5`无法直接调用扫码 API，需要通知主应用的小程序去扫码，然后将结果返回，如此一来，我们就需要编写大量的条件编译来达到一套代码，多端运行的效果。`magic-applet`由此而生。

`magic-applet`中，封装了`h5`和`app-plus`有差异的这部分 API 的条件编译，力求达到一个 API，搞定两个应用环境。

## 使用

### 安装

```shell
pnpm / npm install @tslsmart/magic-applet
# 或者 yarn add @tslsmart/magic-applet
```

### 初始化

需要在`App.vue`中初始化：

```js
// App.vue中
import '@tslsmart/magic-applet'
```

初始化时会将从主应用（小程序端或者 FutureApp 端）获取到的参数存到`localStorage`中，具体细节无需关注，相关 API 详见后文。

## API

### 参数类

#### magic.initParams 初始化

用于持久化保存父应用传递过来的参数，使用`import '@tslsmart/magic-applet'`导入时已自动执行

也可以手动调用，可以在`then`中进行其他操作：

```js
// 业务代码中
import magic from '@tslsmart/magic-applet'
magic.initParams().then((res) => {
  console.log(res)
})
```

#### magic.getAuthInfo 获取授权信息

一般是在封装`axios`，将授权信息添加到`header`中时使用：

```js
// request.js中
import magic from '@tslsmart/magic-applet'

const { authCtxCode, authorization, projectCode } = magic.getAuthInfo()

// 省略其他代码...
if (config.headers) {
  config.headers = {
    ...config.headers,
    authorization,
    projectCode,
    authCtxCode
  }
}
// 省略其他代码...
```

#### magic.getParams 获取所有参数

用于获取从父应用传入的所有参数

```js
// 业务代码中
import magic from '@tslsmart/magic-applet'

const params = magic.getParams()
```

#### magic.getParamsByKey 获取指定参数

用于获取指定的、从父应用传入的所有参数

```js
// 业务代码中
import magic from '@tslsmart/magic-applet'

const params = magic.getParamsByKey('authorization')
```

### 操作类

#### magic.toScan

在 App 端，会直接调用摄像机进行扫码，但在 h5 端，会跳转到主应用界面，然后调用摄像机进行扫码。

因此，我们将扫码的交互统一为：

1. 调用`magic.toScan`进行扫码
2. 在新的页面处理扫码结果

**这个新的页面也可以是发起扫码的页面，但是会丢失之前进入该页面时的`query`参数，这种方式建议使用其他方式进行页面传参**

`magic.toScan`的参数是一个`ScanParams`对象，其中`ScanParams.targetUrl`为必填参数，是扫码之后需要跳转到的页面路由，例如：

```js
import magic from '@tslsmart/magic-applet'
magic.toScan({
  targetUrl: '/pages/result/index',
  fail(err) {
    console.log(err)
  }
})
```

`ScanParams`参数的 ts 类型定义:

```typescript
export type ScanParams = {
  targetUrl: string // 扫码之后需要跳转到的页面路由，必填项
  fail?: CallBack // 调用失败的回调函数，非必填
}
```

#### magic.getScanRes

用于获取扫码的结果，一般是一个字符串，`query`字符串或者`JSON`字符串或者其他字符串，视具体项目而定，需要自行处理

```js
import magic from '@tslsmart/magic-applet'

const scanRes = magic.getScanRes()

// 处理json字符串
const resObj = JSON.parse(scanRes)

// 处理query字符串
import qs from 'qs'
const objRes = qs.parse(scanRes)
```

#### magic.navigateTo

用于跳转到其他子应用。
例如，在会议室相关需求中，预约功能是一个子应用，预约记录是另外一个子应用，预约成功后，可能需要跳转到预约记录中，就会用到这个 API

```js
import magic from '@tslsmart/magic-applet'
magic.navigateTo({
  appid: 'meeting_record_id',
  targetUrl: '/pages/record/index'
})
```

其参数类型定义如下：

```typescript
export type NavigateParams = {
  appid: string // 要跳转的目标应用的appid
  targetUrl: string // 要跳转到目标应用的具体页面，默认是'pages/index/index'
}
```

#### magic.closeApplet---`v1.1`及以上版本

关闭子应用本身。
有时候可能需要通过代码的时候退出子应用，可以使用此API:
```js
import magic from '@tslsmart/magic-applet'

magic.closeApplet()
```


#### magic.openDoor---`v1.2`及以上版本

通过蓝牙发起开门请求。
有时候可能需要通过代码的时候退出子应用，可以使用此API:
```js
import magic from '@tslsmart/magic-applet'

magic.openDoor({
  info: {
    openType: 'remote', // 开门方式,蓝牙:bluetooth 远程开门：remote
    deviceName: '测试门禁1号', // 设备名
    // 蓝牙开门必传参数：
    mac: '11:11:11:11', // 设备MAC地址
    password: '123456', // 设备密码
    // 远程开门必传参数：
    deviceId: '112233', // 设备id
    productId: string // 产品id
  },
  targetUrl: 'pages/result/index' // 开门成功后的跳转地址，一般是回到钥匙列表页
})
```


#### magic.action

其他操作，可能存在尚未被封装的其他的、需要父应用进行的操作，可以使用此 API 请求父应用进行操作：

```typescript
import magic from '@tslsmart/magic-applet'
magic.action({
  actionType: 'scan' // 需要宿主进行的操作，例如扫码、跳转、或者授权
  appid: 'appid' // 跳转时，必须传入的目标应用的appid
  targetUrl: 'pages/path/index' // 宿主执行操作后，要跳转的url，例如跳转到扫码结果页
}).then(res => {
  console.log('操作成功了')
}).catch(err => {
  console.log('操作失败了')
})
```

其参数类型定义如下：

```typescript
export type ActionType = 'scan' | 'navigate' | '/Terminus/Future/Auth/Req' | any

export interface ActionParams {
  actionType: ActionType // 需要宿主进行的操作，例如扫码、跳转、或者授权
  appid?: string // 跳转时，必须传入的目标应用的appid
  targetUrl: string // 宿主执行操作后，要跳转的url，例如跳转到扫码结果页
}
```

实际上，我们的`magic.toScan`和`magic.navigateTo`等都是基于这个 API 进行二次封装的

## bug/需求提交

当前只封装了以上 API，如果有bug或者其他的需求，可以从`GitLab`或者`禅道`中提交，推荐直接在`GitLab`中提交

### GitLab 提交

打开[仓库地址](http://192.168.10.99/Enjoy-Web/magic-applet) => 点击右上角的➕号，新建议题
![](http://oss.stally.cn/202212271804421.png)

填好问题/需求描述后点击提交

[bug模板](http://192.168.10.99/Enjoy-Web/magic-applet/issues/1)

[需求模板](http://192.168.10.99/Enjoy-Web/magic-applet/issues/2)

![](http://oss.stally.cn/202212271820851.png)

### 禅道 提交

![](http://oss.stally.cn/20221227153059.png)
![](http://oss.stally.cn/20221227153205.png)

## 代码贡献

请不要直接在本仓库提交代码，先`fork(派生)`一个仓库后，提交代码，然后发起`PR（合并请求）`。

### 派生仓库

打开[仓库地址](http://192.168.10.99/Enjoy-Web/magic-applet) => 点击左上角的派生按钮 => 选择命名空间 =》拉取并修改代码
![](http://oss.stally.cn/202212271829753.png)
![](http://oss.stally.cn/202212271830441.png)
### 创建合并

提交代码后，打开自己派生的仓库，点击右上角的➕号，新建合并请求
![](http://oss.stally.cn/202212271833214.png)

填写相关信息，以及对应的`issue(议题)`后提交即可

![](http://oss.stally.cn/202212271835459.png)