koishi-plugin-ets2-tools-tmp 3.3.62 → 3.3.63

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.
@@ -13,6 +13,44 @@
13
13
  font-style: normal;
14
14
  }
15
15
 
16
+ /* ============================================================
17
+ 设计令牌:整卡只用一套冷色玻璃体系
18
+ 底色 Ink → 极光 Cyan/Indigo/Violet → 玻璃面 → 语义色(红/金/绿)
19
+ ============================================================ */
20
+ :root {
21
+ --ink-0: #05070f;
22
+ --ink-1: #080d1b;
23
+ --ink-2: #0c1426;
24
+
25
+ --glass-hi: rgba(255, 255, 255, 0.085);
26
+ --glass-mid: rgba(255, 255, 255, 0.032);
27
+ --glass-lo: rgba(255, 255, 255, 0.012);
28
+
29
+ --stroke: rgba(255, 255, 255, 0.09);
30
+ --stroke-hi: rgba(255, 255, 255, 0.16);
31
+
32
+ --t1: #eaf2ff;
33
+ --t2: #a3b4cf;
34
+ --t3: #7a8aa4;
35
+ --t-title: #cfe0f8;
36
+
37
+ --accent: #58d3ff;
38
+ --accent-2: #7b8cff;
39
+ --ok: #4ade80;
40
+ --warn: #fbbf24;
41
+ --danger: #f87171;
42
+
43
+ --r-lg: 18px;
44
+ --r-md: 13px;
45
+ --r-sm: 10px;
46
+
47
+ --sheen-top: linear-gradient(90deg, transparent 5%, rgba(255, 255, 255, 0.16) 50%, transparent 95%);
48
+ --shadow-card:
49
+ 0 12px 28px -18px rgba(0, 0, 0, 0.85),
50
+ inset 0 1px 0 rgba(255, 255, 255, 0.08),
51
+ inset 0 -1px 0 rgba(0, 0, 0, 0.2);
52
+ }
53
+
16
54
  * {
17
55
  margin: 0;
18
56
  padding: 0;
@@ -21,67 +59,107 @@
21
59
 
22
60
  body {
23
61
  font-family: 'segui-emj', 'Segoe UI', -apple-system, sans-serif;
24
- background: #080c20;
62
+ background: var(--ink-0);
25
63
  -webkit-font-smoothing: antialiased;
26
64
  }
27
65
 
66
+ /* ============================================================
67
+ 卡片底:墨蓝渐变 + 极光光斑 + 磨砂噪点
68
+ ============================================================ */
28
69
  #container {
29
70
  width: 480px;
30
- background: linear-gradient(160deg, #0a0e27 0%, #151338 30%, #0d1a3a 60%, #080c20 100%);
31
- padding: 0 0 16px;
71
+ padding: 0 0 18px;
32
72
  overflow: hidden;
33
73
  position: relative;
74
+ background:
75
+ radial-gradient(900px 520px at 86% -8%, rgba(88, 211, 255, 0.11), transparent 62%),
76
+ radial-gradient(780px 540px at -12% 24%, rgba(123, 140, 255, 0.1), transparent 64%),
77
+ radial-gradient(700px 520px at 110% 70%, rgba(168, 132, 255, 0.07), transparent 66%),
78
+ linear-gradient(168deg, #0a1226 0%, #0a1020 40%, #080d1c 68%, #06090f 100%);
79
+ }
80
+
81
+ /* 磨砂玻璃的细颗粒 */
82
+ #container::before {
83
+ content: '';
84
+ position: absolute;
85
+ inset: 0;
86
+ z-index: 0;
87
+ pointer-events: none;
88
+ opacity: 0.05;
89
+ mix-blend-mode: soft-light;
90
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
91
+ }
92
+
93
+ /* 顶部一道极细的高光边(玻璃器皿的棱) */
94
+ #container::after {
95
+ content: '';
96
+ position: absolute;
97
+ top: 0;
98
+ left: 0;
99
+ right: 0;
100
+ height: 2.5px;
101
+ z-index: 5;
102
+ pointer-events: none;
103
+ background: linear-gradient(90deg,
104
+ rgba(88, 211, 255, 0.85), rgba(123, 140, 255, 0.7),
105
+ rgba(168, 132, 255, 0.5), rgba(88, 211, 255, 0.15));
34
106
  }
35
107
 
36
- /* Ambient glow orbs */
108
+ /* 极光光斑:同色系,只做明暗层次 */
37
109
  .ambient-orb {
38
110
  position: absolute;
39
111
  border-radius: 50%;
40
112
  pointer-events: none;
41
- filter: blur(60px);
113
+ filter: blur(70px);
42
114
  z-index: 0;
43
115
  }
44
116
 
45
117
  .ambient-orb.orb1 {
46
- top: -40px;
47
- right: -30px;
48
- width: 180px;
49
- height: 180px;
50
- background: rgba(99, 102, 241, 0.18);
118
+ top: -70px;
119
+ right: -50px;
120
+ width: 260px;
121
+ height: 260px;
122
+ background: rgba(88, 211, 255, 0.16);
51
123
  }
52
124
 
53
125
  .ambient-orb.orb2 {
54
- top: 200px;
55
- left: -50px;
56
- width: 140px;
57
- height: 140px;
58
- background: rgba(168, 85, 247, 0.12);
126
+ top: 210px;
127
+ left: -80px;
128
+ width: 240px;
129
+ height: 240px;
130
+ background: rgba(123, 140, 255, 0.14);
59
131
  }
60
132
 
61
133
  .ambient-orb.orb3 {
62
- bottom: 80px;
63
- right: -20px;
64
- width: 120px;
65
- height: 120px;
66
- background: rgba(59, 130, 246, 0.1);
134
+ bottom: 60px;
135
+ right: -70px;
136
+ width: 230px;
137
+ height: 230px;
138
+ background: rgba(168, 132, 255, 0.11);
67
139
  }
68
140
 
69
141
  /* ===== HEADER ===== */
70
142
  .header {
71
- padding: 24px 24px 18px;
143
+ padding: 23px 22px 18px;
72
144
  display: flex;
73
145
  align-items: center;
74
- gap: 16px;
146
+ gap: 15px;
75
147
  position: relative;
76
148
  z-index: 1;
77
- background: linear-gradient(180deg, rgba(139, 92, 246, 0.08) 0%, transparent 100%);
78
- border-bottom: 1px solid rgba(255, 255, 255, 0.04);
149
+ background: linear-gradient(180deg, rgba(88, 211, 255, 0.09) 0%, rgba(88, 211, 255, 0) 100%);
150
+ border-bottom: 1px solid rgba(255, 255, 255, 0.05);
79
151
  }
80
152
 
