@zhang_jifan/fanui 2.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.
Files changed (127) hide show
  1. package/ACCESSIBILITY.md +61 -0
  2. package/CHANGELOG.md +112 -0
  3. package/COMMERCIAL-LICENSE.md +71 -0
  4. package/DESIGN-SYSTEM.md +294 -0
  5. package/LICENSE +661 -0
  6. package/README.md +348 -0
  7. package/USAGE.md +1926 -0
  8. package/_index.scss +20 -0
  9. package/dist/fanui.cjs +15 -0
  10. package/dist/fanui.css +28209 -0
  11. package/dist/fanui.d.ts +353 -0
  12. package/dist/fanui.esm.js +71 -0
  13. package/dist/fanui.js +4782 -0
  14. package/dist/fanui.min.css +1 -0
  15. package/dist/fanui.mjs +71 -0
  16. package/dist/modules/fanui-advanced.css +830 -0
  17. package/dist/modules/fanui-advanced.min.css +129 -0
  18. package/dist/modules/fanui-alert.css +575 -0
  19. package/dist/modules/fanui-alert.min.css +122 -0
  20. package/dist/modules/fanui-badge.css +426 -0
  21. package/dist/modules/fanui-badge.min.css +77 -0
  22. package/dist/modules/fanui-base.css +1394 -0
  23. package/dist/modules/fanui-base.min.css +236 -0
  24. package/dist/modules/fanui-blog.css +762 -0
  25. package/dist/modules/fanui-blog.min.css +108 -0
  26. package/dist/modules/fanui-button.css +526 -0
  27. package/dist/modules/fanui-button.min.css +56 -0
  28. package/dist/modules/fanui-card.css +291 -0
  29. package/dist/modules/fanui-card.min.css +52 -0
  30. package/dist/modules/fanui-choice.css +539 -0
  31. package/dist/modules/fanui-choice.min.css +83 -0
  32. package/dist/modules/fanui-dashboard.css +1057 -0
  33. package/dist/modules/fanui-dashboard.min.css +160 -0
  34. package/dist/modules/fanui-data.css +687 -0
  35. package/dist/modules/fanui-data.min.css +125 -0
  36. package/dist/modules/fanui-feedback.css +432 -0
  37. package/dist/modules/fanui-feedback.min.css +79 -0
  38. package/dist/modules/fanui-form.css +779 -0
  39. package/dist/modules/fanui-form.min.css +123 -0
  40. package/dist/modules/fanui-glass.css +497 -0
  41. package/dist/modules/fanui-glass.min.css +46 -0
  42. package/dist/modules/fanui-input-plus.css +799 -0
  43. package/dist/modules/fanui-input-plus.min.css +124 -0
  44. package/dist/modules/fanui-interactive.css +960 -0
  45. package/dist/modules/fanui-interactive.min.css +138 -0
  46. package/dist/modules/fanui-layout.css +4399 -0
  47. package/dist/modules/fanui-layout.min.css +1172 -0
  48. package/dist/modules/fanui-marketing.css +1125 -0
  49. package/dist/modules/fanui-marketing.min.css +144 -0
  50. package/dist/modules/fanui-nav.css +458 -0
  51. package/dist/modules/fanui-nav.min.css +76 -0
  52. package/dist/modules/fanui-overlay.css +547 -0
  53. package/dist/modules/fanui-overlay.min.css +91 -0
  54. package/dist/modules/fanui-table.css +290 -0
  55. package/dist/modules/fanui-table.min.css +62 -0
  56. package/dist/modules/fanui-tabs.css +333 -0
  57. package/dist/modules/fanui-tabs.min.css +58 -0
  58. package/dist/modules/fanui-themes.css +2728 -0
  59. package/dist/modules/fanui-themes.min.css +40 -0
  60. package/dist/modules/fanui-tokens.css +587 -0
  61. package/dist/modules/fanui-tokens.min.css +3 -0
  62. package/dist/modules/fanui-utilities.css +6557 -0
  63. package/dist/modules/fanui-utilities.min.css +1762 -0
  64. package/dist/modules/sizes.json +103 -0
  65. package/docs/MIGRATION-v2.md +179 -0
  66. package/examples/01-script-tag.html +149 -0
  67. package/examples/02-react/README.md +39 -0
  68. package/examples/02-react/index.html +12 -0
  69. package/examples/02-react/package.json +20 -0
  70. package/examples/02-react/src/App.jsx +108 -0
  71. package/examples/02-react/src/main.jsx +13 -0
  72. package/examples/02-react/src/useFanUI.js +87 -0
  73. package/examples/02-react/vite.config.js +6 -0
  74. package/examples/03-vue3/README.md +67 -0
  75. package/examples/03-vue3/index.html +12 -0
  76. package/examples/03-vue3/package.json +19 -0
  77. package/examples/03-vue3/src/App.vue +93 -0
  78. package/examples/03-vue3/src/main.js +8 -0
  79. package/examples/03-vue3/src/useFanUI.js +85 -0
  80. package/examples/03-vue3/vite.config.js +6 -0
  81. package/examples/04-vue2-cdn.html +93 -0
  82. package/examples/README.md +34 -0
  83. package/package.json +127 -0
  84. package/src/_functions.scss +319 -0
  85. package/src/_mixins.scss +1202 -0
  86. package/src/_tokens.scss +180 -0
  87. package/src/_variables.scss +491 -0
  88. package/src/base/_animations.scss +210 -0
  89. package/src/base/_reset.scss +359 -0
  90. package/src/base/_rtl.scss +133 -0
  91. package/src/base/_typography.scss +353 -0
  92. package/src/components/_advanced.scss +763 -0
  93. package/src/components/_alert.scss +322 -0
  94. package/src/components/_badge.scss +279 -0
  95. package/src/components/_blog.scss +693 -0
  96. package/src/components/_button.scss +351 -0
  97. package/src/components/_capabilities.scss +463 -0
  98. package/src/components/_card.scss +281 -0
  99. package/src/components/_choice.scss +444 -0
  100. package/src/components/_dashboard.scss +950 -0
  101. package/src/components/_data.scss +618 -0
  102. package/src/components/_feedback.scss +403 -0
  103. package/src/components/_form.scss +408 -0
  104. package/src/components/_glass.scss +374 -0
  105. package/src/components/_input-plus.scss +730 -0
  106. package/src/components/_interactive.scss +885 -0
  107. package/src/components/_marketing.scss +982 -0
  108. package/src/components/_nav.scss +480 -0
  109. package/src/components/_overlay.scss +517 -0
  110. package/src/components/_table.scss +276 -0
  111. package/src/components/_tabs.scss +338 -0
  112. package/src/fanui.d.ts +353 -0
  113. package/src/fanui.js +4782 -0
  114. package/src/fanui.scss +79 -0
  115. package/src/glass/_switches.scss +36 -0
  116. package/src/glass/_tokens.scss +259 -0
  117. package/src/layout/_container.scss +111 -0
  118. package/src/layout/_flex.scss +164 -0
  119. package/src/layout/_grid.scss +180 -0
  120. package/src/themes/_accents.scss +201 -0
  121. package/src/themes/_palettes.scss +180 -0
  122. package/src/utilities/_borders.scss +104 -0
  123. package/src/utilities/_display.scss +145 -0
  124. package/src/utilities/_effects.scss +210 -0
  125. package/src/utilities/_sizing.scss +123 -0
  126. package/src/utilities/_spacing.scss +118 -0
  127. package/src/utilities/_texture.scss +323 -0
