# @qzsy/yunxiao-reporter 中文文档

规范地址：https://npmcn.com/package/%40qzsy/yunxiao-reporter/

版本：0\.4\.0
npm 元数据更新时间：2026-11-02T04:49:24.191Z
中文译文更新时间：2026-11-02T06:43:29.780Z
软件包许可：未声明

本文由上游 README 机器翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40qzsy/yunxiao-reporter
原文对照：https://npmcn.com/package/%40qzsy/yunxiao-reporter/?lang=original

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：385 kB，45 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- 运行依赖：1 个
- peer 依赖：vue ^3\.3\.0，react ^19\.3\.1
- 首次发布：2026-10-28T10:14:53.156Z
- 最新版本发布：2026-11-02T04:49:23.980Z
- 已发布版本数：7（近 12 个月 7 个）

## 中文简介

云效问题上报浏览器 SDK：悬浮按钮、截图标注、console 采集，提交到云效 Bug 平台

## 安装

```sh
npm install @qzsy/yunxiao-reporter
```

也可以使用：`pnpm add @qzsy/yunxiao-reporter`、`yarn add @qzsy/yunxiao-reporter`、`bun add @qzsy/yunxiao-reporter`

## README

# @qzsy/yunxiao-reporter

浏览器端问题上报 SDK。终端用户点悬浮按钮（或宿主调用 `open()`）后，SDK 截取当前页面，在标注面板里画框、箭头、写文字，填写标题和描述后提交。平台收到后在绑定的云效项目里创建 Bug 工作项。

-   UI 用 Preact 实现并打包进产物，渲染在 `<div data-yunxiao-reporter>` 的 Shadow DOM 里，宿主样式和 SDK 样式互不影响。宿主不需要安装 Preact。
-   悬浮按钮支持拖动：位置按项目 Key 存在当前浏览器的 `localStorage`，刷新和重开页面都保留，不影响其他访客；平台配置的位置只作为默认值。
-   截图默认走「系统截屏优先、DOM 快照兜底」：Chromium 桌面端截当前标签页视口（用户允许一次），取消或不可用时自动回退到 snapdom 整页快照。详见「截图方式」。
-   截图用 `@zumer/snapdom`，按需动态加载，截图时排除 SDK 自身。
-   自动附带页面 URL、浏览器、屏幕和视口尺寸、语言、时区，以及最近 50 条 `console.error` / `console.warn` / 未捕获异常（提交前脱敏，浏览器扩展注入的日志不采集）。
-   模块顶层不访问 `window` / `document`，可以在 SSR 代码里 import。
-   可选适配层：`./react`（Provider + Hook）与 `./vue`（插件 + Provider + composable），按宿主框架选装；`./version` 只导出当前插件版本。

## 安装

```bash
bun add @qzsy/yunxiao-reporter
# 或 npm i @qzsy/yunxiao-reporter
```

## 初始化

```ts
import { init } from '@qzsy/yunxiao-reporter'

const reporter = init({ key: 'yxr_pk_xxxxxxxx', endpoint: 'https://bug.example.com' })
```

右下角会出现悬浮按钮。按住按钮可以拖动位置，单击打开标注面板。自定义入口时：

```ts
reporter.open()                                   // 截图并打开标注面板
reporter.close()                                  // 关闭面板
reporter.setUser({ id: 'u_1', name: '张三' })      // 登录态变化时更新上报人
reporter.destroy()                                // 卸载 UI，还原 console，移除监听
```

同一页面重复调用 `init` 时，旧实例会先被 `destroy`。

## 版本检测

平台在 `/config` 里下发它当前支持的插件版本（`sdkVersion`）。宿主内嵌的插件版本与它不一致时，标注面板右上角会提示「请联系维护人员升级插件版本」；版本一致、或平台没有下发（老平台）时不提示。

-   只在面板打开时展示，不影响悬浮按钮、权限判断和提交。
-   每条上报都会带 `sdkVersion`，云效工作项描述里以「插件版本」列出。
-   平台侧可直接读 `@qzsy/yunxiao-reporter/version` 的 `version` 作为期望版本；SDK 内部也用它做比较与上报。

