befly-admin 3.14.5 → 3.14.6

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.5",
4
- "gitHead": "ba7fa7cc0534c48de2df4970d69a14853cf6b2a6",
3
+ "version": "3.14.6",
4
+ "gitHead": "84b6e4e0adde7f566cfc890887b024ccf10b0516",
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.32",
31
+ "befly-admin-ui": "^1.8.33",
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
@@ -5,7 +5,6 @@ import "befly-admin-ui/styles/variables.scss";
5
5
  // 引入全局基础样式(reset、通用类、滚动条等)
6
6
  import "@/styles/global.scss";
7
7
  import App from "./App.vue";
8
- import { setupErrorReport } from "@/plugins/errorReport.js";
9
8
 
10
9
  const app = createApp(App);
11
10
 
@@ -1,10 +1,7 @@
1
- import { $Http } from "@/plugins/http.js";
2
- import { $Config } from "@/plugins/config.js";
3
- import { $Router } from "@/plugins/router.js";
4
-
5
1
  let isReportingError = false;
6
2
  let lastErrorKey = "";
7
3
  let lastErrorTime = 0;
4
+ let fetchWrapped = false;
8
5
 
9
6
  function getRouteInfo() {
10
7
  const currentRoute = $Router.currentRoute.value || {};
@@ -59,7 +56,7 @@ function shouldSkipError(message, detail, errorType) {
59
56
  return false;
60
57
  }
61
58
 
62
- function reportClientError(errorType, message, detail) {
59
+ export function reportClientError(errorType, message, detail) {
63
60
  const safeMessage = String(message || "未知错误").slice(0, 500);
64
61
  const safeDetail = String(detail || "").slice(0, 5000);
65
62
  if (shouldSkipError(safeMessage, safeDetail, errorType)) {
@@ -96,7 +93,102 @@ function reportClientError(errorType, message, detail) {
96
93
  });
97
94
  }
98
95
 
96
+ function setupFetchErrorReport() {
97
+ if (fetchWrapped) {
98
+ return;
99
+ }
100
+
101
+ if (typeof window.fetch !== "function") {
102
+ return;
103
+ }
104
+
105
+ const originalFetch = window.fetch.bind(window);
106
+
107
+ window.fetch = async (...args) => {
108
+ const input = args[0];
109
+ const url = typeof input === "string" ? input : input && typeof input.url === "string" ? input.url : "";
110
+ const apiPath = String($Config.apiPath || "");
111
+ const isApiRequest = Boolean(apiPath) && String(url || "").startsWith(apiPath);
112
+ const isErrorReportRequest = String(url || "").includes("/core/tongJi/errorReport");
113
+
114
+ try {
115
+ const response = await originalFetch(...args);
116
+
117
+ if (isApiRequest && !isErrorReportRequest) {
118
+ if (!response.ok) {
119
+ const parts = [];
120
+
121
+ if (url) {
122
+ parts.push(`url: ${url}`);
123
+ }
124
+
125
+ if (response.status) {
126
+ parts.push(`status: ${response.status}`);
127
+ }
128
+
129
+ reportClientError("http", `请求失败:HTTP ${response.status}`, parts.join("\n"));
130
+ } else {
131
+ try {
132
+ const payload = await response.clone().json();
133
+
134
+ if (payload?.code !== 0) {
135
+ const parts = [];
136
+
137
+ if (url) {
138
+ parts.push(`url: ${url}`);
139
+ }
140
+
141
+ if (response.status) {
142
+ parts.push(`status: ${response.status}`);
143
+ }
144
+
145
+ if (payload?.code !== undefined) {
146
+ parts.push(`code: ${payload.code}`);
147
+ }
148
+
149
+ if (payload?.msg) {
150
+ parts.push(`msg: ${payload.msg}`);
151
+ }
152
+
153
+ if (payload?.detail) {
154
+ parts.push(`detail: ${payload.detail}`);
155
+ }
156
+
157
+ reportClientError("http", payload.msg || "请求失败", parts.join("\n"));
158
+ }
159
+ } catch {
160
+ // 忽略非 JSON 响应
161
+ }
162
+ }
163
+ }
164
+
165
+ return response;
166
+ } catch (error) {
167
+ if (isApiRequest && !isErrorReportRequest) {
168
+ const parts = [];
169
+
170
+ if (url) {
171
+ parts.push(`url: ${url}`);
172
+ }
173
+
174
+ const errorDetail = getErrorDetail(error);
175
+ if (errorDetail) {
176
+ parts.push(`detail: ${errorDetail}`);
177
+ }
178
+
179
+ reportClientError("http", getErrorMessage(error), parts.join("\n"));
180
+ }
181
+
182
+ throw error;
183
+ }
184
+ };
185
+
186
+ fetchWrapped = true;
187
+ }
188
+
99
189
  export function setupErrorReport(app) {
190
+ setupFetchErrorReport();
191
+
100
192
  app.config.errorHandler = (error, _instance, info) => {
101
193
  const message = getErrorMessage(error);
102
194
  const detail = `${String(info || "")}${info ? "\n" : ""}${getErrorDetail(error)}`;
@@ -104,25 +196,55 @@ export function setupErrorReport(app) {
104
196
  reportClientError("vue", message, detail);
105
197
  };
106
198
 
107
- window.addEventListener("error", (event) => {
108
- const parts = [];
109
- if (event.filename) {
110
- parts.push(`file: ${event.filename}`);
111
- }
112
- if (event.lineno || event.colno) {
113
- parts.push(`line: ${event.lineno || 0}, col: ${event.colno || 0}`);
114
- }
115
- const stack = getErrorDetail(event.error);
116
- if (stack) {
117
- parts.push(stack);
118
- }
119
-
120
- reportClientError("window", getErrorMessage(event.message || event.error), parts.join("\n"));
121
- });
199
+ window.addEventListener(
200
+ "error",
201
+ (event) => {
202
+ const target = event.target;
203
+
204
+ if (target && target !== window) {
205
+ const resourceName = target.tagName ? `${String(target.tagName).toLowerCase()} 资源加载失败` : "资源加载失败";
206
+ const parts = [];
207
+
208
+ if (target?.tagName) {
209
+ parts.push(`tag: ${String(target.tagName).toLowerCase()}`);
210
+ }
211
+
212
+ if (target?.src) {
213
+ parts.push(`src: ${target.src}`);
214
+ }
215
+
216
+ if (target?.href) {
217
+ parts.push(`href: ${target.href}`);
218
+ }
219
+
220
+ reportClientError("resource", resourceName, parts.join("\n"));
221
+ return;
222
+ }
223
+
224
+ const parts = [];
225
+ if (event.filename) {
226
+ parts.push(`file: ${event.filename}`);
227
+ }
228
+ if (event.lineno || event.colno) {
229
+ parts.push(`line: ${event.lineno || 0}, col: ${event.colno || 0}`);
230
+ }
231
+ const stack = getErrorDetail(event.error);
232
+ if (stack) {
233
+ parts.push(stack);
234
+ }
235
+
236
+ reportClientError("window", getErrorMessage(event.message || event.error), parts.join("\n"));
237
+ },
238
+ true
239
+ );
122
240
 
123
241
  window.addEventListener("unhandledrejection", (event) => {
124
242
  const reason = event.reason;
125
243
 
126
244
  reportClientError("promise", getErrorMessage(reason), getErrorDetail(reason));
127
245
  });
246
+
247
+ $Router.onError((error) => {
248
+ reportClientError("router", getErrorMessage(error), getErrorDetail(error));
249
+ });
128
250
  }
@@ -56,18 +56,32 @@ $Router.beforeEach((to, _from) => {
56
56
 
57
57
  // 路由就绪后处理
58
58
  $Router.afterEach((to) => {
59
- void $Http(
60
- "/core/tongJi/visitReport",
61
- {
62
- pagePath: to.fullPath || to.path || "",
63
- pageName: String(to.name || to.meta?.title || to.path || ""),
64
- source: "admin",
65
- productName: $Config.productName,
66
- productCode: $Config.productCode,
67
- productVersion: $Config.productVersion
68
- },
69
- [""]
70
- ).catch(() => {
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(() => {
71
85
  // 静默失败:不阻断路由切换
72
86
  });
73
87
  });