fdb2 1.0.13 → 1.0.15

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 (313) hide show
  1. package/README.md +9 -2
  2. package/bin/docker/.env +4 -0
  3. package/package.json +26 -18
  4. package/public/favicon.ico +0 -0
  5. package/public/favicon.png +0 -0
  6. package/view/modules/header.tpl +1 -1
  7. package/.dockerignore +0 -21
  8. package/.editorconfig +0 -11
  9. package/.eslintrc.cjs +0 -14
  10. package/.eslintrc.json +0 -7
  11. package/.prettierrc.js +0 -3
  12. package/.tpl.env +0 -22
  13. package/.vscodeignore +0 -45
  14. package/dist/package.json +0 -115
  15. package/dist/pnpm-lock.yaml +0 -8135
  16. package/dist/public/.vite/manifest.json +0 -82
  17. package/dist/public/bootstrap-icons.woff +0 -0
  18. package/dist/public/bootstrap-icons.woff2 +0 -0
  19. package/dist/public/bootstrap.css +0 -14152
  20. package/dist/public/bootstrap.js +0 -5038
  21. package/dist/public/explorer.css +0 -2379
  22. package/dist/public/explorer.js +0 -50586
  23. package/dist/public/index.css +0 -1099
  24. package/dist/public/index.js +0 -12862
  25. package/dist/public/layout.css +0 -318
  26. package/dist/public/layout.js +0 -25
  27. package/dist/public/vue.css +0 -1
  28. package/dist/public/vue.js +0 -9110
  29. package/dist/scripts/preinstall.js +0 -112
  30. package/dist/server/index.d.ts +0 -2
  31. package/dist/server/index.d.ts.map +0 -1
  32. package/dist/server/index.js +0 -601
  33. package/dist/server/index.js.map +0 -1
  34. package/dist/server/index.ts +0 -681
  35. package/dist/server/model/connection.entity.d.ts +0 -55
  36. package/dist/server/model/connection.entity.d.ts.map +0 -1
  37. package/dist/server/model/connection.entity.js +0 -59
  38. package/dist/server/model/connection.entity.js.map +0 -1
  39. package/dist/server/model/connection.entity.ts +0 -66
  40. package/dist/server/model/database.entity.d.ts +0 -203
  41. package/dist/server/model/database.entity.d.ts.map +0 -1
  42. package/dist/server/model/database.entity.js +0 -211
  43. package/dist/server/model/database.entity.js.map +0 -1
  44. package/dist/server/model/database.entity.ts +0 -246
  45. package/dist/server/service/connection.service.d.ts +0 -84
  46. package/dist/server/service/connection.service.d.ts.map +0 -1
  47. package/dist/server/service/connection.service.js +0 -369
  48. package/dist/server/service/connection.service.js.map +0 -1
  49. package/dist/server/service/connection.service.ts +0 -359
  50. package/dist/server/service/database/base.service.d.ts +0 -183
  51. package/dist/server/service/database/base.service.d.ts.map +0 -1
  52. package/dist/server/service/database/base.service.js +0 -253
  53. package/dist/server/service/database/base.service.js.map +0 -1
  54. package/dist/server/service/database/base.service.ts +0 -407
  55. package/dist/server/service/database/cockroachdb.service.d.ts +0 -116
  56. package/dist/server/service/database/cockroachdb.service.d.ts.map +0 -1
  57. package/dist/server/service/database/cockroachdb.service.js +0 -812
  58. package/dist/server/service/database/cockroachdb.service.js.map +0 -1
  59. package/dist/server/service/database/cockroachdb.service.ts +0 -871
  60. package/dist/server/service/database/database.service.d.ts +0 -495
  61. package/dist/server/service/database/database.service.d.ts.map +0 -1
  62. package/dist/server/service/database/database.service.js +0 -711
  63. package/dist/server/service/database/database.service.js.map +0 -1
  64. package/dist/server/service/database/database.service.ts +0 -776
  65. package/dist/server/service/database/index.d.ts +0 -8
  66. package/dist/server/service/database/index.d.ts.map +0 -1
  67. package/dist/server/service/database/index.js +0 -18
  68. package/dist/server/service/database/index.js.map +0 -1
  69. package/dist/server/service/database/index.ts +0 -7
  70. package/dist/server/service/database/mongodb.service.d.ts +0 -121
  71. package/dist/server/service/database/mongodb.service.d.ts.map +0 -1
  72. package/dist/server/service/database/mongodb.service.js +0 -502
  73. package/dist/server/service/database/mongodb.service.js.map +0 -1
  74. package/dist/server/service/database/mongodb.service.ts +0 -501
  75. package/dist/server/service/database/mssql.service.d.ts +0 -118
  76. package/dist/server/service/database/mssql.service.d.ts.map +0 -1
  77. package/dist/server/service/database/mssql.service.js +0 -871
  78. package/dist/server/service/database/mssql.service.js.map +0 -1
  79. package/dist/server/service/database/mssql.service.ts +0 -932
  80. package/dist/server/service/database/mysql.service.d.ts +0 -114
  81. package/dist/server/service/database/mysql.service.d.ts.map +0 -1
  82. package/dist/server/service/database/mysql.service.js +0 -960
  83. package/dist/server/service/database/mysql.service.js.map +0 -1
  84. package/dist/server/service/database/mysql.service.ts +0 -1026
  85. package/dist/server/service/database/oracle.service.d.ts +0 -126
  86. package/dist/server/service/database/oracle.service.d.ts.map +0 -1
  87. package/dist/server/service/database/oracle.service.js +0 -963
  88. package/dist/server/service/database/oracle.service.js.map +0 -1
  89. package/dist/server/service/database/oracle.service.ts +0 -1036
  90. package/dist/server/service/database/postgres.service.d.ts +0 -122
  91. package/dist/server/service/database/postgres.service.d.ts.map +0 -1
  92. package/dist/server/service/database/postgres.service.js +0 -882
  93. package/dist/server/service/database/postgres.service.js.map +0 -1
  94. package/dist/server/service/database/postgres.service.ts +0 -961
  95. package/dist/server/service/database/sap.service.d.ts +0 -115
  96. package/dist/server/service/database/sap.service.d.ts.map +0 -1
  97. package/dist/server/service/database/sap.service.js +0 -868
  98. package/dist/server/service/database/sap.service.js.map +0 -1
  99. package/dist/server/service/database/sap.service.ts +0 -922
  100. package/dist/server/service/database/sqlite.service.d.ts +0 -112
  101. package/dist/server/service/database/sqlite.service.d.ts.map +0 -1
  102. package/dist/server/service/database/sqlite.service.js +0 -723
  103. package/dist/server/service/database/sqlite.service.js.map +0 -1
  104. package/dist/server/service/database/sqlite.service.ts +0 -787
  105. package/dist/server/service/session.service.ts +0 -158
  106. package/dist/view/index.html +0 -45
  107. package/env.d.ts +0 -1
  108. package/packages/vscode/.vscodeignore +0 -44
  109. package/packages/vscode/README.md +0 -62
  110. package/packages/vscode/out/database-services/base.service.js +0 -236
  111. package/packages/vscode/out/database-services/base.service.js.map +0 -1
  112. package/packages/vscode/out/database-services/cockroachdb.service.js +0 -634
  113. package/packages/vscode/out/database-services/cockroachdb.service.js.map +0 -1
  114. package/packages/vscode/out/database-services/connection.service.js +0 -346
  115. package/packages/vscode/out/database-services/connection.service.js.map +0 -1
  116. package/packages/vscode/out/database-services/database.service.js +0 -571
  117. package/packages/vscode/out/database-services/database.service.js.map +0 -1
  118. package/packages/vscode/out/database-services/index.js +0 -18
  119. package/packages/vscode/out/database-services/index.js.map +0 -1
  120. package/packages/vscode/out/database-services/model/connection.entity.js +0 -11
  121. package/packages/vscode/out/database-services/model/connection.entity.js.map +0 -1
  122. package/packages/vscode/out/database-services/model/database.entity.js +0 -35
  123. package/packages/vscode/out/database-services/model/database.entity.js.map +0 -1
  124. package/packages/vscode/out/database-services/mongodb.service.js +0 -458
  125. package/packages/vscode/out/database-services/mongodb.service.js.map +0 -1
  126. package/packages/vscode/out/database-services/mssql.service.js +0 -694
  127. package/packages/vscode/out/database-services/mssql.service.js.map +0 -1
  128. package/packages/vscode/out/database-services/mysql.service.js +0 -735
  129. package/packages/vscode/out/database-services/mysql.service.js.map +0 -1
  130. package/packages/vscode/out/database-services/oracle.service.js +0 -787
  131. package/packages/vscode/out/database-services/oracle.service.js.map +0 -1
  132. package/packages/vscode/out/database-services/postgres.service.js +0 -696
  133. package/packages/vscode/out/database-services/postgres.service.js.map +0 -1
  134. package/packages/vscode/out/database-services/sap.service.js +0 -695
  135. package/packages/vscode/out/database-services/sap.service.js.map +0 -1
  136. package/packages/vscode/out/database-services/sqlite.service.js +0 -532
  137. package/packages/vscode/out/database-services/sqlite.service.js.map +0 -1
  138. package/packages/vscode/out/extension.js +0 -93
  139. package/packages/vscode/out/extension.js.map +0 -1
  140. package/packages/vscode/out/provider/DatabaseTreeProvider.js +0 -159
  141. package/packages/vscode/out/provider/DatabaseTreeProvider.js.map +0 -1
  142. package/packages/vscode/out/provider/WebViewProvider.js +0 -259
  143. package/packages/vscode/out/provider/WebViewProvider.js.map +0 -1
  144. package/packages/vscode/out/service/ConnectionManager.js +0 -105
  145. package/packages/vscode/out/service/ConnectionManager.js.map +0 -1
  146. package/packages/vscode/out/service/DatabaseServiceBridge.js +0 -395
  147. package/packages/vscode/out/service/DatabaseServiceBridge.js.map +0 -1
  148. package/packages/vscode/out/typings/connection.js +0 -3
  149. package/packages/vscode/out/typings/connection.js.map +0 -1
  150. package/packages/vscode/package.json +0 -144
  151. package/packages/vscode/resources/icon.svg +0 -5
  152. package/packages/vscode/resources/webview/_plugin-vue_export-helper.js +0 -6529
  153. package/packages/vscode/resources/webview/_plugin-vue_export-helper.js.map +0 -1
  154. package/packages/vscode/resources/webview/connection.css +0 -69
  155. package/packages/vscode/resources/webview/connection.js +0 -228
  156. package/packages/vscode/resources/webview/connection.js.map +0 -1
  157. package/packages/vscode/resources/webview/database.css +0 -259
  158. package/packages/vscode/resources/webview/database.js +0 -275
  159. package/packages/vscode/resources/webview/database.js.map +0 -1
  160. package/packages/vscode/resources/webview/favicon.ico +0 -0
  161. package/packages/vscode/resources/webview/index.html +0 -9
  162. package/packages/vscode/resources/webview/modules/header.tpl +0 -14
  163. package/packages/vscode/resources/webview/modules/initial_state.tpl +0 -55
  164. package/packages/vscode/resources/webview/query.css +0 -162
  165. package/packages/vscode/resources/webview/query.js +0 -198
  166. package/packages/vscode/resources/webview/query.js.map +0 -1
  167. package/packages/vscode/src/database-services/base.service.js.map +0 -1
  168. package/packages/vscode/src/database-services/base.service.ts +0 -363
  169. package/packages/vscode/src/database-services/cockroachdb.service.js.map +0 -1
  170. package/packages/vscode/src/database-services/cockroachdb.service.ts +0 -659
  171. package/packages/vscode/src/database-services/connection.service.ts +0 -341
  172. package/packages/vscode/src/database-services/database.service.ts +0 -630
  173. package/packages/vscode/src/database-services/index.ts +0 -7
  174. package/packages/vscode/src/database-services/model/connection.entity.js +0 -11
  175. package/packages/vscode/src/database-services/model/connection.entity.js.map +0 -1
  176. package/packages/vscode/src/database-services/model/connection.entity.ts +0 -66
  177. package/packages/vscode/src/database-services/model/database.entity.js +0 -35
  178. package/packages/vscode/src/database-services/model/database.entity.js.map +0 -1
  179. package/packages/vscode/src/database-services/model/database.entity.ts +0 -246
  180. package/packages/vscode/src/database-services/mongodb.service.js.map +0 -1
  181. package/packages/vscode/src/database-services/mongodb.service.ts +0 -454
  182. package/packages/vscode/src/database-services/mssql.service.js.map +0 -1
  183. package/packages/vscode/src/database-services/mssql.service.ts +0 -723
  184. package/packages/vscode/src/database-services/mysql.service.js.map +0 -1
  185. package/packages/vscode/src/database-services/mysql.service.ts +0 -761
  186. package/packages/vscode/src/database-services/oracle.service.js.map +0 -1
  187. package/packages/vscode/src/database-services/oracle.service.ts +0 -832
  188. package/packages/vscode/src/database-services/postgres.service.js.map +0 -1
  189. package/packages/vscode/src/database-services/postgres.service.ts +0 -741
  190. package/packages/vscode/src/database-services/sap.service.js.map +0 -1
  191. package/packages/vscode/src/database-services/sap.service.ts +0 -713
  192. package/packages/vscode/src/database-services/sqlite.service.js.map +0 -1
  193. package/packages/vscode/src/database-services/sqlite.service.ts +0 -558
  194. package/packages/vscode/src/extension.ts +0 -76
  195. package/packages/vscode/src/provider/DatabaseTreeProvider.ts +0 -167
  196. package/packages/vscode/src/provider/WebViewProvider.ts +0 -277
  197. package/packages/vscode/src/service/DatabaseServiceBridge.ts +0 -414
  198. package/packages/vscode/src/typings/connection.ts +0 -90
  199. package/packages/vscode/tsconfig.json +0 -21
  200. package/scripts/preinstall.js +0 -112
  201. package/server/index.ts +0 -681
  202. package/server/model/connection.entity.js +0 -11
  203. package/server/model/connection.entity.js.map +0 -1
  204. package/server/model/connection.entity.ts +0 -66
  205. package/server/model/database.entity.js +0 -35
  206. package/server/model/database.entity.js.map +0 -1
  207. package/server/model/database.entity.ts +0 -246
  208. package/server/service/connection.service.ts +0 -359
  209. package/server/service/database/base.service.ts +0 -407
  210. package/server/service/database/cockroachdb.service.ts +0 -871
  211. package/server/service/database/database.service.ts +0 -776
  212. package/server/service/database/index.ts +0 -7
  213. package/server/service/database/mongodb.service.ts +0 -501
  214. package/server/service/database/mssql.service.ts +0 -932
  215. package/server/service/database/mysql.service.ts +0 -1026
  216. package/server/service/database/oracle.service.ts +0 -1036
  217. package/server/service/database/postgres.service.ts +0 -961
  218. package/server/service/database/sap.service.ts +0 -922
  219. package/server/service/database/sqlite.service.ts +0 -787
  220. package/server/service/session.service.ts +0 -158
  221. package/server/tsconfig.json +0 -20
  222. package/src/adapter/ajax.ts +0 -135
  223. package/src/assets/base.css +0 -1
  224. package/src/assets/database.css +0 -950
  225. package/src/assets/images/collapse.png +0 -0
  226. package/src/assets/images/no-login.png +0 -0
  227. package/src/assets/images/svg/illustrations/illustration-1.svg +0 -1
  228. package/src/assets/images/svg/illustrations/illustration-2.svg +0 -2
  229. package/src/assets/images/svg/illustrations/illustration-3.svg +0 -50
  230. package/src/assets/images/svg/illustrations/illustration-4.svg +0 -1
  231. package/src/assets/images/svg/illustrations/illustration-5.svg +0 -73
  232. package/src/assets/images/svg/illustrations/illustration-6.svg +0 -89
  233. package/src/assets/images/svg/illustrations/illustration-7.svg +0 -39
  234. package/src/assets/images/svg/illustrations/illustration-8.svg +0 -1
  235. package/src/assets/images/svg/separators/curve-2.svg +0 -3
  236. package/src/assets/images/svg/separators/curve.svg +0 -3
  237. package/src/assets/images/svg/separators/line.svg +0 -3
  238. package/src/assets/images/theme/light/screen-1-1000x800.jpg +0 -0
  239. package/src/assets/images/theme/light/screen-2-1000x800.jpg +0 -0
  240. package/src/assets/login/bg.jpg +0 -0
  241. package/src/assets/login/bg.png +0 -0
  242. package/src/assets/login/left.jpg +0 -0
  243. package/src/assets/logo.svg +0 -73
  244. package/src/assets/logo.webp +0 -0
  245. package/src/assets/main.css +0 -1
  246. package/src/base/config.ts +0 -20
  247. package/src/base/detect.ts +0 -134
  248. package/src/base/entity.ts +0 -92
  249. package/src/base/eventBus.ts +0 -37
  250. package/src/components/connection-editor/index.vue +0 -589
  251. package/src/components/dataGrid/index.vue +0 -163
  252. package/src/components/dataGrid/pagination.vue +0 -106
  253. package/src/components/loading/index.vue +0 -43
  254. package/src/components/modal/index.ts +0 -181
  255. package/src/components/modal/index.vue +0 -560
  256. package/src/components/toast/index.ts +0 -44
  257. package/src/components/toast/toast.vue +0 -58
  258. package/src/components/user/name.vue +0 -104
  259. package/src/components/user/selector.vue +0 -416
  260. package/src/domain/SysConfig.ts +0 -74
  261. package/src/platform/App.vue +0 -8
  262. package/src/platform/database/components/connection-detail.vue +0 -1153
  263. package/src/platform/database/components/data-editor.vue +0 -478
  264. package/src/platform/database/components/data-import-export.vue +0 -1602
  265. package/src/platform/database/components/database-detail.vue +0 -1199
  266. package/src/platform/database/components/database-monitor.vue +0 -1086
  267. package/src/platform/database/components/db-tools.vue +0 -1265
  268. package/src/platform/database/components/query-history.vue +0 -1349
  269. package/src/platform/database/components/sql-executor.vue +0 -738
  270. package/src/platform/database/components/sql-query-editor.vue +0 -1046
  271. package/src/platform/database/components/table-data-grid.vue +0 -273
  272. package/src/platform/database/components/table-detail.vue +0 -1173
  273. package/src/platform/database/components/table-editor.vue +0 -917
  274. package/src/platform/database/explorer.vue +0 -1840
  275. package/src/platform/database/index.vue +0 -1193
  276. package/src/platform/database/layout.vue +0 -367
  277. package/src/platform/database/router.ts +0 -37
  278. package/src/platform/database/styles/common.scss +0 -602
  279. package/src/platform/database/types/common.ts +0 -445
  280. package/src/platform/database/utils/export.ts +0 -232
  281. package/src/platform/database/utils/helpers.ts +0 -437
  282. package/src/platform/index.ts +0 -33
  283. package/src/platform/router.ts +0 -41
  284. package/src/platform/vscode/bridge.ts +0 -121
  285. package/src/platform/vscode/components/ConnectionPanel.vue +0 -272
  286. package/src/platform/vscode/components/DatabasePanel.vue +0 -532
  287. package/src/platform/vscode/components/QueryPanel.vue +0 -371
  288. package/src/platform/vscode/entry/connection.ts +0 -13
  289. package/src/platform/vscode/entry/database.ts +0 -13
  290. package/src/platform/vscode/entry/query.ts +0 -13
  291. package/src/platform/vscode/index.ts +0 -5
  292. package/src/service/base.ts +0 -134
  293. package/src/service/database.ts +0 -506
  294. package/src/service/login.ts +0 -121
  295. package/src/shims-vue.d.ts +0 -7
  296. package/src/stores/connection.ts +0 -266
  297. package/src/stores/session.ts +0 -87
  298. package/src/typings/database-types.ts +0 -413
  299. package/src/typings/database.ts +0 -364
  300. package/src/typings/global.d.ts +0 -58
  301. package/src/typings/pinia.d.ts +0 -8
  302. package/src/utils/clipboard.ts +0 -30
  303. package/src/utils/database-types.ts +0 -243
  304. package/src/utils/modal.ts +0 -124
  305. package/src/utils/request.ts +0 -55
  306. package/src/utils/sleep.ts +0 -4
  307. package/src/utils/toast.ts +0 -73
  308. package/src/utils/util.ts +0 -171
  309. package/src/utils/xlsx.ts +0 -228
  310. package/tsconfig.json +0 -33
  311. package/tsconfig.server.json +0 -19
  312. package/vite.config.ts +0 -424
  313. package/vite.config.vscode.ts +0 -47