## Vue

`vue >= 3.3` 是可选 peerDependency，只有用 `./vue` 入口时才需要。

### 插件（推荐）

```ts
// main.ts
import { createApp } from 'vue'
import { createYunxiaoReporterPlugin } from '@qzsy/yunxiao-reporter/vue'
import App from './App.vue'

createApp(App)
  .use(
    createYunxiaoReporterPlugin({
      publicKey: 'yxr_pk_xxxxxxxx',
      endpoint: 'https://bug.example.com',
    }),
  )
  .mount('#app')
```

组件里打开面板、同步登录人：

```vue
<script setup lang="ts">
import { useReporterUser, useYunxiaoReporter } from '@qzsy/yunxiao-reporter/vue'

const { open, ready } = useYunxiaoReporter()
// me 是 ref / computed / getter，按 id、name、email、phone 内容比较，变化时才 setUser
useReporterUser(me)
</script>

<template>
  <button :disabled="!ready" type="button" @click="open">反馈问题</button>
</template>
```

-   插件在 `app.use()` 时 `init`，`app.unmount()` 时 `destroy`（Vue 3.5+ 用 `app.onUnmount`，更早版本退化为包一层 `unmount`）。
-   `useYunxiaoReporter()` 返回 `{ ready, open, close, setUser }`；不在 Provider / 插件下调用时返回 no-op，不抛错。
-   `useReporterUser(source)` 在实例还没创建时先缓存，创建时补上。

### Provider 组件

不想装插件、或需要按组件树挂载时：

```vue
<script setup lang="ts">
import { YunxiaoReporterProvider } from '@qzsy/yunxiao-reporter/vue'
</script>

<template>
  <YunxiaoReporterProvider public-key="yxr_pk_xxxxxxxx" endpoint="https://bug.example.com" :user="me">
    <App />
  </YunxiaoReporterProvider>
</template>
```

-   Provider 本身不渲染 DOM：`mounted` 时 `init`，`unmounted` 时 `destroy`。
-   选项与 `init` 相同，Key 用 `publicKey`（`key` 是 Vue 的保留属性名）。
-   `user` / `branding` 按内容比较：`user` 变化只 `setUser`，`branding` 变化重建实例。
-   `enabled=false` 时不初始化，只渲染插槽。

### Nuxt 3 / 4

放客户端插件，SSR 阶段不会执行（Nuxt 3 在 `plugins/`，Nuxt 4 在 `app/plugins/`）：

```ts
// Nuxt 3：plugins/yunxiao-reporter.client.ts
// Nuxt 4：app/plugins/yunxiao-reporter.client.ts
import { createYunxiaoReporterPlugin } from '@qzsy/yunxiao-reporter/vue'

export default defineNuxtPlugin((nuxtApp) => {
  nuxtApp.vueApp.use(
    createYunxiaoReporterPlugin({
      publicKey: 'yxr_pk_xxxxxxxx',
      endpoint: 'https://bug.example.com',
    }),
  )
})
```

Vue 的 runtime-dom 在模块加载时读取 `document`，所以不要把 `createApp().mount()` 放在服务端进程；SDK 入口本身可以在服务端 import（模块顶层不访问 window）。

## React

`react` 是可选 peerDependency（>=18），只有用 `./react` 入口时才需要。

```tsx
import { YunxiaoReporterProvider, useYunxiaoReporter } from '@qzsy/yunxiao-reporter/react'

export function App({ children, me }) {
  return (
    <YunxiaoReporterProvider publicKey="yxr_pk_xxxxxxxx" endpoint="https://bug.example.com" user={me}>
      {children}
    </YunxiaoReporterProvider>
  )
}

function FeedbackLink() {
  const { open, ready } = useYunxiaoReporter()
  return (
    <button type="button" disabled={!ready} onClick={() => open()}>
      反馈问题
    </button>
  )
}
```

