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,589 +1,589 @@
1
- <template>
2
- <!-- 统一modal组件 - 添加/编辑连接 -->
3
- <Modal
4
- ref="connectionModal"
5
- :title="editingConnection ? '编辑数据库连接' : '新增数据库连接'"
6
- :closeButton="{ text: '取消', show: true }"
7
- :confirmButton="{ text: '', show: false }"
8
- :isFullScreen="true"
9
- :style="{ maxWidth: '800px', width: '100%' }"
10
- @onClose="handleModalClose"
11
- >
12
- <form @submit.prevent="saveConnection" class="connection-form-modern">
13
- <!-- 基本信息 -->
14
- <div class="form-section">
15
- <div class="section-header">
16
- <div class="section-icon">
17
- <i class="bi bi-info-circle"></i>
18
- </div>
19
- <h3 class="section-title">基本信息</h3>
20
- </div>
21
- <div class="section-content">
22
- <div class="form-grid">
23
- <div class="form-group-modern">
24
- <label class="form-label-modern">
25
- <i class="bi bi-tag me-2"></i>连接名称 <span class="required">*</span>
26
- </label>
27
- <input type="text" class="form-control-modern" v-model="connectionForm.name"
28
- placeholder="为连接起一个易记的名称" required>
29
- </div>
30
- <div class="form-group-modern">
31
- <label class="form-label-modern">
32
- <i class="bi bi-diagram-3 me-2"></i>数据库类型 <span class="required">*</span>
33
- </label>
34
- <select class="form-select-modern" v-model="connectionForm.type" @change="onTypeChange" required>
35
- <option value="">请选择数据库类型</option>
36
- <option v-for="dbType in databaseTypes" :key="dbType.value" :value="dbType.value">
37
- {{ dbType.label }}
38
- </option>
39
- </select>
40
- </div>
41
- </div>
42
- </div>
43
- </div>
44
-
45
- <!-- 连接配置 -->
46
- <div class="form-section" v-if="connectionForm.type !== 'sqlite'">
47
- <div class="section-header">
48
- <div class="section-icon">
49
- <i class="bi bi-hdd-network"></i>
50
- </div>
51
- <h3 class="section-title">连接配置</h3>
52
- </div>
53
- <div class="section-content">
54
- <div class="form-grid">
55
- <div class="form-group-modern">
56
- <label class="form-label-modern">
57
- <i class="bi bi-server me-2"></i>主机地址 <span class="required">*</span>
58
- </label>
59
- <input type="text" class="form-control-modern" v-model="connectionForm.host"
60
- placeholder="数据库服务器地址" required>
61
- </div>
62
- <div class="form-group-modern">
63
- <label class="form-label-modern">
64
- <i class="bi bi-door-closed me-2"></i>端口 <span class="required">*</span>
65
- </label>
66
- <input type="number" class="form-control-modern" v-model.number="connectionForm.port"
67
- placeholder="数据库端口号" min="1" max="65535" required>
68
- </div>
69
- <div class="form-group-modern">
70
- <label class="form-label-modern">
71
- <i class="bi bi-database me-2"></i>数据库名 <span class="required">*</span>
72
- </label>
73
- <input type="text" class="form-control-modern" v-model="connectionForm.database"
74
- placeholder="要连接的数据库名" required>
75
- </div>
76
- <div class="form-group-modern">
77
- <label class="form-label-modern">
78
- <i class="bi bi-clock me-2"></i>连接超时
79
- </label>
80
- <input type="number" class="form-control-modern" v-model.number="connectionForm.options.timeout"
81
- placeholder="连接超时时间(秒)" min="1">
82
- </div>
83
- </div>
84
- </div>
85
- </div>
86
-
87
- <!-- SQLite配置 -->
88
- <div class="form-section" v-if="connectionForm.type === 'sqlite'">
89
- <div class="section-header">
90
- <div class="section-icon">
91
- <i class="bi bi-file-earmark-text"></i>
92
- </div>
93
- <h3 class="section-title">SQLite配置</h3>
94
- </div>
95
- <div class="section-content">
96
- <div class="form-grid">
97
- <div class="form-group-modern">
98
- <label class="form-label-modern">
99
- <i class="bi bi-file-earmark me-2"></i>数据库文件 <span class="required">*</span>
100
- </label>
101
- <input type="text" class="form-control-modern" v-model="connectionForm.database"
102
- placeholder="SQLite数据库文件路径" required>
103
- </div>
104
- </div>
105
- </div>
106
- </div>
107
-
108
- <!-- 认证信息 -->
109
- <div class="form-section">
110
- <div class="section-header">
111
- <div class="section-icon">
112
- <i class="bi bi-shield-lock"></i>
113
- </div>
114
- <h3 class="section-title">认证信息</h3>
115
- </div>
116
- <div class="section-content">
117
- <div class="form-grid">
118
- <div class="form-group-modern">
119
- <label class="form-label-modern">
120
- <i class="bi bi-person me-2"></i>用户名
121
- </label>
122
- <input type="text" class="form-control-modern" v-model="connectionForm.username"
123
- placeholder="数据库用户名">
124
- </div>
125
- <div class="form-group-modern">
126
- <label class="form-label-modern">
127
- <i class="bi bi-key me-2"></i>密码
128
- </label>
129
- <input type="password" class="form-control-modern" v-model="connectionForm.password"
130
- placeholder="数据库密码">
131
- </div>
132
- </div>
133
- </div>
134
- </div>
135
-
136
- <!-- 高级选项 -->
137
- <div class="form-section">
138
- <div class="section-header">
139
- <div class="section-icon">
140
- <i class="bi bi-gear"></i>
141
- </div>
142
- <h3 class="section-title">其他选项</h3>
143
- </div>
144
- <div class="section-content">
145
- <div class="form-group-modern">
146
- <label class="form-label-modern">
147
- <i class="bi bi-toggle-on me-2"></i>连接状态
148
- </label>
149
- <div class="form-check-modern">
150
- <input class="form-check-input-modern" type="checkbox" v-model="connectionForm.enabled" id="enabled">
151
- <label class="form-check-label-modern" for="enabled">
152
- <span class="check-text">启用此连接</span>
153
- <span class="check-description">创建后将自动启用连接</span>
154
- </label>
155
- </div>
156
- </div>
157
- </div>
158
- </div>
159
- </form>
160
-
161
- <!-- 自定义footer -->
162
- <template #footer>
163
- <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
164
- <i class="bi bi-x-circle me-1"></i>取消
165
- </button>
166
- <button type="button" class="btn btn-outline-primary" @click="testConnection(connectionForm)">
167
- <i class="bi bi-wifi me-1"></i>测试连接
168
- </button>
169
- <button type="button" class="btn btn-primary" @click="saveConnection">
170
- <i class="bi bi-save me-1"></i>{{ editingConnection ? '更新配置' : '保存配置' }}
171
- </button>
172
- <button type="button" class="btn btn-success" @click="saveAndTestConnection">
173
- <i class="bi bi-check-circle me-1"></i>保存并测试
174
- </button>
175
- </template>
176
- </Modal>
177
-
178
- <!-- 错误提示模态框 -->
179
- <Modal
180
- ref="errorModal"
181
- title="错误提示"
182
- :closeButton="{ text: '确定', show: true }"
183
- :confirmButton="{ text: '', show: false }"
184
- :isFullScreen="false"
185
- :style="{ maxWidth: '400px!important', width: '100%' }"
186
- >
187
- <div class="error-content">
188
- <div class="error-icon">
189
- <i class="bi bi-exclamation-triangle"></i>
190
- </div>
191
- <div class="error-message">{{ errorMessage }}</div>
192
- </div>
193
- </Modal>
194
- </template>
195
-
196
- <script lang="ts" setup>
197
- import { ref, computed, watch } from 'vue';
198
- import { ConnectionService } from '@/service/database';
199
- import type { ConnectionEntity } from '@/typings/database';
200
- import Modal from '@/components/modal/index.vue';
201
- import Toast from '@/components/toast/toast.vue';
202
-
203
- // Props
204
- interface Props {
205
- modelValue?: boolean;
206
- connection?: ConnectionEntity | null;
207
- }
208
-
209
- const props = withDefaults(defineProps<Props>(), {
210
- modelValue: false,
211
- connection: null
212
- });
213
-
214
- // Emits
215
- const emit = defineEmits<{
216
- 'update:modelValue': [value: boolean];
217
- 'saved': [connection: ConnectionEntity];
218
- }>();
219
-
220
- // 组件实例
221
- const connectionModal = ref();
222
- const toastRef = ref();
223
- const errorModal = ref();
224
- const errorMessage = ref('');
225
-
226
- // 响应式数据
227
- const editingConnection = ref<ConnectionEntity | null>(null);
228
- const connectionForm = ref<ConnectionEntity>({
229
- id: '',
230
- name: '',
231
- type: '',
232
- host: 'localhost',
233
- port: 3306,
234
- database: '',
235
- username: '',
236
- password: '',
237
- options: {},
238
- enabled: true,
239
- createdAt: new Date(),
240
- updatedAt: new Date()
241
- });
242
- const databaseTypes = ref<any[]>([]);
243
-
244
- // 计算属性
245
- const enabledConnections = computed(() => connections.value.filter(conn => conn.enabled));
246
-
247
- // 验证连接配置(前端验证)
248
- function validateConnection(connection: ConnectionEntity): { isValid: boolean; message: string } {
249
- if (!connection.name?.trim()) {
250
- return { isValid: false, message: '连接名称不能为空' };
251
- }
252
-
253
- if (!connection.type?.trim()) {
254
- return { isValid: false, message: '数据库类型不能为空' };
255
- }
256
-
257
- if (connection.type !== 'sqlite') {
258
- if (!connection.host?.trim()) {
259
- return { isValid: false, message: '主机地址不能为空' };
260
- }
261
-
262
- if (!connection.port || connection.port <= 0 || connection.port > 65535) {
263
- return { isValid: false, message: '端口号必须在1-65535之间' };
264
- }
265
- }
266
-
267
- // 对于某些数据库类型,数据库名是必需的
268
- if (['mysql', 'postgres', 'mssql'].includes(connection.type) && !connection.database?.trim()) {
269
- return { isValid: false, message: `${connection.type.toUpperCase()} 数据库名不能为空` };
270
- }
271
-
272
- // SQLite需要数据库文件路径
273
- if (connection.type === 'sqlite' && !connection.database?.trim()) {
274
- return { isValid: false, message: 'SQLite数据库文件路径不能为空' };
275
- }
276
-
277
- return { isValid: true, message: '配置验证通过' };
278
- }
279
-
280
- // 显示模态框
281
- function show() {
282
- connectionModal.value?.show();
283
- }
284
-
285
- // 隐藏模态框
286
- function hide() {
287
- connectionModal.value?.hide();
288
- }
289
-
290
- // 显示添加模态框
291
- function showAddModal() {
292
- editingConnection.value = null;
293
- connectionForm.value = {
294
- id: '',
295
- name: '',
296
- type: '',
297
- host: 'localhost',
298
- port: 3306,
299
- database: '',
300
- username: '',
301
- password: '',
302
- options: {},
303
- enabled: true,
304
- createdAt: new Date(),
305
- updatedAt: new Date()
306
- };
307
- show();
308
- }
309
-
310
- // 显示编辑模态框
311
- function showEditModal(connection: ConnectionEntity) {
312
- editingConnection.value = connection;
313
- connectionForm.value = { ...connection };
314
- show();
315
- }
316
-
317
- // 保存连接配置(不测试连接)
318
- async function saveConnection(closeModal = true) {
319
- try {
320
- // 先进行前端验证
321
- const validation = validateConnection(connectionForm.value);
322
- if (!validation.isValid) {
323
- errorMessage.value = validation.message;
324
- errorModal.value?.show();
325
- return;
326
- }
327
-
328
- const connectionService = new ConnectionService();
329
- if (editingConnection.value) {
330
- await connectionService.updateConnection(editingConnection.value.id!, connectionForm.value);
331
- } else {
332
- await connectionService.addConnection(connectionForm.value);
333
- }
334
-
335
- if (closeModal) {
336
- hide();
337
- }
338
- emit('saved', connectionForm.value);
339
- showToast('', editingConnection.value ? '连接配置更新成功' : '连接配置添加成功');
340
- } catch (error) {
341
- console.error('保存连接配置失败:', error);
342
- let errorMsg = '保存配置失败';
343
- if (error.message) {
344
- if (error.message.includes('连接') && error.message.includes('失败')) {
345
- errorMsg = '配置保存失败,请检查服务器状态';
346
- } else {
347
- errorMsg = `保存配置失败: ${error.message}`;
348
- }
349
- }
350
- errorMessage.value = errorMsg;
351
- errorModal.value?.show();
352
- }
353
- }
354
-
355
- // 保存并测试连接
356
- async function saveAndTestConnection() {
357
- try {
358
- // 先保存配置(不关闭模态框)
359
- await saveConnection(false);
360
-
361
- // 然后测试连接
362
- await testConnection(connectionForm.value);
363
-
364
- // 测试成功后关闭模态框
365
- hide();
366
- emit('saved', connectionForm.value);
367
- showToast('', editingConnection.value ? '连接配置更新并测试成功' : '连接配置添加并测试成功');
368
- } catch (error) {
369
- console.error('保存并测试连接失败:', error);
370
- // 如果是保存失败,错误已经在 saveConnection 中处理了
371
- // 如果是测试失败,显示警告
372
- if (error.message && error.message.includes('连接测试失败')) {
373
- showToast('警告', '配置已保存,但连接测试失败', 'warning');
374
- hide();
375
- emit('saved', connectionForm.value);
376
- }
377
- }
378
- }
379
-
380
- // 测试连接
381
- async function testConnection(connection: ConnectionEntity) {
382
- try {
383
- const connectionService = new ConnectionService();
384
- const response = await connectionService.testConnection(connection);
385
-
386
- if (response) {
387
- showToast('', `"${connection.name}" 连接测试成功`, 'success');
388
- } else {
389
- showToast('', `"${connection.name}" 连接测试失败`, 'error');
390
- }
391
- } catch (error) {
392
- console.error('测试连接失败:', error);
393
- showToast('', `"${connection.name}" 连接测试失败: ${error.message || '未知错误'}`, 'error');
394
- }
395
- }
396
-
397
- // 处理模态框关闭
398
- function handleModalClose() {
399
- hide();
400
- }
401
-
402
- // 数据库类型改变
403
- function onTypeChange() {
404
- const selectedType = databaseTypes.value.find(t => t.value === connectionForm.value.type);
405
- if (selectedType?.defaultPort) {
406
- connectionForm.value.port = selectedType.defaultPort;
407
- }
408
- }
409
-
410
- // Toast 提示
411
- function showToast(title: string, message: string, type?: string) {
412
- toastRef.value?.show(title, message, type);
413
- }
414
-
415
- // 加载数据库类型
416
- async function loadDatabaseTypes() {
417
- try {
418
- const connectionService = new ConnectionService();
419
- const response = await connectionService.getDatabaseTypes();
420
- databaseTypes.value = response?.data || [];
421
- } catch (error) {
422
- console.error('加载数据库类型失败:', error);
423
- }
424
- }
425
-
426
- // 监听 props 变化
427
- watch(() => props.modelValue, (newValue) => {
428
- if (newValue) {
429
- show();
430
- } else {
431
- hide();
432
- }
433
- });
434
-
435
- watch(() => props.connection, (newConnection) => {
436
- if (newConnection) {
437
- showEditModal(newConnection);
438
- }
439
- }, { immediate: true });
440
-
441
- // 暴露方法给父组件
442
- defineExpose({
443
- showAddModal,
444
- showEditModal,
445
- show,
446
- hide
447
- });
448
-
449
- // 生命周期
450
- loadDatabaseTypes();
451
- </script>
452
-
453
- <style scoped>
454
- /* 继承原有的样式,这里可以添加组件特定的样式 */
455
- .connection-form-modern {
456
- padding: 0;
457
- }
458
-
459
- .form-section {
460
- margin-bottom: 2rem;
461
- }
462
-
463
- .section-header {
464
- display: flex;
465
- align-items: center;
466
- margin-bottom: 1.5rem;
467
- padding-bottom: 0.75rem;
468
- border-bottom: 2px solid #f0f0f0;
469
- }
470
-
471
- .section-icon {
472
- width: 40px;
473
- height: 40px;
474
- border-radius: 8px;
475
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
476
- display: flex;
477
- align-items: center;
478
- justify-content: center;
479
- color: white;
480
- margin-right: 1rem;
481
- }
482
-
483
- .section-title {
484
- font-size: 1.1rem;
485
- font-weight: 600;
486
- margin: 0;
487
- color: #333;
488
- }
489
-
490
- .section-content {
491
- padding-left: 56px;
492
- }
493
-
494
- .form-grid {
495
- display: grid;
496
- grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
497
- gap: 1.5rem;
498
- }
499
-
500
- .form-group-modern {
501
- margin-bottom: 1rem;
502
- }
503
-
504
- .form-label-modern {
505
- display: flex;
506
- align-items: center;
507
- font-weight: 500;
508
- color: #555;
509
- margin-bottom: 0.5rem;
510
- font-size: 0.9rem;
511
- }
512
-
513
- .form-control-modern,
514
- .form-select-modern {
515
- width: 100%;
516
- padding: 0.75rem;
517
- border: 2px solid #e9ecef;
518
- border-radius: 8px;
519
- font-size: 0.95rem;
520
- transition: all 0.3s ease;
521
- }
522
-
523
- .form-control-modern:focus,
524
- .form-select-modern:focus {
525
- outline: none;
526
- border-color: #667eea;
527
- box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
528
- }
529
-
530
- .form-check-modern {
531
- display: flex;
532
- align-items: flex-start;
533
- padding: 0;
534
- }
535
-
536
- .form-check-input-modern {
537
- margin-right: 0.75rem;
538
- margin-top: 0.25rem;
539
- }
540
-
541
- .form-check-label-modern {
542
- display: flex;
543
- flex-direction: column;
544
- cursor: pointer;
545
- }
546
-
547
- .check-text {
548
- font-weight: 500;
549
- color: #333;
550
- margin-bottom: 0.25rem;
551
- }
552
-
553
- .check-description {
554
- font-size: 0.85rem;
555
- color: #666;
556
- }
557
-
558
- .required {
559
- color: #dc3545;
560
- margin-left: 0.25rem;
561
- }
562
-
563
- .error-content {
564
- display: flex;
565
- align-items: center;
566
- padding: 1.5rem;
567
- gap: 1rem;
568
- }
569
-
570
- .error-icon {
571
- width: 48px;
572
- height: 48px;
573
- border-radius: 50%;
574
- background: linear-gradient(135deg, #dc3545 0%, #ff6b6b 100%);
575
- display: flex;
576
- align-items: center;
577
- justify-content: center;
578
- color: white;
579
- font-size: 1.5rem;
580
- flex-shrink: 0;
581
- }
582
-
583
- .error-message {
584
- flex: 1;
585
- color: #333;
586
- font-size: 0.95rem;
587
- line-height: 1.6;
588
- }
1
+ <template>
2
+ <!-- 统一modal组件 - 添加/编辑连接 -->
3
+ <Modal
4
+ ref="connectionModal"
5
+ :title="editingConnection ? '编辑数据库连接' : '新增数据库连接'"
6
+ :closeButton="{ text: '取消', show: true }"
7
+ :confirmButton="{ text: '', show: false }"
8
+ :isFullScreen="true"
9
+ :style="{ maxWidth: '800px', width: '100%' }"
10
+ @onClose="handleModalClose"
11
+ >
12
+ <form @submit.prevent="saveConnection" class="connection-form-modern">
13
+ <!-- 基本信息 -->
14
+ <div class="form-section">
15
+ <div class="section-header">
16
+ <div class="section-icon">
17
+ <i class="bi bi-info-circle"></i>
18
+ </div>
19
+ <h3 class="section-title">基本信息</h3>
20
+ </div>
21
+ <div class="section-content">
22
+ <div class="form-grid">
23
+ <div class="form-group-modern">
24
+ <label class="form-label-modern">
25
+ <i class="bi bi-tag me-2"></i>连接名称 <span class="required">*</span>
26
+ </label>
27
+ <input type="text" class="form-control-modern" v-model="connectionForm.name"
28
+ placeholder="为连接起一个易记的名称" required>
29
+ </div>
30
+ <div class="form-group-modern">
31
+ <label class="form-label-modern">
32
+ <i class="bi bi-diagram-3 me-2"></i>数据库类型 <span class="required">*</span>
33
+ </label>
34
+ <select class="form-select-modern" v-model="connectionForm.type" @change="onTypeChange" required>
35
+ <option value="">请选择数据库类型</option>
36
+ <option v-for="dbType in databaseTypes" :key="dbType.value" :value="dbType.value">
37
+ {{ dbType.label }}
38
+ </option>
39
+ </select>
40
+ </div>
41
+ </div>
42
+ </div>
43
+ </div>
44
+
45
+ <!-- 连接配置 -->
46
+ <div class="form-section" v-if="connectionForm.type !== 'sqlite'">
47
+ <div class="section-header">
48
+ <div class="section-icon">
49
+ <i class="bi bi-hdd-network"></i>
50
+ </div>
51
+ <h3 class="section-title">连接配置</h3>
52
+ </div>
53
+ <div class="section-content">
54
+ <div class="form-grid">
55
+ <div class="form-group-modern">
56
+ <label class="form-label-modern">
57
+ <i class="bi bi-server me-2"></i>主机地址 <span class="required">*</span>
58
+ </label>
59
+ <input type="text" class="form-control-modern" v-model="connectionForm.host"
60
+ placeholder="数据库服务器地址" required>
61
+ </div>
62
+ <div class="form-group-modern">
63
+ <label class="form-label-modern">
64
+ <i class="bi bi-door-closed me-2"></i>端口 <span class="required">*</span>
65
+ </label>
66
+ <input type="number" class="form-control-modern" v-model.number="connectionForm.port"
67
+ placeholder="数据库端口号" min="1" max="65535" required>
68
+ </div>
69
+ <div class="form-group-modern">
70
+ <label class="form-label-modern">
71
+ <i class="bi bi-database me-2"></i>数据库名 <span class="required">*</span>
72
+ </label>
73
+ <input type="text" class="form-control-modern" v-model="connectionForm.database"
74
+ placeholder="要连接的数据库名" required>
75
+ </div>
76
+ <div class="form-group-modern">
77
+ <label class="form-label-modern">
78
+ <i class="bi bi-clock me-2"></i>连接超时
79
+ </label>
80
+ <input type="number" class="form-control-modern" v-model.number="connectionForm.options.timeout"
81
+ placeholder="连接超时时间(秒)" min="1">
82
+ </div>
83
+ </div>
84
+ </div>
85
+ </div>
86
+
87
+ <!-- SQLite配置 -->
88
+ <div class="form-section" v-if="connectionForm.type === 'sqlite'">
89
+ <div class="section-header">
90
+ <div class="section-icon">
91
+ <i class="bi bi-file-earmark-text"></i>
92
+ </div>
93
+ <h3 class="section-title">SQLite配置</h3>
94
+ </div>
95
+ <div class="section-content">
96
+ <div class="form-grid">
97
+ <div class="form-group-modern">
98
+ <label class="form-label-modern">
99
+ <i class="bi bi-file-earmark me-2"></i>数据库文件 <span class="required">*</span>
100
+ </label>
101
+ <input type="text" class="form-control-modern" v-model="connectionForm.database"
102
+ placeholder="SQLite数据库文件路径" required>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ </div>
107
+
108
+ <!-- 认证信息 -->
109
+ <div class="form-section">
110
+ <div class="section-header">
111
+ <div class="section-icon">
112
+ <i class="bi bi-shield-lock"></i>
113
+ </div>
114
+ <h3 class="section-title">认证信息</h3>
115
+ </div>
116
+ <div class="section-content">
117
+ <div class="form-grid">
118
+ <div class="form-group-modern">
119
+ <label class="form-label-modern">
120
+ <i class="bi bi-person me-2"></i>用户名
121
+ </label>
122
+ <input type="text" class="form-control-modern" v-model="connectionForm.username"
123
+ placeholder="数据库用户名">
124
+ </div>
125
+ <div class="form-group-modern">
126
+ <label class="form-label-modern">
127
+ <i class="bi bi-key me-2"></i>密码
128
+ </label>
129
+ <input type="password" class="form-control-modern" v-model="connectionForm.password"
130
+ placeholder="数据库密码">
131
+ </div>
132
+ </div>
133
+ </div>
134
+ </div>
135
+
136
+ <!-- 高级选项 -->
137
+ <div class="form-section">
138
+ <div class="section-header">
139
+ <div class="section-icon">
140
+ <i class="bi bi-gear"></i>
141
+ </div>
142
+ <h3 class="section-title">其他选项</h3>
143
+ </div>
144
+ <div class="section-content">
145
+ <div class="form-group-modern">
146
+ <label class="form-label-modern">
147
+ <i class="bi bi-toggle-on me-2"></i>连接状态
148
+ </label>
149
+ <div class="form-check-modern">
150
+ <input class="form-check-input-modern" type="checkbox" v-model="connectionForm.enabled" id="enabled">
151
+ <label class="form-check-label-modern" for="enabled">
152
+ <span class="check-text">启用此连接</span>
153
+ <span class="check-description">创建后将自动启用连接</span>
154
+ </label>
155
+ </div>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </form>
160
+
161
+ <!-- 自定义footer -->
162
+ <template #footer>
163
+ <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">
164
+ <i class="bi bi-x-circle me-1"></i>取消
165
+ </button>
166
+ <button type="button" class="btn btn-outline-primary" @click="testConnection(connectionForm)">
167
+ <i class="bi bi-wifi me-1"></i>测试连接
168
+ </button>
169
+ <button type="button" class="btn btn-primary" @click="saveConnection">
170
+ <i class="bi bi-save me-1"></i>{{ editingConnection ? '更新配置' : '保存配置' }}
171
+ </button>
172
+ <button type="button" class="btn btn-success" @click="saveAndTestConnection">
173
+ <i class="bi bi-check-circle me-1"></i>保存并测试
174
+ </button>
175
+ </template>
176
+ </Modal>
177
+
178
+ <!-- 错误提示模态框 -->
179
+ <Modal
180
+ ref="errorModal"
181
+ title="错误提示"
182
+ :closeButton="{ text: '确定', show: true }"
183
+ :confirmButton="{ text: '', show: false }"
184
+ :isFullScreen="false"
185
+ :style="{ maxWidth: '400px!important', width: '100%' }"
186
+ >
187
+ <div class="error-content">
188
+ <div class="error-icon">
189
+ <i class="bi bi-exclamation-triangle"></i>
190
+ </div>
191
+ <div class="error-message">{{ errorMessage }}</div>
192
+ </div>
193
+ </Modal>
194
+ </template>
195
+
196
+ <script lang="ts" setup>
197
+ import { ref, computed, watch } from 'vue';
198
+ import { ConnectionService } from '@/service/database';
199
+ import type { ConnectionEntity } from '@/typings/database';
200
+ import Modal from '@/components/modal/index.vue';
201
+ import Toast from '@/components/toast/toast.vue';
202
+
203
+ // Props
204
+ interface Props {
205
+ modelValue?: boolean;
206
+ connection?: ConnectionEntity | null;
207
+ }
208
+
209
+ const props = withDefaults(defineProps<Props>(), {
210
+ modelValue: false,
211
+ connection: null
212
+ });
213
+
214
+ // Emits
215
+ const emit = defineEmits<{
216
+ 'update:modelValue': [value: boolean];
217
+ 'saved': [connection: ConnectionEntity];
218
+ }>();
219
+
220
+ // 组件实例
221
+ const connectionModal = ref();
222
+ const toastRef = ref();
223
+ const errorModal = ref();
224
+ const errorMessage = ref('');
225
+
226
+ // 响应式数据
227
+ const editingConnection = ref<ConnectionEntity | null>(null);
228
+ const connectionForm = ref<ConnectionEntity>({
229
+ id: '',
230
+ name: '',
231
+ type: '',
232
+ host: 'localhost',
233
+ port: 3306,
234
+ database: '',
235
+ username: '',
236
+ password: '',
237
+ options: {},
238
+ enabled: true,
239
+ createdAt: new Date(),
240
+ updatedAt: new Date()
241
+ });
242
+ const databaseTypes = ref<any[]>([]);
243
+
244
+ // 计算属性
245
+ const enabledConnections = computed(() => connections.value.filter(conn => conn.enabled));
246
+
247
+ // 验证连接配置(前端验证)
248
+ function validateConnection(connection: ConnectionEntity): { isValid: boolean; message: string } {
249
+ if (!connection.name?.trim()) {
250
+ return { isValid: false, message: '连接名称不能为空' };
251
+ }
252
+
253
+ if (!connection.type?.trim()) {
254
+ return { isValid: false, message: '数据库类型不能为空' };
255
+ }
256
+
257
+ if (connection.type !== 'sqlite') {
258
+ if (!connection.host?.trim()) {
259
+ return { isValid: false, message: '主机地址不能为空' };
260
+ }
261
+
262
+ if (!connection.port || connection.port <= 0 || connection.port > 65535) {
263
+ return { isValid: false, message: '端口号必须在1-65535之间' };
264
+ }
265
+ }
266
+
267
+ // 对于某些数据库类型,数据库名是必需的
268
+ if (['mysql', 'postgres', 'mssql'].includes(connection.type) && !connection.database?.trim()) {
269
+ return { isValid: false, message: `${connection.type.toUpperCase()} 数据库名不能为空` };
270
+ }
271
+
272
+ // SQLite需要数据库文件路径
273
+ if (connection.type === 'sqlite' && !connection.database?.trim()) {
274
+ return { isValid: false, message: 'SQLite数据库文件路径不能为空' };
275
+ }
276
+
277
+ return { isValid: true, message: '配置验证通过' };
278
+ }
279
+
280
+ // 显示模态框
281
+ function show() {
282
+ connectionModal.value?.show();
283
+ }
284
+
285
+ // 隐藏模态框
286
+ function hide() {
287
+ connectionModal.value?.hide();
288
+ }
289
+
290
+ // 显示添加模态框
291
+ function showAddModal() {
292
+ editingConnection.value = null;
293
+ connectionForm.value = {
294
+ id: '',
295
+ name: '',
296
+ type: '',
297
+ host: 'localhost',
298
+ port: 3306,
299
+ database: '',
300
+ username: '',
301
+ password: '',
302
+ options: {},
303
+ enabled: true,
304
+ createdAt: new Date(),
305
+ updatedAt: new Date()
306
+ };
307
+ show();
308
+ }
309
+
310
+ // 显示编辑模态框
311
+ function showEditModal(connection: ConnectionEntity) {
312
+ editingConnection.value = connection;
313
+ connectionForm.value = { ...connection };
314
+ show();
315
+ }
316
+
317
+ // 保存连接配置(不测试连接)
318
+ async function saveConnection(closeModal = true) {
319
+ try {
320
+ // 先进行前端验证
321
+ const validation = validateConnection(connectionForm.value);
322
+ if (!validation.isValid) {
323
+ errorMessage.value = validation.message;
324
+ errorModal.value?.show();
325
+ return;
326
+ }
327
+
328
+ const connectionService = new ConnectionService();
329
+ if (editingConnection.value) {
330
+ await connectionService.updateConnection(editingConnection.value.id!, connectionForm.value);
331
+ } else {
332
+ await connectionService.addConnection(connectionForm.value);
333
+ }
334
+
335
+ if (closeModal) {
336
+ hide();
337
+ }
338
+ emit('saved', connectionForm.value);
339
+ showToast('', editingConnection.value ? '连接配置更新成功' : '连接配置添加成功');
340
+ } catch (error) {
341
+ console.error('保存连接配置失败:', error);
342
+ let errorMsg = '保存配置失败';
343
+ if (error.message) {
344
+ if (error.message.includes('连接') && error.message.includes('失败')) {
345
+ errorMsg = '配置保存失败,请检查服务器状态';
346
+ } else {
347
+ errorMsg = `保存配置失败: ${error.message}`;
348
+ }
349
+ }
350
+ errorMessage.value = errorMsg;
351
+ errorModal.value?.show();
352
+ }
353
+ }
354
+
355
+ // 保存并测试连接
356
+ async function saveAndTestConnection() {
357
+ try {
358
+ // 先保存配置(不关闭模态框)
359
+ await saveConnection(false);
360
+
361
+ // 然后测试连接
362
+ await testConnection(connectionForm.value);
363
+
364
+ // 测试成功后关闭模态框
365
+ hide();
366
+ emit('saved', connectionForm.value);
367
+ showToast('', editingConnection.value ? '连接配置更新并测试成功' : '连接配置添加并测试成功');
368
+ } catch (error) {
369
+ console.error('保存并测试连接失败:', error);
370
+ // 如果是保存失败,错误已经在 saveConnection 中处理了
371
+ // 如果是测试失败,显示警告
372
+ if (error.message && error.message.includes('连接测试失败')) {
373
+ showToast('警告', '配置已保存,但连接测试失败', 'warning');
374
+ hide();
375
+ emit('saved', connectionForm.value);
376
+ }
377
+ }
378
+ }
379
+
380
+ // 测试连接
381
+ async function testConnection(connection: ConnectionEntity) {
382
+ try {
383
+ const connectionService = new ConnectionService();
384
+ const response = await connectionService.testConnection(connection);
385
+
386
+ if (response) {
387
+ showToast('', `"${connection.name}" 连接测试成功`, 'success');
388
+ } else {
389
+ showToast('', `"${connection.name}" 连接测试失败`, 'error');
390
+ }
391
+ } catch (error) {
392
+ console.error('测试连接失败:', error);
393
+ showToast('', `"${connection.name}" 连接测试失败: ${error.message || '未知错误'}`, 'error');
394
+ }
395
+ }
396
+
397
+ // 处理模态框关闭
398
+ function handleModalClose() {
399
+ hide();
400
+ }
401
+
402
+ // 数据库类型改变
403
+ function onTypeChange() {
404
+ const selectedType = databaseTypes.value.find(t => t.value === connectionForm.value.type);
405
+ if (selectedType?.defaultPort) {
406
+ connectionForm.value.port = selectedType.defaultPort;
407
+ }
408
+ }
409
+
410
+ // Toast 提示
411
+ function showToast(title: string, message: string, type?: string) {
412
+ toastRef.value?.show(title, message, type);
413
+ }
414
+
415
+ // 加载数据库类型
416
+ async function loadDatabaseTypes() {
417
+ try {
418
+ const connectionService = new ConnectionService();
419
+ const response = await connectionService.getDatabaseTypes();
420
+ databaseTypes.value = response?.data || [];
421
+ } catch (error) {
422
+ console.error('加载数据库类型失败:', error);
423
+ }
424
+ }
425
+
426
+ // 监听 props 变化
427
+ watch(() => props.modelValue, (newValue) => {
428
+ if (newValue) {
429
+ show();
430
+ } else {
431
+ hide();
432
+ }
433
+ });
434
+
435
+ watch(() => props.connection, (newConnection) => {
436
+ if (newConnection) {
437
+ showEditModal(newConnection);
438
+ }
439
+ }, { immediate: true });
440
+
441
+ // 暴露方法给父组件
442
+ defineExpose({
443
+ showAddModal,
444
+ showEditModal,
445
+ show,
446
+ hide
447
+ });
448
+
449
+ // 生命周期
450
+ loadDatabaseTypes();
451
+ </script>
452
+
453
+ <style scoped>
454
+ /* 继承原有的样式,这里可以添加组件特定的样式 */
455
+ .connection-form-modern {
456
+ padding: 0;
457
+ }
458
+
459
+ .form-section {
460
+ margin-bottom: 2rem;
461
+ }
462
+
463
+ .section-header {
464
+ display: flex;
465
+ align-items: center;
466
+ margin-bottom: 1.5rem;
467
+ padding-bottom: 0.75rem;
468
+ border-bottom: 2px solid #f0f0f0;
469
+ }
470
+
471
+ .section-icon {
472
+ width: 40px;
473
+ height: 40px;
474
+ border-radius: 8px;
475
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
476
+ display: flex;
477
+ align-items: center;
478
+ justify-content: center;
479
+ color: white;
480
+ margin-right: 1rem;
481
+ }
482
+
483
+ .section-title {
484
+ font-size: 1.1rem;
485
+ font-weight: 600;
486
+ margin: 0;
487
+ color: #333;
488
+ }
489
+
490
+ .section-content {
491
+ padding-left: 56px;
492
+ }
493
+
494
+ .form-grid {
495
+ display: grid;
496
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
497
+ gap: 1.5rem;
498
+ }
499
+
500
+ .form-group-modern {
501
+ margin-bottom: 1rem;
502
+ }
503
+
504
+ .form-label-modern {
505
+ display: flex;
506
+ align-items: center;
507
+ font-weight: 500;
508
+ color: #555;
509
+ margin-bottom: 0.5rem;
510
+ font-size: 0.9rem;
511
+ }
512
+
513
+ .form-control-modern,
514
+ .form-select-modern {
515
+ width: 100%;
516
+ padding: 0.75rem;
517
+ border: 2px solid #e9ecef;
518
+ border-radius: 8px;
519
+ font-size: 0.95rem;
520
+ transition: all 0.3s ease;
521
+ }
522
+
523
+ .form-control-modern:focus,
524
+ .form-select-modern:focus {
525
+ outline: none;
526
+ border-color: #667eea;
527
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
528
+ }
529
+
530
+ .form-check-modern {
531
+ display: flex;
532
+ align-items: flex-start;
533
+ padding: 0;
534
+ }
535
+
536
+ .form-check-input-modern {
537
+ margin-right: 0.75rem;
538
+ margin-top: 0.25rem;
539
+ }
540
+
541
+ .form-check-label-modern {
542
+ display: flex;
543
+ flex-direction: column;
544
+ cursor: pointer;
545
+ }
546
+
547
+ .check-text {
548
+ font-weight: 500;
549
+ color: #333;
550
+ margin-bottom: 0.25rem;
551
+ }
552
+
553
+ .check-description {
554
+ font-size: 0.85rem;
555
+ color: #666;
556
+ }
557
+
558
+ .required {
559
+ color: #dc3545;
560
+ margin-left: 0.25rem;
561
+ }
562
+
563
+ .error-content {
564
+ display: flex;
565
+ align-items: center;
566
+ padding: 1.5rem;
567
+ gap: 1rem;
568
+ }
569
+
570
+ .error-icon {
571
+ width: 48px;
572
+ height: 48px;
573
+ border-radius: 50%;
574
+ background: linear-gradient(135deg, #dc3545 0%, #ff6b6b 100%);
575
+ display: flex;
576
+ align-items: center;
577
+ justify-content: center;
578
+ color: white;
579
+ font-size: 1.5rem;
580
+ flex-shrink: 0;
581
+ }
582
+
583
+ .error-message {
584
+ flex: 1;
585
+ color: #333;
586
+ font-size: 0.95rem;
587
+ line-height: 1.6;
588
+ }
589
589
  </style>