81
153
  .avatar-wrapper {
82
154
  position: relative;
83
155
  flex-shrink: 0;
156
+ padding: 2.5px;
157
+ border-radius: 50%;
84
158
  display: none;
159
+ background: conic-gradient(from 150deg, #58d3ff, #7b8cff 45%, #58d3ff 80%, #58d3ff);
160
+ box-shadow:
161
+ 0 0 22px rgba(88, 211, 255, 0.32),
162
+ 0 0 46px rgba(123, 140, 255, 0.18);
85
163
  }
86
164
 
87
165
  .avatar-wrapper.show {
@@ -92,32 +170,29 @@
92
170
  width: 72px;
93
171
  height: 72px;
94
172
  border-radius: 50%;
95
- border: 2.5px solid rgba(139, 92, 246, 0.5);
96
- box-shadow:
97
- 0 0 20px rgba(139, 92, 246, 0.35),
98
- 0 0 40px rgba(139, 92, 246, 0.15),
99
- inset 0 0 12px rgba(139, 92, 246, 0.1);
173
+ display: block;
100
174
  object-fit: cover;
175
+ background: var(--ink-2);
101
176
  }
102
177
 
103
178
  .online-dot {
104
179
  position: absolute;
105
- bottom: 2px;
106
- right: 2px;
107
- width: 14px;
108
- height: 14px;
180
+ bottom: 1px;
181
+ right: 1px;
182
+ width: 15px;
183
+ height: 15px;
109
184
  border-radius: 50%;
110
- border: 2.5px solid #0a0e27;
185
+ border: 3px solid #0a1020;
111
186
  z-index: 2;
112
187
  }
113
188
 
114
189
  .online-dot.online {
115
- background: #22c55e;
116
- box-shadow: 0 0 8px rgba(34, 197, 94, 0.6);
190
+ background: var(--ok);
191
+ box-shadow: 0 0 10px rgba(74, 222, 128, 0.7);
117
192
  }
118
193
 
119
194
  .online-dot.offline {
120
- background: #6b7280;
195
+ background: #64748b;
121
196
  }
122
197
 
123
198
  .header-info {
@@ -128,75 +203,87 @@
128
203
  .player-name {
129
204
  font-size: 20px;
130
205
  font-weight: 700;
131
- color: #f0f0ff;
206
+ color: #f3f8ff;
132
207
  white-space: nowrap;
133
208
  overflow: hidden;
134
209
  text-overflow: ellipsis;
135
- text-shadow: 0 0 20px rgba(139, 92, 246, 0.3);
210
+ letter-spacing: 0.2px;
211
+ text-shadow: 0 0 26px rgba(88, 211, 255, 0.3);
136
212
  }
137
213
 
138
214
  .player-meta {
139
215
  display: flex;
140
216
  align-items: center;
141
- gap: 8px;
142
- margin-top: 6px;
217
+ gap: 7px;
218
+ margin-top: 7px;
143
219
  flex-wrap: wrap;
144
220
  }
145
221
 
146
222
  .tmp-id {
147
- font-size: 13px;
148
- color: #94a3b8;
223
+ font-size: 12.5px;
224
+ color: var(--t2);
149
225
  font-weight: 500;
226
+ letter-spacing: 0.2px;
227
+ font-variant-numeric: tabular-nums;
150
228
  }
151
229
 
152
230
  .group-badge {
153
231
  font-size: 11px;
154
232
  font-weight: 600;
155
- padding: 2px 8px;
156
- border-radius: 10px;
233
+ padding: 3px 9px;
234
+ border-radius: 9px;
157
235
  color: #fff;
158
- text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
236
+ text-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
159
237
  letter-spacing: 0.3px;
238
+ box-shadow:
239
+ inset 0 1px 0 rgba(255, 255, 255, 0.28),
240
+ 0 3px 10px -4px rgba(0, 0, 0, 0.7);
160
241
  }
161
242
 
162
243
  .status-badge {
163
244
  font-size: 11px;
164
245
  font-weight: 600;
165
- padding: 2px 8px;
166
- border-radius: 10px;
246
+ padding: 3px 9px;
247
+ border-radius: 9px;
167
248
  letter-spacing: 0.3px;
249
+ backdrop-filter: blur(8px);
168
250
  }
169
251
 
170
252
  .status-badge.online {
171
- background: rgba(34, 197, 94, 0.2);
172
- color: #4ade80;
173
- border: 1px solid rgba(34, 197, 94, 0.3);
253
+ background: rgba(74, 222, 128, 0.14);
254
+ color: #86efac;
255
+ border: 1px solid rgba(74, 222, 128, 0.34);
256
+ box-shadow: 0 0 14px -4px rgba(74, 222, 128, 0.6);
174
257
  }
175
258
 
176
259
  .status-badge.offline {
177
- background: rgba(107, 114, 128, 0.2);
178
- color: #9ca3af;
179
- border: 1px solid rgba(107, 114, 128, 0.3);
260
+ background: rgba(255, 255, 255, 0.05);
261
+ color: var(--t2);
262
+ border: 1px solid rgba(255, 255, 255, 0.1);
180
263
  }
181
264
 
182
- /* ===== GLASS SECTIONS ===== */
265
+ /* ============================================================
266
+ 玻璃板块:统一配方(磨砂 + 顶部高光 + 左上斜向反光 + 悬浮阴影)
267
+ ============================================================ */
183
268
  .section {
184
- margin: 10px 16px;
185
- background: linear-gradient(135deg, rgba(255, 255, 255, 0.06) 0%, rgba(255, 255, 255, 0.02) 100%);
186
- border: 1px solid rgba(255, 255, 255, 0.08);
187
- border-radius: 16px;
269
+ --acc-line: rgba(88, 211, 255, 0.42);
270
+ --acc-tint: rgba(88, 211, 255, 0.06);
271
+ margin: 11px 14px;
188
272
  padding: 14px 16px;
189
- backdrop-filter: blur(20px) saturate(1.2);
190
- -webkit-backdrop-filter: blur(20px) saturate(1.2);
273
+ border-radius: var(--r-lg);
191
274
  position: relative;
192
275
  z-index: 1;
193
- box-shadow:
194
- 0 4px 16px rgba(0, 0, 0, 0.15),
195
- 0 1px 2px rgba(255, 255, 255, 0.05) inset;
196
276
  overflow: hidden;
277
+ backdrop-filter: blur(22px) saturate(1.3);
278
+ -webkit-backdrop-filter: blur(22px) saturate(1.3);
279
+ background:
280
+ radial-gradient(130% 86% at 0% 0%, var(--acc-tint), transparent 62%),
281
+ linear-gradient(150deg, var(--glass-hi) 0%, var(--glass-mid) 46%, var(--glass-lo) 100%);
282
+ border: 1px solid var(--stroke);
283
+ box-shadow: var(--shadow-card);
197
284
  }
198
285
 
199
- /* Glass highlight on top */
286
+ /* 顶部 1px 高光 */
200
287
  .section::before {
201
288
  content: '';
202
289
  position: absolute;
@@ -204,16 +291,34 @@
204
291
  left: 0;
205
292
  right: 0;
206
293
  height: 1px;
207
- background: linear-gradient(90deg, transparent 5%, rgba(255, 255, 255, 0.12) 50%, transparent 95%);
208
294
  z-index: 2;
295
+ background: var(--sheen-top);
296
+ }
297
+
298
+ /* 左上角眩光 + 斜向反光,制造玻璃弧度 */
299
+ .section::after {
300
+ content: '';
301
+ position: absolute;
302
+ left: 0;
303
+ right: 0;
304
+ top: 0;
305
+ height: 58%;
306
+ z-index: 2;
307
+ pointer-events: none;
308
+ border-radius: inherit;
309
+ background:
310
+ radial-gradient(54% 46% at 0% 0%, rgba(255, 255, 255, 0.08), transparent 72%),
311
+ linear-gradient(122deg,
312
+ rgba(255, 255, 255, 0.05) 0%,
313
+ rgba(255, 255, 255, 0.012) 30%,
314
+ transparent 58%);
209
315
  }
210
316
 
211
317
  .section-title {
212
- font-size: 12px;
318
+ font-size: 11.5px;
213
319
  font-weight: 700;
214
- color: #c4b5fd;
215
- text-transform: uppercase;
216
- letter-spacing: 1.2px;
320
+ color: var(--t-title);
321
+ letter-spacing: 1.1px;
217
322
  margin-bottom: 10px;
218
323
  display: flex;
219
324
  align-items: center;
@@ -226,8 +331,8 @@
226
331
  content: '';
227
332
  flex: 1;
228
333
  height: 1px;
229
- background: linear-gradient(90deg, rgba(139, 92, 246, 0.4), rgba(139, 92, 246, 0.05), transparent);
230
334
  margin-left: 8px;
335
+ background: linear-gradient(90deg, var(--acc-line), rgba(255, 255, 255, 0) 80%);
231
336
  }
232
337
 
233
338
  /* ===== INFO ROWS ===== */
@@ -235,18 +340,19 @@
235
340
  display: flex;
236
341
  align-items: center;
237
342
  justify-content: space-between;
238
- padding: 5px 0;
343
+ gap: 10px;
344
+ padding: 6px 0;
239
345
  position: relative;
240
346
  z-index: 1;
241
347
  }
242
348
 
243
349
  .info-row + .info-row {
244
- border-top: 1px solid rgba(255, 255, 255, 0.04);
350
+ border-top: 1px solid rgba(255, 255, 255, 0.045);
245
351
  }
246
352
 
247
353
  .info-label {
248
- font-size: 13px;
249
- color: #94a3b8;
354
+ font-size: 12.5px;
355
+ color: var(--t2);
250
356
  display: flex;
251
357
  align-items: center;
252
358
  gap: 4px;
@@ -257,45 +363,49 @@
257
363
 
258
364
  .info-value {
259
365
  font-size: 13px;
260
- color: #e2e8f0;
366
+ font-weight: 500;
367
+ color: var(--t1);
261
368
  text-align: right;
262
369
  white-space: nowrap;
263
370
  overflow: hidden;
264
371
  text-overflow: ellipsis;
265
372
  max-width: 70%;
266
373
  margin-left: 8px;
374
+ font-variant-numeric: tabular-nums;
267
375
  }
268
376
 
269
377
  .info-value.gold {
270
- color: #fbbf24;
271
- font-weight: 600;
272
- text-shadow: 0 0 8px rgba(251, 191, 36, 0.3);
378
+ color: var(--warn);
379
+ font-weight: 700;
380
+ text-shadow: 0 0 10px rgba(251, 191, 36, 0.28);
273
381
  }
274
382
 
275
383
  .info-value.green {
276
- color: #4ade80;
384
+ color: #7ee7a5;
385
+ font-weight: 600;
277
386
  }
278
387
 
279
388
  .info-value.red {
280
- color: #f87171;
389
+ color: #ff9c9c;
390
+ font-weight: 600;
281
391
  }
282
392
 
283
393
  .info-value.dim {
284
- color: #6b7280;
394
+ color: var(--t3);
285
395
  font-size: 12px;
396
+ font-weight: 400;
286
397
  }
287
398
 
288
399
  /* ===== VTC HISTORY ===== */
289
400
  .vtc-history-item {
290
- background: linear-gradient(135deg, rgba(255, 255, 255, 0.04) 0%, rgba(255, 255, 255, 0.01) 100%);
291
- border: 1px solid rgba(255, 255, 255, 0.06);
292
- border-radius: 10px;
293
- padding: 10px 12px;
294
401
  margin-top: 8px;
295
- backdrop-filter: blur(10px);
296
- -webkit-backdrop-filter: blur(10px);
402
+ padding: 10px 12px;
403
+ border-radius: var(--r-md);
297
404
  position: relative;
298
405
  z-index: 1;
406
+ background: linear-gradient(150deg, rgba(255, 255, 255, 0.055) 0%, rgba(255, 255, 255, 0.015) 100%);
407
+ border: 1px solid rgba(255, 255, 255, 0.07);
408
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
299
409
  }
300
410
 
301
411
  .vtc-history-item + .vtc-history-item {
@@ -304,42 +414,39 @@
304
414
 
305
415
  .vtc-history-name {
306
416
  font-size: 13px;
307
- color: #fbbf24;
417
+ color: var(--warn);
308
418
  font-weight: 600;
309
419
  margin-bottom: 4px;
310
420
  }
311
421
 
312
422
  .vtc-history-dates {
313
423
  font-size: 11px;
314
- color: #64748b;
424
+ color: var(--t3);
315
425
  }
316
426
 
317
427
  /* ===== BAN SECTION ===== */
318
428
  .section.ban-section {
319
- background: linear-gradient(135deg, rgba(239, 68, 68, 0.1) 0%, rgba(239, 68, 68, 0.03) 100%);
320
- border-color: rgba(239, 68, 68, 0.15);
429
+ --acc-line: rgba(248, 113, 113, 0.5);
430
+ --acc-tint: rgba(248, 113, 113, 0.08);
321
431
  }
322
432
 
323
433
  .section.ban-section .section-title {
324
- color: #f87171;
325
- }
326
-
327
- .section.ban-section .section-title::after {
328
- background: linear-gradient(90deg, rgba(248, 113, 113, 0.4), rgba(248, 113, 113, 0.05), transparent);
434
+ color: #ffb4b4;
329
435
  }
330
436
 
331
437
  .ban-hidden-text {
332
- color: #6b7280;
333
- font-size: 13px;
438
+ color: var(--t3);
439
+ font-size: 12.5px;
334
440
  font-style: italic;
335
441
  text-align: center;
336
442
  padding: 4px 0;
443
+ position: relative;
444
+ z-index: 1;
337
445
  }
338
446
 
339
447
  /* ===== MAP SECTION ===== */
340
448
  .section.map-section {
341
449
  padding: 0;
342
- overflow: hidden;
343
450
  }
344
451
 
345
452
  .map-section .section-title {
@@ -350,75 +457,70 @@
350
457
  .map-section #map {
351
458
  width: 100%;
352
459
  height: 180px;
353
- border-radius: 0 0 16px 16px;
354
- /* 未加载到瓦片的区域使用卡片同款深蓝底色,避免白底突兀 */
355
- background: #0f1439;
460
+ border-radius: 0 0 var(--r-lg) var(--r-lg);
461
+ background: #0b1223;
462
+ position: relative;
356
463
  }
357
464
 
358
- /* 瓦片暗色化:部分反转(0.6)让底色为浅中灰,色相回转 + 提饱和保持黄色道路鲜亮 */
465
+ /* 瓦片暗色化:整体偏冷,和卡片同色系 */
359
466
  .map-section .leaflet-tile-pane {
360
- filter: invert(0.78) hue-rotate(180deg) saturate(1.35) brightness(2);
467
+ filter: invert(0.8) hue-rotate(185deg) saturate(1.15) brightness(1.9);
468
+ }
469
+
470
+ /* 地图上下压暗,让地图边缘融进玻璃卡片 */
471
+ .map-section #map::after {
472
+ content: '';
473
+ position: absolute;
474
+ inset: 0;
475
+ z-index: 500;
476
+ pointer-events: none;
477
+ background:
478
+ linear-gradient(180deg, rgba(10, 16, 34, 0.55) 0%, rgba(10, 16, 34, 0) 42%),
479
+ linear-gradient(0deg, rgba(8, 13, 27, 0.6) 0%, rgba(8, 13, 27, 0) 34%);
480
+ }
481
+
482
+ .map-section .leaflet-control-attribution {
483
+ display: none;
361
484
  }
362
485
 
363
486
  /* ===== SPONSOR SECTION ===== */
364
487
  .section.sponsor-section {
365
- background: linear-gradient(135deg, rgba(250, 204, 21, 0.08) 0%, rgba(250, 204, 21, 0.02) 100%);
366
- border-color: rgba(250, 204, 21, 0.15);
488
+ --acc-line: rgba(251, 191, 36, 0.45);
489
+ --acc-tint: rgba(251, 191, 36, 0.055);
367
490
  }
368
491
 
369
492
  .section.sponsor-section .section-title {
370
- color: #facc15;
371
- }
372
-
373
- .section.sponsor-section .section-title::after {
374
- background: linear-gradient(90deg, rgba(250, 204, 21, 0.4), rgba(250, 204, 21, 0.05), transparent);
493
+ color: #ffd98a;
375
494
  }
376
495
 
377
496
  /* ===== GAME TIME SECTION ===== */
378
497
  .section.game-section {
379
- background: linear-gradient(135deg, rgba(59, 130, 246, 0.08) 0%, rgba(59, 130, 246, 0.02) 100%);
380
- border-color: rgba(59, 130, 246, 0.15);
381
- }
382
-
383
- .section.game-section .section-title {
384
- color: #60a5fa;
385
- }
386
-
387
- .section.game-section .section-title::after {
388
- background: linear-gradient(90deg, rgba(96, 165, 250, 0.4), rgba(96, 165, 250, 0.05), transparent);
498
+ --acc-line: rgba(123, 140, 255, 0.45);
499
+ --acc-tint: rgba(123, 140, 255, 0.07);
389
500
  }
390
501
 
391
502
  /* ===== MILEAGE SECTION ===== */
392
503
  .section.mileage-section {
393
- background: linear-gradient(135deg, rgba(16, 185, 129, 0.08) 0%, rgba(16, 185, 129, 0.02) 100%);
394
- border-color: rgba(16, 185, 129, 0.15);
395
- }
396
-
397
- .section.mileage-section .section-title {
398
- color: #34d399;
504
+ --acc-line: rgba(52, 211, 153, 0.42);
505
+ --acc-tint: rgba(52, 211, 153, 0.06);
399
506
  }
400
507
 
401
- .section.mileage-section .section-title::after {
402
- background: linear-gradient(90deg, rgba(52, 211, 153, 0.4), rgba(52, 211, 153, 0.05), transparent);
403
- }
404
-
405
- /* ===== GLASS BAR (ban count, last online) ===== */
508
+ /* ===== GLASS BAR ===== */
406
509
  .glass-bar {
407
- margin: 10px 16px;
510
+ margin: 11px 14px;
408
511
  display: flex;
409
512
  align-items: center;
410
513
  justify-content: space-between;
411
- padding: 10px 16px;
412
- background: linear-gradient(135deg, rgba(255, 255, 255, 0.05) 0%, rgba(255, 255, 255, 0.02) 100%);
413
- border: 1px solid rgba(255, 255, 255, 0.07);
414
- border-radius: 12px;
415
- backdrop-filter: blur(16px) saturate(1.1);
416
- -webkit-backdrop-filter: blur(16px) saturate(1.1);
514
+ gap: 10px;
515
+ padding: 11px 16px;
516
+ border-radius: var(--r-md);
417
517
  position: relative;
418
518
  z-index: 1;
419
- box-shadow:
420
- 0 2px 8px rgba(0, 0, 0, 0.1),
421
- 0 1px 1px rgba(255, 255, 255, 0.04) inset;
519
+ backdrop-filter: blur(18px) saturate(1.2);
520
+ -webkit-backdrop-filter: blur(18px) saturate(1.2);
521
+ background: linear-gradient(150deg, var(--glass-hi) 0%, var(--glass-lo) 100%);
522
+ border: 1px solid var(--stroke);
523
+ box-shadow: var(--shadow-card);
422
524
  }
423
525
 
424
526
  .glass-bar::before {
@@ -428,25 +530,290 @@
428
530
  left: 0;
429
531
  right: 0;
430
532
  height: 1px;
431
- background: linear-gradient(90deg, transparent 10%, rgba(255, 255, 255, 0.08) 50%, transparent 90%);
533
+ background: var(--sheen-top);
432
534
  }
433
535
 
434
536
  .glass-bar .info-label {
435
- font-size: 13px;
537
+ font-size: 12.5px;
436
538
  }
437
539
 
438
540
  .glass-bar .info-value {
439
541
  font-size: 14px;
440
542
  font-weight: 700;
543
+ max-width: none;
441
544
  }
442
545
 
443
546
  .glass-bar .info-value.has-bans {
444
- color: #f87171;
445
- text-shadow: 0 0 6px rgba(248, 113, 113, 0.3);
547
+ color: #ff9c9c;
548
+ text-shadow: 0 0 10px rgba(248, 113, 113, 0.32);
446
549
  }
447
550
 
448
551
  .glass-bar .info-value.no-bans {
449
- color: #4ade80;
552
+ color: #7ee7a5;
553
+ }
554
+
555
+ /* ============================================================
556
+ TELEMETRY(实时遥测)
557
+ ============================================================ */
558
+ .section.telemetry-section {
559
+ --acc-line: rgba(88, 211, 255, 0.45);
560
+ --acc-tint: rgba(88, 211, 255, 0.07);
561
+ }
562
+
563
+ .tl-meta {
564
+ font-size: 10px;
565
+ font-weight: 600;
566
+ color: var(--t3);
567
+ letter-spacing: 0.2px;
568
+ text-transform: none;
569
+ display: inline-flex;
570
+ align-items: center;
571
+ gap: 4px;
572
+ flex-shrink: 0;
573
+ font-variant-numeric: tabular-nums;
574
+ }
575
+
576
+ .tl-live {
577
+ width: 6px;
578
+ height: 6px;
579
+ border-radius: 50%;
580
+ display: inline-block;
581
+ }
582
+
583
+ .tl-live.live {
584
+ background: var(--ok);
585
+ box-shadow: 0 0 7px rgba(74, 222, 128, 0.85);
586
+ }
587
+
588
+ .tl-live.stale {
589
+ background: #64748b;
590
+ }
591
+
592
+ /* 关键指标磁贴 */
593
+ .tl-tiles {
594
+ display: flex;
595
+ gap: 8px;
596
+ position: relative;
597
+ z-index: 1;
598
+ }
599
+
600
+ .tl-tile {
601
+ flex: 1 1 0;
602
+ min-width: 0;
603
+ padding: 9px 5px 8px;
604
+ border-radius: var(--r-md);
605
+ text-align: center;
606
+ background: linear-gradient(150deg, rgba(255, 255, 255, 0.075) 0%, rgba(255, 255, 255, 0.022) 100%);
607
+ border: 1px solid rgba(255, 255, 255, 0.08);
608
+ box-shadow:
609
+ inset 0 1px 0 rgba(255, 255, 255, 0.09),
610
+ 0 6px 16px -12px rgba(0, 0, 0, 0.8);
611
+ }
612
+
613
+ .tl-tile .v {
614
+ font-size: 17px;
615
+ font-weight: 700;
616
+ color: #f4faff;
617
+ line-height: 1.15;
618
+ white-space: nowrap;
619
+ overflow: hidden;
620
+ text-overflow: ellipsis;
621
+ font-variant-numeric: tabular-nums;
622
+ text-shadow: 0 0 16px rgba(88, 211, 255, 0.35);
623
+ }
624
+
625
+ .tl-tile .k {
626
+ font-size: 10px;
627
+ color: var(--t3);
628
+ margin-top: 3px;
629
+ letter-spacing: 0.3px;
630
+ white-space: nowrap;
631
+ overflow: hidden;
632
+ text-overflow: ellipsis;
633
+ }
634
+
635
+ /* 紧凑网格:一行 2 列,字段多了也不会拉得很长 */
636
+ .tl-grid {
637
+ display: grid;
638
+ gap: 6px 8px;
639
+ position: relative;
640
+ z-index: 1;
641
+ margin-top: 6px;
642
+ }
643
+
644
+ .tl-grid.cols-1 {
645
+ grid-template-columns: 1fr;
646
+ }
647
+
648
+ .tl-grid.cols-2 {
649
+ grid-template-columns: repeat(2, minmax(0, 1fr));
650
+ }
651
+
652
+ .tl-grid.cols-3 {
653
+ grid-template-columns: repeat(3, minmax(0, 1fr));
654
+ }
655
+
656
+ .tl-item {
657
+ min-width: 0;
658
+ padding: 5px 9px;
659
+ border-radius: var(--r-sm);
660
+ background: rgba(255, 255, 255, 0.045);
661
+ border: 1px solid rgba(255, 255, 255, 0.065);
662
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05);
663
+ }
664
+
665
+ .tl-item.wide {
666
+ grid-column: 1 / -1;
667
+ }
668
+
669
+ .tl-cell {
670
+ display: flex;
671
+ align-items: center;
672
+ justify-content: space-between;
673
+ gap: 8px;
674
+ min-height: 29px;
675
+ }
676
+
677
+ .tl-cell-k {
678
+ font-size: 11px;
679
+ color: var(--t2);
680
+ letter-spacing: 0.2px;
681
+ white-space: nowrap;
682
+ overflow: hidden;
683
+ text-overflow: ellipsis;
684
+ flex: 0 0 auto;
685
+ }
686
+
687
+ .tl-cell-v {
688
+ font-size: 12.5px;
689
+ font-weight: 600;
690
+ color: var(--t1);
691
+ text-align: right;
692
+ flex: 1 1 auto;
693
+ min-width: 0;
694
+ word-break: break-word;
695
+ line-height: 1.25;
696
+ font-variant-numeric: tabular-nums;
697
+ }
698
+
699
+ .tl-cell-v.gold {
700
+ color: var(--warn);
701
+ }
702
+
703
+ .tl-cell-v.red {
704
+ color: #ff9c9c;
705
+ }
706
+
707
+ .tl-cell-v.dim {
708
+ color: var(--t3);
709
+ font-weight: 500;
710
+ }
711
+
712
+ .tl-cell-v.mono {
713
+ font-family: 'Consolas', 'Courier New', monospace;
714
+ font-size: 11.5px;
715
+ letter-spacing: 0.2px;
716
+ }
717
+
718
+ /* 分组小标题 */
719
+ .tl-group-title {
720
+ font-size: 11px;
721
+ font-weight: 700;
722
+ color: #a9c8ef;
723
+ letter-spacing: 0.8px;
724
+ margin: 12px 0 0;
725
+ padding-left: 9px;
726
+ position: relative;
727
+ z-index: 1;
728
+ }
729
+
730
+ .tl-group-title::before {
731
+ content: '';
732
+ position: absolute;
733
+ left: 0;
734
+ top: 1px;
735
+ bottom: 1px;
736
+ width: 2.5px;
737
+ border-radius: 2px;
738
+ background: linear-gradient(180deg, var(--accent), rgba(88, 211, 255, 0.1));
739
+ }
740
+
741
+ /* 进度条(作为网格单元) */
742
+ .tl-bar {
743
+ position: relative;
744
+ z-index: 1;
745
+ }
746
+
747
+ .tl-bar-head {
748
+ display: flex;
749
+ align-items: baseline;
750
+ justify-content: space-between;
751
+ font-size: 11.5px;
752
+ margin-bottom: 5px;
753
+ gap: 8px;
754
+ }
755
+
756
+ .tl-bar-head .l {
757
+ color: var(--t2);
758
+ white-space: nowrap;
759
+ min-width: 0;
760
+ overflow: hidden;
761
+ text-overflow: ellipsis;
762
+ }
763
+
764
+ .tl-bar-head .r {
765
+ font-weight: 700;
766
+ font-size: 12.5px;
767
+ white-space: nowrap;
768
+ flex-shrink: 0;
769
+ font-variant-numeric: tabular-nums;
770
+ }
771
+
772
+ .tl-bar-head .r .hint {
773
+ font-weight: 500;
774
+ color: var(--t3);
775
+ font-size: 11px;
776
+ }
777
+
778
+ .tl-bar-track {
779
+ height: 6px;
780
+ border-radius: 3px;
781
+ background: rgba(255, 255, 255, 0.07);
782
+ overflow: hidden;
783
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.35);
784
+ }
785
+
786
+ .tl-bar-fill {
787
+ height: 100%;
788
+ border-radius: 3px;
789
+ }
790
+
791
+ /* 告警标签 */
792
+ .tl-warn-tags {
793
+ display: flex;
794
+ flex-wrap: wrap;
795
+ gap: 6px;
796
+ position: relative;
797
+ z-index: 1;
798
+ padding-top: 3px;
799
+ }
800
+
801
+ .tl-warn-tag {
802
+ font-size: 11px;
803
+ font-weight: 600;
804
+ padding: 3px 9px;
805
+ border-radius: 9px;
806
+ background: rgba(248, 113, 113, 0.13);
807
+ color: #fca5a5;
808
+ border: 1px solid rgba(248, 113, 113, 0.26);
809
+ box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.06);
810
+ }
811
+
812
+ .tl-warn-tag.urgent {
813
+ background: rgba(239, 68, 68, 0.26);
814
+ color: #fecaca;
815
+ border-color: rgba(239, 68, 68, 0.48);
816
+ box-shadow: 0 0 16px -6px rgba(239, 68, 68, 0.9);
450
817
  }
