@usethink/cf-admin-fe 0.1.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,2006 @@
1
+ /* ═══════════════════════════════════════════════════════
2
+ @usethink/cf-admin-fe — Admin primitives (from cf-shop admin.css)
3
+ Products import this + tokens; page-only rules stay product-local.
4
+ ═══════════════════════════════════════════════════════ */
5
+
6
+ /* ── 页面切换过渡(Vue <Transition> 配合 AdminLayout) ── */
7
+ .admin-fade-enter-active,
8
+ .admin-fade-leave-active {
9
+ transition: opacity 0.15s ease;
10
+ }
11
+ .admin-fade-enter-from,
12
+ .admin-fade-leave-to {
13
+ opacity: 0;
14
+ }
15
+
16
+ /* ── 页面容器 ──
17
+ * 垂直区块间距唯一真相:--admin-stack-gap(base.css)。
18
+ * 子块(notice / stats / chips / table)禁止再写 margin-bottom 叠距。
19
+ */
20
+ .admin-page {
21
+ display: flex;
22
+ flex-direction: column;
23
+ gap: var(--admin-stack-gap, 8px);
24
+ width: 100%;
25
+ height: 100%;
26
+ min-height: 0;
27
+ }
28
+
29
+ /* ── 工具栏 ── */
30
+ .toolbar {
31
+ display: flex;
32
+ align-items: center;
33
+ justify-content: space-between;
34
+ gap: var(--admin-control-gap, 8px);
35
+ padding: var(--admin-card-pad, 10px) 12px;
36
+ background: var(--tg-secondary-bg, #151b28);
37
+ border-radius: var(--r-lg, 12px);
38
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
39
+ position: sticky;
40
+ top: 0;
41
+ z-index: 10;
42
+ flex-shrink: 0;
43
+ flex-wrap: wrap;
44
+ }
45
+
46
+ .filters {
47
+ display: flex;
48
+ align-items: center;
49
+ gap: var(--admin-control-gap, 8px);
50
+ flex-wrap: wrap;
51
+ flex: 1;
52
+ min-width: 0;
53
+ }
54
+
55
+ /*
56
+ * 工具栏文本控件(input / select)垂直居中铁律:
57
+ * - 固定 height: 34px(border-box)时,禁止再加上下 padding(8+8 会挤爆行高导致字被圆角裁切)
58
+ * - 只用左右 padding;line-height = 内容高度 32px(34 − 上下 border 各 1)
59
+ * - 覆盖 body 的 line-height: 1.6,否则中文底沿会被裁
60
+ */
61
+ .filters input:not([type='checkbox']):not([type='radio']),
62
+ .filters select {
63
+ box-sizing: border-box;
64
+ height: 34px;
65
+ padding: 0 10px;
66
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
67
+ border-radius: var(--r-md, 8px);
68
+ /* 不透明底:半透明 surface 在暗色下易导致 select 字色发灰、选中态异常 */
69
+ background-color: var(--tg-secondary-bg, #151b28);
70
+ color: var(--tg-text, #f0f2f5);
71
+ -webkit-text-fill-color: var(--tg-text, #f0f2f5);
72
+ font-size: 13px;
73
+ line-height: 32px;
74
+ width: auto;
75
+ flex: 1 1 auto;
76
+ min-width: 120px;
77
+ max-width: 260px;
78
+ color-scheme: dark;
79
+ vertical-align: middle;
80
+ }
81
+
82
+ /* 工具栏内筛选项勾选(如「仅余额 > 0」) */
83
+ .filters .filter-check {
84
+ display: inline-flex;
85
+ align-items: center;
86
+ gap: 8px;
87
+ height: 34px;
88
+ padding: 0 10px;
89
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
90
+ border-radius: var(--r-md, 8px);
91
+ background: var(--surface, rgba(255, 255, 255, 0.04));
92
+ color: var(--tg-text, #f0f2f5);
93
+ font-size: 13px;
94
+ line-height: 1.3;
95
+ white-space: nowrap;
96
+ cursor: pointer;
97
+ user-select: none;
98
+ flex: 0 0 auto;
99
+ min-width: 0;
100
+ max-width: none;
101
+ transition: background-color var(--duration-fast, 150ms) ease,
102
+ border-color var(--duration-fast, 150ms) ease;
103
+ }
104
+
105
+ .filters .filter-check:hover {
106
+ background: var(--surface-hover, rgba(255, 255, 255, 0.1));
107
+ border-color: var(--border-strong, rgba(255, 255, 255, 0.16));
108
+ }
109
+
110
+ .filters .filter-check:has(input:focus-visible) {
111
+ border-color: var(--admin-accent, #f59e0b);
112
+ }
113
+
114
+ .filters .filter-check input[type='checkbox'] {
115
+ width: 14px;
116
+ height: 14px;
117
+ margin: 0;
118
+ flex: 0 0 auto;
119
+ padding: 0;
120
+ border: none;
121
+ border-radius: 0;
122
+ background: transparent;
123
+ min-width: 0;
124
+ max-width: none;
125
+ accent-color: var(--admin-accent, #f59e0b);
126
+ cursor: pointer;
127
+ }
128
+
129
+ .filters .filter-check span {
130
+ color: var(--tg-hint, #9aa4b2);
131
+ }
132
+
133
+ .filters .filter-check:has(input:checked) span {
134
+ color: var(--tg-text, #f0f2f5);
135
+ }
136
+
137
+ .filters select {
138
+ /* 右侧留给箭头;上下 padding 必须保持 0(见上方铁律) */
139
+ padding: 0 32px 0 10px;
140
+ appearance: none;
141
+ -webkit-appearance: none;
142
+ /* 与 background-color 拆开写,避免 background 简写冲掉箭头 */
143
+ background-color: var(--tg-secondary-bg, #151b28);
144
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239aa4b2' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
145
+ background-repeat: no-repeat;
146
+ background-position: right 10px center;
147
+ background-size: 12px;
148
+ }
149
+
150
+ /* 工具栏下拉:展开项与选中态(配合 base color-scheme: dark) */
151
+ .filters select option,
152
+ .modal-form select option {
153
+ background-color: var(--tg-secondary-bg, #151b28);
154
+ color: var(--tg-text, #f0f2f5);
155
+ }
156
+
157
+ .filters select option:checked,
158
+ .modal-form select option:checked {
159
+ background-color: var(--admin-accent-soft, rgba(245, 158, 11, 0.28));
160
+ color: var(--tg-text, #f0f2f5);
161
+ }
162
+
163
+ .filters .btn {
164
+ flex: 0 0 auto;
165
+ height: 34px;
166
+ padding: 0 14px;
167
+ font-size: 14px;
168
+ }
169
+
170
+ .toolbar-actions {
171
+ display: flex;
172
+ align-items: center;
173
+ gap: var(--admin-inline-gap, 8px);
174
+ flex-shrink: 0;
175
+ }
176
+
177
+ /* ── 表格容器 ──
178
+ * 滚动裁剪发生在这里(overflow: auto)。sticky 表头相对 .table-wrap 吸顶。
179
+ * 禁止在页面 scoped 再写一套 table-wrap / th sticky。
180
+ */
181
+ .table-wrap {
182
+ background: var(--tg-secondary-bg, #151b28);
183
+ border-radius: var(--r-lg, 12px);
184
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
185
+ overflow: auto;
186
+ overscroll-behavior: contain;
187
+ scrollbar-gutter: stable;
188
+ flex: 1;
189
+ min-height: 0;
190
+ /* 建立独立层叠上下文,避免表头与外部 sticky toolbar 抢 z-index */
191
+ isolation: isolate;
192
+ }
193
+
194
+ /* ── 表格 ──
195
+ *
196
+ * 【强制】sticky 表头不得透出滚动中的 tbody 文字(线上曾出现表头与行内容叠字)。
197
+ * 根因与禁令(必须同时满足,缺一就会复发):
198
+ * 1. 表头背景必须「实色不透明」:禁止仅用 --surface-2 / rgba(255,255,255,0.07)
199
+ * 等半透明底;半透明会让上滑行内容从 th 底下透出来。
200
+ * 2. z-index 必须明显高于 tbody(含行内 .btn / .tag):至少 5;tbody 锁在 0。
201
+ * 3. 使用 border-collapse: separate + border-spacing: 0(collapse 模式下部分
202
+ * 浏览器 sticky 边框/层叠异常,表头会被盖住或闪烁)。
203
+ * 4. 新 Admin 表格只许用 .table-wrap > .admin-table,禁止页内重写 th 背景为透明。
204
+ * 门禁:scripts/27-verify-admin-styles.mjs 会扫描半透明 th 与缺失 sticky/z-index。
205
+ * 规约:docs/049_Admin共享样式与开发约束规约_2026-07-23.md
206
+ */
207
+ .admin-table {
208
+ width: 100%;
209
+ /* separate:sticky thead/th 在 Chrome/Safari/Firefox 行为一致;collapse 易透字/错层 */
210
+ border-collapse: separate;
211
+ border-spacing: 0;
212
+ /* 主字号小一号、行高紧凑:token 在 base.css --admin-font-base / --admin-cell-pad */
213
+ font-size: var(--admin-font-base, 13px);
214
+ line-height: 1.35;
215
+ table-layout: auto;
216
+ color: var(--tg-text, #f0f2f5);
217
+ }
218
+
219
+ .admin-table th,
220
+ .admin-table td {
221
+ padding: var(--admin-cell-pad, 6px 8px);
222
+ text-align: left;
223
+ vertical-align: middle;
224
+ border-bottom: 1px solid var(--border, rgba(255, 255, 255, 0.1));
225
+ white-space: normal;
226
+ overflow-wrap: break-word;
227
+ min-width: 50px;
228
+ line-height: 1.35;
229
+ }
230
+
231
+ /* 表体整体压在表头之下,避免单元格内定位元素盖住 sticky 表头 */
232
+ .admin-table tbody {
233
+ position: relative;
234
+ z-index: 0;
235
+ }
236
+
237
+ /*
238
+ * sticky 表头:唯一权威实现。
239
+ * background-color = 与 .table-wrap 相同的不透明底;
240
+ * background-image = 再叠一层 surface 色调(视觉仍偏「表头略亮」),但整体不透明。
241
+ * 禁止改回:background: var(--surface-2) 或任意 alpha < 1 的单层背景。
242
+ */
243
+ .admin-table thead th,
244
+ .admin-table th {
245
+ color: var(--tg-hint, #9aa4b2);
246
+ font-weight: 600;
247
+ font-size: var(--admin-font-sm, 11px);
248
+ /* 实色底:与滚动容器同色,保证上滑行内容无法透出 */
249
+ background-color: var(--tg-secondary-bg, #151b28);
250
+ /* 叠表面 tint(画在实色之上;合成结果仍不透明) */
251
+ background-image: linear-gradient(
252
+ var(--surface-2, rgba(255, 255, 255, 0.07)),
253
+ var(--surface-2, rgba(255, 255, 255, 0.07))
254
+ );
255
+ position: sticky;
256
+ top: 0;
257
+ z-index: 5;
258
+ /* 滚动时与正文的分界(不依赖 border-collapse 画线) */
259
+ box-shadow: inset 0 -1px 0 var(--border, rgba(255, 255, 255, 0.1));
260
+ /* 防止子节点创建异常层叠 */
261
+ vertical-align: middle;
262
+ }
263
+
264
+ .admin-table tbody tr:hover {
265
+ background: var(--surface-hover, rgba(255, 255, 255, 0.1));
266
+ }
267
+
268
+ .admin-table tbody tr {
269
+ transition: background-color 0.12s ease;
270
+ }
271
+
272
+ /* 单元格内交互控件不得抬到表头之上 */
273
+ .admin-table tbody td {
274
+ position: relative;
275
+ z-index: 0;
276
+ }
277
+
278
+ /* 选中行:琥珀体系(禁止 rgba(96,165,250) 等弱蓝) */
279
+ .admin-table tbody tr.is-selected,
280
+ .admin-table tbody tr.is-selected:nth-child(even) {
281
+ background: var(--admin-accent-soft, rgba(245, 158, 11, 0.14));
282
+ }
283
+
284
+ .admin-table tbody tr.is-selected:hover,
285
+ .admin-table tbody tr.is-selected:nth-child(even):hover {
286
+ background: var(--admin-accent-hover, rgba(245, 158, 11, 0.22));
287
+ }
288
+
289
+ /* 暗色表面用白透明分层,黑透明在深底上几乎不可见 */
290
+ .admin-table tbody tr:nth-child(even) {
291
+ background: rgba(255, 255, 255, 0.025);
292
+ }
293
+
294
+ .admin-table tbody tr:nth-child(even):hover {
295
+ background: var(--surface-hover, rgba(255, 255, 255, 0.1));
296
+ }
297
+
298
+ /* ── 空状态 ── */
299
+ .empty-text {
300
+ padding: 18px;
301
+ text-align: center;
302
+ color: var(--tg-hint, #9aa4b2);
303
+ font-size: 13px;
304
+ }
305
+
306
+ .table-error {
307
+ display: flex;
308
+ align-items: center;
309
+ justify-content: space-between;
310
+ gap: var(--admin-control-gap, 8px);
311
+ padding: 8px 12px;
312
+ border: 0.5px solid rgba(239, 68, 68, 0.24);
313
+ border-radius: var(--r-lg, 12px);
314
+ background: rgba(239, 68, 68, 0.07);
315
+ color: var(--tg-destructive, #dc2626);
316
+ font-size: 13px;
317
+ margin: 0;
318
+ flex-shrink: 0;
319
+ }
320
+
321
+ .table-error .btn {
322
+ flex: 0 0 auto;
323
+ }
324
+
325
+ /* ── 标签(暗色表面对比:字色略提亮,底用低饱和 tint) ── */
326
+ .tag {
327
+ display: inline-flex;
328
+ align-items: center;
329
+ justify-content: center;
330
+ flex: 0 0 auto;
331
+ padding: 2px 8px;
332
+ border-radius: 999px;
333
+ font-size: 12px;
334
+ font-weight: 550;
335
+ line-height: 1.4;
336
+ white-space: nowrap;
337
+ vertical-align: middle;
338
+ border: 1px solid transparent;
339
+ }
340
+
341
+ .tag-success {
342
+ background: rgba(52, 211, 153, 0.14);
343
+ color: #6ee7b7;
344
+ border-color: rgba(52, 211, 153, 0.22);
345
+ }
346
+
347
+ .tag-warning {
348
+ background: rgba(251, 191, 36, 0.14);
349
+ color: #fbbf24;
350
+ border-color: rgba(251, 191, 36, 0.22);
351
+ }
352
+
353
+ /* info 标签与 accent 对齐,暗底可读 */
354
+ .tag-info {
355
+ background: var(--admin-info-soft, rgba(245, 158, 11, 0.14));
356
+ color: var(--admin-info, #fbbf24);
357
+ border-color: var(--admin-info-border, rgba(245, 158, 11, 0.38));
358
+ }
359
+
360
+ .tag-muted {
361
+ background: var(--surface-2, rgba(255, 255, 255, 0.07));
362
+ color: var(--tg-hint, #9aa4b2);
363
+ border-color: var(--border, rgba(255, 255, 255, 0.1));
364
+ }
365
+
366
+ .muted-text {
367
+ color: var(--tg-hint, #9aa4b2);
368
+ font-size: 12px;
369
+ white-space: nowrap;
370
+ }
371
+
372
+ .tag-danger {
373
+ background: rgba(248, 113, 113, 0.14);
374
+ color: #fca5a5;
375
+ border-color: rgba(248, 113, 113, 0.24);
376
+ }
377
+
378
+ /* ── 小按钮 ── */
379
+ .btn-xs,
380
+ .btn-sm {
381
+ padding: 4px 8px;
382
+ font-size: 12px;
383
+ border-radius: var(--r-sm, 6px);
384
+ }
385
+
386
+ /* ── 键盘 focus 可见样式(Admin 强调色,避免弱蓝描边) ── */
387
+ :focus-visible {
388
+ outline: 2px solid var(--admin-accent, #f59e0b);
389
+ outline-offset: 2px;
390
+ }
391
+
392
+ /* ── 滚动条(Webkit;暗底用白透明才可见) ── */
393
+ ::-webkit-scrollbar {
394
+ width: 8px;
395
+ height: 8px;
396
+ }
397
+
398
+ ::-webkit-scrollbar-track {
399
+ background: transparent;
400
+ }
401
+
402
+ ::-webkit-scrollbar-thumb {
403
+ background: rgba(255, 255, 255, 0.18);
404
+ border-radius: 4px;
405
+ }
406
+
407
+ ::-webkit-scrollbar-thumb:hover {
408
+ background: rgba(255, 255, 255, 0.28);
409
+ }
410
+
411
+ /* ── 危险操作按钮 ── */
412
+ .btn-danger {
413
+ background: var(--tg-destructive, #ef4444);
414
+ color: #fff;
415
+ border-color: transparent;
416
+ }
417
+ .btn-danger:hover {
418
+ background: #dc2626;
419
+ border-color: transparent;
420
+ }
421
+
422
+ /* ── 表格操作按钮组(同一单元格内多个按钮) ──
423
+ * 默认允许换行:抽奖/订单等行内按钮多时,nowrap 会把整表横向撑爆。
424
+ * 需要单行滚动时再加 .table-actions-nowrap。
425
+ */
426
+ .table-actions {
427
+ display: inline-flex;
428
+ align-items: center;
429
+ gap: 4px;
430
+ flex-wrap: nowrap;
431
+ white-space: nowrap;
432
+ }
433
+
434
+ .table-actions-nowrap {
435
+ flex-wrap: nowrap;
436
+ white-space: nowrap;
437
+ max-width: none;
438
+ }
439
+
440
+ /* ── 表格内上架/启用类开关(紧凑,与 btn-xs 齐高) ── */
441
+ .status-switch {
442
+ display: inline-flex;
443
+ align-items: center;
444
+ gap: 4px;
445
+ margin: 0;
446
+ min-height: 28px;
447
+ padding: 0 2px 0 0;
448
+ cursor: pointer;
449
+ user-select: none;
450
+ white-space: nowrap;
451
+ color: var(--tg-hint, #9aa4b2);
452
+ font-size: 12px;
453
+ line-height: 1;
454
+ }
455
+
456
+ .status-switch input {
457
+ position: absolute;
458
+ opacity: 0;
459
+ width: 1px;
460
+ height: 1px;
461
+ margin: 0;
462
+ pointer-events: none;
463
+ }
464
+
465
+ .status-switch-track {
466
+ position: relative;
467
+ display: inline-flex;
468
+ align-items: center;
469
+ width: 32px;
470
+ height: 18px;
471
+ flex: 0 0 auto;
472
+ border-radius: 999px;
473
+ background: rgba(148, 163, 184, 0.28);
474
+ border: 1px solid rgba(255, 255, 255, 0.12);
475
+ transition: background 0.15s ease, border-color 0.15s ease;
476
+ }
477
+
478
+ .status-switch-thumb {
479
+ position: absolute;
480
+ top: 1px;
481
+ left: 1px;
482
+ width: 14px;
483
+ height: 14px;
484
+ border-radius: 50%;
485
+ background: #f8fafc;
486
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.28);
487
+ transition: transform 0.15s ease;
488
+ }
489
+
490
+ .status-switch.is-on {
491
+ color: var(--admin-success, #6ee7b7);
492
+ }
493
+
494
+ .status-switch.is-on .status-switch-track {
495
+ background: rgba(16, 185, 129, 0.42);
496
+ border-color: rgba(52, 211, 153, 0.45);
497
+ }
498
+
499
+ .status-switch.is-on .status-switch-thumb {
500
+ transform: translateX(14px);
501
+ }
502
+
503
+ .status-switch-text {
504
+ min-width: 2em;
505
+ font-weight: 500;
506
+ }
507
+
508
+ .status-switch.is-disabled,
509
+ .status-switch:has(input:disabled) {
510
+ opacity: 0.55;
511
+ cursor: not-allowed;
512
+ }
513
+
514
+ .status-switch input:focus-visible + .status-switch-track {
515
+ outline: 2px solid var(--admin-accent, #f59e0b);
516
+ outline-offset: 2px;
517
+ }
518
+
519
+ @media (prefers-reduced-motion: reduce) {
520
+ .status-switch-track,
521
+ .status-switch-thumb {
522
+ transition: none;
523
+ }
524
+ }
525
+
526
+ /* ── 模态框表单 ── */
527
+ .modal-form {
528
+ display: flex;
529
+ flex-direction: column;
530
+ gap: var(--admin-card-gap, 10px);
531
+ /* 防止焦点环被父级裁切:表单自身不裁剪 */
532
+ overflow: visible;
533
+ min-width: 0;
534
+ }
535
+
536
+ .modal-form label {
537
+ display: flex;
538
+ flex-direction: column;
539
+ gap: 4px;
540
+ font-size: 13px;
541
+ color: var(--tg-text, #f0f2f5);
542
+ min-width: 0;
543
+ overflow: visible;
544
+ }
545
+
546
+ .modal-form label span:first-child {
547
+ font-weight: 500;
548
+ font-size: 12px;
549
+ color: var(--tg-hint, #9aa4b2);
550
+ }
551
+
552
+ .modal-form input:not([type='checkbox']):not([type='radio']),
553
+ .modal-form textarea,
554
+ .modal-form select {
555
+ box-sizing: border-box;
556
+ width: 100%;
557
+ max-width: 100%;
558
+ min-width: 0;
559
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
560
+ border-radius: var(--r-md, 8px);
561
+ background-color: var(--tg-secondary-bg, #151b28);
562
+ color: var(--tg-text, #f0f2f5);
563
+ -webkit-text-fill-color: var(--tg-text, #f0f2f5);
564
+ font-size: 13px;
565
+ color-scheme: dark;
566
+ /* 焦点环在控件盒外侧,避免被父级 overflow 裁切观感(配合 modal-body 内边距) */
567
+ }
568
+
569
+ .modal-form input:not([type='checkbox']):not([type='radio']),
570
+ .modal-form select {
571
+ height: 34px;
572
+ padding: 0 10px;
573
+ line-height: 32px;
574
+ }
575
+
576
+ .modal-form textarea {
577
+ padding: 8px 10px;
578
+ line-height: 1.45;
579
+ min-height: 72px;
580
+ resize: vertical;
581
+ }
582
+
583
+ .modal-form select {
584
+ appearance: none;
585
+ -webkit-appearance: none;
586
+ padding: 0 32px 0 10px;
587
+ background-color: var(--tg-secondary-bg, #151b28);
588
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%239aa4b2' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
589
+ background-repeat: no-repeat;
590
+ background-position: right 10px center;
591
+ background-size: 12px;
592
+ }
593
+
594
+ /* 表单内勾选行:横向排列,控件保持原生小尺寸 */
595
+ .checkbox-label {
596
+ flex-direction: row !important;
597
+ align-items: center;
598
+ gap: 8px !important;
599
+ cursor: pointer;
600
+ font-size: 13px;
601
+ color: var(--tg-text, #f0f2f5);
602
+ }
603
+
604
+ .checkbox-label input[type='checkbox'],
605
+ .checkbox-label input[type='radio'],
606
+ .modal-form input[type='checkbox'],
607
+ .modal-form input[type='radio'] {
608
+ width: 14px;
609
+ height: 14px;
610
+ margin: 0;
611
+ flex: 0 0 auto;
612
+ padding: 0;
613
+ border: none;
614
+ border-radius: 0;
615
+ background: transparent;
616
+ accent-color: var(--admin-accent, #f59e0b);
617
+ cursor: pointer;
618
+ }
619
+
620
+ /* ── 详情弹窗 ── */
621
+ .detail-box {
622
+ display: flex;
623
+ flex-direction: column;
624
+ gap: var(--admin-card-gap, 10px);
625
+ }
626
+
627
+ .detail-section {
628
+ padding: 10px 12px;
629
+ border-radius: var(--r-md, 8px);
630
+ background: var(--surface-2, rgba(255, 255, 255, 0.07));
631
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
632
+ }
633
+
634
+ .detail-title {
635
+ font-size: 13px;
636
+ font-weight: 600;
637
+ margin-bottom: 6px;
638
+ color: var(--tg-text, #f0f2f5);
639
+ }
640
+
641
+ .detail-row {
642
+ display: flex;
643
+ align-items: flex-start;
644
+ justify-content: space-between;
645
+ gap: 10px;
646
+ padding: 4px 0;
647
+ font-size: 13px;
648
+ }
649
+
650
+ .detail-label {
651
+ color: var(--tg-hint, #9aa4b2);
652
+ flex-shrink: 0;
653
+ font-size: 12px;
654
+ }
655
+
656
+ .detail-value {
657
+ font-weight: 500;
658
+ word-break: break-all;
659
+ text-align: right;
660
+ font-size: 13px;
661
+ color: var(--tg-text, #f0f2f5);
662
+ }
663
+
664
+ .detail-actions {
665
+ display: flex;
666
+ gap: 8px;
667
+ }
668
+
669
+ /* ── 骨架屏行(用于表格 loading) ── */
670
+ @keyframes shimmer-row {
671
+ 0% { background-position: -200% 0; }
672
+ 100% { background-position: 200% 0; }
673
+ }
674
+
675
+ .skeleton-row td {
676
+ padding: 8px;
677
+ }
678
+
679
+ .skeleton-cell {
680
+ height: 14px;
681
+ background: linear-gradient(
682
+ 90deg,
683
+ var(--surface, rgba(255, 255, 255, 0.04)) 25%,
684
+ var(--surface-hover, rgba(255, 255, 255, 0.1)) 50%,
685
+ var(--surface, rgba(255, 255, 255, 0.04)) 75%
686
+ );
687
+ background-size: 200% 100%;
688
+ animation: shimmer-row 1.5s ease-in-out infinite;
689
+ border-radius: var(--r-sm, 6px);
690
+ }
691
+
692
+ /* ═══════════════════════════════════════════════════════
693
+ 小桌面断点(≥770px,对应 sidebar 可见的最小宽度)
694
+ admin-content-pad / admin-cell-pad / stack-gap 由 base.css 变量驱动
695
+ 禁止在此硬编码放大 .admin-page / .toolbar 的 gap(会叠出过大留白)
696
+ ═══════════════════════════════════════════════════════ */
697
+ @media (min-width: 770px) {
698
+ .empty-text {
699
+ padding: 24px;
700
+ font-size: 13px;
701
+ }
702
+
703
+ .modal-form {
704
+ gap: var(--admin-card-gap, 12px);
705
+ }
706
+
707
+ .detail-box {
708
+ gap: var(--admin-card-gap, 12px);
709
+ }
710
+
711
+ .detail-section {
712
+ padding: var(--admin-card-pad, 12px);
713
+ }
714
+
715
+ .detail-row {
716
+ padding: 5px 0;
717
+ }
718
+ }
719
+
720
+ /* ═══════════════════════════════════════════════════════
721
+ 桌面断点覆盖(≥1024px)
722
+ ═══════════════════════════════════════════════════════ */
723
+ @media (min-width: 1024px) {
724
+ /* 禁止在此重写上下 padding / 放大字号;会再次裁切 select 文字 */
725
+ .filters input:not([type='checkbox']):not([type='radio']),
726
+ .filters select {
727
+ font-size: 13px;
728
+ }
729
+
730
+ .filters select {
731
+ padding: 0 32px 0 12px;
732
+ }
733
+
734
+ .table-wrap {
735
+ border-radius: var(--r-xl, 16px);
736
+ }
737
+
738
+ .admin-table th,
739
+ .admin-table td {
740
+ /* 沿用 token 紧凑 padding,勿在此写大一号硬编码 */
741
+ padding: var(--admin-cell-pad, 6px 10px);
742
+ }
743
+
744
+ .empty-text {
745
+ padding: 28px 24px;
746
+ font-size: 14px;
747
+ }
748
+
749
+ .modal-form {
750
+ gap: var(--admin-card-gap, 12px);
751
+ }
752
+
753
+ .modal-form label {
754
+ font-size: 13px;
755
+ }
756
+
757
+ .modal-form input:not([type='checkbox']):not([type='radio']) {
758
+ /* 保持 height + line-height 垂直居中,勿加上下 padding */
759
+ font-size: 13px;
760
+ }
761
+
762
+ .modal-form textarea {
763
+ padding: 8px 10px;
764
+ font-size: 13px;
765
+ }
766
+
767
+ .detail-box {
768
+ gap: var(--admin-card-gap, 12px);
769
+ }
770
+
771
+ .detail-section {
772
+ padding: var(--admin-card-pad, 12px);
773
+ }
774
+
775
+ .detail-row {
776
+ padding: 6px 0;
777
+ font-size: 13px;
778
+ }
779
+ }
780
+
781
+ /* ── 批量选择操作条 ── */
782
+ .bulk-bar {
783
+ display: flex;
784
+ align-items: center;
785
+ justify-content: space-between;
786
+ flex-wrap: wrap;
787
+ gap: var(--admin-control-gap, 8px);
788
+ padding: 8px 12px;
789
+ border: 1px solid var(--admin-accent-border, rgba(245, 158, 11, 0.38));
790
+ border-radius: var(--r-lg, 12px);
791
+ background: var(--admin-accent-soft, rgba(245, 158, 11, 0.14));
792
+ color: var(--tg-text, #f0f2f5);
793
+ font-size: 13px;
794
+ box-shadow: 0 6px 18px rgba(0, 0, 0, 0.2);
795
+ /* 间距交给 .admin-page gap,禁止 margin-bottom */
796
+ margin: 0;
797
+ flex-shrink: 0;
798
+ }
799
+
800
+ .bulk-bar > span:first-child {
801
+ font-weight: 600;
802
+ }
803
+
804
+ .bulk-bar[aria-busy='true'] {
805
+ cursor: progress;
806
+ }
807
+
808
+ .bulk-actions {
809
+ display: flex;
810
+ align-items: center;
811
+ flex-wrap: wrap;
812
+ gap: var(--admin-inline-gap, 8px);
813
+ }
814
+
815
+ .table-command-bar {
816
+ display: flex;
817
+ align-items: center;
818
+ justify-content: space-between;
819
+ gap: var(--admin-inline-gap, 8px) var(--admin-control-gap, 8px);
820
+ min-height: 34px;
821
+ flex-wrap: wrap;
822
+ margin: 0;
823
+ flex-shrink: 0;
824
+ }
825
+
826
+ /* ═══════════════════════════════════════════════════════
827
+ 快捷筛选 Chip / 视图 Tab(对齐 /admin/cards 琥珀色)
828
+ 页面可用 .quick-chip / .action-chip / .admin-tab 等
829
+ ═══════════════════════════════════════════════════════ */
830
+ .quick-strip,
831
+ .action-strip,
832
+ .admin-tabs,
833
+ .tabs {
834
+ display: flex;
835
+ flex-wrap: wrap;
836
+ align-items: center;
837
+ gap: var(--admin-inline-gap, 8px);
838
+ margin: 0;
839
+ }
840
+
841
+ .quick-chip,
842
+ .action-chip,
843
+ .admin-tab,
844
+ .tab {
845
+ appearance: none;
846
+ display: inline-flex;
847
+ align-items: center;
848
+ justify-content: center;
849
+ gap: 6px;
850
+ white-space: nowrap;
851
+ padding: 7px 12px;
852
+ min-height: 32px;
853
+ border-radius: var(--r-full, 999px);
854
+ border: 1px solid var(--admin-accent-border, rgba(245, 158, 11, 0.38));
855
+ background: var(--admin-accent-soft, rgba(245, 158, 11, 0.14));
856
+ color: var(--admin-accent-text, #fbbf24);
857
+ font-size: 13px;
858
+ font-weight: 550;
859
+ line-height: 1.3;
860
+ cursor: pointer;
861
+ transition:
862
+ background-color var(--duration-fast, 150ms) ease,
863
+ border-color var(--duration-fast, 150ms) ease,
864
+ color var(--duration-fast, 150ms) ease,
865
+ box-shadow var(--duration-fast, 150ms) ease;
866
+ }
867
+
868
+ .quick-chip:hover,
869
+ .action-chip:hover,
870
+ .admin-tab:hover,
871
+ .tab:hover {
872
+ background: var(--admin-accent-hover, rgba(245, 158, 11, 0.22));
873
+ border-color: rgba(245, 158, 11, 0.55);
874
+ color: #fcd34d;
875
+ }
876
+
877
+ .quick-chip:focus-visible,
878
+ .action-chip:focus-visible,
879
+ .admin-tab:focus-visible,
880
+ .tab:focus-visible {
881
+ outline: 2px solid var(--admin-accent, #f59e0b);
882
+ outline-offset: 2px;
883
+ }
884
+
885
+ .quick-chip.is-active,
886
+ .action-chip.is-active,
887
+ .admin-tab.active,
888
+ .admin-tab.is-active,
889
+ .tab.active,
890
+ .tab.is-active,
891
+ .tab[aria-selected='true'] {
892
+ background: rgba(245, 158, 11, 0.28);
893
+ border-color: var(--admin-accent, #f59e0b);
894
+ color: #fff7ed;
895
+ box-shadow: 0 0 0 1px rgba(245, 158, 11, 0.25);
896
+ font-weight: 600;
897
+ }
898
+
899
+ /* 矩形 Tab 变体(余额页 segmented 风格) */
900
+ .admin-tab-bar {
901
+ display: flex;
902
+ gap: 4px;
903
+ margin-bottom: 4px;
904
+ padding: 3px;
905
+ border-radius: var(--r-md, 8px);
906
+ background: var(--surface-2, rgba(255, 255, 255, 0.07));
907
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
908
+ width: fit-content;
909
+ max-width: 100%;
910
+ }
911
+
912
+ .admin-tab-bar .admin-tab,
913
+ .admin-tab-bar .tab-btn {
914
+ border-radius: var(--r-sm, 6px);
915
+ min-height: 30px;
916
+ padding: 6px 14px;
917
+ border: 1px solid transparent;
918
+ background: transparent;
919
+ color: var(--tg-hint, #9aa4b2);
920
+ box-shadow: none;
921
+ font-weight: 500;
922
+ }
923
+
924
+ .admin-tab-bar .admin-tab:hover,
925
+ .admin-tab-bar .tab-btn:hover {
926
+ background: var(--admin-accent-soft, rgba(245, 158, 11, 0.14));
927
+ color: var(--admin-accent-text, #fbbf24);
928
+ border-color: transparent;
929
+ }
930
+
931
+ .admin-tab-bar .admin-tab.active,
932
+ .admin-tab-bar .admin-tab.is-active,
933
+ .admin-tab-bar .tab-btn.active {
934
+ background: var(--admin-accent-soft, rgba(245, 158, 11, 0.14));
935
+ border-color: var(--admin-accent-border, rgba(245, 158, 11, 0.38));
936
+ color: var(--admin-accent-text, #fbbf24);
937
+ box-shadow: none;
938
+ font-weight: 600;
939
+ }
940
+
941
+ /* 说明 / 提示卡(cards notice 同色)
942
+ * 垂直间距仅靠 .admin-page gap;禁止页面再写 margin-bottom。
943
+ */
944
+ .notice-card,
945
+ .admin-notice {
946
+ display: flex;
947
+ flex-wrap: wrap;
948
+ align-items: baseline;
949
+ gap: 4px var(--admin-inline-gap, 8px);
950
+ padding: 8px 12px;
951
+ border-radius: var(--r-md, 8px);
952
+ border: 1px solid var(--admin-accent-border, rgba(245, 158, 11, 0.38));
953
+ background: var(--admin-accent-soft, rgba(245, 158, 11, 0.14));
954
+ font-size: 13px;
955
+ line-height: 1.5;
956
+ color: var(--tg-text, #f0f2f5);
957
+ margin: 0;
958
+ flex-shrink: 0;
959
+ }
960
+
961
+ .notice-card strong,
962
+ .admin-notice strong {
963
+ color: var(--admin-accent, #f59e0b);
964
+ font-weight: 650;
965
+ }
966
+
967
+ /* 小标签(同码库存等) */
968
+ .same-code-tag,
969
+ .admin-pill-amber {
970
+ display: inline-flex;
971
+ align-items: center;
972
+ white-space: nowrap;
973
+ padding: 2px 6px;
974
+ border-radius: var(--r-full, 999px);
975
+ font-size: 11px;
976
+ line-height: 1.4;
977
+ font-weight: 550;
978
+ color: var(--admin-accent-text, #fbbf24);
979
+ background: var(--admin-accent-soft, rgba(245, 158, 11, 0.14));
980
+ border: 1px solid var(--admin-accent-border, rgba(245, 158, 11, 0.38));
981
+ }
982
+
983
+ .table-command-actions {
984
+ display: flex;
985
+ align-items: center;
986
+ justify-content: flex-end;
987
+ gap: 8px;
988
+ min-width: 0;
989
+ margin-left: auto;
990
+ flex-wrap: wrap;
991
+ }
992
+
993
+ .selection-summary {
994
+ color: var(--tg-text, #f0f2f5);
995
+ font-size: 12px;
996
+ font-weight: 600;
997
+ white-space: nowrap;
998
+ }
999
+
1000
+ .table-command-actions[aria-busy='true'] {
1001
+ cursor: progress;
1002
+ }
1003
+
1004
+ .select-cell {
1005
+ width: 42px;
1006
+ min-width: 42px !important;
1007
+ text-align: center !important;
1008
+ }
1009
+
1010
+ .select-cell input {
1011
+ width: 15px;
1012
+ height: 15px;
1013
+ margin: 0;
1014
+ padding: 0;
1015
+ border: none;
1016
+ background: transparent;
1017
+ accent-color: var(--admin-accent, #f59e0b);
1018
+ cursor: pointer;
1019
+ touch-action: manipulation;
1020
+ }
1021
+
1022
+ .select-cell input:disabled {
1023
+ cursor: not-allowed;
1024
+ }
1025
+
1026
+ @media (max-width: 640px) {
1027
+ .toolbar {
1028
+ align-items: stretch;
1029
+ }
1030
+
1031
+ .toolbar-actions,
1032
+ .toolbar > .actions {
1033
+ flex: 1 0 100%;
1034
+ width: 100%;
1035
+ }
1036
+
1037
+ .filters {
1038
+ display: grid;
1039
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1040
+ gap: 8px;
1041
+ flex: 1 0 100%;
1042
+ width: 100%;
1043
+ }
1044
+
1045
+ .filters input:not([type='checkbox']):not([type='radio']),
1046
+ .filters select {
1047
+ width: 100%;
1048
+ min-width: 0;
1049
+ max-width: none;
1050
+ }
1051
+
1052
+ .filters .filter-check {
1053
+ width: 100%;
1054
+ justify-content: flex-start;
1055
+ }
1056
+
1057
+ .filters .btn {
1058
+ width: 100%;
1059
+ }
1060
+
1061
+ .toolbar-actions,
1062
+ .toolbar > .actions {
1063
+ overflow-x: auto;
1064
+ padding-bottom: 2px;
1065
+ scrollbar-width: thin;
1066
+ }
1067
+
1068
+ .toolbar-actions .btn,
1069
+ .toolbar > .actions .btn {
1070
+ flex: 0 0 auto;
1071
+ }
1072
+
1073
+ .bulk-bar {
1074
+ align-items: stretch;
1075
+ }
1076
+
1077
+ .bulk-actions {
1078
+ width: 100%;
1079
+ flex-wrap: nowrap;
1080
+ overflow-x: auto;
1081
+ padding-bottom: 2px;
1082
+ scrollbar-width: thin;
1083
+ }
1084
+
1085
+ .bulk-actions .btn {
1086
+ flex: 0 0 auto;
1087
+ }
1088
+
1089
+ .table-command-bar {
1090
+ align-items: stretch;
1091
+ }
1092
+
1093
+ .table-command-actions {
1094
+ width: 100%;
1095
+ justify-content: flex-start;
1096
+ flex-wrap: wrap;
1097
+ overflow: visible;
1098
+ padding-bottom: 0;
1099
+ }
1100
+
1101
+ .table-command-actions .selection-summary {
1102
+ flex: 1 0 100%;
1103
+ }
1104
+
1105
+ .table-command-actions .btn {
1106
+ flex: 0 0 auto;
1107
+ }
1108
+
1109
+ .select-cell input {
1110
+ width: 18px;
1111
+ height: 18px;
1112
+ }
1113
+
1114
+ .admin-table .btn-xs {
1115
+ min-height: 30px;
1116
+ padding: 4px 8px;
1117
+ }
1118
+ }
1119
+
1120
+ @media (prefers-reduced-motion: reduce) {
1121
+ .admin-table tbody tr {
1122
+ transition: none;
1123
+ }
1124
+ }
1125
+
1126
+ /* ═══════════════════════════════════════════════════════
1127
+ Admin 设计系统 · 共享 primitives(页面禁止再复制一套)
1128
+ ═══════════════════════════════════════════════════════ */
1129
+
1130
+ /* ── 页面标题 ── */
1131
+ .page-title {
1132
+ margin: 0;
1133
+ font-size: 16px;
1134
+ font-weight: 650;
1135
+ color: var(--tg-text, #f0f2f5);
1136
+ letter-spacing: 0.01em;
1137
+ }
1138
+
1139
+ /* ── 表单辅助说明 ── */
1140
+ .field-hint {
1141
+ display: block;
1142
+ margin-top: 4px;
1143
+ color: var(--tg-hint, #9aa4b2);
1144
+ font-size: 12px;
1145
+ line-height: 1.5;
1146
+ }
1147
+
1148
+ /* ── 弹窗底部操作(页面 scoped 内可复用 class) ── */
1149
+ .modal-actions {
1150
+ display: flex;
1151
+ justify-content: flex-end;
1152
+ flex-wrap: wrap;
1153
+ gap: 8px;
1154
+ margin-top: 4px;
1155
+ }
1156
+
1157
+ /* ── JSON / 代码详情块 ── */
1158
+ .detail-json {
1159
+ background: var(--surface-2, rgba(255, 255, 255, 0.07));
1160
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
1161
+ color: var(--tg-text, #f0f2f5);
1162
+ padding: 12px;
1163
+ border-radius: var(--r-md, 8px);
1164
+ font-size: 12px;
1165
+ line-height: 1.5;
1166
+ overflow: auto;
1167
+ max-height: 50vh;
1168
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);
1169
+ white-space: pre-wrap;
1170
+ word-break: break-word;
1171
+ }
1172
+
1173
+ /* ── 骨架 ── */
1174
+ .skeleton-list {
1175
+ display: flex;
1176
+ flex-direction: column;
1177
+ gap: 8px;
1178
+ }
1179
+
1180
+ .skeleton-line {
1181
+ height: 12px;
1182
+ border-radius: 6px;
1183
+ background: linear-gradient(
1184
+ 90deg,
1185
+ var(--surface, rgba(255, 255, 255, 0.04)) 25%,
1186
+ var(--surface-hover, rgba(255, 255, 255, 0.1)) 50%,
1187
+ var(--surface, rgba(255, 255, 255, 0.04)) 75%
1188
+ );
1189
+ background-size: 200% 100%;
1190
+ animation: shimmer-row 1.5s ease-in-out infinite;
1191
+ }
1192
+
1193
+ .skeleton-line.w-40 { width: 40%; }
1194
+ .skeleton-line.w-60 { width: 60%; }
1195
+ .skeleton-line.w-80 { width: 80%; }
1196
+
1197
+ /* ── 状态横幅 / 健康卡 / 余额开关卡 ── */
1198
+ .admin-banner,
1199
+ .status-banner,
1200
+ .health-card,
1201
+ .balance-card.tone-panel {
1202
+ margin: 0 0 12px;
1203
+ padding: 10px 12px;
1204
+ border-radius: var(--r-lg, 12px);
1205
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
1206
+ font-size: 13px;
1207
+ line-height: 1.5;
1208
+ color: var(--tg-text, #f0f2f5);
1209
+ }
1210
+
1211
+ .admin-banner strong,
1212
+ .status-banner strong,
1213
+ .health-card strong {
1214
+ display: block;
1215
+ margin-bottom: 4px;
1216
+ color: inherit;
1217
+ }
1218
+
1219
+ .admin-banner p,
1220
+ .health-card p {
1221
+ margin: 0;
1222
+ color: inherit;
1223
+ opacity: 0.92;
1224
+ }
1225
+
1226
+ /* 成功 / 危险 / 警告 / 信息(信息=琥珀,禁止弱蓝) */
1227
+ .tone-success,
1228
+ .admin-banner--success,
1229
+ .status-banner-success,
1230
+ .health-card-success,
1231
+ .balance-card-success {
1232
+ background: var(--admin-success-soft, rgba(52, 211, 153, 0.12));
1233
+ color: var(--admin-success, #6ee7b7);
1234
+ border-color: var(--admin-success-border, rgba(52, 211, 153, 0.28));
1235
+ }
1236
+
1237
+ .tone-danger,
1238
+ .admin-banner--error,
1239
+ .status-banner-error,
1240
+ .health-card-error {
1241
+ background: var(--admin-danger-soft, rgba(248, 113, 113, 0.12));
1242
+ color: var(--admin-danger, #fca5a5);
1243
+ border-color: var(--admin-danger-border, rgba(248, 113, 113, 0.28));
1244
+ }
1245
+
1246
+ .tone-warning,
1247
+ .admin-banner--warning,
1248
+ .status-banner-warning {
1249
+ background: var(--admin-warning-soft, rgba(251, 191, 36, 0.12));
1250
+ color: var(--admin-warning, #fbbf24);
1251
+ border-color: var(--admin-warning-border, rgba(251, 191, 36, 0.28));
1252
+ }
1253
+
1254
+ .tone-info,
1255
+ .admin-banner--info,
1256
+ .status-banner-info,
1257
+ .balance-card-muted {
1258
+ background: var(--admin-info-soft, rgba(245, 158, 11, 0.14));
1259
+ color: var(--admin-info, #fbbf24);
1260
+ border-color: var(--admin-info-border, rgba(245, 158, 11, 0.38));
1261
+ }
1262
+
1263
+ /* 关闭/中性信息态:正文用主字色,标题用琥珀,避免整段弱蓝 */
1264
+ .balance-card-muted,
1265
+ .status-banner-info {
1266
+ color: var(--tg-text, #f0f2f5);
1267
+ }
1268
+
1269
+ .balance-card-muted .balance-card-head strong,
1270
+ .status-banner-info strong {
1271
+ color: var(--admin-accent-text, #fbbf24);
1272
+ }
1273
+
1274
+ .balance-card-muted .balance-card-head p,
1275
+ .balance-card-muted .balance-card-hint {
1276
+ color: var(--tg-hint, #9aa4b2);
1277
+ }
1278
+
1279
+ .balance-card {
1280
+ margin-bottom: 12px;
1281
+ padding: 10px 12px;
1282
+ border-radius: var(--r-lg, 12px);
1283
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
1284
+ display: flex;
1285
+ flex-direction: column;
1286
+ gap: 10px;
1287
+ background: var(--tg-secondary-bg, #151b28);
1288
+ }
1289
+
1290
+ .balance-card-head {
1291
+ display: flex;
1292
+ align-items: flex-start;
1293
+ justify-content: space-between;
1294
+ gap: 10px;
1295
+ }
1296
+
1297
+ .balance-card-head strong {
1298
+ display: block;
1299
+ margin-bottom: 4px;
1300
+ font-size: 13px;
1301
+ color: var(--tg-text, #f0f2f5);
1302
+ }
1303
+
1304
+ .balance-card-head p,
1305
+ .balance-card-hint {
1306
+ margin: 0;
1307
+ font-size: 12px;
1308
+ line-height: 1.5;
1309
+ color: var(--tg-hint, #9aa4b2);
1310
+ }
1311
+
1312
+ .balance-card-success .balance-card-head strong {
1313
+ color: var(--admin-success, #6ee7b7);
1314
+ }
1315
+
1316
+ .balance-card-success .balance-card-head p,
1317
+ .balance-card-success .balance-card-hint {
1318
+ color: rgba(110, 231, 183, 0.88);
1319
+ }
1320
+
1321
+ .balance-card-actions {
1322
+ display: flex;
1323
+ flex-wrap: wrap;
1324
+ gap: 8px;
1325
+ align-items: center;
1326
+ }
1327
+
1328
+ .balance-card-hint code {
1329
+ font-size: 12px;
1330
+ color: var(--admin-accent-text, #fbbf24);
1331
+ }
1332
+
1333
+ /* ── 列表卡片(支付渠道 / 配置块) ── */
1334
+ .admin-card,
1335
+ .provider-card {
1336
+ background: var(--tg-secondary-bg, #151b28);
1337
+ border-radius: var(--r-lg, 12px);
1338
+ padding: 12px;
1339
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
1340
+ display: flex;
1341
+ align-items: flex-start;
1342
+ justify-content: space-between;
1343
+ gap: 12px;
1344
+ flex: 0 0 auto;
1345
+ transition: border-color 0.12s ease, background-color 0.12s ease;
1346
+ }
1347
+
1348
+ .admin-card:hover,
1349
+ .provider-card:hover {
1350
+ border-color: var(--border-strong, rgba(255, 255, 255, 0.16));
1351
+ }
1352
+
1353
+ .admin-card-list,
1354
+ .provider-list {
1355
+ display: flex;
1356
+ flex-direction: column;
1357
+ gap: 10px;
1358
+ flex: 1;
1359
+ min-height: 0;
1360
+ overflow-y: auto;
1361
+ overscroll-behavior: contain;
1362
+ scrollbar-gutter: stable;
1363
+ padding: 1px 2px 2px 1px;
1364
+ }
1365
+
1366
+ .admin-card-list:focus-visible,
1367
+ .provider-list:focus-visible {
1368
+ outline: 2px solid var(--admin-accent, #f59e0b);
1369
+ outline-offset: 2px;
1370
+ }
1371
+
1372
+ /* ── 事实网格 ── */
1373
+ .fact-grid,
1374
+ .provider-facts {
1375
+ display: grid;
1376
+ grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
1377
+ gap: 8px;
1378
+ margin: 0;
1379
+ }
1380
+
1381
+ .fact-grid > div,
1382
+ .provider-facts > div {
1383
+ min-width: 0;
1384
+ padding: 8px 10px;
1385
+ border-radius: var(--r-md, 8px);
1386
+ background: var(--surface-2, rgba(255, 255, 255, 0.07));
1387
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.08));
1388
+ }
1389
+
1390
+ .fact-grid dt,
1391
+ .provider-facts dt {
1392
+ margin: 0 0 3px;
1393
+ color: var(--tg-hint, #9aa4b2);
1394
+ font-size: 12px;
1395
+ }
1396
+
1397
+ .fact-grid dd,
1398
+ .provider-facts dd {
1399
+ margin: 0;
1400
+ color: var(--tg-text, #f0f2f5);
1401
+ font-size: 13px;
1402
+ line-height: 1.35;
1403
+ overflow-wrap: anywhere;
1404
+ }
1405
+
1406
+ /* ── 配置网格 ── */
1407
+ .config-grid {
1408
+ display: grid;
1409
+ gap: 10px;
1410
+ }
1411
+
1412
+ .config-grid.two-cols {
1413
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1414
+ }
1415
+
1416
+ .config-grid input:disabled,
1417
+ .modal-form input:disabled {
1418
+ color: var(--tg-hint, #9aa4b2);
1419
+ background: var(--surface-2, rgba(255, 255, 255, 0.07));
1420
+ cursor: not-allowed;
1421
+ }
1422
+
1423
+ /* ── 分段按钮(支付方式等) ── */
1424
+ .segmented {
1425
+ display: inline-flex;
1426
+ flex-wrap: wrap;
1427
+ gap: 6px;
1428
+ justify-content: flex-end;
1429
+ }
1430
+
1431
+ .segment-btn {
1432
+ min-height: 34px;
1433
+ padding: 0 12px;
1434
+ white-space: nowrap;
1435
+ border: 1px solid var(--admin-accent-border, rgba(245, 158, 11, 0.38));
1436
+ border-radius: var(--r-sm, 6px);
1437
+ background: var(--admin-accent-soft, rgba(245, 158, 11, 0.1));
1438
+ color: var(--admin-accent-text, #fbbf24);
1439
+ font-size: 13px;
1440
+ font-weight: 550;
1441
+ cursor: pointer;
1442
+ transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
1443
+ }
1444
+
1445
+ .segment-btn:hover {
1446
+ background: var(--admin-accent-hover, rgba(245, 158, 11, 0.2));
1447
+ border-color: rgba(245, 158, 11, 0.55);
1448
+ color: #fcd34d;
1449
+ }
1450
+
1451
+ .segment-btn.active,
1452
+ .segment-btn.is-active,
1453
+ .segment-btn[aria-pressed='true'] {
1454
+ border-color: var(--admin-accent, #f59e0b);
1455
+ background: rgba(245, 158, 11, 0.28);
1456
+ color: #fff7ed;
1457
+ font-weight: 600;
1458
+ }
1459
+
1460
+ /* ── 快捷设置行 ── */
1461
+ .quick-setting {
1462
+ display: flex;
1463
+ align-items: center;
1464
+ justify-content: space-between;
1465
+ gap: 12px;
1466
+ padding: 10px;
1467
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
1468
+ border-radius: var(--r-md, 8px);
1469
+ background: var(--surface, rgba(255, 255, 255, 0.04));
1470
+ }
1471
+
1472
+ .quick-label {
1473
+ color: var(--tg-text, #f0f2f5);
1474
+ font-size: 14px;
1475
+ font-weight: 600;
1476
+ }
1477
+
1478
+ .quick-copy {
1479
+ display: grid;
1480
+ gap: 2px;
1481
+ }
1482
+
1483
+ .quick-copy small {
1484
+ color: var(--tg-hint, #9aa4b2);
1485
+ font-size: 12px;
1486
+ line-height: 1.4;
1487
+ }
1488
+
1489
+ /* ── 回调 / 代码框 ── */
1490
+ .callback-box {
1491
+ display: grid;
1492
+ gap: 6px;
1493
+ padding: 10px;
1494
+ border: 1px dashed var(--admin-accent-border, rgba(245, 158, 11, 0.38));
1495
+ border-radius: var(--r-md, 8px);
1496
+ background: var(--admin-accent-soft, rgba(245, 158, 11, 0.08));
1497
+ }
1498
+
1499
+ .callback-box span {
1500
+ color: var(--admin-accent-text, #fbbf24);
1501
+ font-size: 12px;
1502
+ font-weight: 550;
1503
+ }
1504
+
1505
+ .callback-box code {
1506
+ color: var(--tg-text, #f0f2f5);
1507
+ font-size: 12px;
1508
+ overflow-wrap: anywhere;
1509
+ white-space: normal;
1510
+ }
1511
+
1512
+ /* ── 双行关系单元格 ── */
1513
+ .relation-cell {
1514
+ display: inline-flex;
1515
+ flex-direction: column;
1516
+ gap: 2px;
1517
+ /* 覆盖默认 min-width:auto:超长 failureReason / SQL 不得把整表撑成只剩名称列 */
1518
+ min-width: 0;
1519
+ max-width: 280px;
1520
+ }
1521
+
1522
+ .relation-cell small {
1523
+ color: var(--tg-hint, #9aa4b2);
1524
+ font-size: 11px;
1525
+ overflow: hidden;
1526
+ text-overflow: ellipsis;
1527
+ white-space: nowrap;
1528
+ max-width: 100%;
1529
+ }
1530
+
1531
+ .relation-cell .failure-snippet {
1532
+ color: var(--admin-danger, #fca5a5);
1533
+ white-space: normal;
1534
+ display: -webkit-box;
1535
+ -webkit-line-clamp: 2;
1536
+ -webkit-box-orient: vertical;
1537
+ overflow: hidden;
1538
+ line-height: 1.35;
1539
+ max-width: 100%;
1540
+ }
1541
+
1542
+ /* ── 金额正负 ── */
1543
+ .is-positive {
1544
+ color: var(--admin-success, #6ee7b7);
1545
+ font-weight: 600;
1546
+ }
1547
+
1548
+ .is-negative {
1549
+ color: var(--admin-danger, #fca5a5);
1550
+ font-weight: 600;
1551
+ }
1552
+
1553
+ /* ── 辅助文案 ── */
1554
+ .admin-helper,
1555
+ .balance-helper {
1556
+ margin: 0 0 12px;
1557
+ color: var(--tg-hint, #9aa4b2);
1558
+ font-size: 12px;
1559
+ line-height: 1.5;
1560
+ }
1561
+
1562
+ .admin-helper code,
1563
+ .balance-helper code {
1564
+ font-size: 11px;
1565
+ color: var(--admin-accent-text, #fbbf24);
1566
+ }
1567
+
1568
+ /* ── 生成结果(优惠码 / 充值码批量输出) ── */
1569
+ .generated-result {
1570
+ display: flex;
1571
+ flex-direction: column;
1572
+ gap: 10px;
1573
+ }
1574
+
1575
+ .generated-result p {
1576
+ margin: 0;
1577
+ color: var(--tg-hint, #9aa4b2);
1578
+ font-size: 13px;
1579
+ }
1580
+
1581
+ .generated-result textarea {
1582
+ width: 100%;
1583
+ resize: vertical;
1584
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);
1585
+ }
1586
+
1587
+ /* ── 图片上传控件(商品封面 / 渠道 Logo) ── */
1588
+ .image-upload-row {
1589
+ display: flex;
1590
+ align-items: center;
1591
+ gap: 10px;
1592
+ flex-wrap: wrap;
1593
+ }
1594
+
1595
+ .image-upload-button {
1596
+ position: relative;
1597
+ overflow: hidden;
1598
+ cursor: pointer;
1599
+ }
1600
+
1601
+ .image-upload-button.is-disabled {
1602
+ cursor: wait;
1603
+ opacity: 0.65;
1604
+ }
1605
+
1606
+ .image-upload-button input {
1607
+ position: absolute;
1608
+ width: 1px;
1609
+ height: 1px;
1610
+ opacity: 0;
1611
+ pointer-events: none;
1612
+ }
1613
+
1614
+ /* ── 等宽单元格 / 密钥单元格 ── */
1615
+ .mono-cell {
1616
+ font-family: var(--font-mono, ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace);
1617
+ font-size: 12px;
1618
+ color: var(--tg-hint, #9aa4b2);
1619
+ }
1620
+
1621
+ .secret-cell {
1622
+ display: inline-flex;
1623
+ align-items: center;
1624
+ gap: 6px;
1625
+ flex-wrap: wrap;
1626
+ }
1627
+
1628
+ /* ── 统计小卡(充值码概览等) ──
1629
+ * 垂直间距仅靠 .admin-page gap;禁止 margin-bottom 叠距。
1630
+ */
1631
+ .stat-strip {
1632
+ display: grid;
1633
+ grid-template-columns: repeat(4, minmax(120px, 1fr));
1634
+ gap: var(--admin-inline-gap, 8px);
1635
+ margin: 0;
1636
+ flex-shrink: 0;
1637
+ }
1638
+
1639
+ .stat-item {
1640
+ min-height: 76px;
1641
+ padding: 10px 12px;
1642
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
1643
+ border-radius: var(--r-md, 8px);
1644
+ background: var(--tg-secondary-bg, #151b28);
1645
+ display: flex;
1646
+ flex-direction: column;
1647
+ gap: 3px;
1648
+ }
1649
+
1650
+ .stat-item span,
1651
+ .stat-item small {
1652
+ color: var(--tg-hint, #9aa4b2);
1653
+ font-size: 12px;
1654
+ }
1655
+
1656
+ .stat-item strong {
1657
+ font-size: 20px;
1658
+ color: var(--tg-text, #f0f2f5);
1659
+ }
1660
+
1661
+ /* ── 段切换别名:dir-btn 与 segment-btn 同语义(余额加减方向等) ── */
1662
+ .dir-btn {
1663
+ appearance: none;
1664
+ min-height: 34px;
1665
+ padding: 8px 10px;
1666
+ white-space: nowrap;
1667
+ border: 1px solid var(--admin-accent-border, rgba(245, 158, 11, 0.38));
1668
+ border-radius: var(--r-md, 8px);
1669
+ background: var(--admin-accent-soft, rgba(245, 158, 11, 0.1));
1670
+ color: var(--admin-accent-text, #fbbf24);
1671
+ font-size: 13px;
1672
+ font-weight: 550;
1673
+ cursor: pointer;
1674
+ transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
1675
+ }
1676
+
1677
+ .dir-btn:hover {
1678
+ background: var(--admin-accent-hover, rgba(245, 158, 11, 0.2));
1679
+ border-color: rgba(245, 158, 11, 0.55);
1680
+ color: #fcd34d;
1681
+ }
1682
+
1683
+ .dir-btn.active,
1684
+ .dir-btn.is-active,
1685
+ .dir-btn[aria-pressed='true'] {
1686
+ border-color: var(--admin-accent, #f59e0b);
1687
+ background: rgba(245, 158, 11, 0.28);
1688
+ color: #fff7ed;
1689
+ font-weight: 600;
1690
+ }
1691
+
1692
+ /* ── 配置区 section(系统配置页骨架,可跨页复用) ── */
1693
+ .config-section {
1694
+ background: var(--tg-secondary-bg, #151b28);
1695
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
1696
+ border-radius: var(--r-xl, 16px);
1697
+ padding: 12px;
1698
+ display: flex;
1699
+ flex-direction: column;
1700
+ gap: 12px;
1701
+ }
1702
+
1703
+ .config-section-head {
1704
+ display: flex;
1705
+ justify-content: space-between;
1706
+ gap: 12px;
1707
+ padding-bottom: 10px;
1708
+ border-bottom: 1px solid var(--border, rgba(255, 255, 255, 0.1));
1709
+ }
1710
+
1711
+ .config-section-head h3 {
1712
+ margin: 0;
1713
+ font-size: 15px;
1714
+ color: var(--tg-text, #f0f2f5);
1715
+ }
1716
+
1717
+ .config-section-head p {
1718
+ margin: 4px 0 0;
1719
+ color: var(--tg-hint, #9aa4b2);
1720
+ font-size: 12px;
1721
+ line-height: 1.45;
1722
+ }
1723
+
1724
+ .config-count {
1725
+ flex: 0 0 auto;
1726
+ white-space: nowrap;
1727
+ padding: 3px 8px;
1728
+ border-radius: 999px;
1729
+ background: var(--surface-2, rgba(255, 255, 255, 0.07));
1730
+ color: var(--tg-hint, #9aa4b2);
1731
+ font-size: 12px;
1732
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
1733
+ }
1734
+
1735
+ /* ── 支付渠道卡标题区 ── */
1736
+ .provider-header {
1737
+ display: flex;
1738
+ flex-direction: column;
1739
+ gap: 10px;
1740
+ min-width: 0;
1741
+ flex: 1 1 auto;
1742
+ }
1743
+
1744
+ .provider-title-row {
1745
+ display: flex;
1746
+ align-items: flex-start;
1747
+ gap: 8px;
1748
+ }
1749
+
1750
+ .provider-name {
1751
+ margin: 0;
1752
+ font-size: 14px;
1753
+ font-weight: 650;
1754
+ color: var(--tg-text, #f0f2f5);
1755
+ }
1756
+
1757
+ .provider-desc {
1758
+ margin: 4px 0 0;
1759
+ font-size: 12px;
1760
+ color: var(--tg-hint, #9aa4b2);
1761
+ line-height: 1.45;
1762
+ }
1763
+
1764
+ .provider-status {
1765
+ display: flex;
1766
+ gap: 6px;
1767
+ flex-wrap: wrap;
1768
+ }
1769
+
1770
+ .provider-actions {
1771
+ display: flex;
1772
+ align-items: flex-start;
1773
+ gap: 6px;
1774
+ flex: 0 0 auto;
1775
+ flex-wrap: wrap;
1776
+ justify-content: flex-end;
1777
+ }
1778
+
1779
+ .callback-value {
1780
+ display: flex;
1781
+ flex-wrap: wrap;
1782
+ align-items: center;
1783
+ gap: 6px;
1784
+ }
1785
+
1786
+ @media (max-width: 640px) {
1787
+ .admin-card,
1788
+ .provider-card {
1789
+ flex-direction: column;
1790
+ }
1791
+
1792
+ .config-grid.two-cols,
1793
+ .fact-grid,
1794
+ .provider-facts {
1795
+ grid-template-columns: 1fr;
1796
+ }
1797
+
1798
+ .quick-setting {
1799
+ align-items: flex-start;
1800
+ flex-direction: column;
1801
+ }
1802
+
1803
+ .segmented {
1804
+ justify-content: flex-start;
1805
+ }
1806
+
1807
+ .stat-strip {
1808
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1809
+ }
1810
+
1811
+ .provider-actions {
1812
+ justify-content: flex-start;
1813
+ width: 100%;
1814
+ }
1815
+ }
1816
+
1817
+ /* ── 表单分区(抽奖创建/商品编辑等共享) ───────────────────
1818
+ * 必须暗色 token:曾误用 #333/#e0e0e0 浅色 fallback,暗色 Admin 下分区标题几乎不可读。
1819
+ * 与商品页 product-editor 分区语义对齐;页面 scoped 可再细化,但全局 fallback 必须可读。
1820
+ */
1821
+ .form-section {
1822
+ display: flex;
1823
+ flex-direction: column;
1824
+ gap: var(--admin-card-gap, 10px);
1825
+ padding: var(--admin-card-pad, 12px);
1826
+ margin: 0;
1827
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.12));
1828
+ border-radius: var(--r-md, 8px);
1829
+ background: var(--tg-secondary-bg, rgba(0, 0, 0, 0.18));
1830
+ min-width: 0;
1831
+ }
1832
+
1833
+ .form-section-head {
1834
+ display: flex;
1835
+ flex-direction: column;
1836
+ gap: 2px;
1837
+ padding-bottom: 4px;
1838
+ border-bottom: 1px solid var(--border, rgba(255, 255, 255, 0.08));
1839
+ }
1840
+
1841
+ .form-section-title {
1842
+ margin: 0;
1843
+ padding: 0;
1844
+ border: none;
1845
+ width: auto;
1846
+ font-size: 13px;
1847
+ font-weight: 600;
1848
+ letter-spacing: 0.01em;
1849
+ color: var(--tg-text, #e5e7eb);
1850
+ line-height: 1.35;
1851
+ }
1852
+
1853
+ /* fieldset + legend 时取消浏览器默认浮标样式 */
1854
+ .form-section > legend.form-section-title {
1855
+ float: none;
1856
+ padding: 0;
1857
+ }
1858
+
1859
+ .form-section-desc {
1860
+ margin: 0;
1861
+ font-size: 11px;
1862
+ line-height: 1.45;
1863
+ color: var(--tg-hint, #9ca3af);
1864
+ }
1865
+
1866
+ .form-grid {
1867
+ display: grid;
1868
+ grid-template-columns: 1fr;
1869
+ gap: 10px 12px;
1870
+ min-width: 0;
1871
+ }
1872
+
1873
+ .form-grid.two-cols {
1874
+ grid-template-columns: repeat(2, minmax(0, 1fr));
1875
+ }
1876
+
1877
+ .form-grid > label,
1878
+ .form-grid .field-span-2 {
1879
+ min-width: 0;
1880
+ }
1881
+
1882
+ .form-grid .field-span-2,
1883
+ .form-grid > label.field-span-2 {
1884
+ grid-column: 1 / -1;
1885
+ }
1886
+
1887
+ /* datetime-local:暗色原生控件 + 足够宽度,避免 macOS/Chrome 裁切日期时间段 */
1888
+ .modal-form input[type='datetime-local'],
1889
+ .modal-form input[type='date'],
1890
+ .modal-form input[type='time'] {
1891
+ color-scheme: dark;
1892
+ min-width: 0;
1893
+ width: 100%;
1894
+ font-variant-numeric: tabular-nums;
1895
+ letter-spacing: 0.01em;
1896
+ }
1897
+
1898
+ /* 时间快捷条(创建抽奖等) */
1899
+ .time-preset-row {
1900
+ display: flex;
1901
+ flex-wrap: wrap;
1902
+ gap: 6px;
1903
+ align-items: center;
1904
+ }
1905
+
1906
+ .time-preset-row .btn {
1907
+ flex: 0 0 auto;
1908
+ }
1909
+
1910
+ .time-timeline {
1911
+ display: flex;
1912
+ flex-direction: column;
1913
+ gap: 4px;
1914
+ margin: 0;
1915
+ padding: 8px 10px;
1916
+ border-radius: var(--r-md, 8px);
1917
+ border: 1px solid var(--border, rgba(255, 255, 255, 0.1));
1918
+ background: var(--tg-bg, rgba(0, 0, 0, 0.15));
1919
+ font-size: 12px;
1920
+ line-height: 1.45;
1921
+ color: var(--tg-hint, #9ca3af);
1922
+ }
1923
+
1924
+ .time-timeline strong {
1925
+ color: var(--tg-text, #e5e7eb);
1926
+ font-weight: 600;
1927
+ font-variant-numeric: tabular-nums;
1928
+ }
1929
+
1930
+ .time-timeline.is-invalid {
1931
+ border-color: rgba(239, 68, 68, 0.35);
1932
+ background: rgba(239, 68, 68, 0.08);
1933
+ color: var(--tg-destructive, #fca5a5);
1934
+ }
1935
+
1936
+ .time-timeline.is-invalid strong {
1937
+ color: var(--tg-destructive, #fca5a5);
1938
+ }
1939
+
1940
+ .admin-table .col-name {
1941
+ max-width: 220px;
1942
+ min-width: 100px;
1943
+ vertical-align: middle;
1944
+ }
1945
+
1946
+ .admin-table .col-name .draw-name {
1947
+ overflow: hidden;
1948
+ text-overflow: ellipsis;
1949
+ white-space: nowrap;
1950
+ max-width: 100%;
1951
+ }
1952
+
1953
+ .admin-table .col-name .draw-desc {
1954
+ overflow: hidden;
1955
+ text-overflow: ellipsis;
1956
+ white-space: nowrap;
1957
+ }
1958
+
1959
+ /* 紧凑数字/时间列,避免抽奖表被长 ISO 撑开 */
1960
+ .admin-table .col-num {
1961
+ width: 1%;
1962
+ white-space: nowrap;
1963
+ font-variant-numeric: tabular-nums;
1964
+ text-align: right;
1965
+ }
1966
+
1967
+ .admin-table .col-status {
1968
+ width: 1%;
1969
+ white-space: nowrap;
1970
+ }
1971
+
1972
+ .admin-table .col-actions {
1973
+ width: 1%;
1974
+ white-space: nowrap;
1975
+ }
1976
+
1977
+ .admin-table .time-stack {
1978
+ display: inline-flex;
1979
+ flex-direction: column;
1980
+ gap: 2px;
1981
+ min-width: 0;
1982
+ font-variant-numeric: tabular-nums;
1983
+ font-size: 12px;
1984
+ line-height: 1.35;
1985
+ }
1986
+
1987
+ .admin-table .time-stack-end {
1988
+ align-items: flex-end;
1989
+ width: 100%;
1990
+ }
1991
+
1992
+ .admin-table .time-stack small {
1993
+ color: var(--tg-hint, #9aa4b2);
1994
+ font-size: 11px;
1995
+ }
1996
+
1997
+ @media (max-width: 640px) {
1998
+ .form-grid.two-cols {
1999
+ grid-template-columns: 1fr;
2000
+ }
2001
+
2002
+ .table-actions {
2003
+ flex-wrap: wrap;
2004
+ white-space: normal;
2005
+ }
2006
+ }