issues-reporter-plugin
v0.1.7
Published
嵌入式问题上报插件
Maintainers
Readme
issues-reporter-plugin 项目描述
1. 项目概述
issues-reporter-plugin 是一个独立的外部前端插件,用于嵌入到各个业务子系统(如 BDC 不动产登记中心各模块)中,实现自动化提取报错信息,并将 Bug 信息固化为标准结构,为下游团队提供可直接复现、信息完整的标准文档。
插件以 TypeScript + 原生 DOM 构建,零前端框架依赖,一套代码可同时支持 Vue 2/3、React、Angular 以及原生 JS 项目接入。
2. 技术选型
| 维度 | 选型 | 说明 |
|------|------|------|
| 技术栈 | TypeScript + 原生 DOM | 零框架依赖,一套代码支持 Vue 2/3、React、Angular、原生 JS |
| 构建工具 | Vite | 极速冷启动,HMR 更快,原生 ES Module 支持 |
| 截图引擎 | SnapDOM(DOM 序列化)+ getDisplayMedia(精准捕获) | SnapDOM 基于 SVG foreignObject 由浏览器真实渲染,还原度高;精准模式像素级抓帧、零 DOM 遍历 |
| 样式 | 原生 CSS | 无预处理器依赖,浏览器原生支持,避免与宿主样式冲突 |
| UI 实现 | 原生 DOM API | createElement / appendChild / addEventListener,无框架绑定 |
| Word 导出 | docx 库 | 纯 JS 生成,样式精确可控 |
| PDF 导出 | jspdf + autotable | 原生支持中文,表格插件成熟 |
设计原则: UI 层采用纯原生 DOM + TypeScript 实现,不依赖 Vue、React、Angular 等任何前端框架。核心逻辑层(异常捕获、Bug 构建器、截图引擎、导出服务)本来就是纯 TS,零框架依赖,可在任何技术栈的项目中一致使用。
3. 架构设计
采用三层架构设计,各层职责清晰:
┌─────────────────────────────────────────────────────────┐
│ 第 1 层:核心逻辑层(纯 TS,零框架依赖) │
│ ├── HTTP 拦截器(axios / fetch 双拦截) │
│ ├── 统一异常捕获协调器(HttpInterceptor) │
│ │ ├── JS 运行时错误监听 │
│ │ ├── Vue 错误处理(Vue 2/3) │
│ │ ├── Promise 未捕获拒绝监听 │
│ │ ├── XMLHttpRequest 拦截器 │
│ │ ├── console.error 拦截器 │
│ │ └── 资源加载错误监听(img/script/css) │
│ ├── 错误分类器(按来源与严重程度分类) │
│ ├── 截图引擎(SnapDOM / 精准标签页捕获) │
│ ├── Bug 构建器(标准结构) │
│ └── 导出服务(JSON / MD / CSV / Word / PDF) │
├─────────────────────────────────────────────────────────┤
│ 第 2 层:UI 层(纯 DOM + TypeScript) │
│ ├── 悬浮工具栏(两个入口:创建 / 列表) │
│ ├── Bug 弹窗 │
│ │ ├── 模块一:我的问题清单(issueList) │
│ │ │ └── 展示当前用户已提交到后端的问题列表 │
│ │ └── 模块二:创建问题(createPreflight) │
│ │ └── 选取本地捕获的 Bug → 填写标题/描述 → 提交后端 │
│ └── 截图标注画布(矩形 / 箭头 / 文字 / 马赛克) │
├─────────────────────────────────────────────────────────┤
│ 第 3 层:分发适配层 │
│ ├── npm 模式(插件完全控制截图) │
│ └── iframe 模式(宿主提供截图钩子,插件调用) │
└─────────────────────────────────────────────────────────┘所有异常捕获模块统一输出 CaptureErrorInfo,由 NpmAdapter.handleCaptureError() 统一处理:分类 → 去重 → 截图 → 构建 Bug 报告 → 存储到本地 → 回调通知。用户点击"创建"时,本地 Bug 作为候选数据带入创建表单,最终提交到后端。
4. 核心能力
4.1 自动异常捕获(后台静默运行)
插件启动后自动监听以下七类异常,捕获到的 Bug 暂存于本地内存,等待用户通过"创建问题"提交到后端:
| 来源 | 监听方式 | 捕获内容 | 默认是否开启 |
|------|---------|---------|------------|
| http-error | axios / fetch 拦截 | URL、method、status、请求参数、响应体 | 是 |
| js-error | window.onerror | message、filename、lineno、colno、stack | 是 |
| vue-error | Vue.config.errorHandler / app.config.errorHandler | 组件名、props、info、error.stack | 否(需显式设置 Vue 实例) |
| promise-error | window.unhandledrejection | reason、reason.stack | 是 |
| xhr-error | monkey-patch XMLHttpRequest | URL、method、headers、body、status、错误类型 | 是 |
| console-error | monkey-patch console.error | 调用参数列表 | 否 |
| resource-error | window.addEventListener('error', ..., true) | 资源标签名、src / href | 是 |
每类异常都生成统一的 CaptureErrorInfo,保留完整上下文(堆栈、组件、资源 URL、请求参数等),由 ErrorClassifier 按来源分级为 critical / high / medium / low。
自动截图: 接口报错时自动截取当前页面(可配置 delay 等待错误提示框弹出)。截图引擎与性能调度见 4.7 截图方案。
忽略规则: 支持配置忽略特定 URL、状态码、业务码,避免误报。
4.2 模块一:我的问题清单(issueList)
点击悬浮工具栏 "列表" 按钮,弹窗展示当前用户已提交到后端的全部问题。
- 数据来源:
GET /issues-api/issues(后端按当前登录用户过滤) - 展示字段:问题编号、标题、来源、状态、严重程度、指派给、上报人、创建时间
- 操作:点击某行可跳转查看问题详情(含截图、接口信息、环境信息等)
4.3 模块二:创建问题(createPreflight)
点击悬浮工具栏 "创建" 按钮,弹窗展示本地已捕获的 Bug 列表,用户勾选后填写标题、严重程度、描述,可选截图标注,最终提交到后端生成问题单。
- 数据来源:插件运行期间自动捕获并存入本地的 Bug 列表
- 创建流程:选取 Bug → 填写标题 / 描述 / 严重程度 → 截图标注(可选) → 提交后端(
POST /issues-api/issues) - 支持截图标注(矩形框、箭头、文字、马赛克)
- 支持撤销 / 重做操作
4.4 多格式导出
支持批量导出 Bug 为多种格式:
| 格式 | 用途 | |------|------| | JSON | 标准数据,适合系统对接 | | Markdown | 文档报告,适合 Git 归档 | | CSV | 表格数据,适合 Excel 分析 | | Word | 可编辑文档,适合协作打印 | | PDF | 只读文档,适合归档提交 |
4.5 AI 员工 Inbox 导出
支持将 Bug 一键推送到局域网内指定 AI 员工电脑的本地 Inbox 目录,由该电脑上的集成 AI 自动监听并处理。支持 JSON / Markdown / Word / PDF 四种导出格式后发送。
4.6 标准化输出结构
所有 Bug 信息统一固化为标准 JSON 结构(BugReport),包含:
- 基础信息(ID、来源、严重程度、时间)
- 环境信息(子系统、浏览器、路由、分辨率)
- 接口信息(URL、方法、参数、响应)
- 描述信息(标题、自动描述、手动描述)
- 截图标注(Base64 图片 + 标注数据)
- 扩展信息(标签、上报人、自定义字段、源详情
errorDetails)
4.7 截图方案
截图采用双引擎 + 空闲调度 + Worker 编码 + IndexedDB 工作集的架构,目标是在复杂页面(二三维地图、大量 iframe)上把主线程阻塞压缩到最低,同时控制常驻内存占用。
4.7.1 双截图引擎
| 引擎 | 触发方式 | 原理 | 适用场景 |
|------|---------|------|---------|
| 普通截图(默认) | 自动/手动截图 | SnapDOM:SVG foreignObject 由浏览器真实渲染当前视口,clip: 'viewport' 剪枝视口外子树 | 绝大多数页面;同域 iframe 由 IframeCapturer 逐个截取后合成到主截图 |
| 精准截图 | 用户授权后替代普通引擎 | getDisplayMedia({ preferCurrentTab: true }) 抓取当前标签页真实渲染帧 | 像素级还原 WebGL 地图与跨域 iframe;零 DOM 遍历、零序列化、主线程几乎零阻塞 |
精准模式说明:
- 仅 HTTPS 安全上下文 + Chromium 系浏览器(Chrome 94+)可用,
isSupported()特性检测决定「精准」按钮是否显示(位于悬浮工具栏) - 必须在用户点击时发起授权(浏览器限制);授权一次后 MediaStream 常驻,后续抓帧不再弹授权窗
- 用户点击浏览器「停止共享」→
track.ended→ 自动停用,回退 SnapDOM 路径 - WebGL 地图:普通 SnapDOM 无法读取 WebGL 画布内容(
preserveDrawingBuffer: false帧合成后缓冲清空),ImagePreprocessor会在截图前劫持一次requestAnimationFrame,在地图渲染的同帧将 canvas 拷贝为 2D 画布临时替换 DOM,截完恢复
4.7.2 自动截图性能调度(防卡顿)
自动截图(错误触发)经过多层调度,保证报错瞬间的用户交互不受影响:
错误触发
→ 错误风暴断路器(StormGuard):风暴期直接跳过,避免昂贵 DOM 操作雪上加霜
→ 15s 冷却窗口:窗口内只做一次 SnapDOM 序列化,手动截图不受限
→ 并发锁 + 排队:正在截图时新请求入队等待
→ 300ms 尾沿 debounce:密集报错时让开页面最忙时刻
→ requestIdleCallback 空闲帧执行(2s 超时兜底)
→ 实际截图单次截图的主线程成本压缩手段:
| 手段 | 效果 |
|------|------|
| clip: 'viewport' | 视口外子树在样式计算/资源内联前被剪枝,长页面不全文档光栅化 |
| scale: 0.75 × dpr: 1 | 留证用途降采样,HiDPI 屏(dpr=2)像素量不翻倍 |
| embedFonts: false | 跳过非图标字体内联(字体抓取+解析是序列化重头) |
| excludeMode: 'remove' | 插件自身 UI 直接从克隆树丢弃(fixed 定位不影响布局) |
| 空闲预热 preCache | mount 后空闲时预内联字体/图片/CSS,首次真实截图不再承担全部资源内联成本 |
| Worker 编码 | JPEG 编码/压缩/iframe 合成经 createImageBitmap 零拷贝转移至 Worker(OffscreenCanvas),主线程无 canvas.toBlob 长任务;Worker 不可用时自动回退主线程 |
复杂页面单次自动截图的主线程占用约 70–350ms,且被拆散在空闲帧执行;相比全文档遍历 + 主线程编码的传统方案下降约一个数量级。
4.7.3 内存管理:IndexedDB 工作集 + 惰性水合
截图 Blob 不长期驻留内存,常驻内存从「每张截图几百 KB」降为「一个占位字符串」:
- 写入:Bug 入库时截图 Blob write-through 镜像到 IndexedDB,写成功后立即释放内存——revoke Object URL、
url换成idb:{bugId}:{index}占位符、删除blob字段 - 读取(惰性水合):消费点(详情展示/导出 Word·PDF·JSON/提交后端/发送 AI)在使用前调
hydrateBugs()从 IDB 读回内存,用完releaseBugs()释放 - 提交链路:提交前统一水合,截图二进制以
ref:占位符写入 JSON 元数据、multipartfiles携带二进制,files 数量与截图数一致 - 隐私模式兜底:IDB 不可用(隐私浏览/配额/禁用)时写入返回失败,内存 Blob 常驻不释放,所有消费点按
blob优先正常工作 - 会话启动时清空上次残留的 IDB 记录(不跨会话恢复),防止无上限增长
4.7.4 截图模式配置(screenshotMode)
运行时配置项 screenshotMode,可由管理平台「系统设置 → 插件配置 → 截图配置」统一下发(GET /issues-api/system-settings/plugin),也可在构造配置中静态指定;缺失或非法值回退 normal。
| 值 | 行为 |
|---|---|
| off | 关闭截图:不自动截图、「精准」按钮隐藏;Bug 照常捕获上报(无图),用户手动上传/粘贴图片不受限 |
| normal | 普通 DOM 序列化截图(默认),「精准」按钮不显示 |
| precision | 悬浮工具栏显示「精准」按钮(HTTPS + Chromium),授权后自动截图走像素级抓帧,未授权时回退 SnapDOM |
5. 分发方式
插件支持三种集成方式:
npm 包引入 —
import IssuesReporter from 'issues-reporter-plugin',适用于可修改源码的子项目- 插件完全控制截图
- 直接访问宿主 DOM
iframe 嵌入 — 通过
<iframe src="...">方式嵌入,适用于无法修改源码的遗留系统- 宿主提供截图钩子(
window.IssuesReporterScreenshot) - 插件通过 postMessage 调用
- 支持 HTTP/HTTPS 混合环境(协议自适应)
- 宿主提供截图钩子(
<script>自动初始化 — 直接引用构建产物issues-reporter.auto.umd.js,通过data-*属性完成配置,无需编写业务代码- 自动发现 Vue 2 / Vue 3 实例并绑定
errorHandler - 自动发现页面上的 axios 实例
- 脚本解析后立即启动所有错误监听器,DOM 就绪后挂载 UI
- 自动发现 Vue 2 / Vue 3 实例并绑定
6. 项目集成示例
6.1 Vue 3 项目
// main.ts
import { createApp } from 'vue'
import App from './App.vue'
import IssuesReporter from 'issues-reporter-plugin'
import axios from 'axios'
const app = createApp(App)
app.mount('#app')
const issuesReporter = new IssuesReporter({
projectKey: 'BDC', // 必填:项目标识(对应后端 Project.key)
projectName: '不动产登记中心', // 必填:项目名称,首次上报时自动创建
version: 'v2.0.0', // 必填:版本号,对应 Issue.version
token: 'eyJhbGc...', // 必填(与 externalUserInfo 二选一):JWT token 登录
// externalUserInfo: { userName: 'admin', userNickname: '管理员' }, // 或传用户信息免密登录
// server: '', // 后端地址为空时使用当前页面域名;跨域时填写完整前缀
})
// 注入 axios 实例(可选,用于精确提取请求参数)
issuesReporter.setAxiosInstance(axios)
// 注入 Vue 实例,开启 Vue 组件错误监控
issuesReporter.setVueInstance(app)
// 挂载 UI(悬浮工具栏 + Bug 弹窗)
issuesReporter.mount()
window.addEventListener('beforeunload', () => issuesReporter.destroy())另一种登录方式:若外部系统无法提供 JWT,可改为传入
externalUserInfo(与token二选一),插件会调用loginByUser接口免密登录:const issuesReporter = new IssuesReporter({ projectKey: 'BDC', projectName: '不动产登记中心', version: 'v2.0.0', externalUserInfo: { userName: 'admin', userNickname: '管理员' }, })
其余参数(screenshotOptions、ignore、jsError、consoleError 等)均有合理默认值,完整说明见 6.7 关键配置说明。
6.2 Vue 2 项目
// main.js
import Vue from 'vue'
import App from './App.vue'
import IssuesReporter from 'issues-reporter-plugin'
import axios from 'axios'
new Vue({ render: h => h(App) }).$mount('#app')
const issuesReporter = new IssuesReporter({
projectKey: 'PUB3',
projectName: '智治平台',
version: 'v2.0.0',
token: 'eyJhbGc...', // 必填(与 externalUserInfo 二选一):JWT token 登录
// externalUserInfo: { userName: 'admin', userNickname: '管理员' }, // 或传用户信息免密登录
})
issuesReporter.setAxiosInstance(Vue.prototype.$axios || axios)
issuesReporter.setVueInstance(Vue)
issuesReporter.mount()
window.addEventListener('beforeunload', () => issuesReporter.destroy())6.3 React 项目
// App.tsx
import { useEffect } from 'react'
import IssuesReporter from 'issues-reporter-plugin'
function App() {
useEffect(() => {
const issuesReporter = new IssuesReporter({
projectKey: 'REACT_APP',
projectName: 'React 业务系统',
version: 'v1.0.0',
token: 'eyJhbGc...', // 必填(与 externalUserInfo 二选一)
// externalUserInfo: { userName: 'admin', userNickname: '管理员' },
})
issuesReporter.mount()
return () => issuesReporter.destroy()
}, [])
return <div>{/* 业务组件 */}</div>
}
export default App6.4 Angular 项目
// app.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core'
import IssuesReporter from 'issues-reporter-plugin'
@Component({
selector: 'app-root',
template: '<router-outlet></router-outlet>'
})
export class AppComponent implements OnInit, OnDestroy {
private issuesReporter!: IssuesReporter
ngOnInit() {
this.issuesReporter = new IssuesReporter({
projectKey: 'NG_APP',
projectName: 'Angular 业务系统',
version: 'v1.0.0',
token: 'eyJhbGc...', // 必填(与 externalUserInfo 二选一)
// externalUserInfo: { userName: 'admin', userNickname: '管理员' },
})
this.issuesReporter.mount()
}
ngOnDestroy() {
this.issuesReporter.destroy()
}
}6.5 自动初始化入口(<script> 零代码集成)
如果目标系统不想引入 npm 包或在业务代码里实例化插件,可以直接通过 <script> 引用自动初始化产物。推荐把脚本放在 <head> 或业务 bundle 之前,以便尽早启动错误监听,捕获 Vue 挂载阶段和早期接口错误。
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="//your-static-server/issues-reporter-plugin/0.2.20/style.css">
<script
src="//your-static-server/issues-reporter-plugin/0.2.20/issues-reporter.auto.umd.js"
data-project-key="PUB3"
data-token="eyJhbGc..."
data-delay="600"
data-vue-error="true"
data-ignore-urls="/heartbeat/"
data-ignore-business-codes="401"
></script>
</head>
<body>
<div id="app"></div>
<script src="/app.js"></script>
</body>
</html>常用 data-* 属性:
| 属性 | 说明 | 示例 |
|---|---|---|
| data-project-key | 项目唯一标识,必填 | PUB3 |
| data-project-name | 项目名称,首次上报时自动创建 | 智治平台 |
| data-server | 后端接口前缀,为空时使用当前页面域名 | http://192.168.1.100:3000 |
| data-token | JWT token(与 data-user-name 二选一),用于 loginByToken 免密登录 | eyJhbGc... |
| data-user-name | 用户账号(与 data-token 二选一),用于 loginByUser 免密登录 | admin |
| data-user-nickname | 用户昵称(配合 data-user-name 使用),首次创建用户时写入 | 管理员 |
| data-delay | 截图延迟 ms | 600 |
| data-quality | 截图质量 0-1 | 0.6 |
| data-max-size | 截图最大字节 | 307200 |
| data-show-flash | 截图闪光提示 | false |
| data-auto-screenshot | 是否自动截图 | true |
| data-js-error | 监听 JS 运行时错误 | true |
| data-vue-error | 监听 Vue 错误 | true |
| data-promise-error | 监听 Promise 未捕获拒绝 | true |
| data-xhr-error | 监听 XHR 错误 | true |
| data-console-error | 监听 console.error | false |
| data-resource-error | 监听资源加载失败 | true |
| data-ignore-urls | 忽略 URL/正则,逗号分隔 | /heartbeat/,/poll/ |
| data-ignore-status-codes | 忽略 HTTP 状态码 | 401,500 |
| data-ignore-business-codes | 忽略业务错误码 | 401,9999 |
| data-ignore-methods | 忽略 HTTP 方法 | GET,OPTIONS |
布尔属性写 true 或只写属性名都算开启;要关闭直接省略该属性。
如需更高优先级覆盖,仍可保留 window.IssuesReporterConfig:
<script>
window.IssuesReporterConfig = { projectKey: 'PUB3', vueError: true }
</script>配置合并优先级:window.IssuesReporterConfig > <script data-*>。
注意: 自动初始化产物在脚本解析完成后立即启动错误监听器,UI 则在 DOM 就绪后挂载。若把脚本放在页面末尾,仍可能错过
<head>或 body 前半段发生的错误,因此建议放在业务 JS 之前。
6.6 原生 JS 项目
<!-- index.html -->
<script type="module">
import IssuesReporter from 'https://your-cdn.com/issues-reporter-plugin/dist/issues-reporter.es.js'
const issuesReporter = new IssuesReporter({
projectKey: 'NATIVE_APP',
token: 'eyJhbGc...', // 必填(与 externalUserInfo 二选一)
enabled: true,
autoScreenshot: true,
screenshotOptions: { delay: 500 },
jsError: true,
promiseError: true,
xhrError: true,
resourceError: true
})
issuesReporter.mount()
window.addEventListener('beforeunload', () => {
issuesReporter.destroy()
})
</script>6.7 关键配置说明
| 配置项 | 说明 | 常用值 |
|--------|------|--------|
| projectKey | 项目唯一标识,对应后端 Project.key | 'BDC' / 'BDCDJ3' / 'BDCYC2' / 'BDCQD' / 'PUB3' |
| projectName | 项目名称,首次上报时若项目不存在则自动创建 | '不动产登记中心' |
| version | 项目版本号,对应后端 Issue.version | 'v2.0.0' |
| server | 后端接口前缀,插件据此拼接 /issues-api/* 地址;为空时使用当前页面域名 | '' / 'http://192.168.1.100:3000' |
| token | 必填(与 externalUserInfo 二选一),外部系统传入的 JWT token,用于 loginByToken 免密登录 | 'eyJhbGc...' |
| externalUserInfo | 必填(与 token 二选一),外部系统传入的用户信息,用于 loginByUser 免密登录(无 JWT 场景) | { userName: 'admin', userNickname: '管理员' } |
| enabled | 是否启用插件 | true(默认) |
| autoScreenshot | 接口报错时是否自动截图 | true(默认) |
| screenshotMode | 截图模式:off 关闭 / normal 普通(默认)/ precision 精准(需 HTTPS + Chromium);可被系统设置下发覆盖,详见 4.7.4 | 'normal' |
| screenshotOptions.delay | 报错后等待多少毫秒再截图,等待错误提示框弹出 | 500 ~ 1000 |
| ignore.urls | 忽略的 URL 正则/字符串列表 | [/heartbeat/, /poll/] |
| ignore.statusCodes | 忽略的 HTTP 状态码 | [401, 403] |
| ignore.businessCodes | 忽略的业务错误码 | ['401', '9999'] |
| isBackendEnabled | 是否显示"提交后台"按钮 | true(默认),显式 false 时隐藏 |
| backendApiHeaders | 提交到后台时的额外请求头 | { 'x-gisq-token': 'Bearer ...' } |
| jsError | 是否监听 window.onerror | true(默认) |
| vueError | 是否监听 Vue 错误(需调用 setVueInstance) | 随 setVueInstance 自动开启 |
| promiseError | 是否监听未捕获 Promise 拒绝 | true(默认) |
| xhrError | 是否拦截 XMLHttpRequest 错误 | true(默认) |
| consoleError | 是否拦截 console.error | false(默认,避免噪音) |
| resourceError | 是否监听图片/脚本/CSS 加载失败 | true(默认) |
| onBugCaptured | Bug 捕获后的回调函数 | (bug) => { ... } |
| loadPluginConfig | 异步加载运行时配置的钩子,覆盖默认的 /system-settings/plugin 拉取逻辑 | async () => ({ resourceError: false }) |
| setAxiosInstance / setAxiosInstances | 注入 axios 实例;支持单个或数组批量注入多个实例 | axios / [RegAxios, WorkflowAxios] |
6.8 AI Inbox 配置(可选)
const issuesReporter = new IssuesReporter({
projectKey: 'BDC',
// ... 其他配置
aiInbox: {
enabled: true,
defaultEmployeeId: '001ai',
defaultFormat: 'word',
employees: [
{
id: '001ai',
name: '001号 AI 员工',
host: '192.168.1.105',
port: 8765
}
]
}
})或在运行时动态注入:
issuesReporter.setAiInboxConfig({
enabled: true,
defaultEmployeeId: '001ai',
defaultFormat: 'word',
employees: [...]
})7. 业务背景
本插件服务于 BDC 不动产登记中心 系列子系统,已知子系统包括:
| 项目标识 (projectKey) | 业务范围 | 接入模式 | 关键配置 |
|-----------|---------|---------|---------|
| BDC | 不动产通用(电子证照、纳税申报、评估报告等) | npm (Vue 3) | delay: 800,忽略证照刷新轮询 |
| BDCDJ3 | 登记业务(影像规则、规则检查等) | npm (Vue 3) | delay: 500,忽略规则检查轮询 |
| BDCYC2 | 不动产预告 / 转移 / 抵押 / 变更等全流程 | npm (Vue 3) | delay: 1000,忽略流程状态轮询 |
| BDCQD | 地籍调查(业务中心配置等) | iframe / npm | delay: 600,忽略地图瓦片加载 |
| PUB3 | 智治平台 | npm (Vue 2) | delay: 600,忽略心跳接口 |
HTTP/HTTPS 混合环境: BDC 系统同时存在 HTTP 和 HTTPS,插件服务需部署两套,配置使用 // 协议自适应。
常见 Bug 类型:
- 表单必填校验缺失(字段标 * 但未校验)
- 流程步骤间数据传递异常(挂单元重复、数据未刷新)
- 界面响应异常(无响应、转圈、跳转失败)
- 国产环境兼容性问题
- 配置项不生效
8. 目录结构
issues-reporter-plugin/
├── src/ # 主应用(调试 / 演示入口)
│ ├── main.ts
│ ├── App.vue
│ ├── router/
│ └── views/
│ └── DemoPage.vue # 插件演示页
│
├── packages/ # 插件核心模块
│ ├── core/ # 📦 核心逻辑层(纯 TS)
│ │ ├── interceptors/
│ │ │ ├── http-interceptor.ts # HTTP 拦截(axios / fetch)与统一协调
│ │ │ ├── error-classifier.ts # 错误分类与分级
│ │ │ ├── error-capture.ts # 统一捕获接口定义
│ │ │ ├── js-error-listener.ts # JS 运行时错误监听
│ │ │ ├── vue-error-handler.ts # Vue 2/3 错误处理
│ │ │ ├── promise-error-listener.ts # Promise 未捕获拒绝监听
│ │ │ ├── xhr-interceptor.ts # XMLHttpRequest 拦截
│ │ │ ├── console-error-interceptor.ts# console.error 拦截
│ │ │ └── resource-error-listener.ts # 资源加载错误监听
│ │ ├── services/
│ │ │ ├── screenshot.service.ts # 截图引擎 Facade(SnapDOM + 调度)
│ │ │ ├── screenshot/
│ │ │ │ ├── image-preprocessor.ts # 图片等待 + WebGL 画布同帧快照
│ │ │ │ ├── scroll-sync.ts # 滚动同步
│ │ │ │ ├── iframe-capturer.ts # iframe 截图合成(Worker 优先)
│ │ │ │ ├── rasterize-client.ts # Worker 光栅化客户端(编码/压缩/合成移出主线程)
│ │ │ │ ├── storm-guard.ts # 错误风暴断路器
│ │ │ │ └── precision-capturer.ts # 精准截图(getDisplayMedia 标签页捕获)
│ │ │ ├── screenshot-idb.store.ts # 截图 Blob IndexedDB 工作集(写入 + 惰性水合)
│ │ │ ├── bug-builder.service.ts # Bug 构建器(标准结构)
│ │ │ ├── bug-store.service.ts # 数据存储(内存)+ Blob 惰性水合/释放
│ │ │ └── export.service.ts # 导出服务框架
│ │ ├── exporters/
│ │ │ ├── json.exporter.ts
│ │ │ ├── markdown.exporter.ts
│ │ │ ├── csv.exporter.ts
│ │ │ ├── word.exporter.ts
│ │ │ └── pdf.exporter.ts
│ │ └── index.ts
│ │
│ ├── ui/ # 📦 UI 层(纯 DOM + TS)
│ │ ├── components/
│ │ │ ├── floating-toolbar.ts # 悬浮工具栏(原生 DOM)
│ │ │ ├── bug-dialog.ts # Bug 弹窗容器(原生 DOM)
│ │ │ ├── bug-list.ts # Bug 列表组件(原生 DOM)
│ │ │ ├── bug-create.ts # 创建 Bug 表单(原生 DOM)
│ │ │ ├── export-panel.ts # 导出面板(原生 DOM)
│ │ │ ├── send-ai-dialog.ts # 发送 AI 对话框
│ │ │ ├── panels/ # 弹窗内各面板
│ │ │ │ ├── issue-list-panel.ts # 模块一:我的问题清单(后端数据)
│ │ │ │ ├── issue-edit-panel.ts # 问题详情 / 编辑
│ │ │ │ ├── bug-create-panel.ts # 模块二:创建问题表单
│ │ │ │ ├── bug-preflight-panel.ts # 创建预检(选取本地 Bug → 提交)
│ │ │ │ ├── bug-list-panel.ts # 本地 Bug 列表
│ │ │ │ ├── bug-detail-panel.ts # 本地 Bug 详情
│ │ │ │ ├── bug-export-panel.ts # 导出面板
│ │ │ │ ├── ai-inbox-panel.ts # 发送 AI 对话框
│ │ │ │ └── dialog-utils.ts # 弹窗工具函数
│ │ │ └── annotation-canvas.ts # 截图标注画布(原生 DOM)
│ │ ├── styles/
│ │ │ ├── toolbar.css # 工具栏样式
│ │ │ ├── dialog.css # 弹窗样式
│ │ │ ├── list.css # 列表样式
│ │ │ └── canvas.css # 画布样式
│ │ └── index.ts
│ │
│ ├── adapters/ # 📦 分发适配层
│ │ ├── npm-adapter.ts # npm 模式(完全控制截图)
│ │ └── iframe-adapter.ts # iframe 模式(postMessage)
│ │
│ ├── types/ # 📦 类型定义
│ │ ├── bug-report.ts
│ │ ├── config.ts
│ │ └── index.ts
│ │
│ ├── index.ts # 统一入口
│ └── auto-init.ts # <script> 自动初始化入口
│
├── scripts/ # 构建与发布脚本
│ ├── release.js # 交互式 npm 发布脚本
│ └── chalk.js # 命令行日志工具
│
├── ai-inbox-server/ # AI Inbox 接收服务
│ └── ai-inbox-server.js
│
├── vite.config.ts # 主库构建配置
├── vite.auto.config.ts # 自动初始化入口构建配置
├── tsconfig.json
└── package.json9. 开发规范
9.1 代码风格
- 前端技术栈:TypeScript + 原生 DOM(零框架依赖)
- 样式:原生 CSS(无预处理器依赖)
- UI 实现:原生 DOM API(
createElement、appendChild、addEventListener) - 接口层:Service 封装,统一管理
- 组件命名:PascalCase(如
FloatingToolbar) - 类命名:PascalCase(如
IssuesReporterService) - 文件名:kebab-case(如
http-interceptor.service.ts)
10. 项目目标
核心目标: 固化 Bug 描述标准结构,让下游团队拿到的是可直接复现、信息完整的标准文档,而非模糊的口头描述。
关键指标
- [ ] 接口报错自动捕获覆盖率 ≥ 95%
- [ ] 全端异常捕获覆盖率 ≥ 90%(JS / Vue / Promise / XHR / console / resource)
- [ ] Bug 信息完整度(接口地址 + 入参 + 路由 + 描述 + 截图)≥ 100%
- [ ] 支持 npm 包 + iframe 两种分发方式
- [ ] 自动截图 + 手动标注功能可用
- [ ] 支持 JSON / Markdown / CSV / Word / PDF 五种导出格式
- [ ] 支持批量勾选导出
- [ ] 支持导出到局域网 AI 员工 Inbox(JSON / MD / PDF / Word)
- [ ] 适配 BDC / BDCDJ3 / BDCYC2 / BDCQD / PUB3 五个子系统
- [ ] 支持 Vue 2 / Vue 3 / React / Angular 项目接入
- [ ] 支持 HTTP/HTTPS 混合环境
11. 注意事项
- 插件作为外部独立模块,不能侵入宿主业务系统的业务逻辑代码,仅通过拦截器 / Hook 机制采集信息。
- 截图功能需考虑浏览器兼容性:普通截图基于 SnapDOM(SVG foreignObject,国产信创浏览器基于 Chromium,完美支持);精准截图依赖
getDisplayMedia,仅 HTTPS 安全上下文 + Chromium 系浏览器(Chrome 94+)可用,插件通过特性检测自动隐藏不可用入口。 - iframe 模式下需处理跨域通信(postMessage),宿主提供截图钩子,插件调用。
- npm 包模式下需提供完善的配置项,适配不同子系统的技术栈差异(Vue 2、Vue 3、React、Angular、纯 JS)。
- HTTP/HTTPS 混合环境需部署两套插件服务,配置使用协议自适应(
//plugin.example.com)。 - 异常捕获模块(XHR / console 等)采用 monkey-patch 时,必须保存原始引用并在
stop()时精确恢复,避免与宿主业务代码冲突。
