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,1193 @@
1
+ <template>
2
+ <div class="database-dashboard-modern">
3
+ <div class="container-fluid">
4
+ <!-- 现代欢迎横幅 -->
5
+ <section class="hero-section">
6
+ <div class="hero-content">
7
+ <div class="hero-background">
8
+ <div class="hero-shapes">
9
+ <div class="hero-shape shape-1"></div>
10
+ <div class="hero-shape shape-2"></div>
11
+ <div class="hero-shape shape-3"></div>
12
+ </div>
13
+ </div>
14
+ <div class="row align-items-center">
15
+ <div class="col-lg-7">
16
+ <div class="hero-text">
17
+ <div class="hero-badge">
18
+ <i class="bi bi-stars"></i>
19
+ <span>欢迎回来</span>
20
+ </div>
21
+ <h1 class="hero-title">
22
+ <span class="gradient-text">数据库管理平台</span>
23
+ </h1>
24
+ <p class="hero-description">
25
+ 专业的数据库管理和监控工具,为开发者提供一站式数据库操作体验。支持多种主流数据库类型,提供直观的可视化界面和强大的查询功能。
26
+ </p>
27
+ <div class="hero-actions">
28
+ <router-link to="/database/explorer" class="btn-hero btn-primary-hero">
29
+ <i class="bi bi-plus-lg"></i>
30
+ <span>添加连接</span>
31
+ <div class="btn-glow"></div>
32
+ </router-link>
33
+ <router-link to="/database/explorer" class="btn-hero btn-secondary-hero">
34
+ <i class="bi bi-terminal"></i>
35
+ <span>SQL查询</span>
36
+ </router-link>
37
+ </div>
38
+ <div class="hero-stats">
39
+ <div class="hero-stat">
40
+ <span class="stat-number">{{ connections.length }}</span>
41
+ <span class="stat-label">连接数</span>
42
+ </div>
43
+ <div class="hero-stat">
44
+ <span class="stat-number">5</span>
45
+ <span class="stat-label">支持类型</span>
46
+ </div>
47
+ <div class="hero-stat">
48
+ <span class="stat-number">24/7</span>
49
+ <span class="stat-label">在线监控</span>
50
+ </div>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ <div class="col-lg-5">
55
+ <div class="hero-visual">
56
+ <div class="database-illustration">
57
+ <div class="db-layer layer-1">
58
+ <i class="bi bi-database"></i>
59
+ </div>
60
+ <div class="db-layer layer-2">
61
+ <i class="bi bi-diagram-3"></i>
62
+ </div>
63
+ <div class="db-layer layer-3">
64
+ <i class="bi bi-table"></i>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
70
+ </div>
71
+ </section>
72
+
73
+ <!-- 快速统计卡片 -->
74
+ <section class="stats-section">
75
+ <div class="row g-4">
76
+ <div class="col-md-3">
77
+ <div class="stat-card-modern stat-gradient-1">
78
+ <div class="stat-icon-wrapper">
79
+ <div class="stat-icon">
80
+ <i class="bi bi-plugin"></i>
81
+ </div>
82
+ <div class="stat-trend">
83
+ <i class="bi bi-arrow-up"></i>
84
+ <span>+12%</span>
85
+ </div>
86
+ </div>
87
+ <div class="stat-content">
88
+ <div class="stat-number">{{ connections.length }}</div>
89
+ <div class="stat-title">数据库连接</div>
90
+ <div class="stat-description">活跃连接配置</div>
91
+ </div>
92
+ </div>
93
+ </div>
94
+ <div class="col-md-3">
95
+ <div class="stat-card-modern stat-gradient-2">
96
+ <div class="stat-icon-wrapper">
97
+ <div class="stat-icon">
98
+ <i class="bi bi-activity"></i>
99
+ </div>
100
+ <div class="stat-trend">
101
+ <i class="bi bi-arrow-up"></i>
102
+ <span>98%</span>
103
+ </div>
104
+ </div>
105
+ <div class="stat-content">
106
+ <div class="stat-number">{{ enabledConnections }}</div>
107
+ <div class="stat-title">在线状态</div>
108
+ <div class="stat-description">正常运行</div>
109
+ </div>
110
+ </div>
111
+ </div>
112
+ <div class="col-md-3">
113
+ <div class="stat-card-modern stat-gradient-3">
114
+ <div class="stat-icon-wrapper">
115
+ <div class="stat-icon">
116
+ <i class="bi bi-diagram-3"></i>
117
+ </div>
118
+ <div class="stat-trend">
119
+ <i class="bi bi-arrow-right"></i>
120
+ <span>5种</span>
121
+ </div>
122
+ </div>
123
+ <div class="stat-content">
124
+ <div class="stat-number">{{ dbTypesCount }}</div>
125
+ <div class="stat-title">数据库类型</div>
126
+ <div class="stat-description">支持的引擎</div>
127
+ </div>
128
+ </div>
129
+ </div>
130
+ <div class="col-md-3">
131
+ <div class="stat-card-modern stat-gradient-4">
132
+ <div class="stat-icon-wrapper">
133
+ <div class="stat-icon">
134
+ <i class="bi bi-lightning"></i>
135
+ </div>
136
+ <div class="stat-trend">
137
+ <i class="bi bi-arrow-up"></i>
138
+ <span>15ms</span>
139
+ </div>
140
+ </div>
141
+ <div class="stat-content">
142
+ <div class="stat-number">极速</div>
143
+ <div class="stat-title">响应时间</div>
144
+ <div class="stat-description">平均查询性能</div>
145
+ </div>
146
+ </div>
147
+ </div>
148
+ </div>
149
+ </section>
150
+
151
+ <!-- 功能特性展示 -->
152
+ <section class="features-section">
153
+ <div class="section-header">
154
+ <div class="section-icon">
155
+ <i class="bi bi-stars"></i>
156
+ </div>
157
+ <h2 class="section-title">核心功能</h2>
158
+ <p class="section-subtitle">强大的数据库管理功能,满足您的所有需求</p>
159
+ </div>
160
+
161
+ <div class="features-grid">
162
+ <div class="feature-card" v-for="(feature, index) in features" :key="index">
163
+ <div class="feature-icon-wrapper">
164
+ <div class="feature-icon" :class="`feature-${index + 1}`">
165
+ <i :class="feature.icon"></i>
166
+ </div>
167
+ <div class="feature-number">{{ String(index + 1).padStart(2, '0') }}</div>
168
+ </div>
169
+ <div class="feature-content">
170
+ <h3 class="feature-title">{{ feature.title }}</h3>
171
+ <p class="feature-description">{{ feature.description }}</p>
172
+ <div class="feature-tags">
173
+ <span class="feature-tag" v-for="tag in feature.tags" :key="tag">{{ tag }}</span>
174
+ </div>
175
+ </div>
176
+ <div class="feature-action">
177
+ <router-link :to="feature.link" class="feature-link">
178
+ <i class="bi bi-arrow-right"></i>
179
+ </router-link>
180
+ </div>
181
+ </div>
182
+ </div>
183
+ </section>
184
+
185
+ <!-- 快速连接访问 -->
186
+ <section class="quick-access-section" v-if="connections.length > 0">
187
+ <div class="section-header">
188
+ <div class="section-icon">
189
+ <i class="bi bi-lightning"></i>
190
+ </div>
191
+ <h2 class="section-title">快速访问</h2>
192
+ <p class="section-subtitle">一键访问您的数据库连接</p>
193
+ </div>
194
+
195
+ <div class="quick-access-grid">
196
+ <div class="quick-access-card" v-for="connection in connections.slice(0, 6)" :key="connection.id">
197
+ <div class="quick-access-header">
198
+ <div class="db-avatar" :class="getDbTypeClass(connection.type)">
199
+ <i :class="getDbTypeIcon(connection.type)"></i>
200
+ </div>
201
+ <div class="connection-status" :class="connection.enabled ? 'status-online' : 'status-offline'">
202
+ <div class="status-dot"></div>
203
+ </div>
204
+ </div>
205
+ <div class="quick-access-body">
206
+ <h4 class="connection-name">{{ connection.name }}</h4>
207
+ <p class="connection-info">
208
+ <span class="db-type">{{ getDbTypeLabel(connection.type) }}</span>
209
+ <span class="connection-host">{{ connection.host }}:{{ connection.port }}</span>
210
+ </p>
211
+ </div>
212
+ <div class="quick-access-footer">
213
+ <button class="quick-access-btn" @click="goToSchemas(connection)">
214
+ <i class="bi bi-folder2-open"></i>
215
+ <span>查看</span>
216
+ </button>
217
+ </div>
218
+ </div>
219
+ </div>
220
+
221
+ <div class="view-all-connections" v-if="connections.length > 6">
222
+ <router-link to="/database/explorer" class="view-all-btn">
223
+ <i class="bi bi-grid"></i>
224
+ <span>查看所有连接 ({{ connections.length }})</span>
225
+ <i class="bi bi-arrow-right"></i>
226
+ </router-link>
227
+ </div>
228
+ </section>
229
+
230
+ <!-- 空状态 -->
231
+ <section class="empty-state-section" v-else>
232
+ <div class="empty-state-content">
233
+ <div class="empty-illustration">
234
+ <div class="empty-icon-wrapper">
235
+ <i class="bi bi-inbox"></i>
236
+ </div>
237
+ <div class="empty-shapes">
238
+ <div class="empty-shape shape-a"></div>
239
+ <div class="empty-shape shape-b"></div>
240
+ <div class="empty-shape shape-c"></div>
241
+ </div>
242
+ </div>
243
+ <h2 class="empty-title">开始您的数据库管理之旅</h2>
244
+ <p class="empty-description">
245
+ 还没有配置数据库连接?让我们添加第一个连接,开始强大的数据库管理体验。
246
+ </p>
247
+ <router-link to="/database/explorer" class="btn-hero btn-primary-hero">
248
+ <i class="bi bi-plus-lg"></i>
249
+ <span>添加数据库连接</span>
250
+ <div class="btn-glow"></div>
251
+ </router-link>
252
+ </div>
253
+ </section>
254
+ </div>
255
+ </div>
256
+ </template>
257
+
258
+ <script lang="ts" setup>
259
+ import { ref, onMounted, computed } from 'vue';
260
+ import { useRouter } from 'vue-router';
261
+ import { ConnectionService } from '@/service/database';
262
+ import type { ConnectionEntity } from '@/typings/database';
263
+
264
+ const router = useRouter();
265
+ const connectionService = new ConnectionService();
266
+
267
+ // 响应式数据
268
+ const connections = ref<ConnectionEntity[]>([]);
269
+
270
+ // 功能特性数据
271
+ const features = ref([
272
+ {
273
+ title: '连接管理',
274
+ icon: 'bi bi-plugin',
275
+ description: '支持MySQL、PostgreSQL、SQLite、SQL Server、Oracle等主流数据库的连接配置和管理',
276
+ tags: ['多数据库支持', '安全连接', '连接池'],
277
+ link: '/database/explorer'
278
+ },
279
+ {
280
+ title: '结构浏览',
281
+ icon: 'bi bi-diagram-3',
282
+ description: '直观展示数据库表结构、索引、外键关系、视图、存储过程等数据库对象',
283
+ tags: ['可视化', '关系图', '详细视图'],
284
+ link: '/database/explorer'
285
+ },
286
+ {
287
+ title: '数据操作',
288
+ icon: 'bi bi-table',
289
+ description: '查看、编辑、删除表数据,支持批量操作、分页浏览、条件筛选和数据导入导出',
290
+ tags: ['CRUD操作', '批量处理', '导入导出'],
291
+ link: '/database/explorer'
292
+ },
293
+ {
294
+ title: 'SQL查询',
295
+ icon: 'bi bi-terminal',
296
+ description: '强大的SQL编辑器,支持语法高亮、自动补全、查询历史和结果导出功能',
297
+ tags: ['语法高亮', '查询历史', '结果导出'],
298
+ link: '/database/explorer'
299
+ }
300
+ ]);
301
+
302
+ // 计算属性
303
+ const enabledConnections = computed(() =>
304
+ connections.value?.filter?.(c => c.enabled)?.length
305
+ );
306
+
307
+ const dbTypesCount = computed(() => {
308
+ const types = new Set(connections.value?.map?.(c => c.type)||[]);
309
+ return types.size;
310
+ });
311
+
312
+ // 生命周期
313
+ onMounted(async () => {
314
+ await loadConnections();
315
+ });
316
+
317
+ // 方法
318
+ async function loadConnections() {
319
+ try {
320
+ const response = await connectionService.getAllConnections();
321
+ connections.value = response || [];
322
+ } catch (error) {
323
+ console.error('加载连接列表失败:', error);
324
+ }
325
+ }
326
+
327
+ function goToSchemas(connection: ConnectionEntity) {
328
+ router.push(`/database/explorer?connectionId=${connection.id}`);
329
+ }
330
+
331
+ function getDbTypeLabel(type: string): string {
332
+ const labelMap: Record<string, string> = {
333
+ mysql: 'MySQL',
334
+ postgres: 'PostgreSQL',
335
+ sqlite: 'SQLite',
336
+ mssql: 'SQL Server',
337
+ oracle: 'Oracle'
338
+ };
339
+ return labelMap[type] || type;
340
+ }
341
+
342
+ function getDbTypeIcon(type: string): string {
343
+ const iconMap: Record<string, string> = {
344
+ mysql: 'bi-database',
345
+ postgres: 'bi-database',
346
+ sqlite: 'bi-database',
347
+ mssql: 'bi-database',
348
+ oracle: 'bi-database'
349
+ };
350
+ return iconMap[type] || 'bi-database';
351
+ }
352
+
353
+ function getDbTypeClass(type: string): string {
354
+ const classMap: Record<string, string> = {
355
+ mysql: 'db-mysql',
356
+ postgres: 'db-postgres',
357
+ sqlite: 'db-sqlite',
358
+ mssql: 'db-mssql',
359
+ oracle: 'db-oracle'
360
+ };
361
+ return classMap[type] || 'db-default';
362
+ }
363
+ </script>
364
+
365
+ <style scoped>
366
+ /* 主容器 */
367
+ .database-dashboard-modern {
368
+ min-height: calc(100vh - 200px);
369
+ background: linear-gradient(135deg, #f8fafc 0%, #e2e8f0 100%);
370
+ padding: 2rem 0;
371
+ }
372
+
373
+ /* Hero 区域 */
374
+ .hero-section {
375
+ position: relative;
376
+ margin-bottom: 4rem;
377
+ overflow: hidden;
378
+ }
379
+
380
+ .hero-content {
381
+ background: linear-gradient(135deg, #ffffff 0%, #f8fafc 100%);
382
+ border-radius: 24px;
383
+ padding: 3rem;
384
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
385
+ border: 1px solid rgba(226, 232, 240, 0.5);
386
+ position: relative;
387
+ overflow: hidden;
388
+ }
389
+
390
+ .hero-background {
391
+ position: absolute;
392
+ top: 0;
393
+ left: 0;
394
+ width: 100%;
395
+ height: 100%;
396
+ pointer-events: none;
397
+ z-index: 1;
398
+ }
399
+
400
+ .hero-shapes {
401
+ position: absolute;
402
+ width: 100%;
403
+ height: 100%;
404
+ }
405
+
406
+ .hero-shape {
407
+ position: absolute;
408
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.05));
409
+ border-radius: 50%;
410
+ animation: float 6s ease-in-out infinite;
411
+ }
412
+
413
+ .shape-1 {
414
+ width: 200px;
415
+ height: 200px;
416
+ top: -50px;
417
+ right: -50px;
418
+ animation-delay: 0s;
419
+ }
420
+
421
+ .shape-2 {
422
+ width: 150px;
423
+ height: 150px;
424
+ bottom: -30px;
425
+ left: 20%;
426
+ animation-delay: 2s;
427
+ }
428
+
429
+ .shape-3 {
430
+ width: 100px;
431
+ height: 100px;
432
+ top: 20%;
433
+ right: 10%;
434
+ animation-delay: 4s;
435
+ }
436
+
437
+ @keyframes float {
438
+ 0%, 100% { transform: translateY(0px) rotate(0deg); }
439
+ 50% { transform: translateY(-20px) rotate(180deg); }
440
+ }
441
+
442
+ .hero-text {
443
+ position: relative;
444
+ z-index: 2;
445
+ }
446
+
447
+ .hero-badge {
448
+ display: inline-flex;
449
+ align-items: center;
450
+ gap: 0.5rem;
451
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
452
+ color: white;
453
+ padding: 0.5rem 1rem;
454
+ border-radius: 20px;
455
+ font-size: 0.875rem;
456
+ font-weight: 600;
457
+ margin-bottom: 1.5rem;
458
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3);
459
+ }
460
+
461
+ .hero-title {
462
+ font-size: 3rem;
463
+ font-weight: 800;
464
+ line-height: 1.1;
465
+ margin-bottom: 1.5rem;
466
+ color: #1e293b;
467
+ }
468
+
469
+ .gradient-text {
470
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
471
+ -webkit-background-clip: text;
472
+ -webkit-text-fill-color: transparent;
473
+ background-clip: text;
474
+ }
475
+
476
+ .hero-description {
477
+ font-size: 1.125rem;
478
+ color: #64748b;
479
+ line-height: 1.6;
480
+ margin-bottom: 2rem;
481
+ max-width: 600px;
482
+ }
483
+
484
+ .hero-actions {
485
+ display: flex;
486
+ gap: 1rem;
487
+ margin-bottom: 2rem;
488
+ flex-wrap: wrap;
489
+ }
490
+
491
+ .btn-hero {
492
+ display: inline-flex;
493
+ align-items: center;
494
+ gap: 0.5rem;
495
+ padding: 1rem 2rem;
496
+ border-radius: 12px;
497
+ text-decoration: none;
498
+ font-weight: 600;
499
+ font-size: 1rem;
500
+ transition: all 0.3s ease;
501
+ position: relative;
502
+ overflow: hidden;
503
+ }
504
+
505
+ .btn-primary-hero {
506
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
507
+ color: white;
508
+ box-shadow: 0 8px 16px rgba(102, 126, 234, 0.3);
509
+ }
510
+
511
+ .btn-primary-hero:hover {
512
+ transform: translateY(-2px);
513
+ box-shadow: 0 12px 24px rgba(102, 126, 234, 0.4);
514
+ }
515
+
516
+ .btn-secondary-hero {
517
+ background: white;
518
+ color: #667eea;
519
+ border: 2px solid #667eea;
520
+ }
521
+
522
+ .btn-secondary-hero:hover {
523
+ background: #667eea;
524
+ color: white;
525
+ transform: translateY(-2px);
526
+ }
527
+
528
+ .btn-glow {
529
+ position: absolute;
530
+ top: 50%;
531
+ left: 50%;
532
+ width: 0;
533
+ height: 0;
534
+ border-radius: 50%;
535
+ background: rgba(255, 255, 255, 0.3);
536
+ transform: translate(-50%, -50%);
537
+ transition: width 0.6s, height 0.6s;
538
+ }
539
+
540
+ .btn-hero:active .btn-glow {
541
+ width: 300px;
542
+ height: 300px;
543
+ }
544
+
545
+ .hero-stats {
546
+ display: flex;
547
+ gap: 3rem;
548
+ margin-top: 2rem;
549
+ }
550
+
551
+ .hero-stat {
552
+ display: flex;
553
+ flex-direction: column;
554
+ gap: 0.25rem;
555
+ }
556
+
557
+ .stat-number {
558
+ font-size: 2rem;
559
+ font-weight: 700;
560
+ color: #1e293b;
561
+ }
562
+
563
+ .stat-label {
564
+ font-size: 0.875rem;
565
+ color: #64748b;
566
+ font-weight: 500;
567
+ }
568
+
569
+ /* 可视化插图 */
570
+ .hero-visual {
571
+ position: relative;
572
+ z-index: 2;
573
+ display: flex;
574
+ justify-content: center;
575
+ align-items: center;
576
+ height: 400px;
577
+ }
578
+
579
+ .database-illustration {
580
+ position: relative;
581
+ width: 300px;
582
+ height: 300px;
583
+ }
584
+
585
+ .db-layer {
586
+ position: absolute;
587
+ width: 100%;
588
+ height: 100%;
589
+ display: flex;
590
+ align-items: center;
591
+ justify-content: center;
592
+ border-radius: 20px;
593
+ font-size: 4rem;
594
+ color: white;
595
+ animation: rotate 20s linear infinite;
596
+ }
597
+
598
+ .layer-1 {
599
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
600
+ animation-delay: 0s;
601
+ z-index: 3;
602
+ }
603
+
604
+ .layer-2 {
605
+ background: linear-gradient(135deg, #10b981 0%, #059669 100%);
606
+ animation-delay: 6s;
607
+ transform: scale(0.8) rotate(45deg);
608
+ z-index: 2;
609
+ }
610
+
611
+ .layer-3 {
612
+ background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%);
613
+ animation-delay: 12s;
614
+ transform: scale(0.6) rotate(90deg);
615
+ z-index: 1;
616
+ }
617
+
618
+ @keyframes rotate {
619
+ from { transform: rotate(0deg); }
620
+ to { transform: rotate(360deg); }
621
+ }
622
+
623
+ /* 统计卡片区域 */
624
+ .stats-section {
625
+ margin-bottom: 4rem;
626
+ }
627
+
628
+ .stat-card-modern {
629
+ background: white;
630
+ border-radius: 20px;
631
+ padding: 1.5rem;
632
+ border: 1px solid rgba(226, 232, 240, 0.5);
633
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
634
+ transition: all 0.3s ease;
635
+ position: relative;
636
+ overflow: hidden;
637
+ }
638
+
639
+ .stat-card-modern:hover {
640
+ transform: translateY(-4px);
641
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.15), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
642
+ }
643
+
644
+ .stat-icon-wrapper {
645
+ display: flex;
646
+ justify-content: space-between;
647
+ align-items: flex-start;
648
+ margin-bottom: 1rem;
649
+ }
650
+
651
+ .stat-icon {
652
+ width: 60px;
653
+ height: 60px;
654
+ border-radius: 12px;
655
+ display: flex;
656
+ align-items: center;
657
+ justify-content: center;
658
+ color: white;
659
+ font-size: 1.5rem;
660
+ flex-shrink: 0;
661
+ }
662
+
663
+ .stat-trend {
664
+ display: flex;
665
+ flex-direction: column;
666
+ align-items: end;
667
+ gap: 0.25rem;
668
+ font-size: 0.875rem;
669
+ font-weight: 600;
670
+ color: #10b981;
671
+ }
672
+
673
+ .stat-gradient-1 .stat-icon { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); }
674
+ .stat-gradient-2 .stat-icon { background: linear-gradient(135deg, #10b981 0%, #059669 100%); }
675
+ .stat-gradient-3 .stat-icon { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }
676
+ .stat-gradient-4 .stat-icon { background: linear-gradient(135deg, #ef4444 0%, #dc2626 100%); }
677
+
678
+ .stat-content {
679
+ display: flex;
680
+ flex-direction: column;
681
+ gap: 0.25rem;
682
+ }
683
+
684
+ .stat-card-modern .stat-number {
685
+ font-size: 2.5rem;
686
+ font-weight: 700;
687
+ color: #1e293b;
688
+ line-height: 1;
689
+ }
690
+
691
+ .stat-title {
692
+ font-size: 1rem;
693
+ font-weight: 600;
694
+ color: #374151;
695
+ }
696
+
697
+ .stat-description {
698
+ font-size: 0.875rem;
699
+ color: #6b7280;
700
+ }
701
+
702
+ /* 功能特性区域 */
703
+ .features-section {
704
+ margin-bottom: 4rem;
705
+ }
706
+
707
+ .section-header {
708
+ text-align: center;
709
+ margin-bottom: 3rem;
710
+ }
711
+
712
+ .section-icon {
713
+ width: 60px;
714
+ height: 60px;
715
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
716
+ border-radius: 16px;
717
+ display: flex;
718
+ align-items: center;
719
+ justify-content: center;
720
+ color: white;
721
+ font-size: 1.5rem;
722
+ margin: 0 auto 1rem;
723
+ box-shadow: 0 8px 16px rgba(102, 126, 234, 0.3);
724
+ }
725
+
726
+ .section-title {
727
+ font-size: 2rem;
728
+ font-weight: 700;
729
+ color: #1e293b;
730
+ margin-bottom: 0.5rem;
731
+ }
732
+
733
+ .section-subtitle {
734
+ color: #64748b;
735
+ font-size: 1.125rem;
736
+ max-width: 600px;
737
+ margin: 0 auto;
738
+ }
739
+
740
+ .features-grid {
741
+ display: grid;
742
+ grid-template-columns: repeat(auto-fit, minmax(350px, 1fr));
743
+ gap: 2rem;
744
+ }
745
+
746
+ .feature-card {
747
+ background: white;
748
+ border-radius: 20px;
749
+ padding: 2rem;
750
+ border: 1px solid rgba(226, 232, 240, 0.5);
751
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
752
+ transition: all 0.3s ease;
753
+ position: relative;
754
+ overflow: hidden;
755
+ display: flex;
756
+ flex-direction: column;
757
+ gap: 1.5rem;
758
+ }
759
+
760
+ .feature-card:hover {
761
+ transform: translateY(-4px);
762
+ box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.15), 0 10px 10px -5px rgba(0, 0, 0, 0.04);
763
+ }
764
+
765
+ .feature-icon-wrapper {
766
+ display: flex;
767
+ justify-content: space-between;
768
+ align-items: center;
769
+ position: relative;
770
+ }
771
+
772
+ .feature-icon {
773
+ width: 60px;
774
+ height: 60px;
775
+ border-radius: 12px;
776
+ display: flex;
777
+ align-items: center;
778
+ justify-content: center;
779
+ color: white;
780
+ font-size: 1.5rem;
781
+ position: relative;
782
+ z-index: 2;
783
+ }
784
+
785
+ .feature-1 { background: linear-gradient(135deg, #3b82f6 0%, #2563eb 100%); }
786
+ .feature-2 { background: linear-gradient(135deg, #10b981 0%, #059669 100%); }
787
+ .feature-3 { background: linear-gradient(135deg, #f59e0b 0%, #d97706 100%); }
788
+ .feature-4 { background: linear-gradient(135deg, #8b5cf6 0%, #7c3aed 100%); }
789
+
790
+ .feature-number {
791
+ font-size: 3rem;
792
+ font-weight: 700;
793
+ color: rgba(102, 126, 234, 0.1);
794
+ position: absolute;
795
+ right: 0;
796
+ top: 50%;
797
+ transform: translateY(-50%);
798
+ z-index: 1;
799
+ }
800
+
801
+ .feature-content {
802
+ flex-grow: 1;
803
+ display: flex;
804
+ flex-direction: column;
805
+ gap: 0.75rem;
806
+ }
807
+
808
+ .feature-title {
809
+ font-size: 1.25rem;
810
+ font-weight: 600;
811
+ color: #1e293b;
812
+ margin-bottom: 0;
813
+ }
814
+
815
+ .feature-description {
816
+ color: #64748b;
817
+ line-height: 1.6;
818
+ flex-grow: 1;
819
+ }
820
+
821
+ .feature-tags {
822
+ display: flex;
823
+ gap: 0.5rem;
824
+ flex-wrap: wrap;
825
+ }
826
+
827
+ .feature-tag {
828
+ background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
829
+ color: #64748b;
830
+ padding: 0.25rem 0.75rem;
831
+ border-radius: 20px;
832
+ font-size: 0.75rem;
833
+ font-weight: 500;
834
+ }
835
+
836
+ .feature-action {
837
+ display: flex;
838
+ justify-content: end;
839
+ }
840
+
841
+ .feature-link {
842
+ width: 40px;
843
+ height: 40px;
844
+ border-radius: 10px;
845
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
846
+ color: white;
847
+ display: flex;
848
+ align-items: center;
849
+ justify-content: center;
850
+ text-decoration: none;
851
+ transition: all 0.3s ease;
852
+ }
853
+
854
+ .feature-link:hover {
855
+ transform: scale(1.1) rotate(45deg);
856
+ }
857
+
858
+ /* 快速访问区域 */
859
+ .quick-access-section {
860
+ margin-bottom: 4rem;
861
+ }
862
+
863
+ .quick-access-grid {
864
+ display: grid;
865
+ grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
866
+ gap: 1.5rem;
867
+ margin-bottom: 2rem;
868
+ }
869
+
870
+ .quick-access-card {
871
+ background: white;
872
+ border-radius: 16px;
873
+ padding: 1.5rem;
874
+ border: 1px solid rgba(226, 232, 240, 0.5);
875
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.07), 0 2px 4px rgba(0, 0, 0, 0.05);
876
+ transition: all 0.3s ease;
877
+ display: flex;
878
+ flex-direction: column;
879
+ gap: 1rem;
880
+ }
881
+
882
+ .quick-access-card:hover {
883
+ transform: translateY(-4px);
884
+ box-shadow: 0 8px 16px rgba(0, 0, 0, 0.12), 0 4px 8px rgba(0, 0, 0, 0.08);
885
+ }
886
+
887
+ .quick-access-header {
888
+ display: flex;
889
+ justify-content: space-between;
890
+ align-items: center;
891
+ }
892
+
893
+ .db-avatar {
894
+ width: 50px;
895
+ height: 50px;
896
+ border-radius: 12px;
897
+ display: flex;
898
+ align-items: center;
899
+ justify-content: center;
900
+ color: white;
901
+ font-size: 1.25rem;
902
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
903
+ }
904
+
905
+ .connection-status {
906
+ width: 10px;
907
+ height: 10px;
908
+ border-radius: 50%;
909
+ position: relative;
910
+ }
911
+
912
+ .status-online {
913
+ background: #10b981;
914
+ box-shadow: 0 0 8px rgba(16, 185, 129, 0.5);
915
+ }
916
+
917
+ .status-offline {
918
+ background: #6b7280;
919
+ }
920
+
921
+ .status-dot {
922
+ width: 100%;
923
+ height: 100%;
924
+ border-radius: 50%;
925
+ background: currentColor;
926
+ animation: pulse 2s ease-in-out infinite;
927
+ }
928
+
929
+ .quick-access-body {
930
+ flex-grow: 1;
931
+ }
932
+
933
+ .connection-name {
934
+ font-size: 1.1rem;
935
+ font-weight: 600;
936
+ color: #1e293b;
937
+ margin-bottom: 0.5rem;
938
+ }
939
+
940
+ .connection-info {
941
+ display: flex;
942
+ flex-direction: column;
943
+ gap: 0.25rem;
944
+ font-size: 0.875rem;
945
+ color: #64748b;
946
+ }
947
+
948
+ .db-type {
949
+ font-weight: 600;
950
+ color: #667eea;
951
+ }
952
+
953
+ .connection-host {
954
+ color: #94a3b8;
955
+ }
956
+
957
+ .quick-access-footer {
958
+ margin-top: auto;
959
+ }
960
+
961
+ .quick-access-btn {
962
+ width: 100%;
963
+ padding: 0.75rem;
964
+ background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
965
+ border: 1px solid #e2e8f0;
966
+ border-radius: 10px;
967
+ color: #667eea;
968
+ font-weight: 500;
969
+ cursor: pointer;
970
+ transition: all 0.3s ease;
971
+ display: flex;
972
+ align-items: center;
973
+ justify-content: center;
974
+ gap: 0.5rem;
975
+ }
976
+
977
+ .quick-access-btn:hover {
978
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
979
+ color: white;
980
+ transform: translateY(-1px);
981
+ }
982
+
983
+ .view-all-connections {
984
+ text-align: center;
985
+ }
986
+
987
+ .view-all-btn {
988
+ display: inline-flex;
989
+ align-items: center;
990
+ gap: 0.75rem;
991
+ padding: 1rem 2rem;
992
+ background: white;
993
+ border: 2px solid #e2e8f0;
994
+ border-radius: 12px;
995
+ color: #64748b;
996
+ text-decoration: none;
997
+ font-weight: 600;
998
+ transition: all 0.3s ease;
999
+ }
1000
+
1001
+ .view-all-btn:hover {
1002
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
1003
+ color: white;
1004
+ border-color: transparent;
1005
+ transform: translateY(-2px);
1006
+ }
1007
+
1008
+ /* 空状态 */
1009
+ .empty-state-section {
1010
+ text-align: center;
1011
+ padding: 4rem 2rem;
1012
+ }
1013
+
1014
+ .empty-state-content {
1015
+ max-width: 600px;
1016
+ margin: 0 auto;
1017
+ }
1018
+
1019
+ .empty-illustration {
1020
+ position: relative;
1021
+ margin-bottom: 2rem;
1022
+ height: 200px;
1023
+ display: flex;
1024
+ justify-content: center;
1025
+ align-items: center;
1026
+ }
1027
+
1028
+ .empty-icon-wrapper {
1029
+ width: 100px;
1030
+ height: 100px;
1031
+ background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%);
1032
+ border-radius: 20px;
1033
+ display: flex;
1034
+ align-items: center;
1035
+ justify-content: center;
1036
+ color: #94a3b8;
1037
+ font-size: 3rem;
1038
+ position: relative;
1039
+ z-index: 2;
1040
+ }
1041
+
1042
+ .empty-shapes {
1043
+ position: absolute;
1044
+ width: 100%;
1045
+ height: 100%;
1046
+ }
1047
+
1048
+ .empty-shape {
1049
+ position: absolute;
1050
+ background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(118, 75, 162, 0.05));
1051
+ border-radius: 50%;
1052
+ animation: float 6s ease-in-out infinite;
1053
+ }
1054
+
1055
+ .shape-a {
1056
+ width: 60px;
1057
+ height: 60px;
1058
+ top: 20px;
1059
+ left: 20px;
1060
+ animation-delay: 0s;
1061
+ }
1062
+
1063
+ .shape-b {
1064
+ width: 40px;
1065
+ height: 40px;
1066
+ bottom: 30px;
1067
+ right: 30px;
1068
+ animation-delay: 2s;
1069
+ }
1070
+
1071
+ .shape-c {
1072
+ width: 30px;
1073
+ height: 30px;
1074
+ top: 50%;
1075
+ left: 70%;
1076
+ animation-delay: 4s;
1077
+ }
1078
+
1079
+ .empty-title {
1080
+ font-size: 2rem;
1081
+ font-weight: 700;
1082
+ color: #1e293b;
1083
+ margin-bottom: 1rem;
1084
+ }
1085
+
1086
+ .empty-description {
1087
+ font-size: 1.125rem;
1088
+ color: #64748b;
1089
+ line-height: 1.6;
1090
+ margin-bottom: 2rem;
1091
+ }
1092
+
1093
+ /* 数据库类型颜色 */
1094
+ .db-mysql { background: linear-gradient(135deg, #00758f 0%, #005a70 100%); }
1095
+ .db-postgres { background: linear-gradient(135deg, #336791 0%, #2a5278 100%); }
1096
+ .db-sqlite { background: linear-gradient(135deg, #003b57 0%, #002d42 100%); }
1097
+ .db-mssql { background: linear-gradient(135deg, #cc2927 0%, #a62220 100%); }
1098
+ .db-oracle { background: linear-gradient(135deg, #f80000 0%, #d40000 100%); }
1099
+ .db-default { background: linear-gradient(135deg, #64748b 0%, #475569 100%); }
1100
+
1101
+ /* 响应式设计 */
1102
+ @media (max-width: 1024px) {
1103
+ .features-grid {
1104
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
1105
+ }
1106
+
1107
+ .quick-access-grid {
1108
+ grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
1109
+ }
1110
+ }
1111
+
1112
+ @media (max-width: 768px) {
1113
+ .database-dashboard-modern {
1114
+ padding: 1rem 0;
1115
+ }
1116
+
1117
+ .hero-content {
1118
+ padding: 2rem;
1119
+ }
1120
+
1121
+ .hero-title {
1122
+ font-size: 2rem;
1123
+ }
1124
+
1125
+ .hero-description {
1126
+ font-size: 1rem;
1127
+ }
1128
+
1129
+ .hero-actions {
1130
+ flex-direction: column;
1131
+ align-items: stretch;
1132
+ }
1133
+
1134
+ .hero-stats {
1135
+ gap: 2rem;
1136
+ justify-content: center;
1137
+ }
1138
+
1139
+ .hero-visual {
1140
+ height: 300px;
1141
+ margin-top: 2rem;
1142
+ }
1143
+
1144
+ .database-illustration {
1145
+ width: 200px;
1146
+ height: 200px;
1147
+ }
1148
+
1149
+ .db-layer {
1150
+ font-size: 3rem;
1151
+ }
1152
+
1153
+ .section-title {
1154
+ font-size: 1.5rem;
1155
+ }
1156
+
1157
+ .section-subtitle {
1158
+ font-size: 1rem;
1159
+ }
1160
+
1161
+ .features-grid {
1162
+ grid-template-columns: 1fr;
1163
+ }
1164
+
1165
+ .quick-access-grid {
1166
+ grid-template-columns: 1fr;
1167
+ }
1168
+ }
1169
+
1170
+ @media (max-width: 480px) {
1171
+ .hero-content {
1172
+ padding: 1.5rem;
1173
+ }
1174
+
1175
+ .hero-title {
1176
+ font-size: 1.75rem;
1177
+ }
1178
+
1179
+ .stat-card-modern .stat-number {
1180
+ font-size: 2rem;
1181
+ }
1182
+
1183
+ .feature-card {
1184
+ padding: 1.5rem;
1185
+ }
1186
+ }
1187
+
1188
+ /* 动画效果 */
1189
+ @keyframes pulse {
1190
+ 0%, 100% { opacity: 1; }
1191
+ 50% { opacity: 0.5; }
1192
+ }
1193
+ </style>