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 +3 -3
- package/src/main.js +1 -2
- package/src/plugins/{errorReport.js → report.js} +128 -6
- package/src/plugins/router.js +0 -33
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "befly-admin",
|
|
3
|
-
"version": "3.14.
|
|
4
|
-
"gitHead": "
|
|
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.
|
|
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
|
@@ -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
|
|
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
|
-
|
|
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 || "未知错误")
|
|
61
|
-
const safeDetail = String(detail || "")
|
|
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 =
|
|
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
|
-
|
|
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
|
+
}
|
package/src/plugins/router.js
CHANGED
|
@@ -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
|
-
});
|