fdb2 1.0.7 → 1.0.9

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 (234) hide show
  1. package/.dockerignore +21 -21
  2. package/.editorconfig +11 -11
  3. package/.eslintrc.cjs +14 -14
  4. package/.eslintrc.json +7 -7
  5. package/.prettierrc.js +3 -3
  6. package/.tpl.env +21 -21
  7. package/.vscodeignore +45 -45
  8. package/README.md +312 -312
  9. package/bin/build.sh +28 -28
  10. package/bin/deploy.sh +8 -8
  11. package/bin/dev.sh +10 -10
  12. package/bin/docker/dev-docker-compose.yml +43 -43
  13. package/bin/docker/dev.Dockerfile +24 -24
  14. package/bin/docker/prod-docker-compose.yml +17 -17
  15. package/bin/docker/prod.Dockerfile +29 -29
  16. package/bin/fdb2.js +220 -220
  17. package/dist/package.json +29 -29
  18. package/dist/pnpm-lock.yaml +1042 -354
  19. package/dist/public/explorer.css +1464 -1437
  20. package/dist/public/explorer.js +764 -226
  21. package/dist/public/index.css +1026 -1026
  22. package/dist/public/index.js +15 -9
  23. package/dist/public/layout.css +221 -221
  24. package/dist/public/layout.js +1 -1
  25. package/dist/public/vue.js +8 -2
  26. package/dist/scripts/preinstall.js +112 -112
  27. package/dist/server/index.d.ts.map +1 -1
  28. package/dist/server/index.js +8 -0
  29. package/dist/server/index.js.map +1 -1
  30. package/dist/server/index.ts +680 -671
  31. package/dist/server/model/connection.entity.ts +65 -65
  32. package/dist/server/model/database.entity.ts +245 -245
  33. package/dist/server/service/connection.service.d.ts +6 -1
  34. package/dist/server/service/connection.service.d.ts.map +1 -1
  35. package/dist/server/service/connection.service.js +15 -0
  36. package/dist/server/service/connection.service.js.map +1 -1
  37. package/dist/server/service/connection.service.ts +356 -341
  38. package/dist/server/service/database/base.service.d.ts +27 -0
  39. package/dist/server/service/database/base.service.d.ts.map +1 -1
  40. package/dist/server/service/database/base.service.js +17 -0
  41. package/dist/server/service/database/base.service.js.map +1 -1
  42. package/dist/server/service/database/base.service.ts +406 -367
  43. package/dist/server/service/database/cockroachdb.service.d.ts +16 -0
  44. package/dist/server/service/database/cockroachdb.service.d.ts.map +1 -1
  45. package/dist/server/service/database/cockroachdb.service.js +220 -154
  46. package/dist/server/service/database/cockroachdb.service.js.map +1 -1
  47. package/dist/server/service/database/cockroachdb.service.ts +871 -782
  48. package/dist/server/service/database/database.service.d.ts +4 -0
  49. package/dist/server/service/database/database.service.d.ts.map +1 -1
  50. package/dist/server/service/database/database.service.js +123 -0
  51. package/dist/server/service/database/database.service.js.map +1 -1
  52. package/dist/server/service/database/database.service.ts +775 -638
  53. package/dist/server/service/database/index.ts +6 -6
  54. package/dist/server/service/database/mongodb.service.d.ts +16 -0
  55. package/dist/server/service/database/mongodb.service.d.ts.map +1 -1
  56. package/dist/server/service/database/mongodb.service.js +35 -0
  57. package/dist/server/service/database/mongodb.service.js.map +1 -1
  58. package/dist/server/service/database/mongodb.service.ts +39 -1
  59. package/dist/server/service/database/mssql.service.d.ts +16 -0
  60. package/dist/server/service/database/mssql.service.d.ts.map +1 -1
  61. package/dist/server/service/database/mssql.service.js +168 -96
  62. package/dist/server/service/database/mssql.service.js.map +1 -1
  63. package/dist/server/service/database/mssql.service.ts +931 -840
  64. package/dist/server/service/database/mysql.service.d.ts +16 -0
  65. package/dist/server/service/database/mysql.service.d.ts.map +1 -1
  66. package/dist/server/service/database/mysql.service.js +189 -80
  67. package/dist/server/service/database/mysql.service.js.map +1 -1
  68. package/dist/server/service/database/mysql.service.ts +1025 -890
  69. package/dist/server/service/database/oracle.service.d.ts +16 -0
  70. package/dist/server/service/database/oracle.service.d.ts.map +1 -1
  71. package/dist/server/service/database/oracle.service.js +182 -120
  72. package/dist/server/service/database/oracle.service.js.map +1 -1
  73. package/dist/server/service/database/oracle.service.ts +1035 -959
  74. package/dist/server/service/database/postgres.service.d.ts +16 -0
  75. package/dist/server/service/database/postgres.service.d.ts.map +1 -1
  76. package/dist/server/service/database/postgres.service.js +154 -88
  77. package/dist/server/service/database/postgres.service.js.map +1 -1
  78. package/dist/server/service/database/postgres.service.ts +960 -871
  79. package/dist/server/service/database/sap.service.d.ts +16 -0
  80. package/dist/server/service/database/sap.service.d.ts.map +1 -1
  81. package/dist/server/service/database/sap.service.js +66 -0
  82. package/dist/server/service/database/sap.service.js.map +1 -1
  83. package/dist/server/service/database/sap.service.ts +89 -0
  84. package/dist/server/service/database/sqlite.service.d.ts +16 -0
  85. package/dist/server/service/database/sqlite.service.d.ts.map +1 -1
  86. package/dist/server/service/database/sqlite.service.js +77 -18
  87. package/dist/server/service/database/sqlite.service.js.map +1 -1
  88. package/dist/server/service/database/sqlite.service.ts +787 -708
  89. package/dist/server/service/session.service.ts +158 -158
  90. package/dist/view/index.html +38 -38
  91. package/env.d.ts +1 -1
  92. package/package.json +1 -1
  93. package/packages/vscode/.vscodeignore +44 -44
  94. package/packages/vscode/README.md +62 -62
  95. package/packages/vscode/out/database-services/cockroachdb.service.js +154 -154
  96. package/packages/vscode/out/database-services/mssql.service.js +96 -96
  97. package/packages/vscode/out/database-services/mysql.service.js +80 -80
  98. package/packages/vscode/out/database-services/oracle.service.js +120 -120
  99. package/packages/vscode/out/database-services/postgres.service.js +88 -88
  100. package/packages/vscode/out/database-services/sqlite.service.js +18 -18
  101. package/packages/vscode/out/provider/WebViewProvider.js +32 -32
  102. package/packages/vscode/package.json +142 -142
  103. package/packages/vscode/resources/icon.svg +5 -5
  104. package/packages/vscode/resources/webview/connection.css +41 -41
  105. package/packages/vscode/resources/webview/database.css +163 -163
  106. package/packages/vscode/resources/webview/index.html +9 -9
  107. package/packages/vscode/resources/webview/modules/header.tpl +13 -13
  108. package/packages/vscode/resources/webview/modules/initial_state.tpl +54 -54
  109. package/packages/vscode/resources/webview/query.css +104 -104
  110. package/packages/vscode/src/database-services/base.service.ts +362 -362
  111. package/packages/vscode/src/database-services/cockroachdb.service.ts +659 -659
  112. package/packages/vscode/src/database-services/connection.service.ts +340 -340
  113. package/packages/vscode/src/database-services/database.service.ts +629 -629
  114. package/packages/vscode/src/database-services/index.ts +6 -6
  115. package/packages/vscode/src/database-services/model/connection.entity.ts +65 -65
  116. package/packages/vscode/src/database-services/model/database.entity.ts +245 -245
  117. package/packages/vscode/src/database-services/mssql.service.ts +722 -722
  118. package/packages/vscode/src/database-services/mysql.service.ts +760 -760
  119. package/packages/vscode/src/database-services/oracle.service.ts +831 -831
  120. package/packages/vscode/src/database-services/postgres.service.ts +740 -740
  121. package/packages/vscode/src/database-services/sqlite.service.ts +558 -558
  122. package/packages/vscode/src/extension.ts +76 -76
  123. package/packages/vscode/src/provider/DatabaseTreeProvider.ts +167 -167
  124. package/packages/vscode/src/provider/WebViewProvider.ts +277 -277
  125. package/packages/vscode/src/service/DatabaseServiceBridge.ts +414 -414
  126. package/packages/vscode/src/typings/connection.ts +90 -90
  127. package/packages/vscode/tsconfig.json +21 -21
  128. package/public/index.html +9 -9
  129. package/public/modules/header.tpl +13 -13
  130. package/public/modules/initial_state.tpl +54 -54
  131. package/scripts/preinstall.js +112 -112
  132. package/server/index.ts +680 -671
  133. package/server/model/connection.entity.ts +65 -65
  134. package/server/model/database.entity.ts +245 -245
  135. package/server/service/connection.service.ts +356 -341
  136. package/server/service/database/base.service.ts +406 -367
  137. package/server/service/database/cockroachdb.service.ts +871 -782
  138. package/server/service/database/database.service.ts +775 -638
  139. package/server/service/database/index.ts +6 -6
  140. package/server/service/database/mongodb.service.ts +39 -1
  141. package/server/service/database/mssql.service.ts +931 -840
  142. package/server/service/database/mysql.service.ts +1025 -890
  143. package/server/service/database/oracle.service.ts +1035 -959
  144. package/server/service/database/postgres.service.ts +960 -871
  145. package/server/service/database/sap.service.ts +89 -0
  146. package/server/service/database/sqlite.service.ts +787 -708
  147. package/server/service/session.service.ts +158 -158
  148. package/server/tsconfig.json +20 -20
  149. package/server.js +149 -149
  150. package/server.pid +1 -0
  151. package/src/adapter/ajax.ts +135 -135
  152. package/src/assets/base.css +1 -1
  153. package/src/assets/database.css +949 -949
  154. package/src/assets/images/svg/illustrations/illustration-1.svg +1 -1
  155. package/src/assets/images/svg/illustrations/illustration-2.svg +2 -2
  156. package/src/assets/images/svg/illustrations/illustration-3.svg +50 -50
  157. package/src/assets/images/svg/illustrations/illustration-4.svg +1 -1
  158. package/src/assets/images/svg/illustrations/illustration-5.svg +73 -73
  159. package/src/assets/images/svg/illustrations/illustration-6.svg +89 -89
  160. package/src/assets/images/svg/illustrations/illustration-7.svg +39 -39
  161. package/src/assets/images/svg/separators/curve-2.svg +3 -3
  162. package/src/assets/images/svg/separators/curve.svg +3 -3
  163. package/src/assets/images/svg/separators/line.svg +3 -3
  164. package/src/assets/logo.svg +73 -73
  165. package/src/assets/main.css +1 -1
  166. package/src/base/config.ts +20 -20
  167. package/src/base/detect.ts +134 -134
  168. package/src/base/entity.ts +92 -92
  169. package/src/base/eventBus.ts +36 -36
  170. package/src/components/connection-editor/index.vue +588 -588
  171. package/src/components/dataGrid/index.vue +104 -104
  172. package/src/components/dataGrid/pagination.vue +105 -105
  173. package/src/components/loading/index.vue +42 -42
  174. package/src/components/modal/index.ts +180 -180
  175. package/src/components/modal/index.vue +560 -560
  176. package/src/components/toast/index.ts +43 -43
  177. package/src/components/toast/toast.vue +57 -57
  178. package/src/components/user/name.vue +103 -103
  179. package/src/components/user/selector.vue +416 -416
  180. package/src/domain/SysConfig.ts +74 -74
  181. package/src/platform/App.vue +7 -7
  182. package/src/platform/database/components/connection-detail.vue +1153 -1154
  183. package/src/platform/database/components/data-editor.vue +477 -477
  184. package/src/platform/database/components/database-detail.vue +1173 -1172
  185. package/src/platform/database/components/database-monitor.vue +1085 -1085
  186. package/src/platform/database/components/db-tools.vue +1264 -816
  187. package/src/platform/database/components/query-history.vue +1348 -1348
  188. package/src/platform/database/components/sql-executor.vue +737 -737
  189. package/src/platform/database/components/sql-query-editor.vue +1045 -1045
  190. package/src/platform/database/components/table-detail.vue +1375 -1376
  191. package/src/platform/database/components/table-editor.vue +916 -916
  192. package/src/platform/database/explorer.vue +1839 -1839
  193. package/src/platform/database/index.vue +1192 -1192
  194. package/src/platform/database/layout.vue +366 -366
  195. package/src/platform/database/router.ts +36 -36
  196. package/src/platform/database/styles/common.scss +601 -601
  197. package/src/platform/database/types/common.ts +444 -444
  198. package/src/platform/database/utils/export.ts +231 -231
  199. package/src/platform/database/utils/helpers.ts +436 -436
  200. package/src/platform/index.ts +32 -32
  201. package/src/platform/router.ts +40 -40
  202. package/src/platform/vscode/bridge.ts +121 -121
  203. package/src/platform/vscode/components/ConnectionPanel.vue +272 -272
  204. package/src/platform/vscode/components/DatabasePanel.vue +532 -532
  205. package/src/platform/vscode/components/QueryPanel.vue +371 -371
  206. package/src/platform/vscode/entry/connection.ts +13 -13
  207. package/src/platform/vscode/entry/database.ts +13 -13
  208. package/src/platform/vscode/entry/query.ts +13 -13
  209. package/src/platform/vscode/index.ts +5 -5
  210. package/src/service/base.ts +133 -127
  211. package/src/service/database.ts +505 -495
  212. package/src/service/login.ts +120 -120
  213. package/src/shims-vue.d.ts +6 -6
  214. package/src/stores/connection.ts +266 -266
  215. package/src/stores/session.ts +87 -87
  216. package/src/typings/database-types.ts +412 -412
  217. package/src/typings/database.ts +363 -363
  218. package/src/typings/global.d.ts +58 -58
  219. package/src/typings/pinia.d.ts +7 -7
  220. package/src/utils/clipboard.ts +29 -29
  221. package/src/utils/database-types.ts +242 -242
  222. package/src/utils/modal.ts +123 -123
  223. package/src/utils/request.ts +55 -55
  224. package/src/utils/sleep.ts +3 -3
  225. package/src/utils/toast.ts +73 -73
  226. package/src/utils/util.ts +171 -171
  227. package/src/utils/xlsx.ts +228 -228
  228. package/tsconfig.json +33 -33
  229. package/view/index.html +9 -9
  230. package/view/modules/header.tpl +13 -13
  231. package/view/modules/initial_state.tpl +19 -19
  232. package/vite.config.ts +424 -424
  233. package/vite.config.vscode.ts +47 -47
  234. package/server/backups/db_ai_breakout_2026-03-11T08-38-48-677Z.sql +0 -0
@@ -1,1193 +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
- }
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
1193
  </style>