# `qianuni` UniApp web-view 接入指南

> **本指南是纯文档接入,不需要任何原生插件,不需要引用本仓库代码**。
> 客户只要在 UniApp 项目里用 `<web-view>` 组件加载电子签 H5 URL,并准备好签署完成后要跳转的目标页面即可。

## 0. 与原生 SDK 的关键差异:UniApp 只用 `qianuni://`,不用 `qianapp://`

| 场景 | 使用协议 | 上报方式 | 谁负责跳转 |
|---|---|---|---|
| 原生 SDK(Android / iOS / HarmonyOS) | `qianapp://` | SDK 拦截 URL,解析出 `SignResult` 回调给客户 | 客户在回调里自行处理 |
| **UniApp `<web-view>`** | **`qianuni://`** | **无 `postMessage`**,由 **UniApp 自带 JSBridge** 直接执行页面跳转 | **UniApp web-view 内置桥接自动完成** |

> ⚠️ **UniApp 场景不使用 `qianapp://` 协议,也不使用 `uni.postMessage`**。电子签 H5 在 UniApp 的 `<web-view>` 里通过 `qianuni://` 协议,直接调用 UniApp 自带的路由 JSBridge API 完成页面跳转,客户**无需**编写 `@message` / `onMessage` 监听。

## 1. 工作原理

```mermaid
sequenceDiagram
    participant U as 客户 UniApp 页面
    participant WV as <web-view>
    participant H5 as 电子签 H5 (qian.tencent.cn)
    participant JB as UniApp 自带 JSBridge

    U->>WV: 加载 H5 签署 URL(含 flowId)
    WV->>H5: HTTPS GET
    H5-->>WV: 渲染签署页
    Note over H5: 用户完成签署 / 填写 / 拒签

    H5->>JB: 跳转协议 qianuni://{"method":"reLaunch","payload":{...},"appendResult":"qian"}
    JB->>U: 直接执行 uni.reLaunch / navigateTo 等路由 API
    Note over U: 跳转到客户目标页(url 上已附加签署结果 query)
    U->>U: 目标页 onLoad(options) 读取 flowId / action / result / from
```

## 2. `qianuni://` 协议格式

JumpUrl 格式:`qianuni://{YOUR_JSON_DATA}`,需满足 **`qianuni://` 开头**,且后面的 JSON 满足以下 `QianUniRouteType` 格式:

```ts
type QianUniRouteType = {
  method: 'navigateTo' | 'navigateBack' | 'switchTab' | 'reLaunch' | 'redirectTo',
  payload: Object,
  appendResult?: 'qian', // 可选字段
}
```

示例:

```
qianuni://{"method":"reLaunch","payload":{"url":"/pages/index/result?foo=bar"},"appendResult":"qian"}
```

### 2.1 字段说明

| 字段 | 类型 | 必填 | 说明 |
|---|---|:---:|---|
| `method` | String | 是 | 路由方法,对应 UniApp 路由 API:`navigateTo` / `navigateBack` / `switchTab` / `reLaunch` / `redirectTo` |
| `payload` | Object | 是 | 路由参数,格式参考对应 UniApp 路由函数的参数(见 §2.2) |
| `appendResult` | `'qian'` | 否 | 仅当值为 `'qian'` 时,在 `payload.url` 的 query 上附加签署结果参数(见 §3) |

### 2.2 `payload` 数据格式

`payload` 的数据格式,直接参考 UniApp 路由相关几个函数的参数:

| `method` | 对应 UniApp API | `payload` 参考 |
|---|---|---|
| `navigateTo` | `uni.navigateTo` | `{ url }` |
| `navigateBack` | `uni.navigateBack` | `{ delta }` |
| `switchTab` | `uni.switchTab` | `{ url }` |
| `reLaunch` | `uni.reLaunch` | `{ url }` |
| `redirectTo` | `uni.redirectTo` | `{ url }` |

> 详见 UniApp 官方路由 API 文档,`payload` 与这些函数的入参对象一一对应。

## 3. `appendResult` 与返回应用数据