@@ -1,1265 +0,0 @@
1
- <template>
2
- <div class="db-tools">
3
- <div class="tools-header">
4
- <h5 class="tools-title">
5
- <i class="bi bi-tools"></i>
6
- 数据库管理工具
7
- </h5>
8
- </div>
9
-
10
- <div class="tools-content">
11
- <!-- 数据备份 -->
12
- <div class="tool-section">
13
- <h6 class="section-title">
14
- <i class="bi bi-shield-check"></i>
15
- 数据备份
16
- </h6>
17
- <div class="tool-actions">
18
- <button class="btn btn-outline-primary btn-sm" @click="backupDatabase">
19
- <i class="bi bi-download"></i> 备份数据库
20
- </button>
21
- <button class="btn btn-outline-secondary btn-sm" @click="showRestoreModal">
22
- <i class="bi bi-upload"></i> 恢复数据库
23
- </button>
24
- <button class="btn btn-outline-info btn-sm" @click="showScheduleModal">
25
- <i class="bi bi-clock"></i> 定时备份
26
- </button>
27
- </div>
28
- </div>
29
-
30
- <!-- 用户管理 -->
31
- <div class="tool-section">
32
- <h6 class="section-title">
33
- <i class="bi bi-people"></i>
34
- 用户管理
35
- </h6>
36
- <div class="tool-actions">
37
- <button class="btn btn-outline-success btn-sm" @click="showUsersList">
38
- <i class="bi bi-person-lines-fill"></i> 用户列表
39
- </button>
40
- <button class="btn btn-outline-primary btn-sm" @click="showCreateUserModal">
41
- <i class="bi bi-person-plus"></i> 创建用户
42
- </button>
43
- <button class="btn btn-outline-warning btn-sm" @click="showPermissionsModal">
44
- <i class="bi bi-key"></i> 权限管理
45
- </button>
46
- </div>
47
- </div>
48
-
49
- <!-- 性能监控 -->
50
- <div class="tool-section">
51
- <h6 class="section-title">
52
- <i class="bi bi-speedometer2"></i>
53
- 性能监控
54
- </h6>
55
- <div class="tool-actions">
56
- <button class="btn btn-outline-info btn-sm" @click="showProcessList">
57
- <i class="bi bi-activity"></i> 进程列表
58
- </button>
59
- <button class="btn btn-outline-warning btn-sm" @click="showSlowQueries">
60
- <i class="bi bi-hourglass-split"></i> 慢查询
61
- </button>
62
- <button class="btn btn-outline-danger btn-sm" @click="showConnectionsList">
63
- <i class="bi bi-diagram-3"></i> 连接数
64
- </button>
65
- </div>
66
- </div>
67
-
68
- <!-- 数据库优化 -->
69
- <div class="tool-section">
70
- <h6 class="section-title">
71
- <i class="bi bi-gear-wide-connected"></i>
72
- 数据库优化
73
- </h6>
74
- <div class="tool-actions">
75
- <button class="btn btn-outline-success btn-sm" @click="optimizeDatabase">
76
- <i class="bi bi-lightning-charge"></i> 优化数据库
77
- </button>
78
- <button class="btn btn-outline-primary btn-sm" @click="analyzeTables">
79
- <i class="bi bi-search"></i> 分析表
80
- </button>
81
- <button class="btn btn-outline-secondary btn-sm" @click="repairTables">
82
- <i class="bi bi-tools"></i> 修复表
83
- </button>
84
- <button class="btn btn-outline-info btn-sm" @click="clearLogs">
85
- <i class="bi bi-trash"></i> 清理日志
86
- </button>
87
- </div>
88
- </div>
89
-
90
- <!-- 数据迁移 -->
91
- <div class="tool-section">
92
- <h6 class="section-title">
93
- <i class="bi bi-arrow-left-right"></i>
94
- 数据迁移
95
- </h6>
96
- <div class="tool-actions">
97
- <button class="btn btn-outline-primary btn-sm" @click="showExportModal">
98
- <i class="bi bi-box-arrow-up-right"></i> 导出结构
99
- </button>
100
- <button class="btn btn-outline-success btn-sm" @click="showImportModal">
101
- <i class="bi bi-box-arrow-in-down"></i> 导入数据
102
- </button>
103
- <button class="btn btn-outline-warning btn-sm" @click="selectTool('sync')">
104
- <i class="bi bi-arrow-repeat"></i> 数据同步
105
- </button>
106
- </div>
107
- </div>
108
-
109
- <!-- 健康检查 -->
110
- <div class="tool-section">
111
- <h6 class="section-title">
112
- <i class="bi bi-heart-pulse"></i>
113
- 健康检查
114
- </h6>
115
- <div class="tool-actions">
116
- <button class="btn btn-outline-info btn-sm" @click="runHealthCheck">
117
- <i class="bi bi-clipboard-check"></i> 健康检查
118
- </button>
119
- <button class="btn btn-outline-secondary btn-sm" @click="showStatistics">
120
- <i class="bi bi-bar-chart"></i> 数据统计
121
- </button>
122
- <button class="btn btn-outline-warning btn-sm" @click="showAuditLog">
123
- <i class="bi bi-journal-text"></i> 审计日志
124
- </button>
125
- </div>
126
- </div>
127
- </div>
128
-
129
- <!-- 工具组件展示区域 -->
130
- <div class="tool-component-area" v-if="selectedTool">
131
- <div class="component-header">
132
- <h6 class="component-title">
133
- <i :class="getToolIcon(selectedTool)"></i>
134
- {{ getToolTitle(selectedTool) }}
135
- </h6>
136
- <button class="btn btn-outline-secondary btn-sm" @click="closeTool">
137
- <i class="bi bi-x"></i> 关闭
138
- </button>
139
- </div>
140
-
141
- <!-- 数据同步组件 -->
142
- <div v-if="selectedTool === 'sync'" class="tool-component sync-component">
143
- <!-- 源数据库配置 -->
144
- <div class="mb-4">
145
- <h6 class="text-primary mb-2"><i class="bi bi-database"></i> 源数据库</h6>
146
- <div class="row g-3">
147
- <div class="col-md-6">
148
- <label class="form-label">数据库名称</label>
149
- <input type="text" class="form-control" v-model="syncConfig.source.database" readonly>
150
- </div>
151
- <div class="col-md-6">
152
- <label class="form-label">选择表</label>
153
- <select class="form-select" v-model="syncConfig.source.tableName">
154
- <option value="">请选择表</option>
155
- <option v-for="table in tables" :key="table.name" :value="table.name">{{ table.name }}</option>
156
- </select>
157
- </div>
158
- </div>
159
- </div>
160
-
161
- <!-- 目标数据库配置 -->
162
- <div class="mb-4">
163
- <h6 class="text-primary mb-2"><i class="bi bi-database"></i> 目标数据库</h6>
164
-
165
- <!-- 连接模式选择 -->
166
- <div class="mb-3">
167
- <div class="form-check form-switch">
168
- <input class="form-check-input" type="checkbox" v-model="useExistingConnection" id="useExistingConnection">
169
- <label class="form-check-label" for="useExistingConnection">使用已配置的数据库连接</label>
170
- </div>
171
- </div>
172
-
173
- <!-- 已配置连接选择 -->
174
- <div v-if="useExistingConnection" class="row g-3">
175
- <div class="col-md-6">
176
- <label class="form-label">选择数据库连接</label>
177
- <select class="form-select" v-model="selectedConnectionId">
178
- <option value="">请选择连接</option>
179
- <option v-for="conn in connections" :key="conn.id" :value="conn.id">{{ conn.name }} ({{ conn.type }})</option>
180
- </select>
181
- </div>
182
- <div class="col-md-6">
183
- <label class="form-label">选择数据库</label>
184
- <select class="form-select" v-model="selectedDatabaseName">
185
- <option value="">请选择数据库</option>
186
- <option v-for="db in databases" :key="db" :value="db">{{ db }}</option>
187
- </select>
188
- </div>
189
- <div class="col-md-12">
190
- <label class="form-label">目标表名</label>
191
- <input type="text" class="form-control" v-model="syncConfig.target.tableName">
192
- </div>
193
- </div>
194
-
195
- <!-- 手动配置 -->
196
- <div v-else class="row g-3">
197
- <div class="col-md-4">
198
- <label class="form-label">数据库类型</label>
199
- <select class="form-select" v-model="syncConfig.target.dbType">
200
- <option value="mysql">MySQL</option>
201
- <option value="postgresql">PostgreSQL</option>
202
- <option value="sqlite">SQLite</option>
203
- <option value="sqlserver">SQL Server</option>
204
- <option value="oracle">Oracle</option>
205
- </select>
206
- </div>
207
- <div class="col-md-4">
208
- <label class="form-label">主机</label>
209
- <input type="text" class="form-control" v-model="syncConfig.target.host">
210
- </div>
211
- <div class="col-md-4">
212
- <label class="form-label">端口</label>
213
- <input type="number" class="form-control" v-model="syncConfig.target.port">
214
- </div>
215
- <div class="col-md-4">
216
- <label class="form-label">数据库名</label>
217
- <input type="text" class="form-control" v-model="syncConfig.target.database">
218
- </div>
219
- <div class="col-md-4">
220
- <label class="form-label">用户名</label>
221
- <input type="text" class="form-control" v-model="syncConfig.target.username">
222
- </div>
223
- <div class="col-md-4">
224
- <label class="form-label">密码</label>
225
- <input type="password" class="form-control" v-model="syncConfig.target.password">
226
- </div>
227
- <div class="col-md-6">
228
- <label class="form-label">目标表名</label>
229
- <input type="text" class="form-control" v-model="syncConfig.target.tableName">
230
- </div>
231
- </div>
232
- </div>
233
-
234
- <!-- 同步选项 -->
235
- <div class="mb-4">
236
- <h6 class="text-primary mb-2"><i class="bi bi-sliders"></i> 同步选项</h6>
237
- <div class="row g-3">
238
- <div class="col-md-6">
239
- <div class="form-check">
240
- <input type="checkbox" class="form-check-input" v-model="syncConfig.options.syncStructure" id="syncStructure">
241
- <label class="form-check-label" for="syncStructure">同步表结构</label>
242
- </div>
243
- </div>
244
- <div class="col-md-6">
245
- <div class="form-check">
246
- <input type="checkbox" class="form-check-input" v-model="syncConfig.options.syncData" id="syncData">
247
- <label class="form-check-label" for="syncData">同步表数据</label>
248
- </div>
249
- </div>
250
- <div class="col-md-6">
251
- <div class="form-check">
252
- <input type="checkbox" class="form-check-input" v-model="syncConfig.options.dropIfExists" id="dropIfExists">
253
- <label class="form-check-label" for="dropIfExists">目标表存在时删除</label>
254
- </div>
255
- </div>
256
- <div class="col-md-6">
257
- <div class="form-check">
258
- <input type="checkbox" class="form-check-input" v-model="syncConfig.options.bulkInsert" id="bulkInsert">
259
- <label class="form-check-label" for="bulkInsert">批量插入数据</label>
260
- </div>
261
- </div>
262
- <div class="col-md-6">
263
- <div class="form-check">
264
- <input type="checkbox" class="form-check-input" v-model="syncConfig.options.overrideExisting" id="overrideExisting">
265
- <label class="form-check-label" for="overrideExisting">覆盖已存在的数据</label>
266
- </div>
267
- </div>
268
- </div>
269
- </div>
270
-
271
- <!-- 操作按钮 -->
272
- <div class="tool-actions">
273
- <button class="btn btn-primary btn-sm" @click="performSync" :disabled="syncing || !isSyncFormValid">
274
- <i class="bi bi-play-fill"></i> 开始同步
275
- </button>
276
- <button v-if="syncing" class="btn btn-outline-danger btn-sm" @click="stopSync">
277
- <i class="bi bi-stop-fill"></i> 停止同步
278
- </button>
279
- </div>
280
- </div>
281
- </div>
282
-
283
- <!-- 执行结果展示区域 -->
284
- <div class="execution-results">
285
- <div class="results-header">
286
- <h6 class="results-title">
287
- <i class="bi bi-terminal"></i>
288
- 执行结果
289
- </h6>
290
- <button class="btn btn-outline-secondary btn-sm" @click="clearResults">
291
- <i class="bi bi-trash"></i> 清空
292
- </button>
293
- </div>
294
- <div class="results-content" ref="resultsContentRef">
295
- <div v-if="executionResults.length === 0" class="no-results">
296
- <i class="bi bi-inbox"></i>
297
- <p>暂无执行结果</p>
298
- </div>
299
- <div v-for="(result, index) in executionResults" :key="index" class="result-item" :class="`result-${result.status}`">
300
- <div class="result-header" @click="toggleResult(index)">
301
- <div class="result-title">
302
- <i :class="getResultIcon(result.status)"></i>
303
- <span class="operation-name">{{ result.operation }}</span>
304
- <span class="operation-time">{{ result.timestamp }}</span>
305
- </div>
306
- <i class="bi bi-chevron-down toggle-icon" :class="{ 'expanded': result.expanded }"></i>
307
- </div>
308
- <div v-if="result.expanded" class="result-body">
309
- <pre><code v-html="highlightJson(result.data)"></code></pre>
310
- </div>
311
- </div>
312
- </div>
313
- </div>
314
-
315
- <!-- 数据恢复模态框 -->
316
- <div class="modal fade" :class="{ show: restoreModalVisible }" :style="{ display: restoreModalVisible ? 'block' : 'none', zIndex: 1055 }">
317
- <div class="modal-dialog">
318
- <div class="modal-content">
319
- <div class="modal-header">
320
- <h5 class="modal-title">恢复数据库</h5>
321
- <button type="button" class="btn-close" @click="closeRestoreModal"></button>
322
- </div>
323
- <div class="modal-body">
324
- <p>请选择要恢复的备份文件:</p>
325
- <div class="mb-3">
326
- <input type="file" class="form-control" @change="handleFileChange" accept=".sql,.bak">
327
- </div>
328
- <div v-if="selectedFile" class="alert alert-info">
329
- 已选择文件:{{ selectedFile.name }}
330
- </div>
331
- <div class="mb-3 form-check">
332
- <input type="checkbox" class="form-check-input" v-model="restoreOptions.dropExisting" id="dropExisting">
333
- <label class="form-check-label" for="dropExisting">删除现有表</label>
334
- </div>
335
- </div>
336
- <div class="modal-footer">
337
- <button type="button" class="btn btn-secondary" @click="closeRestoreModal">取消</button>
338
- <button type="button" class="btn btn-primary" @click="performRestore" :disabled="!selectedFile">
339
- <span v-if="restoring" class="spinner-border spinner-border-sm me-2"></span>
340
- 恢复
341
- </button>
342
- </div>
343
- </div>
344
- </div>
345
- </div>
346
-
347
-
348
- </div>
349
- </template>
350
-
351
- <script lang="ts" setup>
352
- import { ref, computed, watch, onMounted } from 'vue';
353
- import { DatabaseService, ConnectionService } from '@/service/database';
354
- import { modal } from '@/utils/modal';
355
- import { toast } from '@/utils/toast';
356
-
357
- const connectionService = new ConnectionService();
358
-
359
- const props = defineProps<{
360
- connection: any;
361
- database: string;
362
- }>();
363
-
364
- const emit = defineEmits<{
365
- 'execute-sql': [sql: string];
366
- }>();
367
-
368
- const databaseService = new DatabaseService();
369
-
370
- // 状态管理
371
- const restoreModalVisible = ref(false);
372
- const selectedFile = ref<File | null>(null);
373
- const restoring = ref(false);
374
- const resultsContentRef = ref<HTMLElement | null>(null);
375
-
376
- // 工具组件状态
377
- const selectedTool = ref<string | null>(null);
378
-
379
- // 同步功能状态
380
- const syncing = ref(false);
381
- const tables = ref<any[]>([]);
382
- const connections = ref<any[]>([]);
383
- const databases = ref<any[]>([]);
384
- const useExistingConnection = ref(false);
385
- const selectedConnectionId = ref('');
386
- const selectedDatabaseName = ref('');
387
-
388
- // 同步配置
389
- const syncConfig = ref({
390
- source: {
391
- database: '',
392
- tableName: ''
393
- },
394
- target: {
395
- dbType: 'mysql',
396
- host: 'localhost',
397
- port: 3306,
398
- database: '',
399
- username: 'root',
400
- password: '',
401
- tableName: ''
402
- },
403
- options: {
404
- syncStructure: true,
405
- syncData: true,
406
- dropIfExists: false,
407
- bulkInsert: true,
408
- overrideExisting: false
409
- }
410
- });
411
-
412
- // 监听源表名变化,自动更新目标表名
413
- watch(() => syncConfig.value.source.tableName, (newTableName) => {
414
- if (newTableName) {
415
- syncConfig.value.target.tableName = newTableName;
416
- }
417
- });
418
-
419
- // 组件挂载时初始化同步数据
420
- onMounted(() => {
421
- initSyncData();
422
- });
423
-
424
- // 监听连接ID变化,加载数据库列表
425
- async function loadDatabases(connectionId: string) {
426
- if (!connectionId) {
427
- databases.value = [];
428
- selectedDatabaseName.value = '';
429
- return;
430
- }
431
-
432
- try {
433
- const res = await databaseService.getDatabases(connectionId);
434
- if (res.ret === 0) {
435
- databases.value = res.data || [];
436
- } else {
437
- databases.value = [];
438
- }
439
- selectedDatabaseName.value = '';
440
- } catch (error) {
441
- console.error('加载数据库列表失败:', error);
442
- databases.value = [];
443
- selectedDatabaseName.value = '';
444
- }
445
- }
446
-
447
- // 监听连接ID变化
448
- watch(selectedConnectionId, (newVal) => {
449
- loadDatabases(newVal);
450
- });
451
-
452
- // 执行结果历史
453
- interface ExecutionResult {
454
- operation: string;
455
- status: 'success' | 'error' | 'info';
456
- timestamp: string;
457
- data: any;
458
- expanded: boolean;
459
- }
460
-
461
- const executionResults = ref<ExecutionResult[]>([]);
462
-
463
- const restoreOptions = ref({
464
- dropExisting: false
465
- });
466
-
467
- // 验证同步表单
468
- const isSyncFormValid = computed(() => {
469
- if (useExistingConnection.value) {
470
- return syncConfig.value.source.tableName &&
471
- selectedConnectionId.value &&
472
- selectedDatabaseName.value &&
473
- syncConfig.value.target.tableName &&
474
- (syncConfig.value.options.syncStructure || syncConfig.value.options.syncData);
475
- } else {
476
- return syncConfig.value.source.tableName &&
477
- syncConfig.value.target.host &&
478
- syncConfig.value.target.port &&
479
- syncConfig.value.target.database &&
480
- syncConfig.value.target.username &&
481
- syncConfig.value.target.tableName &&
482
- (syncConfig.value.options.syncStructure || syncConfig.value.options.syncData);
483
- }
484
- });
485
-
486
- // 添加执行结果
487
- function addExecutionResult(operation: string, status: 'success' | 'error' | 'info', data: any) {
488
- const timestamp = new Date().toLocaleString('zh-CN', {
489
- year: 'numeric',
490
- month: '2-digit',
491
- day: '2-digit',
492
- hour: '2-digit',
493
- minute: '2-digit',
494
- second: '2-digit'
495
- });
496
-
497
- executionResults.value.unshift({
498
- operation,
499
- status,
500
- timestamp,
501
- data,
502
- expanded: false
503
- });
504
-
505
- // 只保留最近50条记录
506
- if (executionResults.value.length > 50) {
507
- executionResults.value = executionResults.value.slice(0, 50);
508
- }
509
-
510
- // 自动滚动到底部(显示最新结果在顶部,所以滚动到0)
511
- setTimeout(() => {
512
- if (resultsContentRef.value) {
513
- resultsContentRef.value.scrollTop = 0;
514
- }
515
- }, 100);
516
- }
517
-
518
- // 清空执行结果
519
- function clearResults() {
520
- executionResults.value = [];
521
- }
522
-
523
- // 切换结果展开/收起
524
- function toggleResult(index: number) {
525
- const result = executionResults.value[index];
526
- if (result) {
527
- result.expanded = !result.expanded;
528
- }
529
- }
530
-
531
- // 格式化错误信息
532
- function formatError(error: any): any {
533
- const formatted: any = {
534
- success: false,
535
- message: error.msg || error.message || '未知错误'
536
- };
537
- if (error.stack) {
538
- formatted.stack = error.stack;
539
- }
540
- return formatted;
541
- }
542
-
543
- // JSON 语法高亮
544
- function highlightJson(data: any): string {
545
- if (data === null || data === undefined) return '';
546
- const jsonStr = JSON.stringify(data, null, 2);
547
- return jsonStr.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
548
- let cls = 'json-number';
549
- if (/^"/.test(match)) {
550
- if (/:$/.test(match)) {
551
- cls = 'json-key';
552
- } else {
553
- cls = 'json-string';
554
- }
555
- } else if (/true|false/.test(match)) {
556
- cls = 'json-boolean';
557
- } else if (/null/.test(match)) {
558
- cls = 'json-null';
559
- }
560
- return '<span class="' + cls + '">' + match + '</span>';
561
- });
562
- }
563
-
564
- // 获取结果图标
565
- function getResultIcon(status: string): string {
566
- switch (status) {
567
- case 'success':
568
- return 'bi bi-check-circle-fill text-success';
569
- case 'error':
570
- return 'bi bi-x-circle-fill text-danger';
571
- case 'info':
572
- return 'bi bi-info-circle-fill text-info';
573
- default:
574
- return 'bi bi-dash-circle-fill text-secondary';
575
- }
576
- }
577
-
578
- // 数据备份
579
- async function backupDatabase() {
580
- const operation = '备份数据库';
581
- try {
582
- const res = await databaseService.backupDatabase(props.connection?.id || '', props.database);
583
- if(res.ret === 0) {
584
- addExecutionResult(operation, 'success', res);
585
- } else {
586
- modal.error(res.msg || '备份失败');
587
- addExecutionResult(operation, 'error', formatError(res));
588
- }
589
- } catch (error: any) {
590
- console.error('备份失败:', error);
591
- modal.error(error.msg || error.message || '备份失败');
592
- addExecutionResult(operation, 'error', formatError(error));
593
- }
594
- }
595
-
596
- // 用户管理
597
- function showUsersList() {
598
- addExecutionResult('用户列表', 'info', { message: '用户列表功能开发中...' });
599
- }
600
-
601
- function showCreateUserModal() {
602
- addExecutionResult('创建用户', 'info', { message: '创建用户功能开发中...' });
603
- }
604
-
605
- function showPermissionsModal() {
606
- addExecutionResult('权限管理', 'info', { message: '权限管理功能开发中...' });
607
- }
608
-
609
- // 性能监控
610
- function showProcessList() {
611
- const sql = 'SHOW PROCESSLIST';
612
- addExecutionResult('进程列表', 'info', { sql: sql, message: '已发送 SQL 查询' });
613
- emit('execute-sql', sql);
614
- }
615
-
616
- function showSlowQueries() {
617
- const sql = 'SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 10';
618
- addExecutionResult('慢查询', 'info', { sql: sql, message: '已发送 SQL 查询' });
619
- emit('execute-sql', sql);
620
- }
621
-
622
- function showConnectionsList() {
623
- const sql = 'SHOW STATUS LIKE "Threads_connected"';
624
- addExecutionResult('连接数', 'info', { sql: sql, message: '已发送 SQL 查询' });
625
- emit('execute-sql', sql);
626
- }
627
-
628
- // 数据库优化
629
- async function optimizeDatabase() {
630
- const operation = '优化数据库';
631
- try {
632
- const res = await databaseService.optimizeDatabase(props.connection?.id || '', props.database);
633
- if(res.ret === 0) {
634
- addExecutionResult(operation, 'success', res.data);
635
- } else {
636
- modal.error(res.msg || '优化失败');
637
- addExecutionResult(operation, 'error', formatError(res));
638
- }
639
- } catch (error: any) {
640
- console.error('优化失败:', error);
641
- modal.error(error.msg || error.message || '优化失败');
642
- addExecutionResult(operation, 'error', formatError(error));
643
- }
644
- }
645
-
646
- async function analyzeTables() {
647
- const operation = '分析表';
648
- try {
649
- const res = await databaseService.analyzeTables(props.connection?.id || '', props.database);
650
- if(res.ret === 0) {
651
- addExecutionResult(operation, 'success', res.data);
652
- } else {
653
- modal.error(res.msg || '分析失败');
654
- addExecutionResult(operation, 'error', formatError(res));
655
- }
656
- } catch (error: any) {
657
- console.error('分析失败:', error);
658
- modal.error(res.msg || error.message || '分析失败');
659
- addExecutionResult(operation, 'error', formatError(error));
660
- }
661
- }
662
-
663
- async function repairTables() {
664
- const operation = '修复表';
665
- try {
666
- const res = await databaseService.repairTables(props.connection?.id || '', props.database);
667
- if(res.ret === 0) {
668
- addExecutionResult(operation, 'success', res.data);
669
- } else {
670
- modal.error(res.msg || '修复失败');
671
- addExecutionResult(operation, 'error', formatError(res));
672
- }
673
- } catch (error: any) {
674
- console.error('修复失败:', error);
675
- modal.error(res.msg || error.message || '修复失败');
676
- addExecutionResult(operation, 'error', formatError(error));
677
- }
678
- }
679
-
680
- async function clearLogs() {
681
- const operation = '清理日志';
682
- const logs = [
683
- 'TRUNCATE TABLE mysql.slow_log',
684
- 'TRUNCATE TABLE mysql.general_log',
685
- 'FLUSH LOGS'
686
- ];
687
-
688
- logs.forEach(sql => {
689
- addExecutionResult(`清理日志 - ${sql.split(' ')[1]}`, 'info', { sql, message: '已发送 SQL 查询' });
690
- emit('execute-sql', sql);
691
- });
692
- }
693
-
694
- // 数据迁移
695
- function showExportModal() {
696
- addExecutionResult('导出结构', 'info', { message: '导出结构功能开发中...' });
697
- }
698
-
699
- function showImportModal() {
700
- addExecutionResult('导入数据', 'info', { message: '导入数据功能开发中...' });
701
- }
702
-
703
- // 健康检查
704
- async function runHealthCheck() {
705
- const operation = '健康检查';
706
- const checks = [
707
- { name: '连接状态', sql: 'SELECT 1 as status' },
708
- { name: '表完整性', sql: 'SELECT COUNT(*) as status FROM information_schema.tables WHERE table_schema = DATABASE() AND table_type = "BASE TABLE"' },
709
- { name: '索引状态', sql: 'SELECT COUNT(*) as status FROM information_schema.statistics WHERE table_schema = DATABASE()' },
710
- { name: '磁盘空间', sql: 'SELECT SUM(data_length + index_length) as status FROM information_schema.tables WHERE table_schema = DATABASE()' }
711
- ];
712
-
713
- const results: any[] = [];
714
- for (const check of checks) {
715
- try {
716
- // 这里应该调用实际的数据库查询
717
- results.push({
718
- name: check.name,
719
- status: 'healthy',
720
- message: '正常'
721
- });
722
- } catch (error: any) {
723
- results.push({
724
- name: check.name,
725
- status: 'error',
726
- message: error.message
727
- });
728
- }
729
- }
730
-
731
- addExecutionResult(operation, 'success', { checks: results });
732
- }
733
-
734
- function showStatistics() {
735
- const sql = `
736
- SELECT
737
- table_name as '表名',
738
- table_rows as '记录数',
739
- ROUND(((data_length + index_length) / 1024 / 1024), 2) as '大小(MB)'
740
- FROM information_schema.tables
741
- WHERE table_schema = DATABASE()
742
- ORDER BY (data_length + index_length) DESC
743
- `;
744
- addExecutionResult('数据统计', 'info', { sql: sql, message: '已发送 SQL 查询' });
745
- emit('execute-sql', sql);
746
- }
747
-
748
- function showAuditLog() {
749
- const sql = 'SELECT * FROM mysql.general_log ORDER BY event_time DESC LIMIT 100';
750
- addExecutionResult('审计日志', 'info', { sql: sql, message: '已发送 SQL 查询' });
751
- emit('execute-sql', sql);
752
- }
753
-
754
- // 恢复功能
755
- function showRestoreModal() {
756
- restoreModalVisible.value = true;
757
- }
758
-
759
- function closeRestoreModal() {
760
- restoreModalVisible.value = false;
761
- selectedFile.value = null;
762
- }
763
-
764
- function handleFileSelect(event: Event) {
765
- const target = event.target as HTMLInputElement;
766
- if (target.files && target.files.length > 0) {
767
- selectedFile.value = target.files[0] as File;
768
- }
769
- }
770
-
771
- async function performRestore() {
772
- if (!selectedFile.value) return;
773
-
774
- const operation = '恢复数据库';
775
- try {
776
- restoring.value = true;
777
- const filePath = selectedFile.value.name;
778
-
779
- const res = await databaseService.restoreDatabase(
780
- props.connection?.id || '',
781
- props.database,
782
- filePath,
783
- { dropExisting: restoreOptions.value.dropExisting }
784
- );
785
-
786
- addExecutionResult(operation, 'success', res);
787
- closeRestoreModal();
788
- } catch (error: any) {
789
- console.error('恢复失败:', error);
790
- modal.error(error.msg || error.message || '恢复失败');
791
- addExecutionResult(operation, 'error', formatError(error));
792
- } finally {
793
- restoring.value = false;
794
- }
795
- }
796
-
797
- // 选择工具
798
- function selectTool(toolName: string) {
799
- selectedTool.value = toolName;
800
- if (toolName === 'sync') {
801
- initSyncData();
802
- }
803
- }
804
-
805
- // 关闭工具
806
- function closeTool() {
807
- selectedTool.value = null;
808
- }
809
-
810
- // 获取工具图标
811
- function getToolIcon(toolName: string) {
812
- const icons: Record<string, string> = {
813
- 'sync': 'bi-arrow-repeat'
814
- };
815
- return icons[toolName] || 'bi-gear';
816
- }
817
-
818
- // 获取工具标题
819
- function getToolTitle(toolName: string) {
820
- const titles: Record<string, string> = {
821
- 'sync': '数据同步'
822
- };
823
- return titles[toolName] || '工具';
824
- }
825
-
826
- // 同步功能 - 初始化数据
827
- async function initSyncData() {
828
- try {
829
- // 加载表列表
830
- const tablesRes = await databaseService.getTables(props.connection?.id || '', props.database);
831
- if (tablesRes.ret === 0) {
832
- tables.value = tablesRes.data || [];
833
- }
834
-
835
- // 加载已配置的数据库连接列表
836
- const connRes = await connectionService.getAllConnections();
837
- if (connRes.ret === 0) {
838
- connections.value = connRes.data || [];
839
- }
840
-
841
- // 设置源数据库信息
842
- syncConfig.value.source.database = props.database;
843
-
844
- // 默认选择当前连接
845
- if (props.connection?.id) {
846
- useExistingConnection.value = true;
847
- selectedConnectionId.value = props.connection.id;
848
- }
849
- } catch (error: any) {
850
- console.error('加载表列表失败:', error);
851
- modal.error('加载表列表失败');
852
- }
853
- }
854
-
855
- // 重置同步状态
856
- function resetSyncState() {
857
- syncing.value = false;
858
- tables.value = [];
859
- databases.value = [];
860
- useExistingConnection.value = false;
861
- selectedConnectionId.value = '';
862
- selectedDatabaseName.value = '';
863
- syncConfig.value = {
864
- source: {
865
- database: '',
866
- tableName: ''
867
- },
868
- target: {
869
- dbType: 'mysql',
870
- host: 'localhost',
871
- port: 3306,
872
- database: '',
873
- username: 'root',
874
- password: '',
875
- tableName: ''
876
- },
877
- options: {
878
- syncStructure: true,
879
- syncData: true,
880
- dropIfExists: false,
881
- bulkInsert: true,
882
- overrideExisting: false
883
- }
884
- };
885
- }
886
-
887
- async function performSync() {
888
- if (!isSyncFormValid.value) {
889
- modal.error('请填写完整的同步配置');
890
- return;
891
- }
892
-
893
- const operation = '数据同步';
894
- syncing.value = true;
895
-
896
- try {
897
- // 构建同步配置
898
- let syncData;
899
- if (useExistingConnection.value) {
900
- // 使用已配置连接
901
- syncData = {
902
- source: {
903
- database: syncConfig.value.source.database,
904
- tableName: syncConfig.value.source.tableName
905
- },
906
- target: {
907
- connectionId: selectedConnectionId.value,
908
- database: selectedDatabaseName.value,
909
- tableName: syncConfig.value.target.tableName
910
- },
911
- options: syncConfig.value.options
912
- };
913
- } else {
914
- // 使用手动配置
915
- syncData = syncConfig.value;
916
- }
917
-
918
- // 添加同步开始记录
919
- addExecutionResult(operation, 'info', {
920
- message: '开始同步数据',
921
- config: syncData
922
- });
923
-
924
- // 执行同步
925
- const res = await databaseService.syncTable(
926
- props.connection?.id || '',
927
- syncData
928
- );
929
-
930
- if (res.ret === 0) {
931
- const tables = res.data?.tables || [];
932
- let successCount = 0;
933
- let totalRows = 0;
934
-
935
- tables.forEach((table: any) => {
936
- if (table.rowsSynced > 0) {
937
- successCount++;
938
- totalRows += table.rowsSynced;
939
- }
940
- });
941
-
942
- addExecutionResult(operation, 'success', {
943
- message: `数据同步成功,${successCount}/${tables.length} 个表同步完成,共同步 ${totalRows} 行数据`,
944
- data: res.data
945
- });
946
- toast.success(`数据同步成功,${successCount}/${tables.length} 个表同步完成`);
947
- } else {
948
- addExecutionResult(operation, 'error', {
949
- message: res.msg || '同步失败',
950
- error: res.error
951
- });
952
- toast.error(res.msg || '同步失败');
953
- }
954
- } catch (error: any) {
955
- console.error('同步失败:', error);
956
- addExecutionResult(operation, 'error', formatError(error));
957
- toast.error(error.msg || error.message || '同步失败');
958
- } finally {
959
- syncing.value = false;
960
- }
961
- }
962
-
963
- function showScheduleModal() {
964
- addExecutionResult('定时备份', 'info', { message: '定时备份功能开发中...' });
965
- }
966
- </script>
967
-
968
- <style scoped>
969
- .db-tools {
970
- background: white;
971
- border-radius: 12px;
972
- box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
973
- }
974
-
975
- .tools-header {
976
- background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
977
- color: white;
978
- padding: 1rem 1.5rem;
979
- border-bottom: 1px solid #e2e8f0;
980
- }
981
-
982
- .tools-title {
983
- margin: 0;
984
- font-size: 1.1rem;
985
- font-weight: 600;
986
- display: flex;
987
- align-items: center;
988
- gap: 0.5rem;
989
- }
990
-
991
- .tools-content {
992
- padding: 1.5rem;
993
- overflow-y: auto;
994
- }
995
-
996
- .tool-section {
997
- margin-bottom: 2rem;
998
- }
999
-
1000
- .section-title {
1001
- font-size: 0.9rem;
1002
- font-weight: 600;
1003
- color: #374151;
1004
- margin-bottom: 1rem;
1005
- display: flex;
1006
- align-items: center;
1007
- gap: 0.5rem;
1008
- }
1009
-
1010
- .tool-actions {
1011
- display: flex;
1012
- flex-wrap: wrap;
1013
- gap: 0.5rem;
1014
- }
1015
-
1016
- .tool-actions .btn {
1017
- min-width: 120px;
1018
- display: flex;
1019
- align-items: center;
1020
- gap: 0.5rem;
1021
- }
1022
-
1023
- .modal {
1024
- background-color: rgba(0, 0, 0, 0.5);
1025
- }
1026
-
1027
- .modal-dialog {
1028
- max-width: 600px;
1029
- }
1030
-
1031
- .modal-header {
1032
- background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
1033
- border-bottom: 1px solid #e2e8f0;
1034
- }
1035
-
1036
- .modal-title {
1037
- color: #1e293b;
1038
- font-weight: 600;
1039
- }
1040
-
1041
- .modal-footer {
1042
- background: #f8fafc;
1043
- border-top: 1px solid #e2e8f0;
1044
- }
1045
-
1046
- /* 工具组件区域 */
1047
- .tool-component-area {
1048
- border-top: 1px solid #e2e8f0;
1049
- background: #f8fafc;
1050
- }
1051
-
1052
- .component-header {
1053
- display: flex;
1054
- justify-content: space-between;
1055
- align-items: center;
1056
- padding: 1rem 1.5rem;
1057
- background: linear-gradient(135deg, #f1f5f9 0%, #f8fafc 100%);
1058
- border-bottom: 1px solid #e2e8f0;
1059
- }
1060
-
1061
- .component-title {
1062
- margin: 0;
1063
- font-size: 1rem;
1064
- font-weight: 600;
1065
- color: #1e293b;
1066
- display: flex;
1067
- align-items: center;
1068
- gap: 0.5rem;
1069
- }
1070
-
1071
- .tool-component {
1072
- padding: 1.5rem;
1073
- }
1074
-
1075
- .sync-component {
1076
- background: white;
1077
- border-radius: 0.375rem;
1078
- }
1079
-
1080
- /* 执行结果区域 */
1081
- .execution-results {
1082
- border-top: 1px solid #e2e8f0;
1083
- background: #f8fafc;
1084
- }
1085
-
1086
- .results-header {
1087
- display: flex;
1088
- justify-content: space-between;
1089
- align-items: center;
1090
- padding: 1rem 1.5rem;
1091
- background: linear-gradient(135deg, #f1f5f9 0%, #f8fafc 100%);
1092
- border-bottom: 1px solid #e2e8f0;
1093
- }
1094
-
1095
- .results-title {
1096
- margin: 0;
1097
- font-size: 1rem;
1098
- font-weight: 600;
1099
- color: #1e293b;
1100
- display: flex;
1101
- align-items: center;
1102
- gap: 0.5rem;
1103
- }
1104
-
1105
- .results-content {
1106
- max-height: 400px;
1107
- overflow-y: auto;
1108
- padding: 1rem;
1109
- }
1110
-
1111
- .no-results {
1112
- display: flex;
1113
- flex-direction: column;
1114
- align-items: center;
1115
- justify-content: center;
1116
- padding: 3rem 1rem;
1117
- color: #94a3b8;
1118
- }
1119
-
1120
- .no-results i {
1121
- font-size: 3rem;
1122
- margin-bottom: 1rem;
1123
- }
1124
-
1125
- .no-results p {
1126
- margin: 0;
1127
- font-size: 1rem;
1128
- }
1129
-
1130
- .result-item {
1131
- margin-bottom: 0.75rem;
1132
- border: 1px solid #e2e8f0;
1133
- border-radius: 8px;
1134
- background: white;
1135
- overflow: hidden;
1136
- transition: box-shadow 0.2s;
1137
- }
1138
-
1139
- .result-item:hover {
1140
- box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
1141
- }
1142
-
1143
- .result-item.result-success {
1144
- border-left: 4px solid #22c55e;
1145
- }
1146
-
1147
- .result-item.result-error {
1148
- border-left: 4px solid #ef4444;
1149
- }
1150
-
1151
- .result-item.result-info {
1152
- border-left: 4px solid #3b82f6;
1153
- }
1154
-
1155
- .result-header {
1156
- display: flex;
1157
- justify-content: space-between;
1158
- align-items: center;
1159
- padding: 0.75rem 1rem;
1160
- cursor: pointer;
1161
- background: white;
1162
- transition: background 0.2s;
1163
- }
1164
-
1165
- .result-header:hover {
1166
- background: #f8fafc;
1167
- }
1168
-
1169
- .result-title {
1170
- display: flex;
1171
- align-items: center;
1172
- gap: 0.75rem;
1173
- flex: 1;
1174
- }
1175
-
1176
- .result-title i {
1177
- font-size: 1.1rem;
1178
- }
1179
-
1180
- .operation-name {
1181
- font-weight: 600;
1182
- color: #1e293b;
1183
- font-size: 0.95rem;
1184
- }
1185
-
1186
- .operation-time {
1187
- color: #64748b;
1188
- font-size: 0.85rem;
1189
- margin-left: auto;
1190
- }
1191
-
1192
- .toggle-icon {
1193
- transition: transform 0.2s;
1194
- color: #94a3b8;
1195
- font-size: 0.9rem;
1196
- }
1197
-
1198
- .toggle-icon.expanded {
1199
- transform: rotate(180deg);
1200
- }
1201
-
1202
- .result-body {
1203
- padding: 1rem;
1204
- background: #fafafa;
1205
- border-top: 1px solid #e2e8f0;
1206
- }
1207
-
1208
- .result-body pre {
1209
- margin: 0;
1210
- font-size: 0.85rem;
1211
- line-height: 1.5;
1212
- max-height: 300px;
1213
- overflow: auto;
1214
- }
1215
-
1216
- .result-body code {
1217
- font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
1218
- }
1219
-
1220
- /* JSON 语法高亮 - 不使用 scoped 以确保 v-html 内容能应用样式 */
1221
- :deep(.json-key) {
1222
- color: #d04255;
1223
- font-weight: 500;
1224
- }
1225
-
1226
- :deep(.json-string) {
1227
- color: #22863a;
1228
- }
1229
-
1230
- :deep(.json-number) {
1231
- color: #005cc5;
1232
- }
1233
-
1234
- :deep(.json-boolean) {
1235
- color: #d73a49;
1236
- }
1237
-
1238
- :deep(.json-null) {
1239
- color: #6f42c1;
1240
- }
1241
-
1242
- /* 滚动条样式 */
1243
- .results-content::-webkit-scrollbar,
1244
- .result-body pre::-webkit-scrollbar {
1245
- width: 8px;
1246
- height: 8px;
1247
- }
1248
-
1249
- .results-content::-webkit-scrollbar-track,
1250
- .result-body pre::-webkit-scrollbar-track {
1251
- background: #f1f5f9;
1252
- border-radius: 4px;
1253
- }
1254
-
1255
- .results-content::-webkit-scrollbar-thumb,
1256
- .result-body pre::-webkit-scrollbar-thumb {
1257
- background: #cbd5e1;
1258
- border-radius: 4px;
1259
- }
1260
-
1261
- .results-content::-webkit-scrollbar-thumb:hover,
1262
- .result-body pre::-webkit-scrollbar-thumb:hover {
1263
- background: #94a3b8;
1264
- }
1265
- </style>