nv-log-bw 1.0.0

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,1393 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>纯JS调用的日志组件范例</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+
14
+ body {
15
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
16
+ line-height: 1.6;
17
+ color: #333;
18
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
19
+ min-height: 100vh;
20
+ padding: 20px;
21
+ }
22
+
23
+ .container {
24
+ max-width: 800px;
25
+ margin: 0 auto;
26
+ background: white;
27
+ border-radius: 20px;
28
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
29
+ overflow: hidden;
30
+ }
31
+
32
+ header {
33
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
34
+ color: white;
35
+ padding: 40px 30px;
36
+ text-align: center;
37
+ }
38
+
39
+ h1 {
40
+ font-size: 2.5em;
41
+ margin-bottom: 10px;
42
+ font-weight: 700;
43
+ }
44
+
45
+ .subtitle {
46
+ font-size: 1.2em;
47
+ opacity: 0.9;
48
+ margin-bottom: 20px;
49
+ }
50
+
51
+ .highlight {
52
+ background: rgba(255, 255, 255, 0.2);
53
+ padding: 3px 8px;
54
+ border-radius: 4px;
55
+ font-family: 'SF Mono', Monaco, Consolas, monospace;
56
+ }
57
+
58
+ .demo-area {
59
+ padding: 30px;
60
+ }
61
+
62
+ .control-panel {
63
+ display: grid;
64
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
65
+ gap: 20px;
66
+ margin-bottom: 30px;
67
+ }
68
+
69
+ .panel-group {
70
+ background: #f8f9fa;
71
+ border-radius: 12px;
72
+ padding: 20px;
73
+ border: 1px solid #e9ecef;
74
+ }
75
+
76
+ .panel-group h3 {
77
+ color: #495057;
78
+ margin-bottom: 15px;
79
+ padding-bottom: 10px;
80
+ border-bottom: 2px solid #dee2e6;
81
+ display: flex;
82
+ align-items: center;
83
+ gap: 10px;
84
+ }
85
+
86
+ .panel-group h3 i {
87
+ font-size: 1.2em;
88
+ }
89
+
90
+ .btn-grid {
91
+ display: grid;
92
+ grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
93
+ gap: 10px;
94
+ }
95
+
96
+ button {
97
+ padding: 12px 20px;
98
+ border: none;
99
+ border-radius: 8px;
100
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
101
+ color: white;
102
+ cursor: pointer;
103
+ font-size: 14px;
104
+ font-weight: 500;
105
+ transition: all 0.3s ease;
106
+ display: flex;
107
+ align-items: center;
108
+ justify-content: center;
109
+ gap: 8px;
110
+ }
111
+
112
+ button:hover:not(:disabled) {
113
+ transform: translateY(-2px);
114
+ box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
115
+ }
116
+
117
+ button:disabled {
118
+ opacity: 0.5;
119
+ cursor: not-allowed;
120
+ transform: none;
121
+ }
122
+
123
+ button.info { background: linear-gradient(135deg, #4299e1 0%, #3182ce 100%); }
124
+ button.success { background: linear-gradient(135deg, #48bb78 0%, #38a169 100%); }
125
+ button.warning { background: linear-gradient(135deg, #ed8936 0%, #dd6b20 100%); }
126
+ button.error { background: linear-gradient(135deg, #f56565 0%, #e53e3e 100%); }
127
+ button.ignore { background: linear-gradient(135deg, #a0aec0 0%, #718096 100%); }
128
+
129
+ button.secondary {
130
+ background: linear-gradient(135deg, #718096 0%, #4a5568 100%);
131
+ }
132
+
133
+ button.small {
134
+ padding: 8px 12px;
135
+ font-size: 12px;
136
+ }
137
+
138
+ .output-section {
139
+ background: #f8f9fa;
140
+ border-radius: 12px;
141
+ padding: 20px;
142
+ margin-top: 20px;
143
+ }
144
+
145
+ .output-section h3 {
146
+ color: #495057;
147
+ margin-bottom: 15px;
148
+ }
149
+
150
+ .code-block {
151
+ background: #1a202c;
152
+ color: #e2e8f0;
153
+ padding: 20px;
154
+ border-radius: 8px;
155
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', Consolas, 'Courier New', monospace;
156
+ font-size: 13px;
157
+ line-height: 1.6;
158
+ overflow-x: auto;
159
+ margin: 10px 0;
160
+ }
161
+
162
+ .keyword { color: #63b3ed; }
163
+ .string { color: #68d391; }
164
+ .comment { color: #718096; }
165
+ .function { color: #f6e05e; }
166
+ .number { color: #81e6d9; }
167
+
168
+ .status-bar {
169
+ position: fixed;
170
+ bottom: 20px;
171
+ right: 20px;
172
+ background: #2d3748;
173
+ color: white;
174
+ padding: 12px 20px;
175
+ border-radius: 8px;
176
+ box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
177
+ font-size: 13px;
178
+ display: flex;
179
+ align-items: center;
180
+ gap: 10px;
181
+ z-index: 1000;
182
+ }
183
+
184
+ .status-bar.hidden {
185
+ display: none;
186
+ }
187
+
188
+ .log-message {
189
+ background: white;
190
+ border-left: 4px solid #4299e1;
191
+ padding: 10px 15px;
192
+ margin: 10px 0;
193
+ border-radius: 4px;
194
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
195
+ font-size: 13px;
196
+ }
197
+
198
+ .log-message.success { border-left-color: #48bb78; }
199
+ .log-message.warning { border-left-color: #ed8936; }
200
+ .log-message.error { border-left-color: #f56565; }
201
+ .log-message.ignore { border-left-color: #a0aec0; }
202
+
203
+ .stats {
204
+ display: grid;
205
+ grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
206
+ gap: 10px;
207
+ margin: 20px 0;
208
+ }
209
+
210
+ .stat-item {
211
+ background: white;
212
+ padding: 15px;
213
+ border-radius: 8px;
214
+ text-align: center;
215
+ border: 1px solid #e2e8f0;
216
+ }
217
+
218
+ .stat-label {
219
+ font-size: 12px;
220
+ color: #718096;
221
+ text-transform: uppercase;
222
+ letter-spacing: 0.5px;
223
+ }
224
+
225
+ .stat-value {
226
+ font-size: 1.5em;
227
+ font-weight: 700;
228
+ margin-top: 5px;
229
+ }
230
+
231
+ .stat-value.info { color: #4299e1; }
232
+ .stat-value.success { color: #48bb78; }
233
+ .stat-value.warning { color: #ed8936; }
234
+ .stat-value.error { color: #f56565; }
235
+ .stat-value.ignore { color: #a0aec0; }
236
+ .stat-value.total { color: #2d3748; }
237
+
238
+ .divider {
239
+ height: 1px;
240
+ background: linear-gradient(to right, transparent, #e2e8f0, transparent);
241
+ margin: 20px 0;
242
+ }
243
+ </style>
244
+ </head>
245
+ <body>
246
+ <div class="container">
247
+ <header>
248
+ <h1>📊 纯 JavaScript 调用的日志组件</h1>
249
+ <p class="subtitle">无需显示界面,直接在代码中使用的日志组件</p>
250
+ </header>
251
+
252
+ <div class="demo-area">
253
+ <!-- 隐藏的日志组件容器 -->
254
+ <div id="loggerContainer" style="display: none;"></div>
255
+
256
+ <div class="control-panel">
257
+ <!-- 日志操作 -->
258
+ <div class="panel-group">
259
+ <h3><i>📝</i> 添加日志</h3>
260
+ <div class="btn-grid">
261
+ <button class="info" onclick="logInfo()">信息日志</button>
262
+ <button class="success" onclick="logSuccess()">成功日志</button>
263
+ <button class="warning" onclick="logWarning()">警告日志</button>
264
+ <button class="error" onclick="logError()">错误日志</button>
265
+ <button class="ignore" onclick="logIgnore()">忽略日志</button>
266
+ </div>
267
+ </div>
268
+
269
+ <!-- 组件控制 -->
270
+ <div class="panel-group">
271
+ <h3><i>⚙️</i> 组件控制</h3>
272
+ <div class="btn-grid">
273
+ <button onclick="toggleLoggerVisibility()">👁️ 显示/隐藏界面</button>
274
+ <button onclick="clearLogs()">🗑️ 清空日志</button>
275
+ <button onclick="exportLogs()">📥 导出日志</button>
276
+ </div>
277
+ </div>
278
+ </div>
279
+
280
+ <div class="divider"></div>
281
+
282
+ <!-- 数据导入 -->
283
+ <div class="panel-group">
284
+ <h3><i>📤</i> 数据导入</h3>
285
+ <div class="btn-grid" style="grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));">
286
+ <button onclick="importJson()">导入JSON</button>
287
+ <button onclick="importText()">导入文本</button>
288
+ <button onclick="importFromAPI()">从API导入</button>
289
+ <button onclick="importRandomLogs()">随机日志</button>
290
+ </div>
291
+ </div>
292
+
293
+ <div class="divider"></div>
294
+
295
+ <!-- 统计分析 -->
296
+ <div class="panel-group">
297
+ <h3><i>📊</i> 统计分析</h3>
298
+ <div class="stats" id="statsContainer">
299
+ <!-- 统计信息会动态更新 -->
300
+ </div>
301
+ <div class="btn-grid">
302
+ <button onclick="updateStats()">更新统计</button>
303
+ <button onclick="getAllLogs()">获取所有日志</button>
304
+ <button onclick="filterLogs()">按类型过滤</button>
305
+ </div>
306
+ </div>
307
+
308
+ <div class="divider"></div>
309
+
310
+ <!-- 代码示例 -->
311
+ <div class="output-section">
312
+ <h3>JavaScript 代码示例</h3>
313
+ <div class="code-block">
314
+ <span class="comment">// 1. 创建和配置日志组件</span><br>
315
+ <span class="keyword">const</span> logger = <span class="keyword">document</span>.createElement(<span class="string">'nv-log-viewer'</span>);<br>
316
+ logger.setAttribute(<span class="string">'id'</span>, <span class="string">'hiddenLogger'</span>);<br>
317
+ logger.setAttribute(<span class="string">'max-logs'</span>, <span class="string">'200'</span>);<br>
318
+ logger.setAttribute(<span class="string">'theme'</span>, <span class="string">'dark'</span>);<br>
319
+ logger.setAttribute(<span class="string">'show-stats'</span>, <span class="string">'false'</span>);<br>
320
+ logger.style.display = <span class="string">'none'</span>; <span class="comment">// 隐藏界面</span><br><br>
321
+ <span class="keyword">document</span>.body.appendChild(logger);<br><br>
322
+ <span class="comment">// 2. 添加日志</span><br>
323
+ logger.addLog(<span class="string">'应用启动成功'</span>, <span class="string">'success'</span>);<br>
324
+ logger.addLog(<span class="string">'正在加载数据...'</span>, <span class="string">'info'</span>);<br>
325
+ logger.addLog({ <span class="keyword">error</span>: <span class="string">'404'</span>, <span class="keyword">message</span>: <span class="string">'页面不存在'</span> }, <span class="string">'error'</span>);<br><br>
326
+ <span class="comment">// 3. 获取和分析日志</span><br>
327
+ <span class="keyword">const</span> logs = logger.getLogs();<br>
328
+ <span class="keyword">const</span> errors = logger.getLogsByType(<span class="string">'error'</span>);<br>
329
+ <span class="keyword">const</span> stats = logger.getStats();<br><br>
330
+ <span class="comment">// 4. 导出日志</span><br>
331
+ logger.exportLogs(<span class="string">'json'</span>); <span class="comment">// 导出为JSON文件</span><br>
332
+ <span class="function">await</span> logger.copyLogs(); <span class="comment">// 复制到剪贴板</span>
333
+ </div>
334
+ </div>
335
+ </div>
336
+ </div>
337
+
338
+ <!-- 状态栏 -->
339
+ <div class="status-bar hidden" id="statusBar">
340
+ <span id="statusMessage">状态信息</span>
341
+ </div>
342
+
343
+ <script>
344
+ var nvlogbw=(()=>{var w=(c,t)=>()=>(t||c((t={exports:{}}).exports,t),t.exports);var L=w((S,y)=>{var b=class extends HTMLElement{static get observedAttributes(){return["max-logs","theme","show-time","time-format","auto-scroll","show-stats","hide-button","auto-destroy","draggable"]}constructor(){super(),this._state={logs:[],showLogs:!0,filters:{info:!0,success:!0,warning:!0,error:!0,ignore:!0},isOpen:!1,isMinimized:!1,originalPosition:{x:0,y:0},originalSize:{width:"800px",height:"600px"},dragInfo:{isDragging:!1,startX:0,startY:0,initialLeft:0,initialTop:0},isDraggingEnabled:!0},this._eventListeners=new Map,this._createFileInput(),this.attachShadow({mode:"open"}),this._config=this._getConfig(),this._render(),this._setupEventListeners()}attributeChangedCallback(t,e,o){if(e!==o)switch(t){case"max-logs":this._config.maxLogs=Math.max(1,parseInt(o,10)||100),this._trimLogs(),this._updateLogsDisplay(),this._updateStats();break;case"theme":this._config.theme=o==="light"?"light":"dark",this._updateTheme();break;case"show-time":this._config.showTime=o!=="false",this._updateLogsDisplay();break;case"time-format":this._config.timeFormat=o||"HH:mm:ss",this._updateLogsDisplay();break;case"auto-scroll":this._config.autoScroll=o!=="false";break;case"show-stats":this._config.showStats=o!=="false",this._updateStatsVisibility();break;case"hide-button":this._config.hideButton=o!=="false",this._updateButtonsVisibility();break;case"auto-destroy":this._config.autoDestroy=o!=="false",this._updateCloseButton();break;case"draggable":this._config.draggable=o!=="false",this._updateDraggableState();break}}connectedCallback(){this._config=this._getConfig(),this._render(),this._setupEventListeners(),this._config.autoDestroy&&window.addEventListener("beforeunload",()=>this._cleanup())}disconnectedCallback(){this._cleanup()}_createFileInput(){this._fileInput=document.createElement("input"),this._fileInput.type="file",this._fileInput.accept=".json,.txt,.log",this._fileInput.style.position="fixed",this._fileInput.style.top="-1000px",this._fileInput.style.left="-1000px",this._fileInput.style.opacity="0",this._fileInput.style.pointerEvents="none",this._fileInput.style.zIndex="-1000",this._fileInput.addEventListener("change",t=>this._handleFileImport(t)),document.body.appendChild(this._fileInput)}_getConfig(){return{maxLogs:Math.max(1,parseInt(this.getAttribute("max-logs")||100,10)),theme:this.getAttribute("theme")==="light"?"light":"dark",showTime:this.getAttribute("show-time")!=="false",timeFormat:this.getAttribute("time-format")||"HH:mm:ss",autoScroll:this.getAttribute("auto-scroll")!=="false",showStats:this.getAttribute("show-stats")!=="false",hideButton:this.getAttribute("hide-button")==="true",autoDestroy:this.getAttribute("auto-destroy")==="true",draggable:this.getAttribute("draggable")!=="false"}}_trimLogs(){this._state.logs.length>this._config.maxLogs&&(this._state.logs=this._state.logs.slice(-this._config.maxLogs))}_updateLogsDisplay(){let t=this.shadowRoot.querySelector("#logContent");if(!t)return;let e=this._state.logs.filter(o=>this._state.filters[o.type]);if(e.length===0){t.innerHTML='<div class="empty-state">\u{1F4DD} \u6CA1\u6709\u65E5\u5FD7\u8BB0\u5F55</div>';return}t.innerHTML="",e.forEach(o=>{let s=document.createElement("div");s.className=`log-entry ${o.type}`;let r="";if(this._config.showTime){let i=new Date(o.time);if(this._config.timeFormat==="relative")r=this._getRelativeTime(o.time);else if(this._config.timeFormat==="HH:mm:ss")r=i.toLocaleTimeString("zh-CN",{hour12:!1});else if(this._config.timeFormat==="HH:mm:ss.ms"){let n=i.toLocaleTimeString("zh-CN",{hour12:!1}),l=String(i.getMilliseconds()).padStart(3,"0");r=`${n}.${l}`}else r=i.toLocaleString("zh-CN")}s.innerHTML=`
345
+ ${this._config.showTime?`<span class="log-time">${r}</span>`:""}
346
+ <span class="log-message">${this._escapeHtml(String(o.message))}</span>
347
+ `,t.appendChild(s)}),this._config.autoScroll&&(t.scrollTop=t.scrollHeight)}_escapeHtml(t){let e=document.createElement("div");return e.textContent=t,e.innerHTML}_getRelativeTime(t){let o=Date.now()-t;return o<6e4?`${Math.floor(o/1e3)}\u79D2\u524D`:o<36e5?`${Math.floor(o/6e4)}\u5206\u949F\u524D`:o<864e5?`${Math.floor(o/36e5)}\u5C0F\u65F6\u524D`:`${Math.floor(o/864e5)}\u5929\u524D`}_updateDraggableState(){let t=this.shadowRoot.querySelector("#dragHandle"),e=this.shadowRoot.querySelector("#logHeader"),o=this.shadowRoot.querySelector("#minimizedTitle"),s=this.shadowRoot.querySelector("#popupContainer");t&&(this._config.draggable?(t.style.display="block",t.style.cursor="move",e&&(e.style.cursor="move"),o&&(o.style.cursor="move"),s&&(s.style.cursor="default")):(t.style.display="none",t.style.cursor="default",e&&(e.style.cursor="default"),o&&(o.style.cursor="default"),s&&(s.style.cursor="default")))}_render(){let t=document.createElement("style");t.textContent=this._getStyles(),this.shadowRoot.innerHTML="",this.shadowRoot.appendChild(t),this.shadowRoot.innerHTML+=`
348
+ <!-- \u6D6E\u52A8\u6309\u94AE -->
349
+ <div id="floatingContainer" class="floating-container" style="display: ${this._config.hideButton?"none":"block"}">
350
+ ${this._state.isOpen?"":`
351
+ <div class="floating-button" id="floatingButton" title="\u6253\u5F00\u65E5\u5FD7">
352
+ \u{1F4C4}
353
+ </div>
354
+ `}
355
+ </div>
356
+
357
+ <!-- \u5F39\u51FA\u7A97\u53E3 -->
358
+ <div class="popup-container" id="popupContainer" style="display: none;">
359
+ <!-- \u5B8C\u6574\u7684\u65E5\u5FD7\u5BB9\u5668 -->
360
+ <div class="log-container ${this._state.showLogs?"expanded":"collapsed"} ${this._config.theme}">
361
+ <div class="log-header" id="logHeader">
362
+ <div class="log-title">
363
+ ${this._config.draggable?'<span class="drag-handle" id="dragHandle" title="\u62D6\u52A8">\u2194</span>':""}
364
+ <span class="log-icon">\u{1F4C4}</span>
365
+ <h3>\u65E5\u5FD7\u67E5\u770B\u5668</h3>
366
+ <span class="log-count" id="logCount">0</span>
367
+ </div>
368
+ <div class="log-actions">
369
+ <button class="toggle-btn" title="${this._state.showLogs?"\u9690\u85CF\u65E5\u5FD7":"\u663E\u793A\u65E5\u5FD7"}">
370
+ ${this._state.showLogs?"\u{1F441}\uFE0F":"\u{1F441}\uFE0F\u200D\u{1F5E8}\uFE0F"}
371
+ </button>
372
+ <button class="clear-btn" title="\u6E05\u7A7A\u65E5\u5FD7">\u{1F5D1}\uFE0F</button>
373
+ <button class="export-btn" title="\u5BFC\u51FA\u65E5\u5FD7">\u{1F4E5}</button>
374
+ <button class="import-btn" title="\u5BFC\u5165\u65E5\u5FD7">\u{1F4E4}</button>
375
+ <button class="copy-btn" title="\u590D\u5236\u65E5\u5FD7">\u{1F4CB}</button>
376
+ <button class="popup-btn" id="minimizeBtn" title="\u6700\u5C0F\u5316">_</button>
377
+ <button class="popup-btn" id="closeBtn" title="${this._config.autoDestroy?"\u5173\u95ED\u5E76\u9500\u6BC1":"\u9690\u85CF"}">\u2715</button>
378
+ </div>
379
+ </div>
380
+
381
+ <div class="log-content" id="logContent">
382
+ <div class="empty-state">\u{1F4DD} \u6CA1\u6709\u65E5\u5FD7\u8BB0\u5F55</div>
383
+ </div>
384
+
385
+ <div class="log-footer">
386
+ <div class="log-filter">
387
+ <label class="filter-label">
388
+ <input type="checkbox" class="filter-checkbox" data-type="info" checked>
389
+ <span class="filter-badge info">\u4FE1\u606F</span>
390
+ </label>
391
+ <label class="filter-label">
392
+ <input type="checkbox" class="filter-checkbox" data-type="success" checked>
393
+ <span class="filter-badge success">\u6210\u529F</span>
394
+ </label>
395
+ <label class="filter-label">
396
+ <input type="checkbox" class="filter-checkbox" data-type="warning" checked>
397
+ <span class="filter-badge warning">\u8B66\u544A</span>
398
+ </label>
399
+ <label class="filter-label">
400
+ <input type="checkbox" class="filter-checkbox" data-type="error" checked>
401
+ <span class="filter-badge error">\u9519\u8BEF</span>
402
+ </label>
403
+ <label class="filter-label">
404
+ <input type="checkbox" class="filter-checkbox" data-type="ignore" checked>
405
+ <span class="filter-badge ignore">\u5FFD\u7565</span>
406
+ </label>
407
+ </div>
408
+ <div class="log-stats">
409
+ <div class="stat-row">
410
+ <span>\u603B\u8BA1: <span id="totalCount">0</span></span>
411
+ <span>\u4FE1\u606F: <span id="infoCount">0</span></span>
412
+ <span>\u6210\u529F: <span id="successCount">0</span></span>
413
+ <span>\u8B66\u544A: <span id="warningCount">0</span></span>
414
+ <span>\u9519\u8BEF: <span id="errorCount">0</span></span>
415
+ <span>\u5FFD\u7565: <span id="ignoreCount">0</span></span>
416
+ </div>
417
+ </div>
418
+ </div>
419
+ </div>
420
+
421
+ <!-- \u6700\u5C0F\u5316\u65F6\u7684\u6807\u9898\u680F -->
422
+ <div class="minimized-title" id="minimizedTitle" style="display: none;">
423
+ <div class="minimized-title-left">
424
+ <span>\u{1F4C4} \u65E5\u5FD7\u67E5\u770B\u5668</span>
425
+ <span class="minimized-log-count" id="minimizedLogCount">0</span>
426
+ <div class="minimized-actions">
427
+ <button class="minimized-popup-btn" id="maximizeBtn" title="\u6700\u5927\u5316">\u25A1</button>
428
+ <button class="minimized-popup-btn" id="closeMinimizedBtn" title="${this._config.autoDestroy?"\u5173\u95ED\u5E76\u9500\u6BC1":"\u9690\u85CF"}">\u2715</button>
429
+ </div>
430
+ </div>
431
+ </div>
432
+
433
+ <div class="resize-handle" id="resizeHandle"></div>
434
+ </div>
435
+ `,this._updateTheme(),this._updateDraggableState()}_getStyles(){return`
436
+ :host {
437
+ display: block;
438
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
439
+ font-size: 12px;
440
+ width: 100%;
441
+ }
442
+
443
+ /* \u6D6E\u52A8\u6309\u94AE\u6837\u5F0F */
444
+ .floating-container {
445
+ position: fixed;
446
+ bottom: 20px;
447
+ right: 20px;
448
+ z-index: 9998;
449
+ }
450
+
451
+ .floating-button {
452
+ width: 50px;
453
+ height: 50px;
454
+ border-radius: 50%;
455
+ background: #1890ff;
456
+ color: white;
457
+ display: flex;
458
+ align-items: center;
459
+ justify-content: center;
460
+ font-size: 20px;
461
+ cursor: pointer;
462
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
463
+ transition: all 0.3s ease;
464
+ user-select: none;
465
+ }
466
+
467
+ .floating-button:hover {
468
+ background: #40a9ff;
469
+ transform: scale(1.1);
470
+ }
471
+
472
+ .floating-button:active {
473
+ transform: scale(0.95);
474
+ }
475
+
476
+ /* \u5F39\u51FA\u7A97\u53E3\u6837\u5F0F */
477
+ .popup-container {
478
+ position: fixed;
479
+ z-index: 9999;
480
+ width: 800px;
481
+ height: 600px;
482
+ display: none;
483
+ border-radius: 8px;
484
+ overflow: hidden;
485
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
486
+ resize: both;
487
+ min-width: 400px;
488
+ min-height: 300px;
489
+ }
490
+
491
+ .popup-container.open {
492
+ display: block;
493
+ }
494
+
495
+ .popup-container.minimized {
496
+ width: 320px !important;
497
+ height: 40px !important;
498
+ min-height: 40px !important;
499
+ min-width: 320px !important;
500
+ resize: none;
501
+ border-radius: 6px;
502
+ display: block;
503
+ }
504
+
505
+ .popup-container.minimized .log-container {
506
+ display: none !important;
507
+ }
508
+
509
+ .popup-container.minimized .resize-handle {
510
+ display: none;
511
+ }
512
+
513
+ /* \u6700\u5C0F\u5316\u65F6\u7684\u6807\u9898\u680F\u6837\u5F0F */
514
+ .minimized-title {
515
+ position: absolute;
516
+ top: 0;
517
+ left: 0;
518
+ right: 0;
519
+ bottom: 0;
520
+ display: none;
521
+ align-items: center;
522
+ padding: 0 12px;
523
+ background: var(--header-bg);
524
+ color: var(--text-color);
525
+ font-size: 12px;
526
+ font-weight: 600;
527
+ cursor: move;
528
+ border-radius: 6px;
529
+ overflow: hidden;
530
+ z-index: 1000;
531
+ }
532
+
533
+ .popup-container.minimized .minimized-title {
534
+ display: flex;
535
+ }
536
+
537
+ .minimized-title-left {
538
+ display: flex;
539
+ align-items: center;
540
+ gap: 8px;
541
+ flex: 1;
542
+ }
543
+
544
+ .minimized-log-count {
545
+ background: var(--button-bg);
546
+ color: var(--button-text);
547
+ padding: 2px 8px;
548
+ border-radius: 10px;
549
+ font-size: 11px;
550
+ font-weight: 500;
551
+ min-width: 20px;
552
+ text-align: center;
553
+ }
554
+
555
+ .minimized-actions {
556
+ display: flex;
557
+ align-items: center;
558
+ gap: 4px;
559
+ margin-left: auto;
560
+ }
561
+
562
+ .minimized-popup-btn {
563
+ background: var(--button-bg);
564
+ border: none;
565
+ border-radius: 4px;
566
+ color: var(--button-text);
567
+ cursor: pointer;
568
+ font-size: 12px;
569
+ width: 24px;
570
+ height: 24px;
571
+ display: flex;
572
+ align-items: center;
573
+ justify-content: center;
574
+ transition: all 0.2s;
575
+ opacity: 0.8;
576
+ font-family: monospace;
577
+ }
578
+
579
+ .minimized-popup-btn:hover {
580
+ background: var(--button-hover);
581
+ transform: translateY(-1px);
582
+ opacity: 1;
583
+ }
584
+
585
+ .resize-handle {
586
+ position: absolute;
587
+ width: 20px;
588
+ height: 20px;
589
+ right: 0;
590
+ bottom: 0;
591
+ cursor: se-resize;
592
+ z-index: 10000;
593
+ }
594
+
595
+ .popup-container.minimized .resize-handle {
596
+ display: none;
597
+ }
598
+
599
+ /* \u6697\u8272\u4E3B\u9898 */
600
+ .log-container.dark {
601
+ --bg-color: #1e1e1e;
602
+ --header-bg: #252526;
603
+ --border-color: #3e3e42;
604
+ --text-color: #d4d4d4;
605
+ --text-muted: #8c8c8c;
606
+ --log-entry-bg: #2d2d30;
607
+ --hover-bg: #323234;
608
+ --info-color: #3794ff;
609
+ --info-bg: rgba(55, 148, 255, 0.1);
610
+ --success-color: #4ec9b0;
611
+ --success-bg: rgba(78, 201, 176, 0.1);
612
+ --warning-color: #dcdcaa;
613
+ --warning-bg: rgba(220, 220, 170, 0.1);
614
+ --error-color: #f48771;
615
+ --error-bg: rgba(244, 135, 113, 0.1);
616
+ --ignore-color: #8a8a8a;
617
+ --ignore-bg: rgba(138, 138, 138, 0.1);
618
+ --button-bg: #3e3e42;
619
+ --button-hover: #4a4a4e;
620
+ --button-text: #d4d4d4;
621
+ --filter-badge-bg: #3e3e42;
622
+ }
623
+
624
+ /* \u4EAE\u8272\u4E3B\u9898 */
625
+ .log-container.light {
626
+ --bg-color: #ffffff;
627
+ --header-bg: #f5f5f5;
628
+ --border-color: #e0e0e0;
629
+ --text-color: #333333;
630
+ --text-muted: #666666;
631
+ --log-entry-bg: #f9f9f9;
632
+ --hover-bg: #f0f0f0;
633
+ --info-color: #1890ff;
634
+ --info-bg: rgba(24, 144, 255, 0.08);
635
+ --success-color: #52c41a;
636
+ --success-bg: rgba(82, 196, 26, 0.08);
637
+ --warning-color: #faad14;
638
+ --warning-bg: rgba(250, 173, 20, 0.08);
639
+ --error-color: #ff4d4f;
640
+ --error-bg: rgba(255, 77, 79, 0.08);
641
+ --ignore-color: #999999;
642
+ --ignore-bg: rgba(153, 153, 153, 0.08);
643
+ --button-bg: #e8e8e8;
644
+ --button-hover: #d9d9d9;
645
+ --button-text: #595959;
646
+ --filter-badge-bg: #e8e8e8;
647
+ }
648
+
649
+ .log-container {
650
+ border: 1px solid var(--border-color);
651
+ border-radius: 8px;
652
+ overflow: hidden;
653
+ transition: all 0.3s ease;
654
+ background: var(--bg-color);
655
+ color: var(--text-color);
656
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
657
+ width: 100%;
658
+ height: 100%;
659
+ display: flex;
660
+ flex-direction: column;
661
+ }
662
+
663
+ .log-container.collapsed {
664
+ height: auto;
665
+ min-height: 0;
666
+ flex: 0 0 auto;
667
+ }
668
+
669
+ .log-container.collapsed .log-header {
670
+ border-bottom: none;
671
+ }
672
+
673
+ .log-container.collapsed .log-content,
674
+ .log-container.collapsed .log-footer {
675
+ display: none;
676
+ }
677
+
678
+ .log-container.expanded {
679
+ flex: 1;
680
+ min-height: 0;
681
+ }
682
+
683
+ .log-container.expanded .log-content,
684
+ .log-container.expanded .log-footer {
685
+ display: block;
686
+ }
687
+
688
+ .log-header {
689
+ display: flex;
690
+ justify-content: space-between;
691
+ align-items: center;
692
+ padding: 10px 16px;
693
+ background: var(--header-bg);
694
+ border-bottom: 1px solid var(--border-color);
695
+ user-select: none;
696
+ transition: background-color 0.2s;
697
+ flex-shrink: 0;
698
+ }
699
+
700
+ .log-header:hover {
701
+ background: var(--hover-bg);
702
+ }
703
+
704
+ .log-title {
705
+ display: flex;
706
+ align-items: center;
707
+ gap: 10px;
708
+ }
709
+
710
+ .drag-handle {
711
+ cursor: move;
712
+ opacity: 0.6;
713
+ font-size: 16px;
714
+ padding: 4px;
715
+ border-radius: 4px;
716
+ transition: all 0.2s;
717
+ }
718
+
719
+ .drag-handle:hover {
720
+ opacity: 1;
721
+ background: var(--button-bg);
722
+ }
723
+
724
+ .log-icon {
725
+ font-size: 16px;
726
+ opacity: 0.8;
727
+ }
728
+
729
+ .log-title h3 {
730
+ margin: 0;
731
+ font-size: 14px;
732
+ font-weight: 600;
733
+ }
734
+
735
+ .log-count {
736
+ background: var(--button-bg);
737
+ color: var(--button-text);
738
+ padding: 2px 8px;
739
+ border-radius: 10px;
740
+ font-size: 11px;
741
+ font-weight: 500;
742
+ min-width: 20px;
743
+ text-align: center;
744
+ }
745
+
746
+ .log-actions {
747
+ display: flex;
748
+ gap: 6px;
749
+ }
750
+
751
+ /* \u6309\u94AE\u6837\u5F0F */
752
+ .popup-btn,
753
+ .toggle-btn,
754
+ .clear-btn,
755
+ .export-btn,
756
+ .import-btn,
757
+ .copy-btn {
758
+ background: var(--button-bg);
759
+ border: none;
760
+ border-radius: 4px;
761
+ color: var(--button-text);
762
+ cursor: pointer;
763
+ font-size: 13px;
764
+ width: 28px;
765
+ height: 28px;
766
+ display: flex;
767
+ align-items: center;
768
+ justify-content: center;
769
+ transition: all 0.2s;
770
+ opacity: 0.8;
771
+ }
772
+
773
+ .popup-btn:hover,
774
+ .toggle-btn:hover,
775
+ .clear-btn:hover,
776
+ .export-btn:hover,
777
+ .import-btn:hover,
778
+ .copy-btn:hover {
779
+ background: var(--button-hover);
780
+ transform: translateY(-1px);
781
+ opacity: 1;
782
+ }
783
+
784
+ /* \u65E5\u5FD7\u5185\u5BB9\u6837\u5F0F */
785
+ .log-content {
786
+ overflow-y: auto;
787
+ flex: 1;
788
+ padding: 8px;
789
+ font-family: 'SF Mono', Monaco, 'Cascadia Code', Consolas, 'Courier New', monospace;
790
+ line-height: 1.5;
791
+ word-break: break-word;
792
+ white-space: pre-wrap;
793
+ scroll-behavior: smooth;
794
+ min-height: 0;
795
+ }
796
+
797
+ .log-entry {
798
+ margin-bottom: 4px;
799
+ padding: 8px 10px;
800
+ border-radius: 4px;
801
+ border-left: 3px solid transparent;
802
+ background: var(--log-entry-bg);
803
+ transition: all 0.2s ease;
804
+ animation: fadeIn 0.3s ease;
805
+ position: relative;
806
+ }
807
+
808
+ .log-entry:hover {
809
+ background: var(--hover-bg);
810
+ transform: translateX(2px);
811
+ }
812
+
813
+ .log-entry.info {
814
+ border-left-color: var(--info-color);
815
+ background: var(--info-bg);
816
+ }
817
+
818
+ .log-entry.info:hover {
819
+ background: var(--info-bg);
820
+ filter: brightness(1.1);
821
+ }
822
+
823
+ .log-entry.success {
824
+ border-left-color: var(--success-color);
825
+ background: var(--success-bg);
826
+ }
827
+
828
+ .log-entry.success:hover {
829
+ background: var(--success-bg);
830
+ filter: brightness(1.1);
831
+ }
832
+
833
+ .log-entry.warning {
834
+ border-left-color: var(--warning-color);
835
+ background: var(--warning-bg);
836
+ }
837
+
838
+ .log-entry.warning:hover {
839
+ background: var(--warning-bg);
840
+ filter: brightness(1.1);
841
+ }
842
+
843
+ .log-entry.error {
844
+ border-left-color: var(--error-color);
845
+ background: var(--error-bg);
846
+ }
847
+
848
+ .log-entry.error:hover {
849
+ background: var(--error-bg);
850
+ filter: brightness(1.1);
851
+ }
852
+
853
+ .log-entry.ignore {
854
+ border-left-color: var(--ignore-color);
855
+ background: var(--ignore-bg);
856
+ opacity: 0.7;
857
+ }
858
+
859
+ .log-entry.ignore:hover {
860
+ background: var(--ignore-bg);
861
+ filter: brightness(1.1);
862
+ opacity: 0.9;
863
+ }
864
+
865
+ .log-time {
866
+ color: var(--text-muted);
867
+ font-size: 10px;
868
+ margin-right: 10px;
869
+ user-select: none;
870
+ font-variant-numeric: tabular-nums;
871
+ }
872
+
873
+ .log-message {
874
+ font-size: 12px;
875
+ word-break: break-word;
876
+ }
877
+
878
+ .log-footer {
879
+ padding: 10px 16px;
880
+ border-top: 1px solid var(--border-color);
881
+ background: var(--header-bg);
882
+ font-size: 11px;
883
+ flex-shrink: 0;
884
+ }
885
+
886
+ .log-filter {
887
+ display: flex;
888
+ flex-wrap: wrap;
889
+ gap: 12px;
890
+ margin-bottom: 8px;
891
+ }
892
+
893
+ .filter-label {
894
+ display: flex;
895
+ align-items: center;
896
+ gap: 5px;
897
+ cursor: pointer;
898
+ color: var(--text-muted);
899
+ transition: opacity 0.2s;
900
+ }
901
+
902
+ .filter-label:hover {
903
+ opacity: 0.8;
904
+ }
905
+
906
+ .filter-checkbox {
907
+ margin: 0;
908
+ cursor: pointer;
909
+ }
910
+
911
+ .filter-badge {
912
+ padding: 2px 8px;
913
+ border-radius: 3px;
914
+ font-size: 10px;
915
+ font-weight: 500;
916
+ background: var(--filter-badge-bg);
917
+ }
918
+
919
+ .filter-badge.info {
920
+ background: var(--info-bg);
921
+ color: var(--info-color);
922
+ }
923
+
924
+ .filter-badge.success {
925
+ background: var(--success-bg);
926
+ color: var(--success-color);
927
+ }
928
+
929
+ .filter-badge.warning {
930
+ background: var(--warning-bg);
931
+ color: var(--warning-color);
932
+ }
933
+
934
+ .filter-badge.error {
935
+ background: var(--error-bg);
936
+ color: var(--error-color);
937
+ }
938
+
939
+ .filter-badge.ignore {
940
+ background: var(--ignore-bg);
941
+ color: var(--ignore-color);
942
+ }
943
+
944
+ .log-stats {
945
+ color: var(--text-muted);
946
+ }
947
+
948
+ .stat-row {
949
+ display: flex;
950
+ flex-wrap: wrap;
951
+ gap: 12px;
952
+ justify-content: space-between;
953
+ }
954
+
955
+ .stat-row span {
956
+ display: inline-flex;
957
+ align-items: center;
958
+ gap: 4px;
959
+ }
960
+
961
+ .stat-row #totalCount {
962
+ font-weight: 600;
963
+ color: var(--text-color);
964
+ }
965
+
966
+ .stat-row #infoCount {
967
+ color: var(--info-color);
968
+ font-weight: 600;
969
+ }
970
+
971
+ .stat-row #successCount {
972
+ color: var(--success-color);
973
+ font-weight: 600;
974
+ }
975
+
976
+ .stat-row #warningCount {
977
+ color: var(--warning-color);
978
+ font-weight: 600;
979
+ }
980
+
981
+ .stat-row #errorCount {
982
+ color: var(--error-color);
983
+ font-weight: 600;
984
+ }
985
+
986
+ .stat-row #ignoreCount {
987
+ color: var(--ignore-color);
988
+ font-weight: 600;
989
+ }
990
+
991
+ .empty-state {
992
+ text-align: center;
993
+ color: var(--text-muted);
994
+ padding: 40px 20px;
995
+ font-style: italic;
996
+ user-select: none;
997
+ }
998
+
999
+ .import-notice {
1000
+ background: var(--info-bg);
1001
+ border-left: 3px solid var(--info-color);
1002
+ color: var(--text-color);
1003
+ padding: 10px;
1004
+ margin: 8px;
1005
+ border-radius: 4px;
1006
+ font-size: 11px;
1007
+ animation: fadeIn 0.3s ease;
1008
+ }
1009
+
1010
+ .import-notice.success {
1011
+ background: var(--success-bg);
1012
+ border-left-color: var(--success-color);
1013
+ }
1014
+
1015
+ .import-notice.error {
1016
+ background: var(--error-bg);
1017
+ border-left-color: var(--error-color);
1018
+ }
1019
+
1020
+ @keyframes fadeIn {
1021
+ from {
1022
+ opacity: 0;
1023
+ transform: translateY(-5px);
1024
+ }
1025
+ to {
1026
+ opacity: 1;
1027
+ transform: translateY(0);
1028
+ }
1029
+ }
1030
+
1031
+ @keyframes pulse {
1032
+ 0%, 100% {
1033
+ opacity: 1;
1034
+ }
1035
+ 50% {
1036
+ opacity: 0.5;
1037
+ }
1038
+ }
1039
+
1040
+ /* \u6EDA\u52A8\u6761\u6837\u5F0F */
1041
+ .log-content::-webkit-scrollbar {
1042
+ width: 6px;
1043
+ }
1044
+
1045
+ .log-content::-webkit-scrollbar-track {
1046
+ background: var(--bg-color);
1047
+ }
1048
+
1049
+ .log-content::-webkit-scrollbar-thumb {
1050
+ background: var(--border-color);
1051
+ border-radius: 3px;
1052
+ }
1053
+
1054
+ .log-content::-webkit-scrollbar-thumb:hover {
1055
+ background: var(--text-muted);
1056
+ }
1057
+ `}_setupEventListeners(){this._eventListeners.forEach(({type:e,handler:o,target:s})=>{s&&s.removeEventListener&&s.removeEventListener(e,o)}),this._eventListeners.clear();let t=e=>{e.target.classList.contains("toggle-btn")?(e.stopPropagation(),e.preventDefault(),this.toggleVisibility()):e.target.classList.contains("clear-btn")?(e.stopPropagation(),e.preventDefault(),this.clearLogs()):e.target.classList.contains("export-btn")?(e.stopPropagation(),e.preventDefault(),this.exportLogs()):e.target.classList.contains("import-btn")?(e.stopPropagation(),e.preventDefault(),this._triggerImport()):e.target.classList.contains("copy-btn")?(e.stopPropagation(),e.preventDefault(),this.copyLogs()):e.target.classList.contains("filter-checkbox")?(e.stopPropagation(),e.preventDefault(),this._applyFilters()):e.target.id==="minimizeBtn"?(e.stopPropagation(),e.preventDefault(),this._minimize()):e.target.id==="closeBtn"?(e.stopPropagation(),e.preventDefault(),this.clos()):e.target.id==="closeMinimizedBtn"?(e.stopPropagation(),e.preventDefault(),this.clos()):e.target.id==="maximizeBtn"?(e.stopPropagation(),e.preventDefault(),this._restore()):e.target.id==="floatingButton"&&(e.stopPropagation(),e.preventDefault(),this.open())};this.shadowRoot.addEventListener("click",t),this._eventListeners.set("click",{type:"click",handler:t,target:this.shadowRoot}),this._setupFloatingButtonDrag(),this._setupDrag(),this._setupResize()}_setupFloatingButtonDrag(){let t=this.shadowRoot.querySelector("#floatingButton"),e=this.shadowRoot.querySelector("#floatingContainer");if(!t||!e)return;let o=!1,s,r,i,n;t.addEventListener("mousedown",l=>{l.preventDefault(),l.stopPropagation();let a=e.getBoundingClientRect();s=l.clientX,r=l.clientY,i=a.left,n=a.top,o=!0;let g=h=>{if(!o)return;let p=h.clientX-s,u=h.clientY-r,f=i+p,m=n+u,x=window.innerWidth-a.width,v=window.innerHeight-a.height;f=Math.max(0,Math.min(f,x)),m=Math.max(0,Math.min(m,v)),e.style.left=`${f}px`,e.style.top=`${m}px`,e.style.right="auto",e.style.bottom="auto"},d=()=>{o=!1,document.removeEventListener("mousemove",g),document.removeEventListener("mouseup",d)};document.addEventListener("mousemove",g),document.addEventListener("mouseup",d)})}_setupDrag(){if(!this._config.draggable)return;let t=this.shadowRoot.querySelector("#popupContainer"),e=this.shadowRoot.querySelector("#dragHandle"),o=this.shadowRoot.querySelector("#logHeader"),s=this.shadowRoot.querySelector("#minimizedTitle");if(!t)return;let r=i=>{i.preventDefault(),i.stopPropagation();let n=t.getBoundingClientRect();this._state.dragInfo.isDragging=!0,this._state.dragInfo.startX=i.clientX,this._state.dragInfo.startY=i.clientY,this._state.dragInfo.initialLeft=n.left,this._state.dragInfo.initialTop=n.top;let l=g=>{if(!this._state.dragInfo.isDragging)return;let d=g.clientX-this._state.dragInfo.startX,h=g.clientY-this._state.dragInfo.startY,p=this._state.dragInfo.initialLeft+d,u=this._state.dragInfo.initialTop+h,f=window.innerWidth-n.width,m=window.innerHeight-n.height;p=Math.max(0,Math.min(p,f)),u=Math.max(0,Math.min(u,m)),t.style.left=`${p}px`,t.style.top=`${u}px`,t.style.transform="none"},a=()=>{this._state.dragInfo.isDragging=!1,document.removeEventListener("mousemove",l),document.removeEventListener("mouseup",a)};document.addEventListener("mousemove",l),document.addEventListener("mouseup",a)};e&&e.addEventListener("mousedown",r),o&&o.addEventListener("mousedown",i=>{this._config.draggable&&i.target!==e&&!i.target.classList.contains("toggle-btn")&&!i.target.classList.contains("clear-btn")&&!i.target.classList.contains("export-btn")&&!i.target.classList.contains("import-btn")&&!i.target.classList.contains("copy-btn")&&!i.target.classList.contains("popup-btn")&&r(i)}),s&&s.addEventListener("mousedown",i=>{this._config.draggable&&(i.target.classList.contains("minimized-popup-btn")||r(i))})}_setupResize(){let t=this.shadowRoot.querySelector("#popupContainer"),e=this.shadowRoot.querySelector("#resizeHandle");if(!t||!e)return;let o=!1,s,r,i,n,l=d=>{if(t.classList.contains("minimized"))return;o=!0;let h=t.getBoundingClientRect();s=h.width,r=h.height,i=d.clientX,n=d.clientY,d.preventDefault()},a=d=>{if(!o)return;let h=s+(d.clientX-i),p=r+(d.clientY-n);t.style.width=`${Math.max(400,h)}px`,t.style.height=`${Math.max(300,p)}px`,d.preventDefault()},g=()=>{o=!1};e.addEventListener("mousedown",l),document.addEventListener("mousemove",a),document.addEventListener("mouseup",g)}_cleanup(){this._eventListeners.forEach(({type:t,handler:e,target:o})=>{o&&o.removeEventListener&&o.removeEventListener(t,e)}),this._eventListeners.clear(),this._fileInput&&this._fileInput.parentNode&&this._fileInput.parentNode.removeChild(this._fileInput),this._config.autoDestroy&&this.remove()}_updateTheme(){let t=this.shadowRoot.querySelector(".log-container");t&&(t.className=`log-container ${this._state.showLogs?"expanded":"collapsed"} ${this._config.theme}`)}_updateStatsVisibility(){let t=this.shadowRoot.querySelector(".log-footer");t&&(t.style.display=this._config.showStats?"block":"none")}_updateButtonsVisibility(){let t=this.shadowRoot.querySelector("#floatingContainer");t&&(this._config.hideButton?t.style.display="none":t.style.display=this._state.isOpen?"none":"block")}_updateCloseButton(){let t=this.shadowRoot.querySelector("#closeBtn"),e=this.shadowRoot.querySelector("#closeMinimizedBtn"),o=this._config.autoDestroy?"\u5173\u95ED\u5E76\u9500\u6BC1":"\u9690\u85CF";t&&(t.title=o),e&&(e.title=o)}_updateStats(){let t={total:this._state.logs.length,info:0,success:0,warning:0,error:0,ignore:0};this._state.logs.forEach(g=>{t[g.type]!==void 0&&t[g.type]++});let e=this.shadowRoot.querySelector("#logCount"),o=this.shadowRoot.querySelector("#minimizedLogCount"),s=this.shadowRoot.querySelector("#totalCount"),r=this.shadowRoot.querySelector("#infoCount"),i=this.shadowRoot.querySelector("#successCount"),n=this.shadowRoot.querySelector("#warningCount"),l=this.shadowRoot.querySelector("#errorCount"),a=this.shadowRoot.querySelector("#ignoreCount");e&&(e.textContent=t.total),o&&(o.textContent=t.total),s&&(s.textContent=t.total),r&&(r.textContent=t.info),i&&(i.textContent=t.success),n&&(n.textContent=t.warning),l&&(l.textContent=t.error),a&&(a.textContent=t.ignore)}_applyFilters(){this.shadowRoot.querySelectorAll(".filter-checkbox").forEach(e=>{let o=e.dataset.type;this._state.filters[o]=e.checked}),this._updateLogsDisplay()}_triggerImport(){this._fileInput.click()}async _handleFileImport(t){let e=t.target.files[0];if(e)try{this.addLog("\u5F00\u59CB\u5BFC\u5165\u65E5\u5FD7\u6587\u4EF6...","info");let o=await e.text(),s=[],r="unknown";try{let n=JSON.parse(o);Array.isArray(n)?(s=n,r="json"):typeof n=="object"&&n.logs&&Array.isArray(n.logs)&&(s=n.logs,r="json-with-metadata")}catch{let l=o.split(`
1058
+ `).filter(a=>a.trim());s=this._parseTextLogs(l),r="text"}if(s.length===0)throw new Error("\u6587\u4EF6\u4E2D\u6CA1\u6709\u627E\u5230\u6709\u6548\u7684\u65E5\u5FD7\u6570\u636E");let i=s.map(n=>this._normalizeLogEntry(n)).filter(n=>n!==null);if(i.length===0)throw new Error("\u6587\u4EF6\u4E2D\u6CA1\u6709\u6709\u6548\u7684\u65E5\u5FD7\u6761\u76EE");this._state.logs.push(...i),this._trimLogs(),this._updateLogsDisplay(),this._updateStats(),this._showImportNotice(`\u6210\u529F\u5BFC\u5165 ${i.length} \u6761\u65E5\u5FD7 (${r}\u683C\u5F0F)`,"success"),this.addLog(`\u4ECE\u6587\u4EF6\u5BFC\u5165 ${i.length} \u6761\u65E5\u5FD7`,"success"),this.dispatchEvent(new CustomEvent("nv-log-import",{detail:{count:i.length,format:r,logs:i}}))}catch(o){console.error("\u5BFC\u5165\u65E5\u5FD7\u5931\u8D25:",o),this._showImportNotice(`\u5BFC\u5165\u5931\u8D25: ${o.message}`,"error"),this.addLog(`\u5BFC\u5165\u65E5\u5FD7\u5931\u8D25: ${o.message}`,"error")}}_parseTextLogs(t){let e=[],o=/^\[(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}(?:\.\d+)?)\]/,s=/^\[(\d{2}:\d{2}:\d{2}(?:\.\d+)?)\]/,r=/\[(INFO|SUCCESS|WARNING|ERROR|IGNORE)\]/i;return t.forEach((i,n)=>{if(!i.trim())return;let l=Date.now(),a="info",g=i,d=i.match(o)||i.match(s);if(d){try{l=new Date(d[1]).getTime()||Date.now()-(t.length-n)*1e3}catch{l=Date.now()-(t.length-n)*1e3}g=i.substring(d[0].length).trim()}let h=g.match(r);if(h){let p=h[1].toLowerCase();["info","success","warning","error","ignore"].includes(p)&&(a=p),g=g.replace(r,"").trim()}e.push({time:l,type:a,message:g})}),e}_normalizeLogEntry(t){if(t==null)return null;if(typeof t=="string")return{time:Date.now(),type:"info",message:t};if(typeof t=="object"){let e={time:t.time||t.timestamp||Date.now(),type:(t.type||t.level||"info").toLowerCase(),message:String(t.message||t.msg||t.text||JSON.stringify(t))};return["info","success","warning","error","ignore"].includes(e.type)||(e.type="info"),(typeof e.time!="number"||isNaN(e.time))&&(e.time=Date.now()),e}return null}_showImportNotice(t,e="info"){let o=this.shadowRoot.querySelector("#logContent");if(!o)return;let s=document.createElement("div");s.className=`import-notice ${e}`,s.textContent=t,o.appendChild(s),setTimeout(()=>{s.parentNode===o&&o.removeChild(s)},3e3)}addLog(t,e="info",o=Date.now()){let s;if(typeof t=="object"&&t!==null){if(s=this._normalizeLogEntry(t),!s){console.warn("\u65E0\u6548\u7684\u65E5\u5FD7\u5BF9\u8C61:",t);return}}else["info","success","warning","error","ignore"].includes(e)||(e="info"),s={message:String(t),type:e,time:o};this._state.logs.push(s),this._trimLogs(),this._updateLogsDisplay(),this._updateStats(),this.dispatchEvent(new CustomEvent("nv-log-add",{detail:{log:s,total:this._state.logs.length,stats:this._getStats()}}))}clearLogs(){let t=this._state.logs.length;this._state.logs=[],this._updateLogsDisplay(),this._updateStats(),this.dispatchEvent(new CustomEvent("nv-log-clear",{detail:{clearedCount:t}}))}toggleVisibility(){this._state.showLogs=!this._state.showLogs;let t=this.shadowRoot.querySelector(".log-container");t&&(t.className=`log-container ${this._state.showLogs?"expanded":"collapsed"} ${this._config.theme}`);let e=this.shadowRoot.querySelector(".toggle-btn");e&&(e.title=this._state.showLogs?"\u9690\u85CF\u65E5\u5FD7":"\u663E\u793A\u65E5\u5FD7",e.textContent=this._state.showLogs?"\u{1F441}\uFE0F":"\u{1F441}\uFE0F\u200D\u{1F5E8}\uFE0F"),this.dispatchEvent(new CustomEvent("nv-log-toggle",{detail:{visible:this._state.showLogs}}))}exportLogs(t="json"){try{if(this._state.logs.length===0){this.addLog("\u6CA1\u6709\u65E5\u5FD7\u53EF\u5BFC\u51FA","warning");return}let e="",o="",s="";if(t==="json"){let a={exportTime:new Date().toISOString(),totalLogs:this._state.logs.length,logs:this._state.logs};e=JSON.stringify(a,null,2),o="application/json",s="json"}else e=this._state.logs.map(a=>`[${new Date(a.time).toLocaleString("zh-CN")}] [${a.type.toUpperCase()}] ${a.message}`).join(`
1059
+ `),o="text/plain;charset=utf-8",s="txt";let r=new Blob([e],{type:o}),i=URL.createObjectURL(r),n=document.createElement("a"),l=new Date().toISOString().replace(/[:.]/g,"-");n.href=i,n.download=`logs_${l}.${s}`,n.click(),URL.revokeObjectURL(i),this.addLog(`\u65E5\u5FD7\u5DF2\u5BFC\u51FA (${this._state.logs.length} \u6761, ${t}\u683C\u5F0F)`,"success")}catch(e){this.addLog(`\u5BFC\u51FA\u65E5\u5FD7\u5931\u8D25: ${e.message}`,"error")}}async importLogs(t,e="auto"){try{let o=[],s="unknown";if(e==="auto"||e==="json")try{let i=JSON.parse(t);Array.isArray(i)?(o=i,s="json"):typeof i=="object"&&i.logs&&Array.isArray(i.logs)&&(o=i.logs,s="json-with-metadata")}catch{if(e==="json")throw new Error("JSON\u683C\u5F0F\u89E3\u6790\u5931\u8D25")}if(o.length===0){let i=t.split(`
1060
+ `).filter(n=>n.trim());o=this._parseTextLogs(i),s="text"}if(o.length===0)throw new Error("\u6CA1\u6709\u627E\u5230\u6709\u6548\u7684\u65E5\u5FD7\u6570\u636E");let r=o.map(i=>this._normalizeLogEntry(i)).filter(i=>i!==null);if(r.length===0)throw new Error("\u6CA1\u6709\u6709\u6548\u7684\u65E5\u5FD7\u6761\u76EE");return this._state.logs.push(...r),this._trimLogs(),this._updateLogsDisplay(),this._updateStats(),this.addLog(`\u5BFC\u5165 ${r.length} \u6761\u65E5\u5FD7 (${s}\u683C\u5F0F)`,"success"),this.dispatchEvent(new CustomEvent("nv-log-import",{detail:{count:r.length,format:s,logs:r}})),{success:!0,count:r.length,format:s}}catch(o){throw console.error("\u5BFC\u5165\u65E5\u5FD7\u5931\u8D25:",o),this.addLog(`\u5BFC\u5165\u65E5\u5FD7\u5931\u8D25: ${o.message}`,"error"),o}}async copyLogs(){try{let t=this._state.logs.map(e=>`[${new Date(e.time).toLocaleString("zh-CN")}] [${e.type.toUpperCase()}] ${e.message}`).join(`
1061
+ `);await navigator.clipboard.writeText(t),this.addLog("\u65E5\u5FD7\u5DF2\u590D\u5236\u5230\u526A\u8D34\u677F","success")}catch(t){this.addLog(`\u590D\u5236\u65E5\u5FD7\u5931\u8D25: ${t.message}`,"error")}}getLogs(){return[...this._state.logs]}getLogsByType(t){return Array.isArray(t)||(t=[t]),this._state.logs.filter(e=>t.includes(e.type))}_getStats(){let t={total:this._state.logs.length,info:0,success:0,warning:0,error:0,ignore:0};return this._state.logs.forEach(e=>{t[e.type]!==void 0&&t[e.type]++}),t}setMaxLogs(t){this._config.maxLogs=Math.max(1,parseInt(t,10)||100),this._trimLogs(),this._updateLogsDisplay(),this._updateStats()}setTheme(t){this._config.theme=t==="light"?"light":"dark",this._updateTheme()}setShowTime(t){this._config.showTime=!!t,this._updateLogsDisplay()}setTimeFormat(t){this._config.timeFormat=t,this._updateLogsDisplay()}setAutoScroll(t){this._config.autoScroll=!!t}setShowStats(t){this._config.showStats=!!t,this._updateStatsVisibility()}setHideButton(t){this._config.hideButton=!!t,this._updateButtonsVisibility()}setAutoDestroy(t){this._config.autoDestroy=!!t,this._updateCloseButton()}setDraggable(t){this._config.draggable=!!t,this._updateDraggableState()}open(){if(!this._state.isOpen){let t=this.shadowRoot.querySelector("#popupContainer");if(t){t.style.display="block",t.classList.add("open"),t.classList.remove("minimized");let e=this.shadowRoot.querySelector("#minimizedTitle");e&&(e.style.display="none");let o=800,s=600,r=Math.max(0,(window.innerWidth-o)/2),i=Math.max(0,(window.innerHeight-s)/2);if(t.style.width=`${o}px`,t.style.height=`${s}px`,t.style.left=`${r}px`,t.style.top=`${i}px`,t.style.transform="none",this._state.isMinimized=!1,this._state.showLogs=!0,this._updateLogsDisplay(),this._updateTheme(),this._updateDraggableState(),!this._config.hideButton){let n=this.shadowRoot.querySelector("#floatingContainer");n&&(n.style.display="none")}}this._state.isOpen=!0,this.addLog("\u{1F4C1} \u65E5\u5FD7\u67E5\u770B\u5668\u5DF2\u6253\u5F00","info")}}clos(){if(this._state.isOpen){let t=this.shadowRoot.querySelector("#popupContainer");if(t){this._state.originalPosition={x:parseInt(t.style.left)||0,y:parseInt(t.style.top)||0},t.classList.remove("open"),t.style.display="none";let e=this.shadowRoot.querySelector("#minimizedTitle");e&&(e.style.display="none")}if(this._state.isOpen=!1,!this._config.hideButton){let e=this.shadowRoot.querySelector("#floatingContainer");e&&(e.style.display="block")}this.addLog("\u{1F4C1} \u65E5\u5FD7\u67E5\u770B\u5668\u5DF2\u5173\u95ED","info"),this._config.autoDestroy&&setTimeout(()=>{this._cleanup()},100)}}_minimize(){let t=this.shadowRoot.querySelector("#popupContainer"),e=this.shadowRoot.querySelector("#minimizedTitle");t&&e&&(this._state.originalSize={width:t.style.width||"800px",height:t.style.height||"600px",minHeight:t.style.minHeight},t.classList.add("minimized"),t.style.width="320px",t.style.height="40px",t.style.minHeight="40px",t.style.minWidth="320px",e.style.display="flex",this._state.isMinimized=!0,this.addLog("\u{1F4F1} \u65E5\u5FD7\u67E5\u770B\u5668\u5DF2\u6700\u5C0F\u5316","info"))}_restore(){let t=this.shadowRoot.querySelector("#popupContainer"),e=this.shadowRoot.querySelector("#minimizedTitle");t&&e&&(t.classList.remove("minimized"),e.style.display="none",this._state.originalSize?(t.style.width=this._state.originalSize.width,t.style.height=this._state.originalSize.height,this._state.originalSize.minHeight?t.style.minHeight=this._state.originalSize.minHeight:t.style.minHeight="300px"):(t.style.width="800px",t.style.height="600px",t.style.minHeight="300px"),t.style.minWidth="400px",this._state.isMinimized=!1,this.addLog("\u{1F4F1} \u65E5\u5FD7\u67E5\u770B\u5668\u5DF2\u6062\u590D","info"))}};b.ELEMENT_NAME="nv-log-viewer";var _=async(c={})=>{let t=document.createElement("nv-log-viewer");return t.setAttribute("hide-button",c.hideButton!==!1?"true":"false"),t.setAttribute("auto-destroy",c.autoDestroy!==!1?"true":"false"),c.maxLogs&&t.setAttribute("max-logs",c.maxLogs),c.theme&&t.setAttribute("theme",c.theme),c.showTime!==void 0&&t.setAttribute("show-time",c.showTime),c.timeFormat&&t.setAttribute("time-format",c.timeFormat),c.showStats!==void 0&&t.setAttribute("show-stats",c.showStats),c.autoScroll!==void 0&&t.setAttribute("auto-scroll",c.autoScroll),document.body.appendChild(t),await new Promise(e=>setTimeout(e,100)),t.open(),t};customElements.get("nv-log-viewer")||customElements.define("nv-log-viewer",b);typeof y<"u"&&y.exports&&(y.exports={NvLogViewer:b,once:_})});return L();})();
1062
+
1063
+ </script>
1064
+ <script>
1065
+ // 全局变量
1066
+ let logger = null;
1067
+ let isLoggerVisible = false;
1068
+ let logCount = 0;
1069
+
1070
+ // 初始化隐藏的日志组件
1071
+ function initHiddenLogger() {
1072
+ // 创建日志组件
1073
+ logger = document.createElement('nv-log-viewer');
1074
+ logger.id = 'hiddenLogger';
1075
+
1076
+ // 配置组件属性
1077
+ logger.setAttribute('max-logs', '200');
1078
+ logger.setAttribute('theme', 'dark');
1079
+ logger.setAttribute('show-time', 'true');
1080
+ logger.setAttribute('time-format', 'HH:mm:ss');
1081
+ logger.setAttribute('auto-scroll', 'true');
1082
+ logger.setAttribute('show-stats', 'false'); // 不显示统计
1083
+
1084
+ // 完全隐藏组件
1085
+ logger.style.display = 'none';
1086
+ logger.style.position = 'fixed';
1087
+ logger.style.left = '-9999px';
1088
+ logger.style.top = '-9999px';
1089
+ logger.style.width = '0';
1090
+ logger.style.height = '0';
1091
+ logger.style.overflow = 'hidden';
1092
+ logger.style.opacity = '0';
1093
+ logger.style.pointerEvents = 'none';
1094
+ logger.style.visibility = 'hidden';
1095
+
1096
+ // 添加到容器
1097
+ document.getElementById('loggerContainer').appendChild(logger);
1098
+
1099
+ // 监听事件
1100
+ logger.addEventListener('nv-log-add', (e) => {
1101
+ logCount = e.detail.total;
1102
+ updateStatsDisplay();
1103
+ showStatus(`添加日志: ${e.detail.log.message.substring(0, 30)}...`);
1104
+ });
1105
+
1106
+ logger.addEventListener('nv-log-clear', (e) => {
1107
+ logCount = 0;
1108
+ updateStatsDisplay();
1109
+ showStatus(`清空了 ${e.detail.clearedCount} 条日志`, 'success');
1110
+ });
1111
+
1112
+ // 添加初始日志
1113
+ setTimeout(() => {
1114
+ logger.addLog('隐藏的日志组件已初始化', 'success');
1115
+ logger.addLog('组件界面被隐藏,但功能完整', 'info');
1116
+ logger.addLog('可以通过JavaScript API完全控制', 'info');
1117
+ }, 100);
1118
+
1119
+ return logger;
1120
+ }
1121
+
1122
+ // 显示状态消息
1123
+ function showStatus(message, type = 'info') {
1124
+ const statusBar = document.getElementById('statusBar');
1125
+ const statusMessage = document.getElementById('statusMessage');
1126
+
1127
+ statusMessage.textContent = message;
1128
+ statusBar.classList.remove('hidden');
1129
+
1130
+ // 设置颜色
1131
+ statusBar.style.background = type === 'success' ? '#38a169' :
1132
+ type === 'error' ? '#e53e3e' :
1133
+ type === 'warning' ? '#d69e2e' : '#2d3748';
1134
+
1135
+ // 3秒后隐藏
1136
+ setTimeout(() => {
1137
+ statusBar.classList.add('hidden');
1138
+ }, 3000);
1139
+ }
1140
+
1141
+ // 更新统计显示
1142
+ function updateStatsDisplay() {
1143
+ if (!logger) return;
1144
+
1145
+ const stats = logger.getStats();
1146
+ const statsContainer = document.getElementById('statsContainer');
1147
+
1148
+ statsContainer.innerHTML = `
1149
+ <div class="stat-item">
1150
+ <div class="stat-label">总计</div>
1151
+ <div class="stat-value total">${stats.total}</div>
1152
+ </div>
1153
+ <div class="stat-item">
1154
+ <div class="stat-label">信息</div>
1155
+ <div class="stat-value info">${stats.info}</div>
1156
+ </div>
1157
+ <div class="stat-item">
1158
+ <div class="stat-label">成功</div>
1159
+ <div class="stat-value success">${stats.success}</div>
1160
+ </div>
1161
+ <div class="stat-item">
1162
+ <div class="stat-label">警告</div>
1163
+ <div class="stat-value warning">${stats.warning}</div>
1164
+ </div>
1165
+ <div class="stat-item">
1166
+ <div class="stat-label">错误</div>
1167
+ <div class="stat-value error">${stats.error}</div>
1168
+ </div>
1169
+ <div class="stat-item">
1170
+ <div class="stat-label">忽略</div>
1171
+ <div class="stat-value ignore">${stats.ignore}</div>
1172
+ </div>
1173
+ `;
1174
+ }
1175
+
1176
+ // 日志操作函数
1177
+ function logInfo() {
1178
+ logger.addLog(`这是第 ${logCount + 1} 条信息日志`, 'info');
1179
+ }
1180
+
1181
+ function logSuccess() {
1182
+ logger.addLog(`操作成功完成 - ${new Date().toLocaleTimeString()}`, 'success');
1183
+ }
1184
+
1185
+ function logWarning() {
1186
+ logger.addLog(`警告:资源使用率过高 (${Math.random() * 100 | 0}%)`, 'warning');
1187
+ }
1188
+
1189
+ function logError() {
1190
+ const errors = [
1191
+ '数据库连接失败',
1192
+ '网络请求超时',
1193
+ '文件读取错误',
1194
+ '权限验证失败',
1195
+ '内存溢出异常'
1196
+ ];
1197
+ const error = errors[Math.floor(Math.random() * errors.length)];
1198
+ logger.addLog(`错误: ${error}`, 'error');
1199
+ }
1200
+
1201
+ function logIgnore() {
1202
+ logger.addLog(`忽略重复记录 #${Math.floor(Math.random() * 1000)}`, 'ignore');
1203
+ }
1204
+
1205
+ // 组件控制函数
1206
+ function toggleLoggerVisibility() {
1207
+ if (!isLoggerVisible) {
1208
+ // 显示组件
1209
+ logger.style.display = 'block';
1210
+ logger.style.position = 'fixed';
1211
+ logger.style.left = '50%';
1212
+ logger.style.top = '50%';
1213
+ logger.style.transform = 'translate(-50%, -50%)';
1214
+ logger.style.width = '600px';
1215
+ logger.style.height = '400px';
1216
+ logger.style.zIndex = '9999';
1217
+ logger.style.opacity = '1';
1218
+ logger.style.visibility = 'visible';
1219
+ logger.style.pointerEvents = 'auto';
1220
+ logger.style.boxShadow = '0 20px 60px rgba(0, 0, 0, 0.3)';
1221
+ isLoggerVisible = true;
1222
+ showStatus('日志界面已显示', 'success');
1223
+ } else {
1224
+ // 隐藏组件
1225
+ logger.style.display = 'none';
1226
+ isLoggerVisible = false;
1227
+ showStatus('日志界面已隐藏', 'info');
1228
+ }
1229
+ }
1230
+
1231
+ function clearLogs() {
1232
+ if (confirm('确定要清空所有日志吗?')) {
1233
+ logger.clearLogs();
1234
+ }
1235
+ }
1236
+
1237
+ function exportLogs() {
1238
+ logger.exportLogs('json');
1239
+ showStatus('日志已导出为JSON文件', 'success');
1240
+ }
1241
+
1242
+ // 导入函数
1243
+ function importJson() {
1244
+ const jsonData = {
1245
+ exportTime: new Date().toISOString(),
1246
+ totalLogs: 3,
1247
+ logs: [
1248
+ {
1249
+ time: Date.now() - 10000,
1250
+ type: 'info',
1251
+ message: '从JSON导入的日志1'
1252
+ },
1253
+ {
1254
+ time: Date.now() - 5000,
1255
+ type: 'success',
1256
+ message: '从JSON导入的日志2'
1257
+ },
1258
+ {
1259
+ time: Date.now(),
1260
+ type: 'error',
1261
+ message: '从JSON导入的日志3'
1262
+ }
1263
+ ]
1264
+ };
1265
+
1266
+ logger.importLogs(JSON.stringify(jsonData), 'json')
1267
+ .then(result => {
1268
+ showStatus(`从JSON导入了 ${result.count} 条日志`, 'success');
1269
+ })
1270
+ .catch(error => {
1271
+ showStatus(`导入失败: ${error.message}`, 'error');
1272
+ });
1273
+ }
1274
+
1275
+ function importText() {
1276
+ const now = new Date();
1277
+ const time1 = now.toLocaleTimeString('zh-CN', { hour12: false });
1278
+ const time2 = new Date(now.getTime() - 5000).toLocaleTimeString('zh-CN', { hour12: false });
1279
+
1280
+ const textData = `[${time2}] [INFO] 从文本导入的日志1
1281
+ [${time1}] [ERROR] 从文本导入的日志2
1282
+ [${new Date(now.getTime() - 2000).toLocaleTimeString('zh-CN', { hour12: false })}] [SUCCESS] 从文本导入的日志3`;
1283
+
1284
+ logger.importLogs(textData, 'text')
1285
+ .then(result => {
1286
+ showStatus(`从文本导入了 ${result.count} 条日志`, 'success');
1287
+ })
1288
+ .catch(error => {
1289
+ showStatus(`导入失败: ${error.message}`, 'error');
1290
+ });
1291
+ }
1292
+
1293
+ async function importFromAPI() {
1294
+ showStatus('正在从模拟API获取数据...', 'info');
1295
+
1296
+ // 模拟API请求
1297
+ await new Promise(resolve => setTimeout(resolve, 1000));
1298
+
1299
+ const mockApiData = {
1300
+ logs: [
1301
+ {
1302
+ timestamp: Date.now() - 15000,
1303
+ level: 'info',
1304
+ msg: '从API获取的用户数据'
1305
+ },
1306
+ {
1307
+ timestamp: Date.now() - 8000,
1308
+ level: 'success',
1309
+ text: 'API请求成功完成'
1310
+ },
1311
+ {
1312
+ timestamp: Date.now(),
1313
+ level: 'warning',
1314
+ message: 'API响应时间过长'
1315
+ }
1316
+ ]
1317
+ };
1318
+
1319
+ logger.importLogs(JSON.stringify(mockApiData), 'json')
1320
+ .then(result => {
1321
+ showStatus(`从API导入了 ${result.count} 条日志`, 'success');
1322
+ })
1323
+ .catch(error => {
1324
+ showStatus(`API导入失败: ${error.message}`, 'error');
1325
+ });
1326
+ }
1327
+
1328
+ function importRandomLogs() {
1329
+ const types = ['info', 'success', 'warning', 'error', 'ignore'];
1330
+ const messages = [
1331
+ '系统初始化完成',
1332
+ '数据处理成功',
1333
+ '缓存命中率较低',
1334
+ '网络连接异常',
1335
+ '跳过无效记录',
1336
+ '用户登录验证',
1337
+ '文件上传完成',
1338
+ '内存使用警告',
1339
+ '数据库查询错误',
1340
+ '任务调度完成'
1341
+ ];
1342
+
1343
+ for (let i = 0; i < 5; i++) {
1344
+ const type = types[Math.floor(Math.random() * types.length)];
1345
+ const message = `${messages[Math.floor(Math.random() * messages.length)]} #${i + 1}`;
1346
+ const time = Date.now() - Math.floor(Math.random() * 30000);
1347
+
1348
+ logger.addLog(message, type, time);
1349
+ }
1350
+
1351
+ showStatus('已添加5条随机日志', 'success');
1352
+ }
1353
+
1354
+ // 统计分析函数
1355
+ function updateStats() {
1356
+ const stats = logger.getStats();
1357
+ showStatus(`统计更新: 总计 ${stats.total} 条日志`, 'info');
1358
+ }
1359
+
1360
+ function getAllLogs() {
1361
+ const logs = logger.getLogs();
1362
+ const last5 = logs.slice(-5);
1363
+
1364
+ const logList = last5.map(log =>
1365
+ `[${new Date(log.time).toLocaleTimeString()}] [${log.type.toUpperCase()}] ${log.message}`
1366
+ ).join('\n');
1367
+
1368
+ alert(`最后5条日志:\n\n${logList}`);
1369
+ }
1370
+
1371
+ function filterLogs() {
1372
+ const errors = logger.getLogsByType('error');
1373
+ const warnings = logger.getLogsByType('warning');
1374
+
1375
+ alert(`过滤结果:\n错误: ${errors.length} 条\n警告: ${warnings.length} 条`);
1376
+ }
1377
+
1378
+ // 页面加载完成后初始化
1379
+ window.addEventListener('DOMContentLoaded', () => {
1380
+ // 初始化隐藏的日志组件
1381
+ logger = initHiddenLogger();
1382
+
1383
+ // 更新统计显示
1384
+ setTimeout(() => {
1385
+ updateStatsDisplay();
1386
+ }, 500);
1387
+
1388
+ // 显示初始化消息
1389
+ showStatus('隐藏的日志组件已就绪,可通过JavaScript API调用', 'success');
1390
+ });
1391
+ </script>
1392
+ </body>
1393
+ </html>