-   Key 通过 `publicKey` 传入。`key` 是 React 的保留属性，组件拿不到。其余 props 与 `init` 的选项相同。
-   Provider 在 `useEffect` 里 `init`，卸载时 `destroy`，兼容 StrictMode 的 init → destroy → init。
-   `user` 变化时调用 `setUser`，不会重建实例。
-   `enabled={false}` 时不初始化，只渲染 children。
-   Provider 本身不渲染任何 DOM。

## 选项

| 选项 | 类型 | 默认值 | 说明 |
| --- | --- | --- | --- |
| `key` | `string` | 必填 | 项目公开 Key，`yxr_pk_` 开头。React / Vue 里写作 `publicKey` |
| `endpoint` | `string` | 必填 | 平台根地址，如 `https://bug.example.com`，末尾 `/` 会去掉。必须是 http(s) 绝对地址 |
| `user` | `{ id?, name?, email? } | null` | — | 上报人，只作展示，平台标注「宿主提供，未验证」 |
| `fab` | `boolean` | `true` | 是否显示悬浮按钮 |
| `position` | `'bottom-right' | 'bottom-left' | 'top-right' | 'top-left'` | `'bottom-right'` | 悬浮按钮位置（别名 `fabPosition`） |
| `shortcut` | `string` | 不绑定 | 打开面板的快捷键，如 `'ctrl+shift+b'` |
| `zIndex` | `number` | `2147483000` | 宿主元素层级 |
| `captureConsole` | `boolean` | `true` | 是否采集 console.error / warn、`error`、`unhandledrejection`（浏览器扩展注入的日志始终丢弃） |
| `captureMode` | `'auto' | 'dom' | 'screen'` | `'auto'` | 截图方式，见下方「截图方式」 |
| `onSubmitted` | `({ reportId }) => void` | — | 提交成功，`reportId` 即受理号 |
| `onError` | `(error: ReporterError) => void` | — | 提交失败、截图失败或截图上传被跳过时调用 |

截图失败或截图上传失败时，仍然可以不带截图提交，并通过 `onError` 告知宿主。

## 截图方式

浏览器没有「无授权的页面截图」API，能截真实画面的只有 Screen Capture API（`getDisplayMedia`）。它每次调用都要用户点一次允许，而且只能截当前视口，所以 SDK 把它做成了增强路径，失败时始终回退到原来的 DOM 快照：

| `captureMode` | 行为 |
| --- | --- |
| `auto`（默认） | Chromium 桌面端（Chrome / Edge 等）优先弹「允许共享此标签页」，截当前视口；取消、拒绝、iframe 未授权或超时都会静默改用 DOM 快照。Safari / Firefox / 移动端直接用 DOM 快照，避免弹出源选择器 |
| `dom` | 始终 DOM 快照：静默、截整页，不弹授权。对一键上报体验最友好 |
| `screen` | 强制尝试系统截屏（所有支持 `getDisplayMedia` 的浏览器，Safari / Firefox 会弹源选择），失败回退 DOM |

DOM 快照用 snapdom 克隆页面，截的是完整可滚动页面；系统截屏截的是当前视口，但能拿到 canvas / WebGL / 视频 / 跨域 iframe 的真实像素。宿主在跨域 iframe 里接入时，需要 `allow="display-capture"` 才可能用上系统截屏，否则自动回退。

## 错误码

`ReporterError` 带 `code` 和可选的 HTTP `status`。面板里展示的中文文案如下：

| code | HTTP | 文案 |
| --- | --- | --- |
| `KEY_INVALID` | 401 | Key 无效 |
| `KEY_EXPIRED` | 401 | Key 已过期 |
| `ORIGIN_NOT_ALLOWED` | 403 | 当前域名未加入白名单 |
| `PROJECT_DISABLED` | 403 | 项目未启用 |
| `QUOTA_EXCEEDED` | 429 | 今日上报额度已用完 |
| `RATE_LIMITED` | 429 | 提交太频繁，请稍后再试 |
| `VALIDATION_ERROR` | 422 | 内容校验失败 |
| `UPLOAD_UNAVAILABLE` | 503 | 截图上传暂不可用（不带截图继续提交） |
| `NETWORK_ERROR` | — | 网络连接失败，请检查网络后重试 |
| `TIMEOUT` | — | 请求超时，请稍后重试 |
| `NOT_BROWSER` | — | 只能在浏览器中初始化（`init` 抛出） |
| `INVALID_OPTIONS` | — | `key` / `endpoint` 缺失或格式不对（`init` 抛出） |

