vue_zhongyou 1.0.1

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.
@@ -0,0 +1,324 @@
1
+ /**
2
+ * 错误监控工具
3
+ * 用于收集接口错误和前端页面错误,生成日志
4
+ */
5
+
6
+ class ErrorMonitor {
7
+ constructor() {
8
+ this.logs = []
9
+ this.maxLogs = 1000 // 最多保存1000条日志
10
+ this.storageKey = 'error_logs'
11
+ this.init()
12
+ }
13
+
14
+ /**
15
+ * 初始化,从localStorage加载历史日志
16
+ */
17
+ init() {
18
+ try {
19
+ const storedLogs = localStorage.getItem(this.storageKey)
20
+ if (storedLogs) {
21
+ this.logs = JSON.parse(storedLogs)
22
+ // 只保留最近的日志
23
+ if (this.logs.length > this.maxLogs) {
24
+ this.logs = this.logs.slice(-this.maxLogs)
25
+ this.saveLogs()
26
+ }
27
+ }
28
+ } catch (error) {
29
+ console.error('加载错误日志失败:', error)
30
+ this.logs = []
31
+ }
32
+ }
33
+
34
+ /**
35
+ * 保存日志到localStorage
36
+ */
37
+ saveLogs() {
38
+ try {
39
+ localStorage.setItem(this.storageKey, JSON.stringify(this.logs))
40
+ } catch (error) {
41
+ console.error('保存错误日志失败:', error)
42
+ // 如果存储失败,可能是存储空间已满,删除一些旧日志
43
+ if (this.logs.length > 100) {
44
+ this.logs = this.logs.slice(-100)
45
+ try {
46
+ localStorage.setItem(this.storageKey, JSON.stringify(this.logs))
47
+ } catch (e) {
48
+ console.error('清理后保存仍然失败:', e)
49
+ }
50
+ }
51
+ }
52
+ }
53
+
54
+ /**
55
+ * 记录接口错误
56
+ * @param {Object} errorInfo - 错误信息
57
+ */
58
+ logApiError(errorInfo) {
59
+ const log = {
60
+ id: this.generateId(),
61
+ type: 'api', // 错误类型:接口错误
62
+ timestamp: new Date().toISOString(),
63
+ time: new Date().toLocaleString('zh-CN'),
64
+ url: errorInfo.url || '',
65
+ method: errorInfo.method || 'GET',
66
+ params: errorInfo.params || {},
67
+ data: errorInfo.data || {},
68
+ status: errorInfo.status || 0,
69
+ statusText: errorInfo.statusText || '',
70
+ message: errorInfo.message || '',
71
+ stack: errorInfo.stack || '',
72
+ userAgent: navigator.userAgent,
73
+ pageUrl: window.location.href,
74
+ ...errorInfo
75
+ }
76
+
77
+ this.addLog(log)
78
+ this.consoleError('接口错误', log)
79
+ }
80
+
81
+ /**
82
+ * 记录前端页面错误
83
+ * @param {Object} errorInfo - 错误信息
84
+ */
85
+ logPageError(errorInfo) {
86
+ const log = {
87
+ id: this.generateId(),
88
+ type: 'page', // 错误类型:页面错误
89
+ timestamp: new Date().toISOString(),
90
+ time: new Date().toLocaleString('zh-CN'),
91
+ message: errorInfo.message || '',
92
+ stack: errorInfo.stack || '',
93
+ fileName: errorInfo.fileName || '',
94
+ lineNumber: errorInfo.lineNumber || 0,
95
+ columnNumber: errorInfo.columnNumber || 0,
96
+ componentName: errorInfo.componentName || '',
97
+ props: errorInfo.props || {},
98
+ userAgent: navigator.userAgent,
99
+ pageUrl: window.location.href,
100
+ ...errorInfo
101
+ }
102
+
103
+ this.addLog(log)
104
+ this.consoleError('页面错误', log)
105
+ }
106
+
107
+ /**
108
+ * 记录Promise未捕获错误
109
+ * @param {Object} errorInfo - 错误信息
110
+ */
111
+ logPromiseError(errorInfo) {
112
+ const log = {
113
+ id: this.generateId(),
114
+ type: 'promise', // 错误类型:Promise错误
115
+ timestamp: new Date().toISOString(),
116
+ time: new Date().toLocaleString('zh-CN'),
117
+ message: errorInfo.message || '',
118
+ stack: errorInfo.stack || '',
119
+ reason: errorInfo.reason || '',
120
+ userAgent: navigator.userAgent,
121
+ pageUrl: window.location.href,
122
+ ...errorInfo
123
+ }
124
+
125
+ this.addLog(log)
126
+ this.consoleError('Promise错误', log)
127
+ }
128
+
129
+ /**
130
+ * 添加日志
131
+ * @param {Object} log - 日志对象
132
+ */
133
+ addLog(log) {
134
+ this.logs.push(log)
135
+
136
+ // 限制日志数量
137
+ if (this.logs.length > this.maxLogs) {
138
+ this.logs.shift() // 删除最旧的日志
139
+ }
140
+
141
+ // 保存到localStorage
142
+ this.saveLogs()
143
+
144
+ // 可以在这里发送到服务器(可选)
145
+ // this.sendToServer(log)
146
+ }
147
+
148
+ /**
149
+ * 控制台输出错误(开发环境)
150
+ */
151
+ consoleError(title, log) {
152
+ if (import.meta.env.DEV) {
153
+ console.group(`[错误监控] ${title}`)
154
+ console.error(log)
155
+ console.groupEnd()
156
+ }
157
+ }
158
+
159
+ /**
160
+ * 生成唯一ID
161
+ */
162
+ generateId() {
163
+ return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`
164
+ }
165
+
166
+ /**
167
+ * 获取所有日志
168
+ */
169
+ getLogs(type = null) {
170
+ if (type) {
171
+ return this.logs.filter(log => log.type === type)
172
+ }
173
+ return [...this.logs].reverse() // 最新的在前
174
+ }
175
+
176
+ /**
177
+ * 获取接口错误日志
178
+ */
179
+ getApiLogs() {
180
+ return this.getLogs('api')
181
+ }
182
+
183
+ /**
184
+ * 获取页面错误日志
185
+ */
186
+ getPageLogs() {
187
+ return this.getLogs('page')
188
+ }
189
+
190
+ /**
191
+ * 获取Promise错误日志
192
+ */
193
+ getPromiseLogs() {
194
+ return this.getLogs('promise')
195
+ }
196
+
197
+ /**
198
+ * 清空日志
199
+ */
200
+ clearLogs() {
201
+ this.logs = []
202
+ localStorage.removeItem(this.storageKey)
203
+ }
204
+
205
+ /**
206
+ * 导出日志为JSON
207
+ */
208
+ exportLogs() {
209
+ const dataStr = JSON.stringify(this.logs, null, 2)
210
+ const dataBlob = new Blob([dataStr], { type: 'application/json' })
211
+ const url = URL.createObjectURL(dataBlob)
212
+ const link = document.createElement('a')
213
+ link.href = url
214
+ link.download = `error-logs-${new Date().toISOString().split('T')[0]}.json`
215
+ document.body.appendChild(link)
216
+ link.click()
217
+ document.body.removeChild(link)
218
+ URL.revokeObjectURL(url)
219
+ }
220
+
221
+ /**
222
+ * 导出日志为文本
223
+ */
224
+ exportLogsAsText() {
225
+ let text = '错误日志导出\n'
226
+ text += `导出时间: ${new Date().toLocaleString('zh-CN')}\n`
227
+ text += `总计: ${this.logs.length} 条\n\n`
228
+ text += '='.repeat(80) + '\n\n'
229
+
230
+ this.logs.reverse().forEach((log, index) => {
231
+ text += `[${index + 1}] ${log.type.toUpperCase()} 错误\n`
232
+ text += `时间: ${log.time}\n`
233
+ text += `页面: ${log.pageUrl}\n`
234
+
235
+ if (log.type === 'api') {
236
+ text += `接口: ${log.method} ${log.url}\n`
237
+ text += `状态: ${log.status} ${log.statusText}\n`
238
+ if (Object.keys(log.params).length > 0) {
239
+ text += `参数: ${JSON.stringify(log.params, null, 2)}\n`
240
+ }
241
+ } else {
242
+ if (log.fileName) {
243
+ text += `文件: ${log.fileName}:${log.lineNumber}:${log.columnNumber}\n`
244
+ }
245
+ if (log.componentName) {
246
+ text += `组件: ${log.componentName}\n`
247
+ }
248
+ }
249
+
250
+ text += `错误信息: ${log.message}\n`
251
+ if (log.stack) {
252
+ text += `堆栈信息:\n${log.stack}\n`
253
+ }
254
+ text += '\n' + '-'.repeat(80) + '\n\n'
255
+ })
256
+
257
+ const dataBlob = new Blob([text], { type: 'text/plain;charset=utf-8' })
258
+ const url = URL.createObjectURL(dataBlob)
259
+ const link = document.createElement('a')
260
+ link.href = url
261
+ link.download = `error-logs-${new Date().toISOString().split('T')[0]}.txt`
262
+ document.body.appendChild(link)
263
+ link.click()
264
+ document.body.removeChild(link)
265
+ URL.revokeObjectURL(url)
266
+ }
267
+
268
+ /**
269
+ * 发送日志到服务器(可选实现)
270
+ * @param {Object} log - 日志对象
271
+ */
272
+ async sendToServer(log) {
273
+ try {
274
+ // 这里可以调用后端接口发送日志
275
+ // const response = await fetch('/api/logs', {
276
+ // method: 'POST',
277
+ // headers: {
278
+ // 'Content-Type': 'application/json'
279
+ // },
280
+ // body: JSON.stringify(log)
281
+ // })
282
+ // if (!response.ok) {
283
+ // console.error('发送日志到服务器失败')
284
+ // }
285
+ } catch (error) {
286
+ console.error('发送日志到服务器出错:', error)
287
+ }
288
+ }
289
+
290
+ /**
291
+ * 获取错误统计信息
292
+ */
293
+ getStatistics() {
294
+ const stats = {
295
+ total: this.logs.length,
296
+ api: this.logs.filter(log => log.type === 'api').length,
297
+ page: this.logs.filter(log => log.type === 'page').length,
298
+ promise: this.logs.filter(log => log.type === 'promise').length,
299
+ today: 0,
300
+ thisWeek: 0,
301
+ thisMonth: 0
302
+ }
303
+
304
+ const now = new Date()
305
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
306
+ const weekAgo = new Date(today.getTime() - 7 * 24 * 60 * 60 * 1000)
307
+ const monthAgo = new Date(now.getFullYear(), now.getMonth() - 1, now.getDate())
308
+
309
+ this.logs.forEach(log => {
310
+ const logDate = new Date(log.timestamp)
311
+ if (logDate >= today) stats.today++
312
+ if (logDate >= weekAgo) stats.thisWeek++
313
+ if (logDate >= monthAgo) stats.thisMonth++
314
+ })
315
+
316
+ return stats
317
+ }
318
+ }
319
+
320
+ // 创建单例
321
+ const errorMonitor = new ErrorMonitor()
322
+
323
+ export default errorMonitor
324
+
@@ -0,0 +1,103 @@
1
+ import { createApp } from 'vue'
2
+ import { createPinia } from 'pinia'
3
+ import App from './App.vue'
4
+ // import './style.css'
5
+ import './assets/common.css'
6
+
7
+ // 引入 Vant 组件库
8
+ import Vant from 'vant'
9
+ import 'vant/lib/index.css'
10
+
11
+ // 引入路由
12
+ import router from './router'
13
+
14
+ // 引入错误监控
15
+ import errorMonitor from '@/utils/errorMonitor'
16
+
17
+ const app = createApp(App)
18
+
19
+ // 使用 Pinia 状态管理
20
+ app.use(createPinia())
21
+
22
+ // 使用 Vant 组件库
23
+ app.use(Vant)
24
+
25
+ // 使用路由
26
+ app.use(router)
27
+
28
+ // 注册全局错误处理
29
+ // Vue错误处理
30
+ app.config.errorHandler = (err, instance, info) => {
31
+ // 获取组件名称
32
+ let componentName = 'Unknown'
33
+ if (instance) {
34
+ const name = instance.$options?.name || instance.$options?.__name
35
+ componentName = name || componentName
36
+ }
37
+
38
+ // 记录页面错误
39
+ errorMonitor.logPageError({
40
+ message: err.message || String(err),
41
+ stack: err.stack || '',
42
+ componentName: componentName,
43
+ info: info || '',
44
+ props: instance?.$props || {},
45
+ url: instance?.$route?.fullPath || window.location.href
46
+ })
47
+ }
48
+
49
+ // Promise未捕获错误
50
+ window.addEventListener('unhandledrejection', event => {
51
+ event.preventDefault()
52
+
53
+ const reason = event.reason
54
+ const errorInfo = {
55
+ message: reason?.message || String(reason) || 'Promise未捕获错误',
56
+ stack: reason?.stack || '',
57
+ reason: reason
58
+ }
59
+
60
+ // 如果是Error对象,提取更多信息
61
+ if (reason instanceof Error) {
62
+ errorInfo.message = reason.message
63
+ errorInfo.stack = reason.stack
64
+ }
65
+
66
+ errorMonitor.logPromiseError(errorInfo)
67
+
68
+ // 在开发环境输出到控制台
69
+ if (import.meta.env.DEV) {
70
+ console.error('未捕获的Promise错误:', reason)
71
+ }
72
+ })
73
+
74
+ // 全局JavaScript错误
75
+ window.addEventListener('error', event => {
76
+ // 过滤掉资源加载错误(会单独处理)
77
+ if (event.target && event.target !== window) {
78
+ return
79
+ }
80
+
81
+ errorMonitor.logPageError({
82
+ message: event.message || '未知错误',
83
+ stack: event.error?.stack || '',
84
+ fileName: event.filename || '',
85
+ lineNumber: event.lineno || 0,
86
+ columnNumber: event.colno || 0
87
+ })
88
+ })
89
+
90
+ // 资源加载错误(图片、脚本等)
91
+ window.addEventListener('error', event => {
92
+ // 资源加载错误
93
+ if (event.target && event.target !== window) {
94
+ const target = event.target
95
+ errorMonitor.logPageError({
96
+ message: `资源加载失败: ${target.tagName} ${target.src || target.href || ''}`,
97
+ fileName: target.src || target.href || '',
98
+ resourceType: target.tagName
99
+ })
100
+ }
101
+ }, true) // 使用捕获阶段
102
+
103
+ app.mount('#app')
@@ -0,0 +1,89 @@
1
+ import axios from 'axios'
2
+ import errorMonitor from '@/utils/errorMonitor'
3
+
4
+ // 创建 axios 实例
5
+ const request = axios.create({
6
+ baseURL: import.meta.env.VITE_API_BASE_URL || '', // 接口基础地址
7
+ timeout: 10000, // 请求超时时间
8
+ headers: {
9
+ 'Content-Type': 'application/json;charset=UTF-8'
10
+ }
11
+ })
12
+
13
+ // 请求拦截器
14
+ request.interceptors.request.use(
15
+ config => {
16
+ // 在发送请求之前做些什么
17
+ // 例如:添加token
18
+ // const token = localStorage.getItem('token')
19
+ // if (token) {
20
+ // config.headers.Authorization = `Bearer ${token}`
21
+ // }
22
+ return config
23
+ },
24
+ error => {
25
+ // 对请求错误做些什么
26
+ console.error('请求错误:', error)
27
+
28
+ // 记录接口错误
29
+ errorMonitor.logApiError({
30
+ url: error.config?.url || '',
31
+ method: error.config?.method?.toUpperCase() || 'GET',
32
+ params: error.config?.params || {},
33
+ data: error.config?.data || {},
34
+ message: error.message || '请求错误',
35
+ stack: error.stack || ''
36
+ })
37
+
38
+ return Promise.reject(error)
39
+ }
40
+ )
41
+
42
+ // 响应拦截器
43
+ request.interceptors.response.use(
44
+ response => {
45
+ // 对响应数据做点什么
46
+ const { data } = response
47
+
48
+ // 如果业务代码返回错误,也可以在这里记录
49
+ // 例如:if (data.code !== 200) { ... }
50
+
51
+ return data
52
+ },
53
+ error => {
54
+ // 对响应错误做点什么
55
+ console.error('响应错误:', error)
56
+
57
+ // 记录接口错误
58
+ const errorInfo = {
59
+ url: error.config?.url || '',
60
+ method: error.config?.method?.toUpperCase() || 'GET',
61
+ params: error.config?.params || {},
62
+ data: error.config?.data || {},
63
+ message: error.message || '响应错误',
64
+ stack: error.stack || ''
65
+ }
66
+
67
+ // 如果是HTTP错误响应
68
+ if (error.response) {
69
+ errorInfo.status = error.response.status
70
+ errorInfo.statusText = error.response.statusText
71
+ errorInfo.responseData = error.response.data
72
+ } else if (error.request) {
73
+ // 请求已发出但没有收到响应
74
+ errorInfo.status = 0
75
+ errorInfo.statusText = '网络错误或无响应'
76
+ errorInfo.message = '网络错误,请检查网络连接'
77
+ } else {
78
+ // 其他错误
79
+ errorInfo.status = 0
80
+ errorInfo.statusText = '请求配置错误'
81
+ }
82
+
83
+ errorMonitor.logApiError(errorInfo)
84
+
85
+ return Promise.reject(error)
86
+ }
87
+ )
88
+
89
+ export default request