@@ -0,0 +1,950 @@
1
+ // =============================================================================
2
+ // fanUI · 中后台 / 数据场景组件 (Dashboard)
3
+ // -----------------------------------------------------------------------------
4
+ // .fanui-kpi 指标卡(数值 + 环比 + 迷你趋势 + 目标进度)
5
+ // .fanui-bars CSS 柱状图(无需图表库,随主题色联动)
6
+ // .fanui-donut 环形图(conic-gradient 实现)
7
+ // .fanui-legend 图例
8
+ // .fanui-usage 配额 / 用量进度(含阈值变色)
9
+ // .fanui-kanban 看板(列 + 卡 + 拖拽态)
10
+ // .fanui-activity 动态流
11
+ // .fanui-notify 通知中心
12
+ // .fanui-filter-bar 筛选栏(搜索 + 筛选胶囊 + 视图切换)
13
+ // =============================================================================
14
+
15
+ @use "sass:map";
16
+ @use "../functions" as fn;
17
+ @use "../variables" as v;
18
+ @use "../mixins" as mx;
19
+
20
+ // -----------------------------------------------------------------------------
21
+ // 1. 指标卡
22
+ // -----------------------------------------------------------------------------
23
+
24
+ .fanui-kpi {
25
+ display: flex;
26
+ flex-direction: column;
27
+ gap: fn.fanui-var("space-3");
28
+ padding: fn.fanui-var("space-5");
29
+ border-radius: fn.fanui-var("radius-xl");
30
+ @include mx.fanui-liquid-glass();
31
+ @include mx.fanui-transition(transform, v.$fanui-duration-base);
32
+
33
+ &:hover { transform: translateY(-2px); }
34
+
35
+ &__head {
36
+ display: flex;
37
+ align-items: flex-start;
38
+ justify-content: space-between;
39
+ gap: fn.fanui-var("space-3");
40
+ }
41
+
42
+ &__label {
43
+ font-size: fn.fanui-var("font-size-sm");
44
+ color: fn.fanui-var("color-text-muted");
45
+ }
46
+
47
+ &__icon {
48
+ display: grid;
49
+ place-items: center;
50
+ width: 34px;
51
+ height: 34px;
52
+ flex: 0 0 auto;
53
+ border-radius: fn.fanui-var("radius-md");
54
+ color: fn.fanui-var("primary");
55
+ background-color: fn.fanui-var("primary-soft");
56
+ font-size: fn.fanui-var("font-size-md");
57
+ }
58
+
59
+ &__value {
60
+ display: flex;
61
+ align-items: baseline;
62
+ gap: fn.fanui-var("space-2");
63
+ font-size: clamp(1.5rem, 2.4vw, 2rem);
64
+ font-weight: fn.fanui-var("font-weight-bold");
65
+ line-height: 1.15;
66
+ letter-spacing: fn.fanui-var("tracking-tight");
67
+ font-variant-numeric: tabular-nums;
68
+ color: fn.fanui-var("color-text-strong");
69
+
70
+ small {
71
+ font-size: fn.fanui-var("font-size-sm");
72
+ font-weight: fn.fanui-var("font-weight-normal");
73
+ color: fn.fanui-var("color-text-subtle");
74
+ }
75
+ }
76
+
77
+ &__delta {
78
+ display: inline-flex;
79
+ align-items: center;
80
+ gap: 3px;
81
+ padding: 1px 7px;
82
+ font-size: fn.fanui-var("font-size-xs");
83
+ font-weight: fn.fanui-var("font-weight-semibold");
84
+ border-radius: fn.fanui-var("radius-full");
85
+ font-variant-numeric: tabular-nums;
86
+
87
+ // 中国市场惯例:涨为红、跌为绿
88
+ &--up {
89
+ color: fn.fanui-var("danger-ink");
90
+ background-color: fn.fanui-var("danger-soft");
91
+ }
92
+
93
+ &--down {
94
+ color: fn.fanui-var("success-ink");
95
+ background-color: fn.fanui-var("success-soft");
96
+ }
97
+
98
+ &--flat {
99
+ color: fn.fanui-var("color-text-muted");
100
+ background-color: fn.fanui-var("color-surface-sunken");
101
+ }
102
+ }
103
+
104
+ &__foot {
105
+ display: flex;
106
+ align-items: center;
107
+ justify-content: space-between;
108
+ gap: fn.fanui-var("space-3");
109
+ font-size: fn.fanui-var("font-size-xs");
110
+ color: fn.fanui-var("color-text-subtle");
111
+ }
112
+
113
+ // 迷你趋势(用 CSS 柱渲染,免图表库)
114
+ &__spark {
115
+ display: flex;
116
+ align-items: flex-end;
117
+ gap: 2px;
118
+ height: 28px;
119
+ margin-top: fn.fanui-var("space-1");
120
+ }
121
+
122
+ &__spark-bar {
123
+ flex: 1 1 0;
124
+ min-width: 2px;
125
+ height: var(--fanui-spark-h, 40%);
126
+ border-radius: 2px;
127
+ background-image: linear-gradient(to top, var(--fanui-primary), var(--fanui-primary-vivid));
128
+ opacity: 0.85;
129
+ @include mx.fanui-transition((height, opacity), v.$fanui-duration-slow, v.$fanui-ease-spring);
130
+
131
+ &:last-child { opacity: 1; }
132
+ }
133
+
134
+ &:hover &__spark-bar { opacity: 1; }
135
+
136
+ &--accent &__spark-bar { background-image: linear-gradient(to top, var(--fanui-accent), var(--fanui-accent-vivid)); }
137
+ &--accent &__icon { color: fn.fanui-var("accent"); background-color: fn.fanui-var("accent-soft"); }
138
+ &--success &__spark-bar { background-image: linear-gradient(to top, var(--fanui-success), var(--fanui-success-vivid)); }
139
+ &--success &__icon { color: fn.fanui-var("success"); background-color: fn.fanui-var("success-soft"); }
140
+ &--warning &__spark-bar { background-image: linear-gradient(to top, var(--fanui-warning), var(--fanui-warning-vivid)); }
141
+ &--warning &__icon { color: fn.fanui-var("warning"); background-color: fn.fanui-var("warning-soft"); }
142
+ &--danger &__spark-bar { background-image: linear-gradient(to top, var(--fanui-danger), var(--fanui-danger-vivid)); }
143
+ &--danger &__icon { color: fn.fanui-var("danger"); background-color: fn.fanui-var("danger-soft"); }
144
+
145
+ // 紧凑版(用于密集看板)
146
+ &--compact {
147
+ padding: fn.fanui-var("space-4");
148
+ gap: fn.fanui-var("space-2");
149
+
150
+ .fanui-kpi__value { font-size: fn.fanui-var("font-size-xl"); }
151
+ }
152
+ }
153
+
154
+ // -----------------------------------------------------------------------------
155
+ // 2. 柱状图
156
+ // -----------------------------------------------------------------------------
157
+
158
+ .fanui-bars {
159
+ --fanui-bars-height: 180px;
160
+
161
+ display: flex;
162
+ align-items: flex-end;
163
+ gap: var(--fanui-bars-gap, 6px);
164
+ height: var(--fanui-bars-height);
165
+ padding-top: fn.fanui-var("space-4");
166
+
167
+ &__group {
168
+ flex: 1 1 0;
169
+ min-width: 0;
170
+ display: flex;
171
+ flex-direction: column;
172
+ align-items: center;
173
+ gap: fn.fanui-var("space-2");
174
+ height: 100%;
175
+ }
176
+
177
+ &__stack {
178
+ display: flex;
179
+ align-items: flex-end;
180
+ justify-content: center;
181
+ gap: 2px;
182
+ width: 100%;
183
+ flex: 1 1 auto;
184
+ min-height: 0;
185
+ }
186
+
187
+ &__bar {
188
+ position: relative;
189
+ width: 100%;
190
+ max-width: var(--fanui-bars-width, 28px);
191
+ height: var(--fanui-bar-h, 50%);
192
+ border-radius: fn.fanui-var("radius-sm") fn.fanui-var("radius-sm") 2px 2px;
193
+ background-image: linear-gradient(to top, var(--fanui-bar-c1, var(--fanui-primary)), var(--fanui-bar-c2, var(--fanui-primary-vivid)));
194
+ transform-origin: bottom center;
195
+ animation: fanui-bar-grow 520ms v.$fanui-ease-out both;
196
+ @include mx.fanui-transition((opacity, filter), v.$fanui-duration-fast);
197
+
198
+ &:hover { filter: brightness(1.08); }
199
+ }
200
+
201
+ &__group:hover &__bar { opacity: 0.72; }
202
+ &__group:hover &__bar:hover,
203
+ &__bar:hover { opacity: 1; }
204
+
205
+ &__label {
206
+ font-size: fn.fanui-var("font-size-xs");
207
+ color: fn.fanui-var("color-text-subtle");
208
+ white-space: nowrap;
209
+ @include mx.fanui-truncate(1);
210
+ max-width: 100%;
211
+ }
212
+
213
+ // 数据点提示
214
+ &__tip {
215
+ position: absolute;
216
+ bottom: calc(100% + 6px);
217
+ left: 50%;
218
+ transform: translateX(-50%) translateY(4px);
219
+ padding: 3px 8px;
220
+ font-size: fn.fanui-var("font-size-xs");
221
+ font-variant-numeric: tabular-nums;
222
+ white-space: nowrap;
223
+ color: fn.fanui-var("color-text-inverse");
224
+ background-color: fn.fanui-var("color-text-strong");
225
+ border-radius: fn.fanui-var("radius-sm");
226
+ opacity: 0;
227
+ pointer-events: none;
228
+ @include mx.fanui-transition((opacity, transform), v.$fanui-duration-fast);
229
+ }
230
+
231
+ &__bar:hover &__tip { opacity: 1; transform: translateX(-50%) translateY(0); }
232
+
233
+ // 网格线
234
+ &__grid {
235
+ position: relative;
236
+
237
+ &::before {
238
+ content: "";
239
+ position: absolute;
240
+ inset: fn.fanui-var("space-4") 0 0;
241
+ pointer-events: none;
242
+ background-image: repeating-linear-gradient(
243
+ to top,
244
+ fn.fanui-var("color-border-subtle") 0 1px,
245
+ transparent 1px 25%
246
+ );
247
+ opacity: 0.7;
248
+ }
249
+ }
250
+
251
+ &--horizontal {
252
+ flex-direction: column;
253
+ align-items: stretch;
254
+ height: auto;
255
+
256
+ .fanui-bars__group { flex-direction: row; align-items: center; height: auto; gap: fn.fanui-var("space-3"); }
257
+ .fanui-bars__stack { align-items: center; height: 22px; }
258
+ .fanui-bars__bar { height: 12px !important; max-width: none; width: var(--fanui-bar-h, 50%); border-radius: fn.fanui-var("radius-full"); }
259
+ .fanui-bars__label { min-width: 84px; text-align: start; }
260
+ }
261
+ }
262
+
263
+ @keyframes fanui-bar-grow {
264
+ from { transform: scaleY(0); opacity: 0; }
265
+ to { transform: scaleY(1); opacity: 1; }
266
+ }
267
+
268
+ // -----------------------------------------------------------------------------
269
+ // 3. 环形图
270
+ // -----------------------------------------------------------------------------
271
+
272
+ .fanui-donut {
273
+ --fanui-donut-value: 68%;
274
+ --fanui-donut-size: 148px;
275
+ --fanui-donut-thickness: 14px;
276
+ --fanui-donut-c1: var(--fanui-primary);
277
+ --fanui-donut-c2: var(--fanui-primary-vivid);
278
+
279
+ position: relative;
280
+ display: grid;
281
+ place-items: center;
282
+ width: var(--fanui-donut-size);
283
+ height: var(--fanui-donut-size);
284
+ border-radius: fn.fanui-var("radius-full");
285
+ background-image: conic-gradient(
286
+ from -90deg,
287
+ var(--fanui-donut-c1) 0%,
288
+ var(--fanui-donut-c2) var(--fanui-donut-value),
289
+ fn.fanui-var("color-border") var(--fanui-donut-value),
290
+ fn.fanui-var("color-border") 100%
291
+ );
292
+
293
+ // 中心挖空
294
+ &::before {
295
+ content: "";
296
+ position: absolute;
297
+ inset: var(--fanui-donut-thickness);
298
+ border-radius: fn.fanui-var("radius-full");
299
+ background-color: fn.fanui-var("color-surface");
300
+ }
301
+
302
+ &__center {
303
+ position: relative;
304
+ z-index: 1;
305
+ display: flex;
306
+ flex-direction: column;
307
+ align-items: center;
308
+ gap: 2px;
309
+ text-align: center;
310
+ }
311
+
312
+ &__value {
313
+ font-size: fn.fanui-var("font-size-2xl");
314
+ font-weight: fn.fanui-var("font-weight-bold");
315
+ line-height: 1;
316
+ font-variant-numeric: tabular-nums;
317
+ letter-spacing: fn.fanui-var("tracking-tight");
318
+ color: fn.fanui-var("color-text-strong");
319
+ }
320
+
321
+ &__label {
322
+ font-size: fn.fanui-var("font-size-xs");
323
+ color: fn.fanui-var("color-text-subtle");
324
+ }
325
+
326
+ &--sm { --fanui-donut-size: 96px; --fanui-donut-thickness: 10px; }
327
+ &--lg { --fanui-donut-size: 200px; --fanui-donut-thickness: 18px; }
328
+ &--accent { --fanui-donut-c1: var(--fanui-accent); --fanui-donut-c2: var(--fanui-accent-vivid); }
329
+ &--success { --fanui-donut-c1: var(--fanui-success); --fanui-donut-c2: var(--fanui-success-vivid); }
330
+ &--warning { --fanui-donut-c1: var(--fanui-warning); --fanui-donut-c2: var(--fanui-warning-vivid); }
331
+ &--danger { --fanui-donut-c1: var(--fanui-danger); --fanui-donut-c2: var(--fanui-danger-vivid); }
332
+ }
333
+
334
+ // -----------------------------------------------------------------------------
335
+ // 4. 图例
336
+ // -----------------------------------------------------------------------------
337
+
338
+ .fanui-legend {
339
+ display: flex;
340
+ flex-wrap: wrap;
341
+ gap: fn.fanui-var("space-3") fn.fanui-var("space-4");
342
+ margin: 0;
343
+ padding: 0;
344
+ list-style: none;
345
+ font-size: fn.fanui-var("font-size-xs");
346
+ color: fn.fanui-var("color-text-muted");
347
+
348
+ &__item {
349
+ display: inline-flex;
350
+ align-items: center;
351
+ gap: fn.fanui-var("space-2");
352
+ }
353
+
354
+ &__dot {
355
+ width: 10px;
356
+ height: 10px;
357
+ border-radius: 3px;
358
+ background-color: var(--fanui-legend-color, var(--fanui-primary));
359
+ flex: 0 0 auto;
360
+ }
361
+
362
+ &__value {
363
+ font-variant-numeric: tabular-nums;
364
+ color: fn.fanui-var("color-text-strong");
365
+ font-weight: fn.fanui-var("font-weight-medium");
366
+ }
367
+ }
368
+
369
+ // -----------------------------------------------------------------------------
370
+ // 5. 用量 / 配额
371
+ // -----------------------------------------------------------------------------
372
+
373
+ .fanui-usage {
374
+ display: flex;
375
+ flex-direction: column;
376
+ gap: fn.fanui-var("space-2");
377
+
378
+ &__head {
379
+ display: flex;
380
+ align-items: baseline;
381
+ justify-content: space-between;
382
+ gap: fn.fanui-var("space-3");
383
+ font-size: fn.fanui-var("font-size-sm");
384
+ color: fn.fanui-var("color-text-muted");
385
+ }
386
+
387
+ &__value {
388
+ font-variant-numeric: tabular-nums;
389
+ color: fn.fanui-var("color-text-strong");
390
+ font-weight: fn.fanui-var("font-weight-semibold");
391
+ }
392
+
393
+ &__track {
394
+ height: 8px;
395
+ border-radius: fn.fanui-var("radius-full");
396
+ background-color: fn.fanui-var("color-surface-sunken");
397
+ overflow: hidden;
398
+ }
399
+
400
+ &__bar {
401
+ height: 100%;
402
+ width: var(--fanui-usage-value, 60%);
403
+ border-radius: inherit;
404
+ background-image: linear-gradient(to right, var(--fanui-primary), var(--fanui-primary-vivid));
405
+ @include mx.fanui-transition((width, background-color), v.$fanui-duration-slow, v.$fanui-ease-out);
406
+ }
407
+
408
+ // 阈值变色:越界即预警
409
+ &--warning &__bar { background-image: linear-gradient(to right, var(--fanui-warning), var(--fanui-warning-vivid)); }
410
+ &--danger &__bar { background-image: linear-gradient(to right, var(--fanui-danger), var(--fanui-danger-vivid)); }
411
+
412
+ &--striped &__bar {
413
+ background-image: linear-gradient(
414
+ 45deg,
415
+ rgba(255, 255, 255, 0.18) 25%, transparent 25%,
416
+ transparent 50%, rgba(255, 255, 255, 0.18) 50%,
417
+ rgba(255, 255, 255, 0.18) 75%, transparent 75%, transparent
418
+ ),
419
+ linear-gradient(to right, var(--fanui-primary), var(--fanui-primary-vivid));
420
+ background-size: 1rem 1rem, 100% 100%;
421
+ animation: fanui-stripes 1s linear infinite;
422
+ }
423
+ }
424
+
425
+ @keyframes fanui-stripes {
426
+ from { background-position: 0 0, 0 0; }
427
+ to { background-position: 1rem 0, 0 0; }
428
+ }
429
+
430
+ // -----------------------------------------------------------------------------
431
+ // 6. 看板
432
+ // -----------------------------------------------------------------------------
433
+
434
+ .fanui-kanban {
435
+ display: flex;
436
+ gap: fn.fanui-var("space-4");
437
+ align-items: flex-start;
438
+ overflow-x: auto;
439
+ padding-bottom: fn.fanui-var("space-3");
440
+ @include mx.fanui-scrollbar(8px);
441
+
442
+ &__column {
443
+ flex: 0 0 288px;
444
+ display: flex;
445
+ flex-direction: column;
446
+ gap: fn.fanui-var("space-3");
447
+ padding: fn.fanui-var("space-3");
448
+ border-radius: fn.fanui-var("radius-xl");
449
+ background-color: fn.fanui-var("color-surface-sunken");
450
+ @include mx.fanui-transition(background-color, v.$fanui-duration-fast);
451
+
452
+ &.is-dragover {
453
+ background-color: fn.fanui-var("primary-soft");
454
+ box-shadow: inset 0 0 0 2px fn.fanui-var("primary-border");
455
+ }
456
+ }
457
+
458
+ &__head {
459
+ display: flex;
460
+ align-items: center;
461
+ justify-content: space-between;
462
+ gap: fn.fanui-var("space-2");
463
+ padding-inline: fn.fanui-var("space-1");
464
+ }
465
+
466
+ &__title {
467
+ display: inline-flex;
468
+ align-items: center;
469
+ gap: fn.fanui-var("space-2");
470
+ font-size: fn.fanui-var("font-size-sm");
471
+ font-weight: fn.fanui-var("font-weight-semibold");
472
+ color: fn.fanui-var("color-text-strong");
473
+
474
+ &::before {
475
+ content: "";
476
+ width: 8px;
477
+ height: 8px;
478
+ border-radius: fn.fanui-var("radius-full");
479
+ background-color: var(--fanui-column-color, var(--fanui-primary));
480
+ }
481
+ }
482
+
483
+ &__count {
484
+ min-width: 20px;
485
+ padding: 0 6px;
486
+ font-size: fn.fanui-var("font-size-xs");
487
+ line-height: 18px;
488
+ text-align: center;
489
+ font-variant-numeric: tabular-nums;
490
+ color: fn.fanui-var("color-text-muted");
491
+ background-color: fn.fanui-var("color-surface");
492
+ border-radius: fn.fanui-var("radius-full");
493
+ }
494
+
495
+ &__list {
496
+ display: flex;
497
+ flex-direction: column;
498
+ gap: fn.fanui-var("space-2");
499
+ margin: 0;
500
+ padding: 0;
501
+ list-style: none;
502
+ min-height: 24px;
503
+ }
504
+
505
+ &__card {
506
+ display: flex;
507
+ flex-direction: column;
508
+ gap: fn.fanui-var("space-2");
509
+ padding: fn.fanui-var("space-3");
510
+ border: 1px solid fn.fanui-var("color-border");
511
+ border-radius: fn.fanui-var("radius-lg");
512
+ background-color: fn.fanui-var("color-surface");
513
+ cursor: grab;
514
+ @include mx.fanui-transition((transform, box-shadow, opacity, border-color), v.$fanui-duration-fast);
515
+
516
+ &:hover {
517
+ transform: translateY(-1px);
518
+ border-color: fn.fanui-var("primary-border");
519
+ box-shadow: fn.fanui-var("shadow-md");
520
+ }
521
+
522
+ &:active { cursor: grabbing; }
523
+
524
+ &.is-dragging {
525
+ opacity: 0.5;
526
+ transform: rotate(1.5deg) scale(0.98);
527
+ border-style: dashed;
528
+ }
529
+
530
+ &.is-drop-target {
531
+ border-color: fn.fanui-var("primary");
532
+ box-shadow: 0 0 0 3px fn.fanui-var("primary-ring");
533
+ }
534
+ }
535
+
536
+ &__card-title {
537
+ font-size: fn.fanui-var("font-size-sm");
538
+ font-weight: fn.fanui-var("font-weight-medium");
539
+ line-height: fn.fanui-var("line-height-snug");
540
+ color: fn.fanui-var("color-text");
541
+ }
542
+
543
+ &__card-meta {
544
+ display: flex;
545
+ align-items: center;
546
+ justify-content: space-between;
547
+ gap: fn.fanui-var("space-2");
548
+ font-size: fn.fanui-var("font-size-xs");
549
+ color: fn.fanui-var("color-text-subtle");
550
+ }
551
+
552
+ &__labels {
553
+ display: flex;
554
+ flex-wrap: wrap;
555
+ gap: 4px;
556
+ }
557
+
558
+ &__label {
559
+ padding: 1px 7px;
560
+ font-size: 11px;
561
+ border-radius: fn.fanui-var("radius-full");
562
+ background-color: var(--fanui-label-bg, var(--fanui-primary-soft));
563
+ color: var(--fanui-label-fg, var(--fanui-primary-ink));
564
+ }
565
+
566
+ &__add {
567
+ display: flex;
568
+ align-items: center;
569
+ gap: fn.fanui-var("space-2");
570
+ padding: fn.fanui-var("space-2") fn.fanui-var("space-3");
571
+ border: 1px dashed fn.fanui-var("color-border-strong");
572
+ border-radius: fn.fanui-var("radius-lg");
573
+ background: transparent;
574
+ font: inherit;
575
+ font-size: fn.fanui-var("font-size-sm");
576
+ color: fn.fanui-var("color-text-muted");
577
+ cursor: pointer;
578
+ @include mx.fanui-transition((border-color, color, background-color), v.$fanui-duration-fast);
579
+
580
+ &:hover {
581
+ border-color: fn.fanui-var("primary");
582
+ color: fn.fanui-var("primary");
583
+ background-color: fn.fanui-var("primary-soft");
584
+ }
585
+ }
586
+
587
+ &__avatars {
588
+ display: flex;
589
+
590
+ > * {
591
+ width: 22px;
592
+ height: 22px;
593
+ border-radius: fn.fanui-var("radius-full");
594
+ border: 2px solid fn.fanui-var("color-surface");
595
+ margin-inline-start: -6px;
596
+ display: grid;
597
+ place-items: center;
598
+ font-size: 10px;
599
+ background-color: fn.fanui-var("primary-soft-2");
600
+ color: fn.fanui-var("primary-ink");
601
+
602
+ &:first-child { margin-inline-start: 0; }
603
+ }
604
+ }
605
+ }
606
+
607
+ // -----------------------------------------------------------------------------
608
+ // 7. 动态流
609
+ // -----------------------------------------------------------------------------
610
+
611
+ .fanui-activity {
612
+ display: flex;
613
+ flex-direction: column;
614
+ gap: fn.fanui-var("space-4");
615
+ margin: 0;
616
+ padding: 0;
617
+ list-style: none;
618
+
619
+ &__item {
620
+ position: relative;
621
+ display: flex;
622
+ gap: fn.fanui-var("space-3");
623
+ padding-inline-start: fn.fanui-var("space-1");
624
+
625
+ // 连接线
626
+ &:not(:last-child)::before {
627
+ content: "";
628
+ position: absolute;
629
+ inset-block: 26px calc(#{fn.fanui-var("space-4")} * -1);
630
+ inset-inline-start: 13px;
631
+ width: 1px;
632
+ background-color: fn.fanui-var("color-border");
633
+ }
634
+ }
635
+
636
+ &__icon {
637
+ position: relative;
638
+ z-index: 1;
639
+ display: grid;
640
+ place-items: center;
641
+ width: 26px;
642
+ height: 26px;
643
+ flex: 0 0 auto;
644
+ border-radius: fn.fanui-var("radius-full");
645
+ font-size: fn.fanui-var("font-size-xs");
646
+ color: fn.fanui-var("primary-fg");
647
+ background-color: fn.fanui-var("primary");
648
+ box-shadow: 0 0 0 3px fn.fanui-var("color-surface");
649
+ }
650
+
651
+ &__body { flex: 1 1 auto; min-width: 0; }
652
+
653
+ &__text {
654
+ font-size: fn.fanui-var("font-size-sm");
655
+ line-height: fn.fanui-var("line-height-snug");
656
+ color: fn.fanui-var("color-text");
657
+
658
+ strong { font-weight: fn.fanui-var("font-weight-semibold"); color: fn.fanui-var("color-text-strong"); }
659
+ a { color: fn.fanui-var("primary"); text-decoration: none; &:hover { text-decoration: underline; } }
660
+ }
661
+
662
+ &__time {
663
+ display: block;
664
+ margin-top: 2px;
665
+ font-size: fn.fanui-var("font-size-xs");
666
+ color: fn.fanui-var("color-text-subtle");
667
+ }
668
+
669
+ &__quote {
670
+ margin-top: fn.fanui-var("space-2");
671
+ padding: fn.fanui-var("space-2") fn.fanui-var("space-3");
672
+ border-inline-start: 2px solid fn.fanui-var("color-border");
673
+ border-radius: fn.fanui-var("radius-sm");
674
+ background-color: fn.fanui-var("color-surface-sunken");
675
+ font-size: fn.fanui-var("font-size-xs");
676
+ color: fn.fanui-var("color-text-muted");
677
+ }
678
+
679
+ &--compact {
680
+ gap: fn.fanui-var("space-3");
681
+
682
+ .fanui-activity__icon { width: 22px; height: 22px; }
683
+ .fanui-activity__item:not(:last-child)::before { inset-inline-start: 11px; inset-block-start: 22px; }
684
+ }
685
+ }
686
+
687
+ // -----------------------------------------------------------------------------
688
+ // 8. 通知中心
689
+ // -----------------------------------------------------------------------------
690
+
691
+ .fanui-notify {
692
+ position: relative;
693
+
694
+ &__trigger {
695
+ position: relative;
696
+ display: grid;
697
+ place-items: center;
698
+ width: 36px;
699
+ height: 36px;
700
+ border: 1px solid transparent;
701
+ border-radius: fn.fanui-var("radius-md");
702
+ background: transparent;
703
+ color: fn.fanui-var("color-text-muted");
704
+ cursor: pointer;
705
+ @include mx.fanui-transition((background-color, color), v.$fanui-duration-fast);
706
+
707
+ &:hover { background-color: fn.fanui-var("color-hover"); color: fn.fanui-var("color-text"); }
708
+ }
709
+
710
+ &__dot {
711
+ position: absolute;
712
+ inset-block-start: 6px;
713
+ inset-inline-end: 6px;
714
+ min-width: 16px;
715
+ height: 16px;
716
+ padding: 0 4px;
717
+ display: grid;
718
+ place-items: center;
719
+ font-size: 10px;
720
+ font-weight: fn.fanui-var("font-weight-semibold");
721
+ line-height: 1;
722
+ color: fn.fanui-var("danger-fg");
723
+ background-color: fn.fanui-var("danger");
724
+ border-radius: fn.fanui-var("radius-full");
725
+ box-shadow: 0 0 0 2px fn.fanui-var("color-bg");
726
+
727
+ // 有未读时轻微脉冲提示
728
+ &.is-pulse::after {
729
+ content: "";
730
+ position: absolute;
731
+ inset: 0;
732
+ border-radius: inherit;
733
+ background-color: inherit;
734
+ animation: fanui-notify-pulse 1.8s v.$fanui-ease-out infinite;
735
+ }
736
+ }
737
+
738
+ &__panel {
739
+ position: absolute;
740
+ inset-inline-end: 0;
741
+ inset-block-start: calc(100% + 8px);
742
+ z-index: fn.fanui-var("z-dropdown");
743
+ width: 360px;
744
+ max-width: calc(100vw - 32px);
745
+ max-height: 440px;
746
+ display: flex;
747
+ flex-direction: column;
748
+ overflow: hidden;
749
+ border: 1px solid fn.fanui-var("glass-border", #{fn.fanui-var("color-border")});
750
+ border-radius: fn.fanui-var("radius-xl");
751
+ background-color: fn.fanui-var("color-overlay");
752
+ box-shadow: fn.fanui-var("shadow-2xl");
753
+ opacity: 0;
754
+ visibility: hidden;
755
+ transform: translateY(-6px) scale(0.98);
756
+ transform-origin: top right;
757
+ @include mx.fanui-transition((opacity, transform, visibility), v.$fanui-duration-base, v.$fanui-ease-spring);
758
+
759
+ &.is-open { opacity: 1; visibility: visible; transform: none; }
760
+ }
761
+
762
+ &__head {
763
+ display: flex;
764
+ align-items: center;
765
+ justify-content: space-between;
766
+ gap: fn.fanui-var("space-3");
767
+ padding: fn.fanui-var("space-3") fn.fanui-var("space-4");
768
+ border-bottom: 1px solid fn.fanui-var("color-border-subtle");
769
+ font-size: fn.fanui-var("font-size-sm");
770
+ font-weight: fn.fanui-var("font-weight-semibold");
771
+ color: fn.fanui-var("color-text-strong");
772
+ }
773
+
774
+ &__list {
775
+ flex: 1 1 auto;
776
+ overflow-y: auto;
777
+ margin: 0;
778
+ padding: fn.fanui-var("space-1");
779
+ list-style: none;
780
+ @include mx.fanui-scrollbar(6px);
781
+ }
782
+
783
+ &__item {
784
+ display: flex;
785
+ gap: fn.fanui-var("space-3");
786
+ padding: fn.fanui-var("space-3");
787
+ border-radius: fn.fanui-var("radius-md");
788
+ cursor: pointer;
789
+ @include mx.fanui-transition(background-color, v.$fanui-duration-fast);
790
+
791
+ &:hover { background-color: fn.fanui-var("color-hover"); }
792
+
793
+ &.is-unread {
794
+ background-color: fn.fanui-var("primary-soft");
795
+
796
+ .fanui-notify__item-title::after {
797
+ content: "";
798
+ display: inline-block;
799
+ width: 6px;
800
+ height: 6px;
801
+ margin-inline-start: 6px;
802
+ border-radius: fn.fanui-var("radius-full");
803
+ background-color: fn.fanui-var("primary");
804
+ vertical-align: middle;
805
+ }
806
+ }
807
+ }
808
+
809
+ &__item-icon {
810
+ display: grid;
811
+ place-items: center;
812
+ width: 32px;
813
+ height: 32px;
814
+ flex: 0 0 auto;
815
+ border-radius: fn.fanui-var("radius-md");
816
+ background-color: fn.fanui-var("color-surface");
817
+ color: fn.fanui-var("color-text-muted");
818
+ }
819
+
820
+ &__item-title {
821
+ font-size: fn.fanui-var("font-size-sm");
822
+ font-weight: fn.fanui-var("font-weight-medium");
823
+ color: fn.fanui-var("color-text-strong");
824
+ }
825
+
826
+ &__item-desc {
827
+ margin-top: 2px;
828
+ font-size: fn.fanui-var("font-size-xs");
829
+ line-height: fn.fanui-var("line-height-snug");
830
+ color: fn.fanui-var("color-text-muted");
831
+ }
832
+
833
+ &__item-time {
834
+ margin-top: 4px;
835
+ font-size: fn.fanui-var("font-size-xs");
836
+ color: fn.fanui-var("color-text-subtle");
837
+ }
838
+
839
+ &__foot {
840
+ padding: fn.fanui-var("space-2") fn.fanui-var("space-4");
841
+ border-top: 1px solid fn.fanui-var("color-border-subtle");
842
+ text-align: center;
843
+ font-size: fn.fanui-var("font-size-sm");
844
+
845
+ a { color: fn.fanui-var("primary"); text-decoration: none; }
846
+ }
847
+ }
848
+
849
+ @keyframes fanui-notify-pulse {
850
+ 0% { opacity: 0.6; transform: scale(1); }
851
+ 70% { opacity: 0; transform: scale(2.2); }
852
+ 100% { opacity: 0; transform: scale(2.2); }
853
+ }
854
+
855
+ // -----------------------------------------------------------------------------
856
+ // 9. 筛选栏
857
+ // -----------------------------------------------------------------------------
858
+
859
+ .fanui-filter-bar {
860
+ display: flex;
861
+ flex-wrap: wrap;
862
+ align-items: center;
863
+ gap: fn.fanui-var("space-3");
864
+ padding: fn.fanui-var("space-3");
865
+ border-radius: fn.fanui-var("radius-xl");
866
+ @include mx.fanui-liquid-glass();
867
+ --fanui-glass-radius: #{fn.fanui-var("radius-xl")};
868
+
869
+ &__search {
870
+ position: relative;
871
+ flex: 1 1 240px;
872
+ min-width: 200px;
873
+
874
+ input { @include mx.fanui-form-control(); padding-inline-start: 34px; }
875
+
876
+ svg,
877
+ .fanui-icon {
878
+ position: absolute;
879
+ inset-inline-start: 10px;
880
+ inset-block-start: 50%;
881
+ transform: translateY(-50%);
882
+ color: fn.fanui-var("color-text-subtle");
883
+ pointer-events: none;
884
+ }
885
+ }
886
+
887
+ &__chips {
888
+ display: flex;
889
+ flex-wrap: wrap;
890
+ gap: fn.fanui-var("space-2");
891
+ }
892
+
893
+ &__chip {
894
+ display: inline-flex;
895
+ align-items: center;
896
+ gap: fn.fanui-var("space-1");
897
+ padding: 4px 12px;
898
+ border: 1px solid fn.fanui-var("color-border");
899
+ border-radius: fn.fanui-var("radius-full");
900
+ background-color: fn.fanui-var("color-surface");
901
+ font: inherit;
902
+ font-size: fn.fanui-var("font-size-sm");
903
+ color: fn.fanui-var("color-text-muted");
904
+ cursor: pointer;
905
+ @include mx.fanui-transition((color, background-color, border-color, transform), v.$fanui-duration-fast);
906
+
907
+ &:hover { border-color: fn.fanui-var("primary-border"); color: fn.fanui-var("primary-ink"); }
908
+ &:active { transform: scale(0.97); }
909
+
910
+ &.is-active {
911
+ color: fn.fanui-var("primary-fg");
912
+ background-color: fn.fanui-var("primary");
913
+ border-color: fn.fanui-var("primary");
914
+ }
915
+
916
+ &-count {
917
+ font-size: fn.fanui-var("font-size-xs");
918
+ opacity: 0.75;
919
+ font-variant-numeric: tabular-nums;
920
+ }
921
+ }
922
+
923
+ &__spacer { flex: 1 1 auto; }
924
+
925
+ &__views {
926
+ display: inline-flex;
927
+ padding: 2px;
928
+ border-radius: fn.fanui-var("radius-md");
929
+ background-color: fn.fanui-var("glass-divider");
930
+
931
+ button {
932
+ display: grid;
933
+ place-items: center;
934
+ width: 30px;
935
+ height: 26px;
936
+ border: 0;
937
+ border-radius: fn.fanui-var("radius-sm");
938
+ background: transparent;
939
+ color: fn.fanui-var("color-text-muted");
940
+ cursor: pointer;
941
+ @include mx.fanui-transition((background-color, color), v.$fanui-duration-fast);
942
+
943
+ &.is-active {
944
+ background-color: fn.fanui-var("color-surface");
945
+ color: fn.fanui-var("primary");
946
+ box-shadow: fn.fanui-var("shadow-xs");
947
+ }
948
+ }
949
+ }
950
+ }