fdb2 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/.dockerignore +21 -0
  2. package/.editorconfig +11 -0
  3. package/.eslintrc.cjs +14 -0
  4. package/.eslintrc.json +7 -0
  5. package/.prettierrc.js +3 -0
  6. package/.tpl.env +22 -0
  7. package/README.md +260 -0
  8. package/bin/build.sh +28 -0
  9. package/bin/deploy.sh +8 -0
  10. package/bin/dev.sh +10 -0
  11. package/bin/docker/.env +4 -0
  12. package/bin/docker/dev-docker-compose.yml +43 -0
  13. package/bin/docker/dev.Dockerfile +24 -0
  14. package/bin/docker/prod-docker-compose.yml +17 -0
  15. package/bin/docker/prod.Dockerfile +29 -0
  16. package/bin/fdb2.js +142 -0
  17. package/data/connections.demo.json +32 -0
  18. package/env.d.ts +1 -0
  19. package/nw-build.js +120 -0
  20. package/nw-dev.js +65 -0
  21. package/package.json +114 -0
  22. package/public/favicon.ico +0 -0
  23. package/public/index.html +9 -0
  24. package/public/modules/header.tpl +14 -0
  25. package/public/modules/initial_state.tpl +55 -0
  26. package/server/index.ts +677 -0
  27. package/server/model/connection.entity.ts +66 -0
  28. package/server/model/database.entity.ts +246 -0
  29. package/server/service/connection.service.ts +334 -0
  30. package/server/service/database/base.service.ts +363 -0
  31. package/server/service/database/database.service.ts +510 -0
  32. package/server/service/database/index.ts +7 -0
  33. package/server/service/database/mssql.service.ts +723 -0
  34. package/server/service/database/mysql.service.ts +761 -0
  35. package/server/service/database/oracle.service.ts +839 -0
  36. package/server/service/database/postgres.service.ts +744 -0
  37. package/server/service/database/sqlite.service.ts +559 -0
  38. package/server/service/session.service.ts +158 -0
  39. package/server.js +128 -0
  40. package/src/adapter/ajax.ts +135 -0
  41. package/src/assets/base.css +1 -0
  42. package/src/assets/database.css +950 -0
  43. package/src/assets/images/collapse.png +0 -0
  44. package/src/assets/images/no-login.png +0 -0
  45. package/src/assets/images/svg/illustrations/illustration-1.svg +1 -0
  46. package/src/assets/images/svg/illustrations/illustration-2.svg +2 -0
  47. package/src/assets/images/svg/illustrations/illustration-3.svg +50 -0
  48. package/src/assets/images/svg/illustrations/illustration-4.svg +1 -0
  49. package/src/assets/images/svg/illustrations/illustration-5.svg +73 -0
  50. package/src/assets/images/svg/illustrations/illustration-6.svg +89 -0
  51. package/src/assets/images/svg/illustrations/illustration-7.svg +39 -0
  52. package/src/assets/images/svg/illustrations/illustration-8.svg +1 -0
  53. package/src/assets/images/svg/separators/curve-2.svg +3 -0
  54. package/src/assets/images/svg/separators/curve.svg +3 -0
  55. package/src/assets/images/svg/separators/line.svg +3 -0
  56. package/src/assets/images/theme/light/screen-1-1000x800.jpg +0 -0
  57. package/src/assets/images/theme/light/screen-2-1000x800.jpg +0 -0
  58. package/src/assets/login/bg.jpg +0 -0
  59. package/src/assets/login/bg.png +0 -0
  60. package/src/assets/login/left.jpg +0 -0
  61. package/src/assets/logo.svg +73 -0
  62. package/src/assets/logo.webp +0 -0
  63. package/src/assets/main.css +1 -0
  64. package/src/base/config.ts +20 -0
  65. package/src/base/detect.ts +134 -0
  66. package/src/base/entity.ts +92 -0
  67. package/src/base/eventBus.ts +37 -0
  68. package/src/base//345/237/272/347/241/200/345/261/202.md +7 -0
  69. package/src/components/connection-editor/index.vue +590 -0
  70. package/src/components/dataGrid/index.vue +105 -0
  71. package/src/components/dataGrid/pagination.vue +106 -0
  72. package/src/components/loading/index.vue +43 -0
  73. package/src/components/modal/index.ts +181 -0
  74. package/src/components/modal/index.vue +560 -0
  75. package/src/components/toast/index.ts +44 -0
  76. package/src/components/toast/toast.vue +58 -0
  77. package/src/components/user/name.vue +104 -0
  78. package/src/components/user/selector.vue +416 -0
  79. package/src/domain/SysConfig.ts +74 -0
  80. package/src/platform/App.vue +8 -0
  81. package/src/platform/database/components/connection-detail.vue +1154 -0
  82. package/src/platform/database/components/data-editor.vue +478 -0
  83. package/src/platform/database/components/data-import-export.vue +1602 -0
  84. package/src/platform/database/components/database-detail.vue +1173 -0
  85. package/src/platform/database/components/database-monitor.vue +1086 -0
  86. package/src/platform/database/components/db-tools.vue +577 -0
  87. package/src/platform/database/components/query-history.vue +1349 -0
  88. package/src/platform/database/components/sql-executor.vue +738 -0
  89. package/src/platform/database/components/sql-query-editor.vue +1046 -0
  90. package/src/platform/database/components/table-detail.vue +1376 -0
  91. package/src/platform/database/components/table-editor.vue +690 -0
  92. package/src/platform/database/explorer.vue +1840 -0
  93. package/src/platform/database/index.vue +1193 -0
  94. package/src/platform/database/layout.vue +367 -0
  95. package/src/platform/database/router.ts +37 -0
  96. package/src/platform/database/styles/common.scss +602 -0
  97. package/src/platform/database/types/common.ts +445 -0
  98. package/src/platform/database/utils/export.ts +232 -0
  99. package/src/platform/database/utils/helpers.ts +437 -0
  100. package/src/platform/index.ts +33 -0
  101. package/src/platform/router.ts +41 -0
  102. package/src/service/base.ts +128 -0
  103. package/src/service/database.ts +500 -0
  104. package/src/service/login.ts +121 -0
  105. package/src/shims-vue.d.ts +7 -0
  106. package/src/stores/connection.ts +266 -0
  107. package/src/stores/session.ts +87 -0
  108. package/src/typings/database-types.ts +413 -0
  109. package/src/typings/database.ts +364 -0
  110. package/src/typings/global.d.ts +58 -0
  111. package/src/typings/pinia.d.ts +8 -0
  112. package/src/utils/clipboard.ts +30 -0
  113. package/src/utils/database-types.ts +243 -0
  114. package/src/utils/modal.ts +124 -0
  115. package/src/utils/request.ts +55 -0
  116. package/src/utils/sleep.ts +4 -0
  117. package/src/utils/toast.ts +73 -0
  118. package/src/utils/util.ts +171 -0
  119. package/src/utils/xlsx.ts +228 -0
  120. package/tsconfig.json +33 -0
  121. package/tsconfig.server.json +19 -0
  122. package/view/index.html +9 -0
  123. package/view/modules/header.tpl +14 -0
  124. package/view/modules/initial_state.tpl +20 -0
  125. package/vite.config.ts +384 -0