其它错误显示服务端返回的 `error` 字段；没有时显示「上报失败，请稍后重试」。

## 白名单与安全

-   Key 是公开的，会出现在宿主的前端产物和网络请求里，不是秘密。
-   安全靠服务端：平台按请求的 `Origin` 校验 Key 的域名白名单，不在白名单直接返回 403；每把 Key 有每日额度，单 IP 有频率限制；Key 可以随时吊销或轮换。
-   接入前请在平台「我的项目 → Key 管理」把宿主域名加进白名单。本地开发需要打开 localhost 开关。
-   SDK 只能传标题、描述、截图、环境信息和 console 日志。云效项目、指派人、优先级等字段由平台配置决定，SDK 不能覆盖。
-   请求不带 cookie（`credentials: 'omit'`）。截图直传对象存储，不带 Key 头。
-   console 日志和 URL 在离开浏览器前脱敏：键名含 `token`、`password`、`passwd`、`pwd`、`authorization`、`cookie`、`secret`、`api_key`、`session` 的值，URL 查询参数和 hash 里的同类参数，`Bearer` / `Basic` 凭据和 URL 里的密码都会替换为 `***`，单条截断到 500 字符。
-   浏览器扩展造成的噪音直接丢弃，不占 50 条缓冲：日志里出现 `chrome-extension://`、`moz-extension://`、`safari-web-extension://` 等扩展脚本地址（例如扩展包装 `window.fetch` 后产生的 `Failed to fetch`），以及 `Unchecked runtime.lastError`、`Extension context invalidated` 这类扩展固定文案。宿主 console 行为不受影响，只是不上报。
-   截图是公开读链接，会写进云效工作项。页面上的敏感数据请在提交前用矩形框标注提醒，或不上报该页面。

## 平台接口

SDK 调用 `${endpoint}/api/reporter/v1`，请求头 `X-Reporter-Key`：

1.  `POST /uploads` → 预签名表单，SDK 直传截图（png 超过上限时转 jpeg 重试一次）。
2.  `POST /reports` → `{ reportId }`。

## 开发

```bash
bun run build       # dist/：ESM + .d.ts
bun run type-check
bun test
```

## 常见问题

### @qzsy/yunxiao-reporter 是什么？

云效问题上报浏览器 SDK：悬浮按钮、截图标注、console 采集，提交到云效 Bug 平台

### 如何安装 @qzsy/yunxiao-reporter？

在项目目录运行 npm install @qzsy/yunxiao-reporter，或使用 pnpm add / yarn add / bun add。

### @qzsy/yunxiao-reporter 支持 TypeScript 吗？

支持。包内自带 TypeScript 类型声明（package\.json 声明了 types 入口），安装后即可获得类型提示，无需再安装 @types 包。

### @qzsy/yunxiao-reporter 支持 ESM 还是 CommonJS？

以 ES Module 形式发布，推荐使用 import 引入。在 CommonJS 项目中，Node\.js 20\.19\+ / 22\.12\+ 可以直接 require\(\) 不含顶层 await 的 ES Module，更早的版本需要改用动态 import\(\)。

### @qzsy/yunxiao-reporter 可以商用吗？

@qzsy/yunxiao-reporter 使用 未声明 许可。package\.json 没有声明许可证。没有许可证的代码默认保留全部权利，商用前请联系作者或查看仓库中的 LICENSE 文件。以上为概括，不构成法律意见，请以许可原文为准。

### @qzsy/yunxiao-reporter 还在维护吗？

最新版本 0\.4\.0 发布于 2026/11/02（1 天前）；至今共发布 7 个版本，其中过去 12 个月发布了 7 个；首次发布于 2026/10/28。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

