befly-admin 3.14.6 → 3.14.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "befly-admin",
3
- "version": "3.14.6",
4
- "gitHead": "84b6e4e0adde7f566cfc890887b024ccf10b0516",
3
+ "version": "3.14.7",
4
+ "gitHead": "c47d1e515dabe142531e0a8415979cccb2729259",
5
5
  "private": false,
6
6
  "description": "Befly Admin - 基于 Vue3 + TDesign Vue Next 的后台管理系统",
7
7
  "files": [
@@ -28,7 +28,7 @@
28
28
  "preview": "bunx --bun vite preview"
29
29
  },
30
30
  "dependencies": {
31
- "befly-admin-ui": "^1.8.33",
31
+ "befly-admin-ui": "^1.8.34",
32
32
  "befly-shared": "2.0.3",
33
33
  "befly-vite": "^1.5.15",
34
34
  "pinia": "^3.0.4",
package/src/main.js CHANGED
@@ -13,7 +13,6 @@ app.use(createPinia());
13
13
 
14
14
  // 使用路由
15
15
  app.use($Router);
16
-
17
- setupErrorReport(app);
16
+ setupReport(app);
18
17
 
19
18
  app.mount("#app");
@@ -1,9 +1,32 @@
1
+ import { $Config } from "@/plugins/config.js";
2
+ import { $Http } from "@/plugins/http.js";
3
+ import { $Router } from "@/plugins/router.js";
4
+
5
+ const ONLINE_REPORT_MIN_GAP = 15 * 1000;
6
+ const ONLINE_REPORT_HEARTBEAT_INTERVAL = 60 * 1000;
7
+ const ERROR_REPORT_DEDUP_GAP = 3000;
8
+
9
+ let onlineReportHeartbeatStarted = false;
10
+ let lastOnlineReportTime = 0;
11
+ let onlineReportTimeoutId = 0;
12
+ let routeReportBound = false;
1
13
  let isReportingError = false;
2
14
  let lastErrorKey = "";
3
15
  let lastErrorTime = 0;
4
16
  let fetchWrapped = false;
5
17
 
6
- function getRouteInfo() {
18
+ function getRouteReportData(route) {
19
+ return {
20
+ pagePath: route?.fullPath || route?.path || "",
21
+ pageName: String(route?.name || route?.meta?.title || route?.path || ""),
22
+ source: "admin",
23
+ productName: $Config.productName,
24
+ productCode: $Config.productCode,
25
+ productVersion: $Config.productVersion
26
+ };
27
+ }
28
+
29
+ function getCurrentRouteInfo() {
7
30
  const currentRoute = $Router.currentRoute.value || {};
8
31
 
9
32
  return {
@@ -12,6 +35,92 @@ function getRouteInfo() {
12
35
  };
13
36
  }
14
37
 
38
+ function reportOnline(route) {
39
+ return $Http("/core/tongJi/onlineReport", getRouteReportData(route), [""]);
40
+ }
41
+
42
+ function reportInfo(route) {
43
+ return $Http("/core/tongJi/infoReport", getRouteReportData(route), [""]);
44
+ }
45
+
46
+ function triggerActiveOnlineReport(force = false) {
47
+ if (typeof document === "undefined") {
48
+ return;
49
+ }
50
+
51
+ if (document.visibilityState !== "visible") {
52
+ return;
53
+ }
54
+
55
+ if (typeof document.hasFocus === "function" && document.hasFocus() !== true) {
56
+ return;
57
+ }
58
+
59
+ const now = Date.now();
60
+
61
+ if (!force && now - lastOnlineReportTime < ONLINE_REPORT_MIN_GAP) {
62
+ return;
63
+ }
64
+
65
+ lastOnlineReportTime = now;
66
+
67
+ const currentRoute = $Router.currentRoute.value;
68
+
69
+ void reportOnline(currentRoute).catch(() => {
70
+ // 静默失败:不阻断页面交互
71
+ });
72
+ }
73
+
74
+ function queueOnlineReportHeartbeat() {
75
+ if (typeof window === "undefined") {
76
+ return;
77
+ }
78
+
79
+ if (onlineReportTimeoutId) {
80
+ window.clearTimeout(onlineReportTimeoutId);
81
+ }
82
+
83
+ onlineReportTimeoutId = window.setTimeout(() => {
84
+ triggerActiveOnlineReport();
85
+ queueOnlineReportHeartbeat();
86
+ }, ONLINE_REPORT_HEARTBEAT_INTERVAL);
87
+ }
88
+
89
+ function setupOnlineReportHeartbeat() {
90
+ if (onlineReportHeartbeatStarted || typeof window === "undefined") {
91
+ return;
92
+ }
93
+
94
+ onlineReportHeartbeatStarted = true;
95
+
96
+ window.addEventListener("focus", () => {
97
+ triggerActiveOnlineReport();
98
+ });
99
+
100
+ document.addEventListener("visibilitychange", () => {
101
+ triggerActiveOnlineReport();
102
+ });
103
+
104
+ triggerActiveOnlineReport(true);
105
+ queueOnlineReportHeartbeat();
106
+ }
107
+
108
+ function bindRouteReport() {
109
+ if (routeReportBound) {
110
+ return;
111
+ }
112
+
113
+ routeReportBound = true;
114
+
115
+ $Router.afterEach((to) => {
116
+ lastOnlineReportTime = Date.now();
117
+
118
+ void Promise.allSettled([reportOnline(to), reportInfo(to)]).catch(() => {
119
+ // 静默失败:不阻断路由切换
120
+ });
121
+ });
122
+ }
123
+
15
124
  function getErrorMessage(error) {
16
125
  if (typeof error === "string") {
17
126
  return error;
@@ -47,7 +156,8 @@ function getErrorDetail(error) {
47
156
  function shouldSkipError(message, detail, errorType) {
48
157
  const now = Date.now();
49
158
  const key = `${errorType}|${message}|${detail}`;
50
- if (key === lastErrorKey && now - lastErrorTime < 3000) {
159
+
160
+ if (key === lastErrorKey && now - lastErrorTime < ERROR_REPORT_DEDUP_GAP) {
51
161
  return true;
52
162
  }
53
163
 
@@ -57,8 +167,9 @@ function shouldSkipError(message, detail, errorType) {
57
167
  }
58
168
 
59
169
  export function reportClientError(errorType, message, detail) {
60
- const safeMessage = String(message || "未知错误").slice(0, 500);
61
- const safeDetail = String(detail || "").slice(0, 5000);
170
+ const safeMessage = String(message || "未知错误");
171
+ const safeDetail = String(detail || "");
172
+
62
173
  if (shouldSkipError(safeMessage, safeDetail, errorType)) {
63
174
  return;
64
175
  }
@@ -67,7 +178,7 @@ export function reportClientError(errorType, message, detail) {
67
178
  return;
68
179
  }
69
180
 
70
- const routeInfo = getRouteInfo();
181
+ const routeInfo = getCurrentRouteInfo();
71
182
  isReportingError = true;
72
183
 
73
184
  void $Http(
@@ -172,6 +283,7 @@ function setupFetchErrorReport() {
172
283
  }
173
284
 
174
285
  const errorDetail = getErrorDetail(error);
286
+
175
287
  if (errorDetail) {
176
288
  parts.push(`detail: ${errorDetail}`);
177
289
  }
@@ -186,7 +298,7 @@ function setupFetchErrorReport() {
186
298
  fetchWrapped = true;
187
299
  }
188
300
 
189
- export function setupErrorReport(app) {
301
+ function setupErrorReport(app) {
190
302
  setupFetchErrorReport();
191
303
 
192
304
  app.config.errorHandler = (error, _instance, info) => {
@@ -222,13 +334,17 @@ export function setupErrorReport(app) {
222
334
  }
223
335
 
224
336
  const parts = [];
337
+
225
338
  if (event.filename) {
226
339
  parts.push(`file: ${event.filename}`);
227
340
  }
341
+
228
342
  if (event.lineno || event.colno) {
229
343
  parts.push(`line: ${event.lineno || 0}, col: ${event.colno || 0}`);
230
344
  }
345
+
231
346
  const stack = getErrorDetail(event.error);
347
+
232
348
  if (stack) {
233
349
  parts.push(stack);
234
350
  }
@@ -248,3 +364,9 @@ export function setupErrorReport(app) {
248
364
  reportClientError("router", getErrorMessage(error), getErrorDetail(error));
249
365
  });
250
366
  }
367
+
368
+ export function setupReport(app) {
369
+ bindRouteReport();
370
+ setupOnlineReportHeartbeat();
371
+ setupErrorReport(app);
372
+ }
@@ -2,7 +2,6 @@ import { Layouts } from "befly-vite";
2
2
  import { createRouter, createWebHashHistory } from "vue-router";
3
3
  import { routes } from "vue-router/auto-routes";
4
4
  import { $Config } from "@/plugins/config.js";
5
- import { $Http } from "@/plugins/http.js";
6
5
 
7
6
  // 应用自定义布局系统(同时可选注入根路径重定向)
8
7
  const finalRoutes = Layouts(routes, $Config.homePath, (layoutName) => {
@@ -53,35 +52,3 @@ $Router.beforeEach((to, _from) => {
53
52
 
54
53
  return true;
55
54
  });
56
-
57
- // 路由就绪后处理
58
- $Router.afterEach((to) => {
59
- void Promise.allSettled([
60
- $Http(
61
- "/core/tongJi/onlineReport",
62
- {
63
- pagePath: to.fullPath || to.path || "",
64
- pageName: String(to.name || to.meta?.title || to.path || ""),
65
- source: "admin",
66
- productName: $Config.productName,
67
- productCode: $Config.productCode,
68
- productVersion: $Config.productVersion
69
- },
70
- [""]
71
- ),
72
- $Http(
73
- "/core/tongJi/infoReport",
74
- {
75
- pagePath: to.fullPath || to.path || "",
76
- pageName: String(to.name || to.meta?.title || to.path || ""),
77
- source: "admin",
78
- productName: $Config.productName,
79
- productCode: $Config.productCode,
80
- productVersion: $Config.productVersion
81
- },
82
- [""]
83
- )
84
- ]).catch(() => {
85
- // 静默失败:不阻断路由切换
86
- });
87
- });