@@ -0,0 +1,602 @@
1
+ // 数据库平台公共样式
2
+
3
+ // 颜色变量
4
+ :root {
5
+ --primary-color: #667eea;
6
+ --primary-dark: #5a67d8;
7
+ --secondary-color: #764ba2;
8
+ --success-color: #10b981;
9
+ --warning-color: #f59e0b;
10
+ --error-color: #ef4444;
11
+ --info-color: #3b82f6;
12
+
13
+ --text-primary: #1e293b;
14
+ --text-secondary: #64748b;
15
+ --text-muted: #94a3b8;
16
+
17
+ --bg-primary: #ffffff;
18
+ --bg-secondary: #f8fafc;
19
+ --bg-tertiary: #f1f5f9;
20
+
21
+ --border-color: #e5e7eb;
22
+ --border-hover: #d1d5db;
23
+ --border-focus: #667eea;
24
+
25
+ --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
26
+ --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06);
27
+ --shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
28
+ --shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
29
+
30
+ --radius-sm: 4px;
31
+ --radius-md: 8px;
32
+ --radius-lg: 12px;
33
+ --radius-xl: 16px;
34
+ }
35
+
36
+ // 通用渐变背景
37
+ .gradient-primary {
38
+ background: linear-gradient(135deg, var(--primary-color) 0%, var(--secondary-color) 100%);
39
+ }
40
+
41
+ .gradient-secondary {
42
+ background: linear-gradient(135deg, var(--bg-secondary) 0%, var(--bg-tertiary) 100%);
43
+ }
44
+
45
+ .gradient-success {
46
+ background: linear-gradient(135deg, var(--success-color) 0%, #059669 100%);
47
+ }
48
+
49
+ .gradient-warning {
50
+ background: linear-gradient(135deg, var(--warning-color) 0%, #d97706 100%);
51
+ }
52
+
53
+ .gradient-error {
54
+ background: linear-gradient(135deg, var(--error-color) 0%, #dc2626 100%);
55
+ }
56
+
57
+ .gradient-info {
58
+ background: linear-gradient(135deg, var(--info-color) 0%, #2563eb 100%);
59
+ }
60
+
61
+ // 通用按钮样式
62
+ .btn {
63
+ display: inline-flex;
64
+ align-items: center;
65
+ justify-content: center;
66
+ gap: 0.5rem;
67
+ padding: 0.75rem 1.5rem;
68
+ border: 1px solid var(--border-color);
69
+ border-radius: var(--radius-md);
70
+ font-size: 0.875rem;
71
+ font-weight: 500;
72
+ cursor: pointer;
73
+ transition: all 0.2s ease;
74
+ text-decoration: none;
75
+ outline: none;
76
+ user-select: none;
77
+
78
+ &:hover:not(:disabled) {
79
+ transform: translateY(-1px);
80
+ box-shadow: var(--shadow-md);
81
+ }
82
+
83
+ &:active {
84
+ transform: translateY(0);
85
+ }
86
+
87
+ &:disabled {
88
+ opacity: 0.5;
89
+ cursor: not-allowed;
90
+ }
91
+
92
+ &.btn-primary {
93
+ @extend .gradient-primary;
94
+ color: white;
95
+ border: none;
96
+
97
+ &:hover:not(:disabled) {
98
+ background: linear-gradient(135deg, var(--primary-dark) 0%, var(--secondary-color) 100%);
99
+ }
100
+ }
101
+
102
+ &.btn-success {
103
+ @extend .gradient-success;
104
+ color: white;
105
+ border: none;
106
+ }
107
+
108
+ &.btn-warning {
109
+ @extend .gradient-warning;
110
+ color: white;
111
+ border: none;
112
+ }
113
+
114
+ &.btn-danger {
115
+ @extend .gradient-error;
116
+ color: white;
117
+ border: none;
118
+ }
119
+
120
+ &.btn-info {
121
+ @extend .gradient-info;
122
+ color: white;
123
+ border: none;
124
+ }
125
+
126
+ &.btn-outline {
127
+ background: transparent;
128
+ color: var(--primary-color);
129
+ border-color: var(--primary-color);
130
+
131
+ &:hover:not(:disabled) {
132
+ background: var(--primary-color);
133
+ color: white;
134
+ }
135
+ }
136
+
137
+ &.btn-ghost {
138
+ background: transparent;
139
+ color: var(--text-secondary);
140
+
141
+ &:hover:not(:disabled) {
142
+ background: var(--bg-secondary);
143
+ color: var(--text-primary);
144
+ }
145
+ }
146
+
147
+ &.btn-sm {
148
+ padding: 0.5rem 1rem;
149
+ font-size: 0.75rem;
150
+ }
151
+
152
+ &.btn-lg {
153
+ padding: 1rem 2rem;
154
+ font-size: 1rem;
155
+ }
156
+ }
157
+
158
+ // 通用卡片样式
159
+ .card {
160
+ background: var(--bg-primary);
161
+ border: 1px solid var(--border-color);
162
+ border-radius: var(--radius-lg);
163
+ box-shadow: var(--shadow-sm);
164
+ overflow: hidden;
165
+ transition: all 0.2s ease;
166
+
167
+ &:hover {
168
+ box-shadow: var(--shadow-md);
169
+ transform: translateY(-2px);
170
+ }
171
+
172
+ &.card-flat {
173
+ box-shadow: none;
174
+
175
+ &:hover {
176
+ box-shadow: var(--shadow-sm);
177
+ transform: none;
178
+ }
179
+ }
180
+
181
+ .card-header {
182
+ padding: 1rem 1.5rem;
183
+ border-bottom: 1px solid var(--border-color);
184
+ background: var(--bg-secondary);
185
+
186
+ .card-title {
187
+ font-size: 1.125rem;
188
+ font-weight: 600;
189
+ color: var(--text-primary);
190
+ margin: 0;
191
+ }
192
+ }
193
+
194
+ .card-body {
195
+ padding: 1.5rem;
196
+ }
197
+
198
+ .card-footer {
199
+ padding: 1rem 1.5rem;
200
+ border-top: 1px solid var(--border-color);
201
+ background: var(--bg-secondary);
202
+ }
203
+ }
204
+
205
+ // 通用表单样式
206
+ .form-group {
207
+ margin-bottom: 1rem;
208
+
209
+ label {
210
+ display: block;
211
+ margin-bottom: 0.5rem;
212
+ font-weight: 500;
213
+ color: var(--text-primary);
214
+ font-size: 0.875rem;
215
+ }
216
+ }
217
+
218
+ .form-input,
219
+ .form-textarea,
220
+ .form-select {
221
+ width: 100%;
222
+ padding: 0.75rem;
223
+ border: 1px solid var(--border-color);
224
+ border-radius: var(--radius-md);
225
+ font-size: 0.875rem;
226
+ transition: all 0.2s ease;
227
+ background: var(--bg-primary);
228
+
229
+ &:focus {
230
+ outline: none;
231
+ border-color: var(--border-focus);
232
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
233
+ }
234
+
235
+ &.is-invalid {
236
+ border-color: var(--error-color);
237
+
238
+ &:focus {
239
+ box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.1);
240
+ }
241
+ }
242
+ }
243
+
244
+ .form-textarea {
245
+ resize: vertical;
246
+ min-height: 100px;
247
+ }
248
+
249
+ // 通用表格样式
250
+ .table {
251
+ width: 100%;
252
+ border-collapse: collapse;
253
+ font-size: 0.875rem;
254
+
255
+ th {
256
+ background: var(--bg-secondary);
257
+ padding: 0.75rem;
258
+ text-align: left;
259
+ font-weight: 600;
260
+ color: var(--text-primary);
261
+ border-bottom: 2px solid var(--border-color);
262
+
263
+ &.sortable {
264
+ cursor: pointer;
265
+ user-select: none;
266
+
267
+ &:hover {
268
+ background: var(--bg-tertiary);
269
+ }
270
+ }
271
+ }
272
+
273
+ td {
274
+ padding: 0.75rem;
275
+ border-bottom: 1px solid var(--border-color);
276
+ max-width: 200px;
277
+ overflow: hidden;
278
+ text-overflow: ellipsis;
279
+ white-space: nowrap;
280
+ }
281
+
282
+ tr:hover {
283
+ background: var(--bg-secondary);
284
+ }
285
+
286
+ &.table-sm {
287
+ font-size: 0.75rem;
288
+
289
+ th,
290
+ td {
291
+ padding: 0.5rem;
292
+ }
293
+ }
294
+
295
+ &.table-hover {
296
+ tbody tr {
297
+ transition: background-color 0.2s ease;
298
+ }
299
+ }
300
+
301
+ &.table-striped {
302
+ tbody tr:nth-child(even) {
303
+ background: var(--bg-secondary);
304
+ }
305
+ }
306
+ }
307
+
308
+ // 通用徽章样式
309
+ .badge {
310
+ display: inline-flex;
311
+ align-items: center;
312
+ padding: 0.25rem 0.75rem;
313
+ border-radius: 20px;
314
+ font-size: 0.75rem;
315
+ font-weight: 600;
316
+ text-transform: uppercase;
317
+ letter-spacing: 0.05em;
318
+
319
+ &.badge-primary {
320
+ background: rgba(102, 126, 234, 0.1);
321
+ color: var(--primary-color);
322
+ }
323
+
324
+ &.badge-success {
325
+ background: rgba(16, 185, 129, 0.1);
326
+ color: var(--success-color);
327
+ }
328
+
329
+ &.badge-warning {
330
+ background: rgba(245, 158, 11, 0.1);
331
+ color: var(--warning-color);
332
+ }
333
+
334
+ &.badge-error {
335
+ background: rgba(239, 68, 68, 0.1);
336
+ color: var(--error-color);
337
+ }
338
+
339
+ &.badge-info {
340
+ background: rgba(59, 130, 246, 0.1);
341
+ color: var(--info-color);
342
+ }
343
+ }
344
+
345
+ // 通用状态指示器
346
+ .status-indicator {
347
+ display: inline-flex;
348
+ align-items: center;
349
+ gap: 0.5rem;
350
+ font-size: 0.875rem;
351
+
352
+ .status-dot {
353
+ width: 8px;
354
+ height: 8px;
355
+ border-radius: 50%;
356
+
357
+ &.online {
358
+ background: var(--success-color);
359
+ box-shadow: 0 0 8px rgba(16, 185, 129, 0.5);
360
+ }
361
+
362
+ &.offline {
363
+ background: var(--text-muted);
364
+ }
365
+
366
+ &.warning {
367
+ background: var(--warning-color);
368
+ box-shadow: 0 0 8px rgba(245, 158, 11, 0.5);
369
+ }
370
+
371
+ &.error {
372
+ background: var(--error-color);
373
+ box-shadow: 0 0 8px rgba(239, 68, 68, 0.5);
374
+ }
375
+ }
376
+ }
377
+
378
+ // 通用加载动画
379
+ .loading {
380
+ display: inline-flex;
381
+ align-items: center;
382
+ gap: 0.5rem;
383
+
384
+ .spinner {
385
+ width: 16px;
386
+ height: 16px;
387
+ border: 2px solid var(--border-color);
388
+ border-top-color: var(--primary-color);
389
+ border-radius: 50%;
390
+ animation: spin 1s linear infinite;
391
+ }
392
+ }
393
+
394
+ @keyframes spin {
395
+ from {
396
+ transform: rotate(0deg);
397
+ }
398
+ to {
399
+ transform: rotate(360deg);
400
+ }
401
+ }
402
+
403
+ // 通用模态框样式
404
+ .modal {
405
+ position: fixed;
406
+ top: 0;
407
+ left: 0;
408
+ right: 0;
409
+ bottom: 0;
410
+ background: rgba(0, 0, 0, 0.5);
411
+ display: flex;
412
+ justify-content: center;
413
+ align-items: center;
414
+ z-index: 1000;
415
+ backdrop-filter: blur(4px);
416
+
417
+ .modal-content {
418
+ background: var(--bg-primary);
419
+ border-radius: var(--radius-xl);
420
+ box-shadow: var(--shadow-xl);
421
+ max-width: 90vw;
422
+ max-height: 90vh;
423
+ overflow: hidden;
424
+ display: flex;
425
+ flex-direction: column;
426
+
427
+ .modal-header {
428
+ padding: 1.5rem;
429
+ border-bottom: 1px solid var(--border-color);
430
+ display: flex;
431
+ justify-content: space-between;
432
+ align-items: center;
433
+
434
+ .modal-title {
435
+ font-size: 1.25rem;
436
+ font-weight: 600;
437
+ color: var(--text-primary);
438
+ margin: 0;
439
+ }
440
+
441
+ .modal-close {
442
+ background: none;
443
+ border: none;
444
+ font-size: 1.5rem;
445
+ cursor: pointer;
446
+ color: var(--text-muted);
447
+ padding: 0.25rem;
448
+ border-radius: var(--radius-sm);
449
+ transition: all 0.2s ease;
450
+
451
+ &:hover {
452
+ background: var(--bg-secondary);
453
+ color: var(--text-primary);
454
+ }
455
+ }
456
+ }
457
+
458
+ .modal-body {
459
+ padding: 1.5rem;
460
+ flex: 1;
461
+ overflow-y: auto;
462
+ }
463
+
464
+ .modal-footer {
465
+ padding: 1.5rem;
466
+ border-top: 1px solid var(--border-color);
467
+ display: flex;
468
+ justify-content: flex-end;
469
+ gap: 0.5rem;
470
+ }
471
+ }
472
+ }
473
+
474
+ // 通用分页样式
475
+ .pagination {
476
+ display: flex;
477
+ justify-content: center;
478
+ align-items: center;
479
+ gap: 0.5rem;
480
+
481
+ .page-info {
482
+ padding: 0.5rem 1rem;
483
+ font-size: 0.875rem;
484
+ color: var(--text-secondary);
485
+ }
486
+
487
+ .page-btn {
488
+ padding: 0.5rem 0.75rem;
489
+ border: 1px solid var(--border-color);
490
+ border-radius: var(--radius-sm);
491
+ background: var(--bg-primary);
492
+ cursor: pointer;
493
+ transition: all 0.2s ease;
494
+
495
+ &:hover:not(:disabled) {
496
+ background: var(--primary-color);
497
+ color: white;
498
+ border-color: var(--primary-color);
499
+ }
500
+
501
+ &:disabled {
502
+ opacity: 0.5;
503
+ cursor: not-allowed;
504
+ }
505
+
506
+ &.active {
507
+ background: var(--primary-color);
508
+ color: white;
509
+ border-color: var(--primary-color);
510
+ }
511
+ }
512
+ }
513
+
514
+ // 通用工具类
515
+ .text-center { text-align: center; }
516
+ .text-left { text-align: left; }
517
+ .text-right { text-align: right; }
518
+
519
+ .text-primary { color: var(--text-primary); }
520
+ .text-secondary { color: var(--text-secondary); }
521
+ .text-muted { color: var(--text-muted); }
522
+ .text-success { color: var(--success-color); }
523
+ .text-warning { color: var(--warning-color); }
524
+ .text-error { color: var(--error-color); }
525
+ .text-info { color: var(--info-color); }
526
+
527
+ .bg-primary { background: var(--bg-primary); }
528
+ .bg-secondary { background: var(--bg-secondary); }
529
+ .bg-tertiary { background: var(--bg-tertiary); }
530
+
531
+ .border { border: 1px solid var(--border-color); }
532
+ .border-top { border-top: 1px solid var(--border-color); }
533
+ .border-bottom { border-bottom: 1px solid var(--border-color); }
534
+ .border-left { border-left: 1px solid var(--border-color); }
535
+ .border-right { border-right: 1px solid var(--border-color); }
536
+
537
+ .rounded { border-radius: var(--radius-md); }
538
+ .rounded-sm { border-radius: var(--radius-sm); }
539
+ .rounded-lg { border-radius: var(--radius-lg); }
540
+ .rounded-xl { border-radius: var(--radius-xl); }
541
+
542
+ .shadow-sm { box-shadow: var(--shadow-sm); }
543
+ .shadow-md { box-shadow: var(--shadow-md); }
544
+ .shadow-lg { box-shadow: var(--shadow-lg); }
545
+ .shadow-xl { box-shadow: var(--shadow-xl); }
546
+
547
+ .p-0 { padding: 0; }
548
+ .p-1 { padding: 0.25rem; }
549
+ .p-2 { padding: 0.5rem; }
550
+ .p-3 { padding: 0.75rem; }
551
+ .p-4 { padding: 1rem; }
552
+ .p-5 { padding: 1.5rem; }
553
+
554
+ .m-0 { margin: 0; }
555
+ .m-1 { margin: 0.25rem; }
556
+ .m-2 { margin: 0.5rem; }
557
+ .m-3 { margin: 0.75rem; }
558
+ .m-4 { margin: 1rem; }
559
+ .m-5 { margin: 1.5rem; }
560
+
561
+ .d-none { display: none; }
562
+ .d-block { display: block; }
563
+ .d-flex { display: flex; }
564
+ .d-inline { display: inline; }
565
+ .d-inline-block { display: inline-block; }
566
+
567
+ .flex-column { flex-direction: column; }
568
+ .flex-row { flex-direction: row; }
569
+ .justify-center { justify-content: center; }
570
+ .justify-between { justify-content: space-between; }
571
+ .align-center { align-items: center; }
572
+ .flex-1 { flex: 1; }
573
+
574
+ .w-100 { width: 100%; }
575
+ .h-100 { height: 100%; }
576
+
577
+ .overflow-hidden { overflow: hidden; }
578
+ .overflow-auto { overflow: auto; }
579
+ .overflow-scroll { overflow: scroll; }
580
+
581
+ .position-relative { position: relative; }
582
+ .position-absolute { position: absolute; }
583
+ .position-fixed { position: fixed; }
584
+
585
+ // 响应式工具类
586
+ @media (max-width: 640px) {
587
+ .d-sm-none { display: none; }
588
+ .d-sm-block { display: block; }
589
+ .d-sm-flex { display: flex; }
590
+ }
591
+
592
+ @media (max-width: 768px) {
593
+ .d-md-none { display: none; }
594
+ .d-md-block { display: block; }
595
+ .d-md-flex { display: flex; }
596
+ }
597
+
598
+ @media (max-width: 1024px) {
599
+ .d-lg-none { display: none; }
600
+ .d-lg-block { display: block; }
601
+ .d-lg-flex { display: flex; }
602
+ }