w-web-api 1.0.77 → 1.0.79
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/dist/css/{app.ae472c4d.css → app.9fbdb016.css} +1 -1
- package/dist/index.tmp +1 -1
- package/dist/js/app-legacy.075a8cf9.js +1 -0
- package/dist/js/app.075a8cf9.js +1 -0
- package/dist/js/{chunk-vendors.47cf6375.js → chunk-vendors-legacy.de0404f6.js} +1 -1
- package/dist/js/{chunk-vendors-legacy.47cf6375.js → chunk-vendors.de0404f6.js} +1 -1
- package/dist/w-web-api.umd.js +2 -2
- package/dist/w-web-api.umd.js.map +1 -1
- package/docs/WWebApi.html +122 -2
- package/docs/index.html +1 -1
- package/docs/server_WWebApi.mjs.html +5 -2
- package/docs/src_components_Common_CheckYesNo.vue.html +1 -1
- package/package.json +16 -14
- package/server/WWebApi.mjs +4 -1
- package/server/procApis.mjs +26 -14
- package/server/procLang.mjs +0 -17
- package/server/srLog.mjs +13 -4
- package/server/staLogs/staEvent.mjs +8 -120
- package/server/staLogs/staLogsCore.callWorker.mjs +78 -0
- package/server/staLogs/staLogsCore.mjs +420 -0
- package/server/staLogs/staLogsCore.shellWorker.mjs +20 -0
- package/server/staLogs/test_staEvent.mjs +23 -7
- package/settings.json +1 -0
- package/src/components/LayoutContentEdit.vue +14 -2
- package/src/components/LayoutContentStats.vue +445 -557
- package/tableTags.json +1 -1
- package/dist/js/app-legacy.2aa145e5.js +0 -1
- package/dist/js/app.2aa145e5.js +0 -1
- package/logs/2026-06-19T16.log +0 -6
- package/logs/2026-06-20T11.log +0 -4
- package/logs/2026-06-20T14.log +0 -2
- package/logs/2026-06-21T20.log +0 -1
- package/logs/2026-06-21T21.log +0 -4
- package/logs/2026-06-22T07.log +0 -12
- package/logs/2026-06-22T08.log +0 -808
- package/logs/2026-06-22T09.log +0 -104
- package/logs/2026-06-22T10.log +0 -26
- package/logs/2026-06-22T11.log +0 -860
- package/logs/2026-06-22T12.log +0 -430
- package/logs/2026-06-22T23.log +0 -1547
- package/logs/2026-06-23T00.log +0 -473
- package/logs/2026-06-23T08.log +0 -231
- package/logs/2026-06-23T09.log +0 -179
- package/logs/2026-07-01T11.log +0 -488
- package/logs/2026-07-09T22.log +0 -370
- package/logs/2026-07-09T23.log +0 -370
- package/logs/2026-07-10T12.log +0 -284
- package/logs/2026-07-10T13.log +0 -777
- package/logs/2026-07-11T17.log +0 -2
- package/logs/2026-07-23T16.log +0 -856
- package/logs/2026-08-19T22.log +0 -0
- package/logs/2026-08-25T20.log +0 -0
- package/logs/2026-08-25T21.log +0 -1891
- package/z/344/277/256/346/255/243/350/246/217/345/212/203/346/233/270.md +0 -246
|
@@ -1,557 +1,445 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div class="stats-panel" :style="`height:${height}px; overflow-y:auto; padding:20px 24px; box-sizing:border-box;`">
|
|
3
|
-
|
|
4
|
-
<!-- 標題列(ref 供 chartHeight 量測上方實佔高度) -->
|
|
5
|
-
<div ref="$hdr" style="margin-bottom:16px;">
|
|
6
|
-
<div style="font-size:1.4rem; font-weight:600; color:var(--c-1);">{{$t('statisticsInformation')}}</div>
|
|
7
|
-
<div style="font-size:0.875rem; color:var(--c-3); margin-top:4px;">{{$t('statisticsInformationDescription')}}</div>
|
|
8
|
-
</div>
|
|
9
|
-
|
|
10
|
-
<!--
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
<div ref="$ctrl" class="ctrl">
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
<option value="
|
|
21
|
-
<option value="
|
|
22
|
-
<option value="
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
:
|
|
43
|
-
|
|
44
|
-
:
|
|
45
|
-
|
|
46
|
-
>
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
let
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
let
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
let
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
let
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
},
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
.
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
.evt-sel-btn {
|
|
448
|
-
border: 1px solid var(--border);
|
|
449
|
-
border-radius: var(--radius);
|
|
450
|
-
background: var(--bg-1);
|
|
451
|
-
color: var(--c-2);
|
|
452
|
-
font-size: 0.8rem;
|
|
453
|
-
padding: 3px 12px;
|
|
454
|
-
cursor: pointer;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
.evt-sel-btn:hover {
|
|
458
|
-
border-color: var(--accent);
|
|
459
|
-
color: var(--accent);
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
.evt-count {
|
|
463
|
-
font-size: 0.78rem;
|
|
464
|
-
color: var(--c-3);
|
|
465
|
-
margin-left: 4px;
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
/* 事件 chip:flex-wrap 內容自然寬(用滿容器可用寬、排不下才換行),
|
|
469
|
-
置於 .ctrl grid 第二欄與上方控制項對齊;不設 max-width 上限(由容器自然約束) */
|
|
470
|
-
.evt-grid {
|
|
471
|
-
grid-column: 2;
|
|
472
|
-
min-width: 0;
|
|
473
|
-
display: flex;
|
|
474
|
-
flex-wrap: wrap;
|
|
475
|
-
gap: 8px;
|
|
476
|
-
}
|
|
477
|
-
|
|
478
|
-
.evt-chip {
|
|
479
|
-
display: flex;
|
|
480
|
-
align-items: center;
|
|
481
|
-
box-sizing: border-box;
|
|
482
|
-
flex: 0 1 auto;
|
|
483
|
-
min-width: 0;
|
|
484
|
-
max-width: 100%; /* 安全閥:極長事件名時 chip 不超過容器寬(此時 .evt-name 之 ellipsis 才生效) */
|
|
485
|
-
border: 1px solid var(--border);
|
|
486
|
-
border-radius: var(--radius);
|
|
487
|
-
background: var(--bg-1);
|
|
488
|
-
padding: 5px 10px;
|
|
489
|
-
font-size: 0.82rem;
|
|
490
|
-
color: var(--c-2);
|
|
491
|
-
cursor: pointer;
|
|
492
|
-
transition: border-color .12s, background .12s, color .12s;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
.evt-chip:hover {
|
|
496
|
-
border-color: var(--accent);
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
.evt-chip.on {
|
|
500
|
-
color: var(--c-1);
|
|
501
|
-
border-color: var(--accent);
|
|
502
|
-
background: rgba(0, 153, 255, 0.06);
|
|
503
|
-
}
|
|
504
|
-
|
|
505
|
-
.evt-chip .stats-evt-cb {
|
|
506
|
-
cursor: pointer;
|
|
507
|
-
margin: 0 6px 0 0;
|
|
508
|
-
flex-shrink: 0;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
.evt-dot {
|
|
512
|
-
display: inline-block;
|
|
513
|
-
width: 9px;
|
|
514
|
-
height: 9px;
|
|
515
|
-
border-radius: 2px;
|
|
516
|
-
margin-right: 6px;
|
|
517
|
-
flex-shrink: 0;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
.evt-name {
|
|
521
|
-
min-width: 0; /* flex item 預設 min-width:auto 會阻止收縮,明給 0 使安全閥可生效 */
|
|
522
|
-
overflow: hidden;
|
|
523
|
-
text-overflow: ellipsis;
|
|
524
|
-
white-space: nowrap;
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
.stats-table-area {
|
|
528
|
-
overflow-x: auto; /* 極端長事件名時表格自帶水平捲動,不外溢撐破 panel */
|
|
529
|
-
}
|
|
530
|
-
|
|
531
|
-
.stats-table {
|
|
532
|
-
width: 100%;
|
|
533
|
-
border-collapse: collapse;
|
|
534
|
-
font-size: 0.85rem;
|
|
535
|
-
}
|
|
536
|
-
|
|
537
|
-
.stats-table td:first-child {
|
|
538
|
-
white-space: normal; /* 事件名欄允許換行(數值欄維持 nowrap),與 chip 區同源之防溢出處置 */
|
|
539
|
-
overflow-wrap: anywhere;
|
|
540
|
-
}
|
|
541
|
-
|
|
542
|
-
.stats-table th,
|
|
543
|
-
.stats-table td {
|
|
544
|
-
border-bottom: 1px solid var(--border);
|
|
545
|
-
padding: 7px 10px;
|
|
546
|
-
text-align: right;
|
|
547
|
-
color: var(--c-1);
|
|
548
|
-
white-space: nowrap;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
.stats-table th {
|
|
552
|
-
color: var(--c-2);
|
|
553
|
-
font-weight: 600;
|
|
554
|
-
background: var(--bg-2);
|
|
555
|
-
}
|
|
556
|
-
|
|
557
|
-
</style>
|
|
1
|
+
<template>
|
|
2
|
+
<div class="stats-panel" :style="`height:${height}px; overflow-y:auto; padding:20px 24px; box-sizing:border-box;`">
|
|
3
|
+
|
|
4
|
+
<!-- 標題列(ref 供 chartHeight 量測上方實佔高度) -->
|
|
5
|
+
<div ref="$hdr" style="margin-bottom:16px;">
|
|
6
|
+
<div style="font-size:1.4rem; font-weight:600; color:var(--c-1);">{{$t('statisticsInformation')}}</div>
|
|
7
|
+
<div style="font-size:0.875rem; color:var(--c-3); margin-top:4px;">{{$t('statisticsInformationDescription')}}</div>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<!-- 控制區(時間範圍):.ctrl 為兩欄 grid(label 欄寬 max-content 自動隨語系文字),
|
|
11
|
+
.ctrl-row 以 display:contents 使 label 與 field 直接成為 grid 子項,中英文自動對齊。
|
|
12
|
+
事件之顯示/隱藏切換由 echarts 圖例(legend)提供,不另建 checkbox(功能重複)。 -->
|
|
13
|
+
<div ref="$ctrl" class="ctrl">
|
|
14
|
+
|
|
15
|
+
<div class="ctrl-row">
|
|
16
|
+
<span class="ctrl-label">{{$t('timeRange')}}</span>
|
|
17
|
+
<div class="ctrl-field">
|
|
18
|
+
<select id="timeGroupSel" v-model="timeGroup" class="ctrl-select">
|
|
19
|
+
<option value="1hr">{{$t('selectItem1hr')}}</option>
|
|
20
|
+
<option value="4hr">{{$t('selectItem4hr')}}</option>
|
|
21
|
+
<option value="8hr">{{$t('selectItem8hr')}}</option>
|
|
22
|
+
<option value="1day">{{$t('selectItem1day')}}</option>
|
|
23
|
+
</select>
|
|
24
|
+
</div>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
</div>
|
|
28
|
+
|
|
29
|
+
<!-- 錯誤 -->
|
|
30
|
+
<div v-if="errMsg" style="padding:12px 0; font-size:0.875rem; color:var(--danger);">
|
|
31
|
+
{{errMsg}}
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
<!-- 先到先畫(ADR-028):圖表區與統計表區骨架立即渲染,資料未到前各自以轉圈佔位(圖區與圖同高,不跳版);載入後 DOM 與先前一致 -->
|
|
35
|
+
<template v-else>
|
|
36
|
+
|
|
37
|
+
<!-- 圖表區(event 展示區;各事件各一系列、各自顏色,可區分趨勢;點圖例可切換個別事件顯示) -->
|
|
38
|
+
<div class="stats-chart-area">
|
|
39
|
+
<div
|
|
40
|
+
v-if="loading"
|
|
41
|
+
style="display:flex; align-items:center; justify-content:center; gap:8px; color:var(--c-3); font-size:0.875rem;"
|
|
42
|
+
:style="`height:${chartHeight}px;`"
|
|
43
|
+
>
|
|
44
|
+
<WIconLoading :name="'cir-rotate'" :size="24" :color="'#9aa1ac'"></WIconLoading>
|
|
45
|
+
<span>{{$t('waitingData')}}</span>
|
|
46
|
+
</div>
|
|
47
|
+
<div
|
|
48
|
+
v-else-if="hasNoData"
|
|
49
|
+
style="display:flex; align-items:center; justify-content:center; color:var(--c-3); font-size:0.875rem;"
|
|
50
|
+
:style="`height:${chartHeight}px;`"
|
|
51
|
+
>
|
|
52
|
+
{{$t('noStaData')}}
|
|
53
|
+
</div>
|
|
54
|
+
<WEchartsVue
|
|
55
|
+
v-else
|
|
56
|
+
:options="chartOption"
|
|
57
|
+
:style="`width:100%; height:${chartHeight}px;`"
|
|
58
|
+
></WEchartsVue>
|
|
59
|
+
</div>
|
|
60
|
+
|
|
61
|
+
<!-- 事件統計表(各事件為列,依最近1日總數多→少排序;欄位 1日/8時/4時/1時) -->
|
|
62
|
+
<div class="stats-table-area">
|
|
63
|
+
<div style="font-size:1rem; font-weight:600; color:var(--c-1); margin:18px 0 8px;">{{$t('eventStatsTable')}}</div>
|
|
64
|
+
<div v-if="loading" style="display:flex; align-items:center; gap:8px; font-size:0.875rem; color:var(--c-3); padding:8px 0;">
|
|
65
|
+
<WIconLoading :name="'cir-rotate'" :size="20" :color="'#9aa1ac'"></WIconLoading>
|
|
66
|
+
<span>{{$t('waitingData')}}</span>
|
|
67
|
+
</div>
|
|
68
|
+
<div v-else-if="eventRows.length === 0" style="font-size:0.875rem; color:var(--c-3); padding:8px 0;">
|
|
69
|
+
{{$t('noStaData')}}
|
|
70
|
+
</div>
|
|
71
|
+
<table v-else class="stats-table">
|
|
72
|
+
<thead>
|
|
73
|
+
<tr>
|
|
74
|
+
<th style="text-align:left;">{{$t('colEvent')}}</th>
|
|
75
|
+
<th>{{$t('colRecent1day')}}</th>
|
|
76
|
+
<th>{{$t('colRecent8hr')}}</th>
|
|
77
|
+
<th>{{$t('colRecent4hr')}}</th>
|
|
78
|
+
<th>{{$t('colRecent1hr')}}</th>
|
|
79
|
+
</tr>
|
|
80
|
+
</thead>
|
|
81
|
+
<tbody>
|
|
82
|
+
<tr v-for="row in eventRows" :key="row.event" :data-event="row.event">
|
|
83
|
+
<td style="text-align:left;">
|
|
84
|
+
<span class="evt-dot" :style="`background:${colorOf(row.event)};`"></span>{{row.event}}
|
|
85
|
+
</td>
|
|
86
|
+
<td>{{row.d1day}}</td>
|
|
87
|
+
<td>{{row.d8hr}}</td>
|
|
88
|
+
<td>{{row.d4hr}}</td>
|
|
89
|
+
<td>{{row.d1hr}}</td>
|
|
90
|
+
</tr>
|
|
91
|
+
</tbody>
|
|
92
|
+
</table>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
</template>
|
|
96
|
+
|
|
97
|
+
</div>
|
|
98
|
+
</template>
|
|
99
|
+
|
|
100
|
+
<script>
|
|
101
|
+
import ot from 'dayjs'
|
|
102
|
+
import get from 'lodash-es/get.js'
|
|
103
|
+
import map from 'lodash-es/map.js'
|
|
104
|
+
import keys from 'lodash-es/keys.js'
|
|
105
|
+
import groupBy from 'lodash-es/groupBy.js'
|
|
106
|
+
import sumBy from 'lodash-es/sumBy.js'
|
|
107
|
+
import reduce from 'lodash-es/reduce.js'
|
|
108
|
+
import size from 'lodash-es/size.js'
|
|
109
|
+
import isearr from 'wsemi/src/isearr.mjs'
|
|
110
|
+
import WEchartsVue from 'w-echarts-vue/src/components/WEchartsVue.vue'
|
|
111
|
+
import WIconLoading from 'w-component-vue/src/components/WIconLoading.vue'
|
|
112
|
+
|
|
113
|
+
//固定色票(依事件在 allEvents 之索引取色,使同一事件顏色穩定、圖表(含圖例)與統計表一致)
|
|
114
|
+
let COLORS = [
|
|
115
|
+
'#3b82f6', '#ef4444', '#10b981', '#f97316', '#8b5cf6',
|
|
116
|
+
'#6366f1', '#eab308', '#ec4899', '#14b8a6', '#a855f7',
|
|
117
|
+
'#0ea5e9', '#f43f5e', '#22c55e', '#d97706', '#7c3aed',
|
|
118
|
+
]
|
|
119
|
+
|
|
120
|
+
export default {
|
|
121
|
+
components: {
|
|
122
|
+
WEchartsVue,
|
|
123
|
+
WIconLoading,
|
|
124
|
+
},
|
|
125
|
+
props: {
|
|
126
|
+
height: {
|
|
127
|
+
type: Number,
|
|
128
|
+
default: 400,
|
|
129
|
+
},
|
|
130
|
+
},
|
|
131
|
+
data: function() {
|
|
132
|
+
return {
|
|
133
|
+
freq: [],
|
|
134
|
+
timeGroup: '1hr',
|
|
135
|
+
loading: false,
|
|
136
|
+
errMsg: '',
|
|
137
|
+
hUpper: 0, //標題列+控制區實佔高度(ResizeObserver 量測,供 chartHeight 計算;語系導致控制列換行時會變化)
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
mounted: function() {
|
|
141
|
+
let vo = this
|
|
142
|
+
|
|
143
|
+
//ResizeObserver 量測標題列+控制區實佔高(含 margin),取代固定扣除值;
|
|
144
|
+
//ro 為瀏覽器原生物件不放 data(避免被深層響應化),掛 this 成非響應式屬性
|
|
145
|
+
let ro = new ResizeObserver(function() {
|
|
146
|
+
vo.updateUpperHeight()
|
|
147
|
+
})
|
|
148
|
+
if (vo.$refs['$hdr']) {
|
|
149
|
+
ro.observe(vo.$refs['$hdr'])
|
|
150
|
+
}
|
|
151
|
+
if (vo.$refs['$ctrl']) {
|
|
152
|
+
ro.observe(vo.$refs['$ctrl'])
|
|
153
|
+
}
|
|
154
|
+
vo.ro = ro
|
|
155
|
+
vo.updateUpperHeight()
|
|
156
|
+
|
|
157
|
+
vo.load()
|
|
158
|
+
},
|
|
159
|
+
beforeDestroy: function() {
|
|
160
|
+
let vo = this
|
|
161
|
+
if (vo.ro) {
|
|
162
|
+
vo.ro.disconnect()
|
|
163
|
+
vo.ro = null
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
computed: {
|
|
167
|
+
|
|
168
|
+
chartHeight: function() {
|
|
169
|
+
let vo = this
|
|
170
|
+
//panel 上下 padding 40 + 上方實佔(hUpper, 量測) + 表格預留 98(既有設計值:舊公式 height−360
|
|
171
|
+
//於 1440 下等價於保留 chart 底至 panel 內容底 98px,沿用該行為,非新設數字)
|
|
172
|
+
let TABLE_RESERVE = 98
|
|
173
|
+
let h = vo.height - 40 - vo.hUpper - TABLE_RESERVE
|
|
174
|
+
return h < 220 ? 220 : h
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
//所有出現過的事件名(union,排除 count),固定排序
|
|
178
|
+
allEvents: function() {
|
|
179
|
+
let vo = this
|
|
180
|
+
let kp = reduce(vo.freq, function(acc, item) {
|
|
181
|
+
let d = get(item, 'data', {})
|
|
182
|
+
keys(d).forEach(function(k) {
|
|
183
|
+
if (k !== 'count') {
|
|
184
|
+
acc[k] = true
|
|
185
|
+
}
|
|
186
|
+
})
|
|
187
|
+
return acc
|
|
188
|
+
}, {})
|
|
189
|
+
return keys(kp).sort()
|
|
190
|
+
},
|
|
191
|
+
|
|
192
|
+
//時間重採樣(1hr 不變;4hr/8hr/1day 同日合併)— 供圖表 x 軸
|
|
193
|
+
resampledData: function() {
|
|
194
|
+
let vo = this
|
|
195
|
+
let arr = vo.freq
|
|
196
|
+
if (!isearr(arr)) {
|
|
197
|
+
return []
|
|
198
|
+
}
|
|
199
|
+
let tg = vo.timeGroup
|
|
200
|
+
if (tg === '1hr') {
|
|
201
|
+
return arr
|
|
202
|
+
}
|
|
203
|
+
let groupHours = 4
|
|
204
|
+
if (tg === '8hr') {
|
|
205
|
+
groupHours = 8
|
|
206
|
+
}
|
|
207
|
+
else if (tg === '1day') {
|
|
208
|
+
groupHours = 24
|
|
209
|
+
}
|
|
210
|
+
let dataKeyList = vo.allEvents.concat(['count'])
|
|
211
|
+
let gs = groupBy(arr, function(item) {
|
|
212
|
+
let t = ot(item.time)
|
|
213
|
+
let h = t.hour()
|
|
214
|
+
let hGroup = Math.floor(h / groupHours)
|
|
215
|
+
let tNew = t.startOf('day').add(hGroup * groupHours, 'hour')
|
|
216
|
+
return tNew.format()
|
|
217
|
+
})
|
|
218
|
+
return map(gs, function(vs, timeKey) {
|
|
219
|
+
let merged = { time: timeKey, data: {} }
|
|
220
|
+
dataKeyList.forEach(function(k) {
|
|
221
|
+
merged.data[k] = sumBy(vs, function(item) {
|
|
222
|
+
return get(item, `data.${k}`, 0)
|
|
223
|
+
})
|
|
224
|
+
})
|
|
225
|
+
return merged
|
|
226
|
+
})
|
|
227
|
+
},
|
|
228
|
+
|
|
229
|
+
//實際要畫的系列 = 資料中所有事件(各事件一系列、各自顏色);
|
|
230
|
+
//個別事件之顯示/隱藏由 echarts 圖例(legend)切換,不另維護選取狀態
|
|
231
|
+
seriesKeys: function() {
|
|
232
|
+
let vo = this
|
|
233
|
+
return vo.allEvents
|
|
234
|
+
},
|
|
235
|
+
|
|
236
|
+
hasNoData: function() {
|
|
237
|
+
let vo = this
|
|
238
|
+
let arr = vo.freq
|
|
239
|
+
if (!isearr(arr) || size(arr) === 0) {
|
|
240
|
+
return true
|
|
241
|
+
}
|
|
242
|
+
let total = reduce(arr, function(sum, item) {
|
|
243
|
+
return sum + get(item, 'data.count', 0)
|
|
244
|
+
}, 0)
|
|
245
|
+
return total === 0
|
|
246
|
+
},
|
|
247
|
+
|
|
248
|
+
chartOption: function() {
|
|
249
|
+
let vo = this
|
|
250
|
+
let data = vo.resampledData
|
|
251
|
+
let tg = vo.timeGroup
|
|
252
|
+
let sKeys = vo.seriesKeys
|
|
253
|
+
|
|
254
|
+
let formatX = tg === '1day' ? 'MM/DD' : 'MM/DD\nHH:mm'
|
|
255
|
+
let totalBars = size(sKeys) || 1
|
|
256
|
+
let barWidthPct = Math.max(3, Math.floor(100 / (totalBars * 1.5))) + '%'
|
|
257
|
+
|
|
258
|
+
let series = map(sKeys, function(valueKey) {
|
|
259
|
+
return {
|
|
260
|
+
name: valueKey,
|
|
261
|
+
type: 'bar',
|
|
262
|
+
barWidth: barWidthPct,
|
|
263
|
+
data: map(data, function(item) {
|
|
264
|
+
return get(item, `data.${valueKey}`, 0)
|
|
265
|
+
}),
|
|
266
|
+
itemStyle: { color: vo.colorOf(valueKey) },
|
|
267
|
+
emphasis: { focus: 'series' },
|
|
268
|
+
}
|
|
269
|
+
})
|
|
270
|
+
|
|
271
|
+
return {
|
|
272
|
+
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
|
273
|
+
legend: { show: true, type: 'scroll', top: 0 }, //top須明給: echarts 6 起 legend 預設由 top:0 改為 bottom, 不給會落到底部壓住 x 軸時間標籤
|
|
274
|
+
grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
|
|
275
|
+
xAxis: [{
|
|
276
|
+
type: 'category',
|
|
277
|
+
data: map(data, function(item) { return ot(item.time).format(formatX) }),
|
|
278
|
+
axisTick: { alignWithLabel: true },
|
|
279
|
+
}],
|
|
280
|
+
yAxis: [{ type: 'value' }],
|
|
281
|
+
series: series,
|
|
282
|
+
}
|
|
283
|
+
},
|
|
284
|
+
|
|
285
|
+
//事件統計表:各事件之最近 1日/8時/4時/1時 計數(由小時 bucket 加總最後 N 桶),依 1日 多→少排序
|
|
286
|
+
eventRows: function() {
|
|
287
|
+
let vo = this
|
|
288
|
+
let arr = isearr(vo.freq) ? vo.freq : []
|
|
289
|
+
//freq 由後端依時間遞增排序;取最後 N 個小時 bucket 對應「最近 N 小時」
|
|
290
|
+
let sumLastN = function(ev, n) {
|
|
291
|
+
let bs = arr.slice(-n)
|
|
292
|
+
return reduce(bs, function(s, item) {
|
|
293
|
+
return s + get(item, `data.${ev}`, 0)
|
|
294
|
+
}, 0)
|
|
295
|
+
}
|
|
296
|
+
let rows = vo.allEvents.map(function(ev) {
|
|
297
|
+
return {
|
|
298
|
+
event: ev,
|
|
299
|
+
d1day: sumLastN(ev, 24),
|
|
300
|
+
d8hr: sumLastN(ev, 8),
|
|
301
|
+
d4hr: sumLastN(ev, 4),
|
|
302
|
+
d1hr: sumLastN(ev, 1),
|
|
303
|
+
}
|
|
304
|
+
})
|
|
305
|
+
//依最近1日總數多→少排序(同數則 8時、4時、1時 依序 tie-break)
|
|
306
|
+
rows.sort(function(a, b) {
|
|
307
|
+
return (b.d1day - a.d1day) || (b.d8hr - a.d8hr) || (b.d4hr - a.d4hr) || (b.d1hr - a.d1hr)
|
|
308
|
+
})
|
|
309
|
+
return rows
|
|
310
|
+
},
|
|
311
|
+
|
|
312
|
+
},
|
|
313
|
+
methods: {
|
|
314
|
+
|
|
315
|
+
//量測標題列+控制區實佔高(offsetHeight + margin-bottom),存入 hUpper 供 chartHeight 計算
|
|
316
|
+
updateUpperHeight: function() {
|
|
317
|
+
let vo = this
|
|
318
|
+
let mb = function(el) {
|
|
319
|
+
return parseFloat(getComputedStyle(el).marginBottom) || 0
|
|
320
|
+
}
|
|
321
|
+
let h = 0
|
|
322
|
+
let hdr = vo.$refs['$hdr']
|
|
323
|
+
let ctrl = vo.$refs['$ctrl']
|
|
324
|
+
if (hdr) {
|
|
325
|
+
h += hdr.offsetHeight + mb(hdr)
|
|
326
|
+
}
|
|
327
|
+
if (ctrl) {
|
|
328
|
+
h += ctrl.offsetHeight + mb(ctrl)
|
|
329
|
+
}
|
|
330
|
+
vo.hUpper = h
|
|
331
|
+
},
|
|
332
|
+
|
|
333
|
+
colorOf: function(ev) {
|
|
334
|
+
let vo = this
|
|
335
|
+
let idx = vo.allEvents.indexOf(ev)
|
|
336
|
+
if (idx < 0) {
|
|
337
|
+
idx = 0
|
|
338
|
+
}
|
|
339
|
+
return COLORS[idx % COLORS.length]
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
load: function() {
|
|
343
|
+
let vo = this
|
|
344
|
+
vo.loading = true
|
|
345
|
+
vo.errMsg = ''
|
|
346
|
+
vo.$fapi.getStaEvent(7, 'hr')
|
|
347
|
+
.then(function(rs) {
|
|
348
|
+
vo.freq = isearr(rs) ? rs : []
|
|
349
|
+
})
|
|
350
|
+
.catch(function(err) {
|
|
351
|
+
console.log('getStaEvent catch', err)
|
|
352
|
+
vo.errMsg = vo.$t('getDataError')
|
|
353
|
+
})
|
|
354
|
+
.finally(function() {
|
|
355
|
+
vo.loading = false
|
|
356
|
+
})
|
|
357
|
+
},
|
|
358
|
+
|
|
359
|
+
},
|
|
360
|
+
}
|
|
361
|
+
</script>
|
|
362
|
+
|
|
363
|
+
<style scoped>
|
|
364
|
+
|
|
365
|
+
/* 控制區:兩欄 grid 結構性對齊(label 欄 max-content 隨語系文字自動定寬;
|
|
366
|
+
第二欄 minmax(0,1fr) 防內容撐大),取代先前 label 寫死 64px 之魔術數字對齊 */
|
|
367
|
+
.ctrl {
|
|
368
|
+
margin-bottom: 16px;
|
|
369
|
+
display: grid;
|
|
370
|
+
grid-template-columns: max-content minmax(0, 1fr);
|
|
371
|
+
gap: 12px 8px;
|
|
372
|
+
align-items: center;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
.ctrl-row {
|
|
376
|
+
display: contents; /* label 與 field 直接成為 .ctrl 之 grid 子項 */
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.ctrl-label {
|
|
380
|
+
font-size: 0.875rem;
|
|
381
|
+
color: var(--c-2);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.ctrl-field {
|
|
385
|
+
display: flex;
|
|
386
|
+
align-items: center;
|
|
387
|
+
gap: 8px;
|
|
388
|
+
min-width: 0;
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
.ctrl-select {
|
|
392
|
+
border: 1px solid var(--border);
|
|
393
|
+
border-radius: var(--radius);
|
|
394
|
+
background: var(--bg-1);
|
|
395
|
+
color: var(--c-1);
|
|
396
|
+
font-size: 0.875rem;
|
|
397
|
+
padding: 4px 8px;
|
|
398
|
+
outline: none;
|
|
399
|
+
cursor: pointer;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
.ctrl-select:focus {
|
|
403
|
+
border-color: var(--accent);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
.evt-dot {
|
|
407
|
+
display: inline-block;
|
|
408
|
+
width: 9px;
|
|
409
|
+
height: 9px;
|
|
410
|
+
border-radius: 2px;
|
|
411
|
+
margin-right: 6px;
|
|
412
|
+
flex-shrink: 0;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.stats-table-area {
|
|
416
|
+
overflow-x: auto; /* 極端長事件名時表格自帶水平捲動,不外溢撐破 panel */
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.stats-table {
|
|
420
|
+
width: 100%;
|
|
421
|
+
border-collapse: collapse;
|
|
422
|
+
font-size: 0.85rem;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
.stats-table td:first-child {
|
|
426
|
+
white-space: normal; /* 事件名欄允許換行(數值欄維持 nowrap),防極長事件名撐破表格 */
|
|
427
|
+
overflow-wrap: anywhere;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.stats-table th,
|
|
431
|
+
.stats-table td {
|
|
432
|
+
border-bottom: 1px solid var(--border);
|
|
433
|
+
padding: 7px 10px;
|
|
434
|
+
text-align: right;
|
|
435
|
+
color: var(--c-1);
|
|
436
|
+
white-space: nowrap;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
.stats-table th {
|
|
440
|
+
color: var(--c-2);
|
|
441
|
+
font-weight: 600;
|
|
442
|
+
background: var(--bg-2);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
</style>
|