仅当 `appendResult` 等于 `'qian'` 时,会在 `payload.url` 的 query 参数中**附加签署成功状态参数**,附加的字段格式参考 [`qianapp://` 协议规范 §2 字段集合](./qianapp-protocol.md#2-字段集合)(`flowId` / `action` / `result` / `from` 等)。

例如,`payload.url` 为 `/pages/index/result?foo=bar`,附加后:

```
/pages/index/result?foo=bar&flowId=xxx&action=sign&result=success&from=tencent_ess
```

否则(未设置 `appendResult` 或值不为 `'qian'`),直接使用原始的 `payload.url` 完成页面跳转,不做任何附加。

| 字段 | 含义 | 已知值 |
|---|---|---|
| `flowId` | 流程 ID | —— |
| `action` | 动作类型 | `fill` / `sign` / `reject_fill` / `reject_sign` / `view` |
| `result` | 结果状态 | `success` / `fail` |
| `from` | 来源标识 | 固定 `tencent_ess` |

> 完整字段语义、已知值、边界情形与原生 SDK 完全一致,详见 [`qianapp-protocol.md`](./qianapp-protocol.md)。

## 4. 路由路径:绝对路径 vs 相对路径

跳转路由有**绝对路径**和**相对路径**之分:

- **带 `/` 开头**的路由,表示**绝对路径**,如 `/pages/index/result`。
- **不带 `/` 开头**的路由,表示**相对路径**,如 `result`。

> ✅ **推荐统一使用 `/` 开头的绝对路径**,可避免出现找不到页面的情况。

## 5. UniApp 接入示例

客户只需两步:①用 `<web-view>` 加载签署 H5;②准备好签署完成后要跳转的目标页,在其 `onLoad` 里读取结果 query。**无需编写任何 `@message` 监听。**

### 5.1 签署页 `pages/sign/sign.vue`

```vue
<template>
  <view class="container">
    <!-- 无需绑定 @message,跳转由 qianuni:// + UniApp 自带 JSBridge 完成 -->
    <web-view :src="signUrl" />
  </view>
</template>

<script>
export default {
  data() {
    return {
      signUrl: ''
    }
  },
  onLoad(options) {
    // 客户后端发起签署,返回 flowId / 签署 H5 URL
    this.signUrl = options.signUrl
  }
}
</script>
```

### 5.2 结果页 `pages/index/result.vue`

当电子签 H5 发出形如
`qianuni://{"method":"reLaunch","payload":{"url":"/pages/index/result?foo=bar"},"appendResult":"qian"}`
的协议时,UniApp 自带 JSBridge 会直接跳转到该页,并在 query 上附加签署结果。目标页在 `onLoad` 读取即可:

```vue
<script>
export default {
  onLoad(options) {
    // options 已包含原始 query(foo)+ 附加的签署结果
    // options.flowId / options.action / options.result / options.from
    const { flowId, action, result, from } = options
    if (from !== 'tencent_ess') return // 校验来源

    // ⚠️ 前端 query 里的 action / result 仅用于 UI 即时反馈,
    //    受时序、异步回调、页面刷新等影响,不保证是签署流程的实时/最终状态。
    //    最终状态必须以 flowId 为键,由业务后端向电子签服务端查询确认后为准。
    switch (action) {
      case 'sign':
        result === 'success'
          ? uni.showToast({ title: '签署成功' })
          : uni.showToast({ title: '签署失败', icon: 'none' })
        break
      case 'reject_sign':
        uni.showToast({ title: '已拒签' })
        break
      // ⚠️ 务必加 default 分支兜底未来新增的 action
      default:
        console.warn('未知 action', action)
    }

    // ✅ 关键:以 flowId 请求业务后端 → 后端查询电子签服务端拿权威状态,
    //    前端 UI 提示只是「即时信号」,一切以后台校验结果为最终依据。
    // const authoritative = await this.$api.queryFlowStatus(flowId)
    // if (authoritative.status === 'signed') { /* 落库 / 跳转下一步 */ }
  }
}
</script>
```

> ⚠️ **务必进行后台数据校验**:`action` / `result` 是前端 URL query 透传的**即时信号**,受时序、异步回调、页面刷新等影响,**不保证是签署流程的实时/最终状态**。请以 `flowId` 为键,由业务后端向电子签服务端查询流程详情后拿到的状态为最终依据;前端结果仅用于 UI 即时反馈与引导。

## 6. 注意事项

- **不要监听 `@message`**:UniApp 场景由 `qianuni://` + 自带 JSBridge 直接完成路由,不走 `postMessage`;客户只需在目标页 `onLoad` 读取 query。
- **推荐绝对路径**:`payload.url` 统一用 `/` 开头,避免相对路径找不到页面。
- **前端结果非最终状态**:`action` / `result` 仅作 UI 即时提示,不保证实时性;最终签署状态**必须**由业务后端以 `flowId` 向电子签服务端查询确认。
- **`action` / `result` 未知值**:与原生 SDK 一致,未来可能新增,请务必加 `default` 分支兜底。
- **本地调试**:UniApp HBuilderX 真机调试时,`<web-view>` 行为与原生 WebView 一致,可直接验证 `qianuni://` 跳转。

## 7. 自检页

UniApp 客户可以直接加载兼容性自检页:

```vue
<web-view src="https://quick.beta.qian.tencent.cn/compatibilityTest" />
```

> 电子签默认指向 beta 环境,兼容性测试在 beta 验证即可,无需切换生产域名。