451
818
  </style>
452
819
  </head>
@@ -533,6 +900,12 @@
533
900
  <div id="map"></div>
534
901
  </div>
535
902
 
903
+ <!-- Telemetry Section (only shown for own VTC members) -->
904
+ <div class="section telemetry-section" id="telemetry-section" style="display:none;">
905
+ <div class="section-title">📡 实时遥测<span class="tl-meta" id="telemetry-meta"></span></div>
906
+ <div id="telemetry-body"></div>
907
+ </div>
908
+
536
909
  <!-- Ban Section (only when banned) -->
537
910
  <div class="section ban-section" id="ban-section" style="display:none;">
538
911
  <div class="section-title">🚫 封禁信息</div>
@@ -621,6 +994,284 @@
621
994
  return val + ' 米';
622
995
  }
623
996
 
997
+ /* ==================================================================
998
+ * V1 车队平台 · 实时遥测
999
+ * 仅渲染「有值」的字段(null / undefined / 空串一律不显示)
1000
+ * 已排除字段:id / tmpId / teamId / serverId / serverName / mapId /
1001
+ * axisX / axisY / heading / name / steamId / truckPlate
1002
+ * 单位重复的字段只保留可读性更好的那个(m 与 km 二选一)
1003
+ * ================================================================== */
1004
+
1005
+ // 告警灯:true 时显示对应标签
1006
+ var TL_WARNINGS = [
1007
+ ['fuelWarning', '油量告警', false],
1008
+ ['adblueWarning', '尿素告警', false],
1009
+ ['waterTempWarning', '水温告警', false],
1010
+ ['oilPressureWarning', '油压告警', false],
1011
+ ['batteryVoltageWarning', '电瓶电压告警', false],
1012
+ ['brakeAirWarning', '刹车气压告警', false],
1013
+ ['brakeAirEmergency', '刹车气压紧急告警', true]
1014
+ ];
1015
+
1016
+ function tlHas(v) {
1017
+ return v !== null && v !== undefined && v !== '';
1018
+ }
1019
+
1020
+ function tlNum(v) {
1021
+ return typeof v === 'number' && isFinite(v);
1022
+ }
1023
+
1024
+ function tlText(v) {
1025
+ return String(v).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
1026
+ }
1027
+
1028
+ function tlInt(v) {
1029
+ return tlNum(v) ? Math.round(v).toLocaleString('en-US') : tlText(v);
1030
+ }
1031
+
1032
+ function tlFixed(v, d) {
1033
+ return tlNum(v) ? v.toFixed(d) : tlText(v);
1034
+ }
1035
+
1036
+ // 分钟 → X时Y分(负数表示已超时)
1037
+ function tlMinutes(min) {
1038
+ if (!tlNum(min)) return null;
1039
+ var neg = min < 0;
1040
+ var t = Math.abs(Math.round(min));
1041
+ return (neg ? '-' : '') + Math.floor(t / 60) + '时' + (t % 60) + '分';
1042
+ }
1043
+
1044
+ // 秒 → X时Y分
1045
+ function tlSeconds(sec) {
1046
+ if (!tlNum(sec)) return null;
1047
+ var t = Math.abs(Math.round(sec));
1048
+ return Math.floor(t / 3600) + '时' + Math.floor((t % 3600) / 60) + '分';
1049
+ }
1050
+
1051
+ function tlGear(g) {
1052
+ if (!tlNum(g)) return null;
1053
+ if (g > 0) return g + ' 挡';
1054
+ if (g < 0) return '倒挡 R' + Math.abs(g);
1055
+ return '空挡 N';
1056
+ }
1057
+
1058
+ // 紧凑单元格:标签在左、值在右,一个网格单元只占一行的 1/2 宽
1059
+ function tlCell(label, value, cls, mono) {
1060
+ if (!tlHas(value)) return '';
1061
+ var vcls = 'tl-cell-v' + (cls ? ' ' + cls : '') + (mono ? ' mono' : '');
1062
+ return '<div class="tl-cell tl-item"><div class="tl-cell-k">' + label + '</div>' +
1063
+ '<div class="' + vcls + '">' + value + '</div></div>';
1064
+ }
1065
+
1066
+ // 把若干个单元格排成 cols 列;空串(无值字段)自动剔除。
1067
+ // 若最后一格落单,则让它横向铺满,避免留下空位。
1068
+ function tlGrid(cells, cols) {
1069
+ var list = (cells || []).filter(tlHas);
1070
+ if (!list.length) return '';
1071
+ var c = cols || 2;
1072
+ if (list.length % c === 1) {
1073
+ list[list.length - 1] = list[list.length - 1].replace('class="', 'class="wide ');
1074
+ }
1075
+ return '<div class="tl-grid cols-' + c + '">' + list.join('') + '</div>';
1076
+ }
1077
+
1078
+ function tlGroup(title, html) {
1079
+ if (!html) return '';
1080
+ return '<div class="tl-group-title">' + title + '</div>' + html;
1081
+ }
1082
+
1083
+ // mode: fuel(低油量报警) / adblue / damage(越高越危险) / progress
1084
+ function tlBar(label, pct, hint, mode) {
1085
+ if (!tlNum(pct)) return '';
1086
+ var p = Math.max(0, Math.min(100, pct));
1087
+ var color;
1088
+ if (mode === 'damage') {
1089
+ color = p <= 10 ? '#4ade80' : (p <= 40 ? '#fbbf24' : '#f87171');
1090
+ } else if (mode === 'adblue') {
1091
+ color = p <= 10 ? '#f87171' : (p <= 25 ? '#fbbf24' : '#58d3ff');
1092
+ } else if (mode === 'progress') {
1093
+ color = p >= 99.5 ? '#4ade80' : '#58d3ff';
1094
+ } else {
1095
+ color = p <= 15 ? '#f87171' : (p <= 30 ? '#fbbf24' : '#4ade80');
1096
+ }
1097
+ return '<div class="tl-bar tl-item">' +
1098
+ '<div class="tl-bar-head"><span class="l">' + label + '</span>' +
1099
+ '<span class="r" style="color:' + color + ';">' + Math.round(p) + '%' +
1100
+ (hint ? '<span class="hint"> · ' + hint + '</span>' : '') + '</span></div>' +
1101
+ '<div class="tl-bar-track"><div class="tl-bar-fill" style="width:' + p + '%;background:linear-gradient(90deg,' + color + '44,' + color + ');"></div></div>' +
1102
+ '</div>';
1103
+ }
1104
+
1105
+ function buildTelemetry(telemetry) {
1106
+ var pf = (telemetry && telemetry.player) || {};
1107
+ var isLive = !!(telemetry && telemetry.online);
1108
+
1109
+ // ---- 标题右侧:LIVE 指示灯 + 数据时间 ----
1110
+ var metaEl = document.getElementById('telemetry-meta');
1111
+ if (metaEl) {
1112
+ var timeText = '';
1113
+ if (tlHas(pf.updateTimeCn)) timeText = tlText(pf.updateTimeCn).slice(11, 16);
1114
+ else if (tlHas(pf.updateTime)) timeText = tlText(pf.updateTime).slice(11, 16);
1115
+ else if (telemetry && tlHas(telemetry.generatedAt)) timeText = tlText(telemetry.generatedAt).slice(11, 16);
1116
+ metaEl.innerHTML = '<span class="tl-live ' + (isLive ? 'live' : 'stale') + '"></span>' +
1117
+ (timeText ? timeText : '') + (isLive ? '' : ' 离线数据');
1118
+ }
1119
+
1120
+ var html = '';
1121
+
1122
+ /* ---------- ① 关键指标磁贴 ---------- */
1123
+ var tiles = '';
1124
+ function tile(label, value) {
1125
+ if (!tlHas(value)) return;
1126
+ tiles += '<div class="tl-tile"><div class="v">' + value + '</div><div class="k">' + label + '</div></div>';
1127
+ }
1128
+ tile('车速 km/h', tlNum(pf.speedKph) ? Math.round(pf.speedKph) : null);
1129
+ tile('转速 RPM', tlNum(pf.rpm) ? Math.round(pf.rpm) : null);
1130
+ tile('网络延迟 ms', tlNum(pf.ping) ? Math.round(pf.ping) : null);
1131
+ tile('里程表 km', tlNum(pf.odometer) ? Math.round(pf.odometer).toLocaleString('en-US') : null);
1132
+ if (tiles) html += '<div class="tl-tiles">' + tiles + '</div>';
1133
+
1134
+ /* ---------- ② 车辆与驾驶 ---------- */
1135
+ var g1 = [];
1136
+ if (tlHas(pf.truckBrand) || tlHas(pf.truckName)) {
1137
+ g1.push(tlCell('🚚 驾驶车辆', tlText([pf.truckBrand, pf.truckName].filter(tlHas).join(' '))));
1138
+ }
1139
+ g1.push(tlCell('📍 所在城市', tlHas(pf.city) ? tlText(pf.city) : null));
1140
+ g1.push(tlCell('⚙️ 显示档位', tlGear(pf.gear)));
1141
+ if (tlNum(pf.engineGear) && pf.engineGear !== pf.gear) {
1142
+ g1.push(tlCell('🔧 实际档位', tlGear(pf.engineGear)));
1143
+ }
1144
+ if (tlNum(pf.speedLimitKph) && pf.speedLimitKph > 0) {
1145
+ g1.push(tlCell('🚧 导航限速', Math.round(pf.speedLimitKph) + ' km/h'));
1146
+ }
1147
+ var navKm = tlNum(pf.navDistanceKm) ? pf.navDistanceKm
1148
+ : (tlNum(pf.navDistanceM) ? pf.navDistanceM / 1000 : null);
1149
+ if (tlNum(navKm) && navKm > 0) {
1150
+ g1.push(tlCell('🧭 导航剩余', navKm.toFixed(1) + ' km'));
1151
+ }
1152
+ var navTime = tlHas(pf.navTimeStr) ? tlText(pf.navTimeStr)
1153
+ : (tlNum(pf.navTimeSec) && pf.navTimeSec > 0 ? tlSeconds(pf.navTimeSec) : null);
1154
+ if (tlHas(navTime)) {
1155
+ g1.push(tlCell('⏱️ 预计到达', navTime));
1156
+ }
1157
+ if (tlNum(pf.fuelRangeKm) && pf.fuelRangeKm > 0) {
1158
+ g1.push(tlCell('🛣️ 剩余续航', Math.round(pf.fuelRangeKm) + ' km'));
1159
+ }
1160
+ if (tlNum(pf.fuelAvgConsL100) && pf.fuelAvgConsL100 > 0) {
1161
+ g1.push(tlCell('⛽ 平均油耗', pf.fuelAvgConsL100.toFixed(1) + ' L/100km'));
1162
+ }
1163
+ if (tlNum(pf.restStopMin)) {
1164
+ g1.push(tlCell('😴 强制休息', pf.restStopMin < 0
1165
+ ? '已超时 ' + Math.abs(Math.round(pf.restStopMin)) + ' 分钟'
1166
+ : Math.round(pf.restStopMin) + ' 分钟', pf.restStopMin < 0 ? 'red' : ''));
1167
+ }
1168
+ html += tlGroup('🚗 车辆与驾驶', tlGrid(g1, 2));
1169
+
1170
+ /* ---------- ③ 油量与液位 ---------- */
1171
+ var g2 = [];
1172
+ if (tlNum(pf.fuel)) {
1173
+ g2.push(tlBar('油量', pf.fuel, tlNum(pf.fuelLiters) ? Math.round(pf.fuelLiters) + ' L' : '', 'fuel'));
1174
+ } else {
1175
+ g2.push(tlCell('⛽ 油量', tlNum(pf.fuelLiters) ? Math.round(pf.fuelLiters) + ' L' : null));
1176
+ }
1177
+ if (tlNum(pf.adbluePct)) {
1178
+ g2.push(tlBar('尿素', pf.adbluePct, tlNum(pf.adblueLiters) ? Math.round(pf.adblueLiters) + ' L' : '', 'adblue'));
1179
+ } else {
1180
+ g2.push(tlCell('💧 尿素', tlNum(pf.adblueLiters) ? Math.round(pf.adblueLiters) + ' L' : null));
1181
+ }
1182
+ html += tlGroup('⛽ 油量与液位', tlGrid(g2, 2));
1183
+
1184
+ /* ---------- ④ 机况与压力 ---------- */
1185
+ var g3 = [];
1186
+ g3.push(tlCell('🌡️ 冷却液温度', tlNum(pf.waterTempC) ? tlFixed(pf.waterTempC, 1) + ' ℃' : null));
1187
+ g3.push(tlCell('🌡️ 机油温度', tlNum(pf.oilTempC) ? tlFixed(pf.oilTempC, 1) + ' ℃' : null));
1188
+ g3.push(tlCell('🛢️ 机油压力', tlNum(pf.oilPressure) ? tlFixed(pf.oilPressure, 1) + ' bar' : null));
1189
+ g3.push(tlCell('🔋 电瓶电压', tlNum(pf.batteryVoltage) ? tlFixed(pf.batteryVoltage, 1) + ' V' : null));
1190
+ g3.push(tlCell('💨 刹车气压', tlNum(pf.brakeAirPressure) ? tlFixed(pf.brakeAirPressure, 1) + ' bar' : null));
1191
+ g3.push(tlCell('🔥 刹车温度', tlNum(pf.brakeTempC) ? tlFixed(pf.brakeTempC, 1) + ' ℃' : null));
1192
+ html += tlGroup('🌡️ 机况与压力', tlGrid(g3, 2));
1193
+
1194
+ /* ---------- ⑤ 损毁情况 ---------- */
1195
+ var g4 = [];
1196
+ if (tlNum(pf.truckDamagePct)) g4.push(tlBar('车头损毁', pf.truckDamagePct, '', 'damage'));
1197
+ if (tlNum(pf.trailerDamagePct)) g4.push(tlBar('挂车损毁', pf.trailerDamagePct, '', 'damage'));
1198
+ if (tlNum(pf.cargoDamagePct)) g4.push(tlBar('货物损毁', pf.cargoDamagePct, '', 'damage'));
1199
+ if (tlNum(pf.trailerCargoDamagePct)) g4.push(tlBar('挂车货物损毁', pf.trailerCargoDamagePct, '', 'damage'));
1200
+ html += tlGroup('🛠️ 损毁情况', tlGrid(g4, 2));
1201
+
1202
+ /* ---------- ⑥ 当前任务 ---------- */
1203
+ var g5 = [];
1204
+ g5.push(tlCell('📦 货物名称', tlHas(pf.jobCargo) ? tlText(pf.jobCargo) : null, 'gold'));
1205
+ if (tlNum(pf.jobIncome)) {
1206
+ g5.push(tlCell('💰 运输报酬', '€' + Math.round(pf.jobIncome).toLocaleString('en-US'), 'gold'));
1207
+ }
1208
+ var srcCity = tlHas(pf.jobSrcCityCn) ? pf.jobSrcCityCn : pf.jobSrcCity;
1209
+ var dstCity = tlHas(pf.jobDstCityCn) ? pf.jobDstCityCn : pf.jobDstCity;
1210
+ if (tlHas(srcCity) || tlHas(dstCity)) {
1211
+ g5.push(tlCell('🗺️ 运输路线', tlText((srcCity || '?') + ' → ' + (dstCity || '?'))));
1212
+ }
1213
+ if (tlHas(pf.jobSrcCompany) || tlHas(pf.jobDstCompany)) {
1214
+ g5.push(tlCell('🏢 货场', tlText((pf.jobSrcCompany || '?') + ' → ' + (pf.jobDstCompany || '?'))));
1215
+ }
1216
+ var plannedKm = tlNum(pf.jobPlannedKm) ? pf.jobPlannedKm
1217
+ : (tlNum(pf.jobPlannedDistance) ? pf.jobPlannedDistance / 1000 : null);
1218
+ var deliveredKm = tlNum(pf.jobDeliveredKm) ? pf.jobDeliveredKm
1219
+ : (tlNum(pf.jobDeliveredDistance) ? pf.jobDeliveredDistance / 1000 : null);
1220
+ if (tlNum(plannedKm) && plannedKm > 0) {
1221
+ g5.push(tlCell('🛣️ 计划里程', plannedKm.toFixed(1) + ' km'));
1222
+ }
1223
+ if (tlNum(deliveredKm)) {
1224
+ g5.push(tlCell('✅ 已行驶里程', deliveredKm.toFixed(1) + ' km'));
1225
+ }
1226
+ // 任务进度单独占一行(进度条需要完整宽度才看得准)
1227
+ var progress = (tlNum(plannedKm) && plannedKm > 0 && tlNum(deliveredKm))
1228
+ ? tlBar('📊 任务进度', Math.min(100, deliveredKm / plannedKm * 100),
1229
+ deliveredKm.toFixed(0) + ' / ' + plannedKm.toFixed(0) + ' km', 'progress')
1230
+ : '';
1231
+ var g6 = [];
1232
+ var deliveredLeft = tlHas(pf.jobDeliveryTimeStr) ? tlText(pf.jobDeliveryTimeStr)
1233
+ : tlMinutes(pf.jobDeliveryTimeRemain);
1234
+ if (tlHas(deliveredLeft)) {
1235
+ g6.push(tlCell('⏳ 交付剩余', deliveredLeft,
1236
+ tlNum(pf.jobDeliveryTimeRemain) && pf.jobDeliveryTimeRemain < 0 ? 'red' : ''));
1237
+ }
1238
+ if (tlNum(pf.jobCargoMassKg)) {
1239
+ g6.push(tlCell('⚖️ 货物总质量', Math.round(pf.jobCargoMassKg).toLocaleString('en-US') + ' kg'));
1240
+ }
1241
+ if (tlNum(pf.jobCargoUnitCount)) {
1242
+ g6.push(tlCell('🔢 货物件数', pf.jobCargoUnitCount + ' 件'));
1243
+ }
1244
+ if (tlNum(pf.jobCargoUnitMassKg)) {
1245
+ g6.push(tlCell('⚖️ 单件质量', tlFixed(pf.jobCargoUnitMassKg, 1) + ' kg'));
1246
+ }
1247
+ if (tlHas(pf.isSpecialJob)) {
1248
+ g6.push(tlCell('⭐ 特殊/易碎任务', pf.isSpecialJob ? '是' : '否'));
1249
+ }
1250
+ if (tlHas(pf.cargoLoaded)) {
1251
+ g6.push(tlCell('📥 货物已装载', pf.cargoLoaded ? '是' : '否'));
1252
+ }
1253
+ html += tlGroup('📦 当前任务', tlGrid(g5, 2) + tlGrid([progress], 1) + tlGrid(g6, 2));
1254
+
1255
+ /* ---------- ⑦ 告警灯 ---------- */
1256
+ var warns = '';
1257
+ for (var i = 0; i < TL_WARNINGS.length; i++) {
1258
+ if (pf[TL_WARNINGS[i][0]] === true) {
1259
+ warns += '<span class="tl-warn-tag' + (TL_WARNINGS[i][2] ? ' urgent' : '') + '">⚠️ ' + TL_WARNINGS[i][1] + '</span>';
1260
+ }
1261
+ }
1262
+ if (warns) html += tlGroup('🚨 车辆告警', '<div class="tl-warn-tags">' + warns + '</div>');
1263
+
1264
+ /* ---------- ⑧ 城市与货场 ID(SCS 内部名) ---------- */
1265
+ var g7 = [];
1266
+ g7.push(tlCell('🏙️ 起运城市ID', tlHas(pf.jobSrcCityId) ? tlText(pf.jobSrcCityId) : null, 'dim', true));
1267
+ g7.push(tlCell('🏙️ 目的城市ID', tlHas(pf.jobDstCityId) ? tlText(pf.jobDstCityId) : null, 'dim', true));
1268
+ g7.push(tlCell('🏢 起运货场ID', tlHas(pf.jobSrcCompanyId) ? tlText(pf.jobSrcCompanyId) : null, 'dim', true));
1269
+ g7.push(tlCell('🏢 目的货场ID', tlHas(pf.jobDstCompanyId) ? tlText(pf.jobDstCompanyId) : null, 'dim', true));
1270
+ html += tlGroup('🏷️ 城市与货场ID', tlGrid(g7, 2));
1271
+
1272
+ return html;
1273
+ }
1274
+
624
1275
  function init(data) {
625
1276
  // Header
626
1277
  if (data.showAvatar) {
@@ -630,7 +1281,12 @@
630
1281
  document.getElementById('tmp-name').innerText = data.name;
631
1282
  document.getElementById('tmp-id').innerText = 'TMP#' + data.tmpId;
632
1283
  document.getElementById('group-badge').innerText = data.groupName;
633
- document.getElementById('group-badge').style.backgroundColor = '#' + data.groupColor;
1284
+ const groupBadge = document.getElementById('group-badge');
1285
+ groupBadge.style.backgroundColor = '#' + data.groupColor;
1286
+ // 用分组色带一圈同色辉光,让徽章和整体玻璃质感统一
1287
+ if (data.groupColor) {
1288
+ groupBadge.style.boxShadow = 'inset 0 1px 0 rgba(255,255,255,.28), 0 4px 14px -6px #' + data.groupColor;
1289
+ }
634
1290
 
635
1291
  // Online dot & status badge
636
1292
  const dot = document.getElementById('online-dot');
@@ -677,13 +1333,13 @@
677
1333
  // Online Status Section
678
1334
  const statusEl = document.getElementById('tmp-online-status');
679
1335
  if (data.isOnline) {
680
- statusEl.innerHTML = '<span style="color:#4ade80;">在线 🟢</span> <span style="color:#94a3b8;font-size:12px;">(' + data.onlineServerName + ')</span>';
1336
+ statusEl.innerHTML = '<span style="color:#7ee7a5;">在线 🟢</span> <span style="color:#8b9cb8;font-size:12px;">(' + data.onlineServerName + ')</span>';
681
1337
  if (data.onlineCountry && data.onlineCity) {
682
1338
  document.getElementById('online-location-row').style.display = '';
683
1339
  document.getElementById('tmp-online-location').innerText = data.onlineCountry + ' - ' + data.onlineCity;
684
1340
  }
685
1341
  } else {
686
- statusEl.innerHTML = '<span style="color:#9ca3af;">离线 ⚫</span>';
1342
+ statusEl.innerHTML = '<span style="color:#8b9cb8;">离线 ⚫</span>';
687
1343
  }
688
1344
 
689
1345
  // Last Online
@@ -701,6 +1357,16 @@
701
1357
  map(data.onlineMapType, data.onlineX, data.onlineY);
702
1358
  }
703
1359
 
1360
+ // Telemetry(玩家在线 + 本队成员 + 数据未过期时后端才会下发,这里再兜一层在线判断)
1361
+ if (data.isOnline && data.telemetry && data.telemetry.player) {
1362
+ var tlHtml = buildTelemetry(data.telemetry);
1363
+ // 记录存在但所有字段都为空时不显示空壳板块
1364
+ if (tlHtml) {
1365
+ document.getElementById('telemetry-section').style.display = '';
1366
+ document.getElementById('telemetry-body').innerHTML = tlHtml;
1367
+ }
1368
+ }
1369
+
704
1370
  // Ban Section
705
1371
  if (data.isBan) {
706
1372
  document.getElementById('ban-section').style.display = '';