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,410 @@
1
+ <template>
2
+ <div class="error-log-page">
3
+ <!-- 顶部统计卡片 -->
4
+ <div class="stats-section">
5
+ <div class="stat-card" v-for="(value, key) in statistics" :key="key">
6
+ <div class="stat-label">{{ getStatLabel(key) }}</div>
7
+ <div class="stat-value">{{ value }}</div>
8
+ </div>
9
+ </div>
10
+
11
+ <!-- 筛选和操作栏 -->
12
+ <div class="toolbar">
13
+ <van-radio-group v-model="filterType" direction="horizontal" @change="handleFilterChange">
14
+ <van-radio name="">全部</van-radio>
15
+ <van-radio name="api">接口</van-radio>
16
+ <van-radio name="page">页面</van-radio>
17
+ <van-radio name="promise">Promise</van-radio>
18
+ </van-radio-group>
19
+ <div class="actions">
20
+ <van-button size="small" type="danger" plain @click="handleClear">清空</van-button>
21
+ <van-button size="small" type="primary" @click="handleExportJSON">导出JSON</van-button>
22
+ <van-button size="small" type="primary" @click="handleExportText">导出TXT</van-button>
23
+ </div>
24
+ </div>
25
+
26
+ <!-- 日志列表 -->
27
+ <div class="logs-container">
28
+ <van-empty v-if="filteredLogs.length === 0" description="暂无错误日志" />
29
+ <div v-else class="logs-list">
30
+ <div
31
+ v-for="log in filteredLogs"
32
+ :key="log.id"
33
+ class="log-item"
34
+ :class="`log-${log.type}`"
35
+ @click="toggleLogDetail(log)"
36
+ >
37
+ <div class="log-header">
38
+ <div class="log-type-badge" :class="`badge-${log.type}`">
39
+ {{ getTypeLabel(log.type) }}
40
+ </div>
41
+ <div class="log-time">{{ log.time }}</div>
42
+ </div>
43
+ <div class="log-message">{{ log.message || '无错误信息' }}</div>
44
+ <div v-if="log.type === 'api'" class="log-api-info">
45
+ <span class="method">{{ log.method }}</span>
46
+ <span class="url">{{ log.url }}</span>
47
+ <span v-if="log.status" class="status" :class="getStatusClass(log.status)">
48
+ {{ log.status }} {{ log.statusText }}
49
+ </span>
50
+ </div>
51
+ <div v-if="expandedLogs[log.id]" class="log-detail">
52
+ <div class="detail-row">
53
+ <span class="detail-label">页面URL:</span>
54
+ <span class="detail-value">{{ log.pageUrl }}</span>
55
+ </div>
56
+ <div v-if="log.type === 'api'" class="detail-row">
57
+ <span class="detail-label">请求方法:</span>
58
+ <span class="detail-value">{{ log.method }}</span>
59
+ </div>
60
+ <div v-if="log.type === 'api'" class="detail-row">
61
+ <span class="detail-label">请求URL:</span>
62
+ <span class="detail-value">{{ log.url }}</span>
63
+ </div>
64
+ <div v-if="log.type === 'api' && Object.keys(log.params || {}).length > 0" class="detail-row">
65
+ <span class="detail-label">请求参数:</span>
66
+ <pre class="detail-value">{{ JSON.stringify(log.params, null, 2) }}</pre>
67
+ </div>
68
+ <div v-if="log.fileName" class="detail-row">
69
+ <span class="detail-label">文件:</span>
70
+ <span class="detail-value">{{ log.fileName }}:{{ log.lineNumber }}:{{ log.columnNumber }}</span>
71
+ </div>
72
+ <div v-if="log.componentName" class="detail-row">
73
+ <span class="detail-label">组件:</span>
74
+ <span class="detail-value">{{ log.componentName }}</span>
75
+ </div>
76
+ <div v-if="log.stack" class="detail-row">
77
+ <span class="detail-label">堆栈信息:</span>
78
+ <pre class="detail-value stack">{{ log.stack }}</pre>
79
+ </div>
80
+ <div class="detail-row">
81
+ <span class="detail-label">用户代理:</span>
82
+ <span class="detail-value small">{{ log.userAgent }}</span>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ </div>
88
+ </div>
89
+ </template>
90
+
91
+ <script setup>
92
+ import { ref, computed, onMounted } from 'vue'
93
+ import { showConfirmDialog, showToast } from 'vant'
94
+ import errorMonitor from '@/utils/errorMonitor'
95
+
96
+ const filterType = ref('')
97
+ const expandedLogs = ref({})
98
+ const statistics = ref({
99
+ total: 0,
100
+ api: 0,
101
+ page: 0,
102
+ promise: 0,
103
+ today: 0,
104
+ thisWeek: 0,
105
+ thisMonth: 0
106
+ })
107
+
108
+ const allLogs = ref([])
109
+ const filteredLogs = computed(() => {
110
+ if (!filterType.value) {
111
+ return allLogs.value
112
+ }
113
+ return allLogs.value.filter(log => log.type === filterType.value)
114
+ })
115
+
116
+ // 获取统计标签
117
+ const getStatLabel = (key) => {
118
+ const labels = {
119
+ total: '总计',
120
+ api: '接口错误',
121
+ page: '页面错误',
122
+ promise: 'Promise错误',
123
+ today: '今日',
124
+ thisWeek: '本周',
125
+ thisMonth: '本月'
126
+ }
127
+ return labels[key] || key
128
+ }
129
+
130
+ // 获取类型标签
131
+ const getTypeLabel = (type) => {
132
+ const labels = {
133
+ api: '接口',
134
+ page: '页面',
135
+ promise: 'Promise'
136
+ }
137
+ return labels[type] || type
138
+ }
139
+
140
+ // 获取状态样式类
141
+ const getStatusClass = (status) => {
142
+ if (status >= 200 && status < 300) return 'status-success'
143
+ if (status >= 400 && status < 500) return 'status-client-error'
144
+ if (status >= 500) return 'status-server-error'
145
+ return 'status-unknown'
146
+ }
147
+
148
+ // 切换日志详情
149
+ const toggleLogDetail = (log) => {
150
+ expandedLogs.value[log.id] = !expandedLogs.value[log.id]
151
+ }
152
+
153
+ // 筛选改变
154
+ const handleFilterChange = () => {
155
+ expandedLogs.value = {}
156
+ }
157
+
158
+ // 加载日志
159
+ const loadLogs = () => {
160
+ allLogs.value = errorMonitor.getLogs()
161
+ statistics.value = errorMonitor.getStatistics()
162
+ }
163
+
164
+ // 清空日志
165
+ const handleClear = async () => {
166
+ try {
167
+ await showConfirmDialog({
168
+ title: '确认清空',
169
+ message: '确定要清空所有错误日志吗?此操作不可恢复。'
170
+ })
171
+ errorMonitor.clearLogs()
172
+ loadLogs()
173
+ showToast('日志已清空')
174
+ } catch {
175
+ // 用户取消
176
+ }
177
+ }
178
+
179
+ // 导出JSON
180
+ const handleExportJSON = () => {
181
+ try {
182
+ errorMonitor.exportLogs()
183
+ showToast('导出成功')
184
+ } catch (error) {
185
+ showToast('导出失败')
186
+ console.error('导出失败:', error)
187
+ }
188
+ }
189
+
190
+ // 导出TXT
191
+ const handleExportText = () => {
192
+ try {
193
+ errorMonitor.exportLogsAsText()
194
+ showToast('导出成功')
195
+ } catch (error) {
196
+ showToast('导出失败')
197
+ console.error('导出失败:', error)
198
+ }
199
+ }
200
+
201
+ onMounted(() => {
202
+ loadLogs()
203
+ })
204
+ </script>
205
+
206
+ <style lang="scss" scoped>
207
+ .error-log-page {
208
+ padding: 16px;
209
+ background-color: #f5f5f5;
210
+ min-height: 100vh;
211
+ }
212
+
213
+ .stats-section {
214
+ display: grid;
215
+ grid-template-columns: repeat(2, 1fr);
216
+ gap: 12px;
217
+ margin-bottom: 16px;
218
+
219
+ .stat-card {
220
+ background: #fff;
221
+ border-radius: 8px;
222
+ padding: 16px;
223
+ text-align: center;
224
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
225
+
226
+ .stat-label {
227
+ font-size: 12px;
228
+ color: #666;
229
+ margin-bottom: 8px;
230
+ }
231
+
232
+ .stat-value {
233
+ font-size: 24px;
234
+ font-weight: bold;
235
+ color: #333;
236
+ }
237
+ }
238
+ }
239
+
240
+ .toolbar {
241
+ background: #fff;
242
+ border-radius: 8px;
243
+ padding: 12px;
244
+ margin-bottom: 16px;
245
+ display: flex;
246
+ flex-direction: column;
247
+ gap: 12px;
248
+
249
+ .actions {
250
+ display: flex;
251
+ gap: 8px;
252
+ justify-content: flex-end;
253
+ }
254
+ }
255
+
256
+ .logs-container {
257
+ .logs-list {
258
+ display: flex;
259
+ flex-direction: column;
260
+ gap: 12px;
261
+ }
262
+
263
+ .log-item {
264
+ background: #fff;
265
+ border-radius: 8px;
266
+ padding: 16px;
267
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
268
+ cursor: pointer;
269
+ transition: all 0.2s;
270
+
271
+ &:active {
272
+ transform: scale(0.98);
273
+ }
274
+
275
+ .log-header {
276
+ display: flex;
277
+ justify-content: space-between;
278
+ align-items: center;
279
+ margin-bottom: 8px;
280
+
281
+ .log-type-badge {
282
+ display: inline-block;
283
+ padding: 4px 8px;
284
+ border-radius: 4px;
285
+ font-size: 12px;
286
+ font-weight: 500;
287
+ color: #fff;
288
+
289
+ &.badge-api {
290
+ background-color: #ff6b6b;
291
+ }
292
+
293
+ &.badge-page {
294
+ background-color: #4ecdc4;
295
+ }
296
+
297
+ &.badge-promise {
298
+ background-color: #95e1d3;
299
+ }
300
+ }
301
+
302
+ .log-time {
303
+ font-size: 12px;
304
+ color: #999;
305
+ }
306
+ }
307
+
308
+ .log-message {
309
+ font-size: 14px;
310
+ color: #333;
311
+ margin-bottom: 8px;
312
+ word-break: break-all;
313
+ }
314
+
315
+ .log-api-info {
316
+ display: flex;
317
+ align-items: center;
318
+ gap: 8px;
319
+ flex-wrap: wrap;
320
+ font-size: 12px;
321
+
322
+ .method {
323
+ padding: 2px 6px;
324
+ background-color: #f0f0f0;
325
+ border-radius: 3px;
326
+ font-weight: 500;
327
+ }
328
+
329
+ .url {
330
+ color: #666;
331
+ flex: 1;
332
+ word-break: break-all;
333
+ }
334
+
335
+ .status {
336
+ padding: 2px 6px;
337
+ border-radius: 3px;
338
+ font-weight: 500;
339
+
340
+ &.status-success {
341
+ background-color: #d4edda;
342
+ color: #155724;
343
+ }
344
+
345
+ &.status-client-error {
346
+ background-color: #f8d7da;
347
+ color: #721c24;
348
+ }
349
+
350
+ &.status-server-error {
351
+ background-color: #f5c6cb;
352
+ color: #721c24;
353
+ }
354
+
355
+ &.status-unknown {
356
+ background-color: #fff3cd;
357
+ color: #856404;
358
+ }
359
+ }
360
+ }
361
+
362
+ .log-detail {
363
+ margin-top: 12px;
364
+ padding-top: 12px;
365
+ border-top: 1px solid #f0f0f0;
366
+
367
+ .detail-row {
368
+ margin-bottom: 8px;
369
+
370
+ .detail-label {
371
+ font-size: 12px;
372
+ color: #999;
373
+ margin-right: 8px;
374
+ }
375
+
376
+ .detail-value {
377
+ font-size: 12px;
378
+ color: #666;
379
+ word-break: break-all;
380
+
381
+ &.small {
382
+ font-size: 11px;
383
+ }
384
+
385
+ &.stack {
386
+ background-color: #f5f5f5;
387
+ padding: 8px;
388
+ border-radius: 4px;
389
+ white-space: pre-wrap;
390
+ font-family: monospace;
391
+ max-height: 200px;
392
+ overflow-y: auto;
393
+ }
394
+ }
395
+
396
+ pre {
397
+ margin: 4px 0;
398
+ white-space: pre-wrap;
399
+ font-size: 12px;
400
+ background-color: #f5f5f5;
401
+ padding: 8px;
402
+ border-radius: 4px;
403
+ overflow-x: auto;
404
+ }
405
+ }
406
+ }
407
+ }
408
+ }
409
+ </style>
410
+