fdb2 1.0.6 → 1.0.8

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 (193) hide show
  1. package/.vscodeignore +45 -0
  2. package/README.md +24 -85
  3. package/dist/package.json +115 -0
  4. package/dist/pnpm-lock.yaml +7447 -0
  5. package/dist/public/explorer.css +244 -111
  6. package/dist/public/explorer.js +523 -278
  7. package/dist/scripts/preinstall.js +112 -0
  8. package/dist/server/index.d.ts.map +1 -1
  9. package/dist/server/index.js +4 -9
  10. package/dist/server/index.js.map +1 -1
  11. package/dist/server/index.ts +5 -10
  12. package/dist/server/service/connection.service.d.ts.map +1 -1
  13. package/dist/server/service/connection.service.js +1 -0
  14. package/dist/server/service/connection.service.js.map +1 -1
  15. package/dist/server/service/connection.service.ts +1 -0
  16. package/dist/server/service/database/base.service.d.ts +4 -0
  17. package/dist/server/service/database/base.service.d.ts.map +1 -1
  18. package/dist/server/service/database/base.service.js +3 -3
  19. package/dist/server/service/database/base.service.js.map +1 -1
  20. package/dist/server/service/database/base.service.ts +8 -3
  21. package/dist/server/service/database/cockroachdb.service.d.ts +5 -0
  22. package/dist/server/service/database/cockroachdb.service.d.ts.map +1 -1
  23. package/dist/server/service/database/cockroachdb.service.js +112 -0
  24. package/dist/server/service/database/cockroachdb.service.js.map +1 -1
  25. package/dist/server/service/database/cockroachdb.service.ts +123 -0
  26. package/dist/server/service/database/database.service.d.ts +4 -0
  27. package/dist/server/service/database/database.service.d.ts.map +1 -1
  28. package/dist/server/service/database/database.service.js +8 -0
  29. package/dist/server/service/database/database.service.js.map +1 -1
  30. package/dist/server/service/database/database.service.ts +9 -0
  31. package/dist/server/service/database/mongodb.service.d.ts +6 -0
  32. package/dist/server/service/database/mongodb.service.d.ts.map +1 -1
  33. package/dist/server/service/database/mongodb.service.js +8 -0
  34. package/dist/server/service/database/mongodb.service.js.map +1 -1
  35. package/dist/server/service/database/mongodb.service.ts +9 -0
  36. package/dist/server/service/database/mssql.service.d.ts +4 -0
  37. package/dist/server/service/database/mssql.service.d.ts.map +1 -1
  38. package/dist/server/service/database/mssql.service.js +105 -0
  39. package/dist/server/service/database/mssql.service.js.map +1 -1
  40. package/dist/server/service/database/mssql.service.ts +118 -0
  41. package/dist/server/service/database/mysql.service.d.ts +4 -0
  42. package/dist/server/service/database/mysql.service.d.ts.map +1 -1
  43. package/dist/server/service/database/mysql.service.js +116 -0
  44. package/dist/server/service/database/mysql.service.js.map +1 -1
  45. package/dist/server/service/database/mysql.service.ts +130 -0
  46. package/dist/server/service/database/oracle.service.d.ts +4 -0
  47. package/dist/server/service/database/oracle.service.d.ts.map +1 -1
  48. package/dist/server/service/database/oracle.service.js +114 -0
  49. package/dist/server/service/database/oracle.service.js.map +1 -1
  50. package/dist/server/service/database/oracle.service.ts +128 -0
  51. package/dist/server/service/database/postgres.service.d.ts +4 -0
  52. package/dist/server/service/database/postgres.service.d.ts.map +1 -1
  53. package/dist/server/service/database/postgres.service.js +120 -0
  54. package/dist/server/service/database/postgres.service.js.map +1 -1
  55. package/dist/server/service/database/postgres.service.ts +131 -0
  56. package/dist/server/service/database/sap.service.d.ts +4 -0
  57. package/dist/server/service/database/sap.service.d.ts.map +1 -1
  58. package/dist/server/service/database/sap.service.js +107 -0
  59. package/dist/server/service/database/sap.service.js.map +1 -1
  60. package/dist/server/service/database/sap.service.ts +120 -0
  61. package/dist/server/service/database/sqlite.service.d.ts +5 -0
  62. package/dist/server/service/database/sqlite.service.d.ts.map +1 -1
  63. package/dist/server/service/database/sqlite.service.js +133 -0
  64. package/dist/server/service/database/sqlite.service.js.map +1 -1
  65. package/dist/server/service/database/sqlite.service.ts +150 -0
  66. package/fdb2.server.pid +1 -0
  67. package/package.json +18 -9
  68. package/packages/vscode/.vscodeignore +44 -0
  69. package/packages/vscode/README.md +62 -0
  70. package/packages/vscode/out/database-services/base.service.js +236 -0
  71. package/packages/vscode/out/database-services/base.service.js.map +1 -0
  72. package/packages/vscode/out/database-services/cockroachdb.service.js +634 -0
  73. package/packages/vscode/out/database-services/cockroachdb.service.js.map +1 -0
  74. package/packages/vscode/out/database-services/connection.service.js +346 -0
  75. package/packages/vscode/out/database-services/connection.service.js.map +1 -0
  76. package/packages/vscode/out/database-services/database.service.js +571 -0
  77. package/packages/vscode/out/database-services/database.service.js.map +1 -0
  78. package/packages/vscode/out/database-services/index.js +18 -0
  79. package/packages/vscode/out/database-services/index.js.map +1 -0
  80. package/packages/vscode/out/database-services/model/connection.entity.js +11 -0
  81. package/packages/vscode/out/database-services/model/connection.entity.js.map +1 -0
  82. package/packages/vscode/out/database-services/model/database.entity.js +35 -0
  83. package/packages/vscode/out/database-services/model/database.entity.js.map +1 -0
  84. package/packages/vscode/out/database-services/mongodb.service.js +458 -0
  85. package/packages/vscode/out/database-services/mongodb.service.js.map +1 -0
  86. package/packages/vscode/out/database-services/mssql.service.js +694 -0
  87. package/packages/vscode/out/database-services/mssql.service.js.map +1 -0
  88. package/packages/vscode/out/database-services/mysql.service.js +735 -0
  89. package/packages/vscode/out/database-services/mysql.service.js.map +1 -0
  90. package/packages/vscode/out/database-services/oracle.service.js +787 -0
  91. package/packages/vscode/out/database-services/oracle.service.js.map +1 -0
  92. package/packages/vscode/out/database-services/postgres.service.js +696 -0
  93. package/packages/vscode/out/database-services/postgres.service.js.map +1 -0
  94. package/packages/vscode/out/database-services/sap.service.js +695 -0
  95. package/packages/vscode/out/database-services/sap.service.js.map +1 -0
  96. package/packages/vscode/out/database-services/sqlite.service.js +532 -0
  97. package/packages/vscode/out/database-services/sqlite.service.js.map +1 -0
  98. package/packages/vscode/out/extension.js +93 -0
  99. package/packages/vscode/out/extension.js.map +1 -0
  100. package/packages/vscode/out/provider/DatabaseTreeProvider.js +159 -0
  101. package/packages/vscode/out/provider/DatabaseTreeProvider.js.map +1 -0
  102. package/packages/vscode/out/provider/WebViewProvider.js +259 -0
  103. package/packages/vscode/out/provider/WebViewProvider.js.map +1 -0
  104. package/packages/vscode/out/service/ConnectionManager.js +105 -0
  105. package/packages/vscode/out/service/ConnectionManager.js.map +1 -0
  106. package/packages/vscode/out/service/DatabaseServiceBridge.js +395 -0
  107. package/packages/vscode/out/service/DatabaseServiceBridge.js.map +1 -0
  108. package/packages/vscode/out/typings/connection.js +3 -0
  109. package/packages/vscode/out/typings/connection.js.map +1 -0
  110. package/packages/vscode/package.json +142 -0
  111. package/packages/vscode/resources/icon.svg +5 -0
  112. package/packages/vscode/resources/webview/_plugin-vue_export-helper.js +6529 -0
  113. package/packages/vscode/resources/webview/_plugin-vue_export-helper.js.map +1 -0
  114. package/packages/vscode/resources/webview/connection.css +69 -0
  115. package/packages/vscode/resources/webview/connection.js +228 -0
  116. package/packages/vscode/resources/webview/connection.js.map +1 -0
  117. package/packages/vscode/resources/webview/database.css +259 -0
  118. package/packages/vscode/resources/webview/database.js +275 -0
  119. package/packages/vscode/resources/webview/database.js.map +1 -0
  120. package/packages/vscode/resources/webview/favicon.ico +0 -0
  121. package/packages/vscode/resources/webview/index.html +9 -0
  122. package/packages/vscode/resources/webview/modules/header.tpl +14 -0
  123. package/packages/vscode/resources/webview/modules/initial_state.tpl +55 -0
  124. package/packages/vscode/resources/webview/query.css +162 -0
  125. package/packages/vscode/resources/webview/query.js +198 -0
  126. package/packages/vscode/resources/webview/query.js.map +1 -0
  127. package/packages/vscode/src/database-services/base.service.js.map +1 -0
  128. package/packages/vscode/src/database-services/base.service.ts +363 -0
  129. package/packages/vscode/src/database-services/cockroachdb.service.js.map +1 -0
  130. package/packages/vscode/src/database-services/cockroachdb.service.ts +659 -0
  131. package/packages/vscode/src/database-services/connection.service.ts +341 -0
  132. package/packages/vscode/src/database-services/database.service.ts +630 -0
  133. package/packages/vscode/src/database-services/index.ts +7 -0
  134. package/packages/vscode/src/database-services/model/connection.entity.js +11 -0
  135. package/packages/vscode/src/database-services/model/connection.entity.js.map +1 -0
  136. package/packages/vscode/src/database-services/model/connection.entity.ts +66 -0
  137. package/packages/vscode/src/database-services/model/database.entity.js +35 -0
  138. package/packages/vscode/src/database-services/model/database.entity.js.map +1 -0
  139. package/packages/vscode/src/database-services/model/database.entity.ts +246 -0
  140. package/packages/vscode/src/database-services/mongodb.service.js.map +1 -0
  141. package/packages/vscode/src/database-services/mongodb.service.ts +454 -0
  142. package/packages/vscode/src/database-services/mssql.service.js.map +1 -0
  143. package/packages/vscode/src/database-services/mssql.service.ts +723 -0
  144. package/packages/vscode/src/database-services/mysql.service.js.map +1 -0
  145. package/packages/vscode/src/database-services/mysql.service.ts +761 -0
  146. package/packages/vscode/src/database-services/oracle.service.js.map +1 -0
  147. package/packages/vscode/src/database-services/oracle.service.ts +832 -0
  148. package/packages/vscode/src/database-services/postgres.service.js.map +1 -0
  149. package/packages/vscode/src/database-services/postgres.service.ts +741 -0
  150. package/packages/vscode/src/database-services/sap.service.js.map +1 -0
  151. package/packages/vscode/src/database-services/sap.service.ts +713 -0
  152. package/packages/vscode/src/database-services/sqlite.service.js.map +1 -0
  153. package/packages/vscode/src/database-services/sqlite.service.ts +559 -0
  154. package/packages/vscode/src/extension.ts +76 -0
  155. package/packages/vscode/src/provider/DatabaseTreeProvider.ts +167 -0
  156. package/packages/vscode/src/provider/WebViewProvider.ts +277 -0
  157. package/packages/vscode/src/service/DatabaseServiceBridge.ts +414 -0
  158. package/packages/vscode/src/typings/connection.ts +90 -0
  159. package/packages/vscode/tsconfig.json +21 -0
  160. package/public/fdb2.png +0 -0
  161. package/server/backups/db_ai_breakout_2026-03-11T08-38-48-677Z.sql +0 -0
  162. package/server/index.ts +5 -10
  163. package/server/model/connection.entity.js +11 -0
  164. package/server/model/connection.entity.js.map +1 -0
  165. package/server/model/database.entity.js +35 -0
  166. package/server/model/database.entity.js.map +1 -0
  167. package/server/service/connection.service.ts +1 -0
  168. package/server/service/database/base.service.ts +8 -3
  169. package/server/service/database/cockroachdb.service.ts +123 -0
  170. package/server/service/database/database.service.ts +9 -0
  171. package/server/service/database/mongodb.service.ts +9 -0
  172. package/server/service/database/mssql.service.ts +118 -0
  173. package/server/service/database/mysql.service.ts +130 -0
  174. package/server/service/database/oracle.service.ts +128 -0
  175. package/server/service/database/postgres.service.ts +131 -0
  176. package/server/service/database/sap.service.ts +120 -0
  177. package/server/service/database/sqlite.service.ts +150 -0
  178. package/server/tsconfig.json +20 -0
  179. package/src/components/connection-editor/index.vue +0 -1
  180. package/src/platform/database/components/db-tools.vue +414 -174
  181. package/src/platform/database/components/table-detail.vue +3 -2
  182. package/src/platform/database/components/table-editor.vue +245 -18
  183. package/src/platform/vscode/bridge.ts +121 -0
  184. package/src/platform/vscode/components/ConnectionPanel.vue +272 -0
  185. package/src/platform/vscode/components/DatabasePanel.vue +532 -0
  186. package/src/platform/vscode/components/QueryPanel.vue +371 -0
  187. package/src/platform/vscode/entry/connection.ts +13 -0
  188. package/src/platform/vscode/entry/database.ts +13 -0
  189. package/src/platform/vscode/entry/query.ts +13 -0
  190. package/src/platform/vscode/index.ts +5 -0
  191. package/src/service/database.ts +2 -6
  192. package/vite.config.ts +46 -6
  193. package/vite.config.vscode.ts +47 -0
@@ -126,6 +126,38 @@
126
126
  </div>
127
127
  </div>
128
128
 
129
+ <!-- 执行结果展示区域 -->
130
+ <div class="execution-results">
131
+ <div class="results-header">
132
+ <h6 class="results-title">
133
+ <i class="bi bi-terminal"></i>
134
+ 执行结果
135
+ </h6>
136
+ <button class="btn btn-outline-secondary btn-sm" @click="clearResults">
137
+ <i class="bi bi-trash"></i> 清空
138
+ </button>
139
+ </div>
140
+ <div class="results-content" ref="resultsContentRef">
141
+ <div v-if="executionResults.length === 0" class="no-results">
142
+ <i class="bi bi-inbox"></i>
143
+ <p>暂无执行结果</p>
144
+ </div>
145
+ <div v-for="(result, index) in executionResults" :key="index" class="result-item" :class="`result-${result.status}`">
146
+ <div class="result-header" @click="toggleResult(index)">
147
+ <div class="result-title">
148
+ <i :class="getResultIcon(result.status)"></i>
149
+ <span class="operation-name">{{ result.operation }}</span>
150
+ <span class="operation-time">{{ result.timestamp }}</span>
151
+ </div>
152
+ <i class="bi bi-chevron-down toggle-icon" :class="{ 'expanded': result.expanded }"></i>
153
+ </div>
154
+ <div v-if="result.expanded" class="result-body">
155
+ <pre><code v-html="highlightJson(result.data)"></code></pre>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ </div>
160
+
129
161
  <!-- 数据恢复模态框 -->
130
162
  <div class="modal fade" :class="{ show: restoreModalVisible }" :style="{ display: restoreModalVisible ? 'block' : 'none', zIndex: 1055 }">
131
163
  <div class="modal-dialog">
@@ -157,40 +189,6 @@
157
189
  </div>
158
190
  </div>
159
191
  </div>
160
-
161
- <!-- 健康检查结果模态框 -->
162
- <div class="modal fade" :class="{ show: healthModalVisible }" :style="{ display: healthModalVisible ? 'block' : 'none', zIndex: 1055 }">
163
- <div class="modal-dialog modal-lg">
164
- <div class="modal-content">
165
- <div class="modal-header">
166
- <h5 class="modal-title">数据库健康检查</h5>
167
- <button type="button" class="btn-close" @click="closeHealthModal"></button>
168
- </div>
169
- <div class="modal-body">
170
- <div v-if="healthChecking" class="text-center py-4">
171
- <div class="spinner-border text-primary" role="status"></div>
172
- <div class="mt-3">正在检查数据库健康状况...</div>
173
- </div>
174
- <div v-else>
175
- <div class="health-results">
176
- <div v-for="check in healthResults" :key="check.name" class="health-item">
177
- <div class="health-status">
178
- <i :class="check.status === 'healthy' ? 'bi bi-check-circle-fill text-success' :
179
- check.status === 'warning' ? 'bi bi-exclamation-triangle-fill text-warning' :
180
- 'bi bi-x-circle-fill text-danger'"></i>
181
- {{ check.name }}
182
- </div>
183
- <div class="health-message text-wrap">{{ check.message }}</div>
184
- </div>
185
- </div>
186
- </div>
187
- </div>
188
- <div class="modal-footer">
189
- <button type="button" class="btn btn-secondary" @click="closeHealthModal">关闭</button>
190
- </div>
191
- </div>
192
- </div>
193
- </div>
194
192
  </div>
195
193
  </template>
196
194
 
@@ -212,192 +210,294 @@ const databaseService = new DatabaseService();
212
210
 
213
211
  // 状态管理
214
212
  const restoreModalVisible = ref(false);
215
- const healthModalVisible = ref(false);
216
213
  const selectedFile = ref<File | null>(null);
217
214
  const restoring = ref(false);
218
- const healthChecking = ref(false);
219
- const healthResults = ref<any[]>([]);
215
+ const resultsContentRef = ref<HTMLElement | null>(null);
216
+
217
+ // 执行结果历史
218
+ interface ExecutionResult {
219
+ operation: string;
220
+ status: 'success' | 'error' | 'info';
221
+ timestamp: string;
222
+ data: any;
223
+ expanded: boolean;
224
+ }
225
+
226
+ const executionResults = ref<ExecutionResult[]>([]);
220
227
 
221
228
  const restoreOptions = ref({
222
229
  dropExisting: false
223
230
  });
224
231
 
232
+ // 添加执行结果
233
+ function addExecutionResult(operation: string, status: 'success' | 'error' | 'info', data: any) {
234
+ const timestamp = new Date().toLocaleString('zh-CN', {
235
+ year: 'numeric',
236
+ month: '2-digit',
237
+ day: '2-digit',
238
+ hour: '2-digit',
239
+ minute: '2-digit',
240
+ second: '2-digit'
241
+ });
242
+
243
+ executionResults.value.unshift({
244
+ operation,
245
+ status,
246
+ timestamp,
247
+ data,
248
+ expanded: false
249
+ });
250
+
251
+ // 只保留最近50条记录
252
+ if (executionResults.value.length > 50) {
253
+ executionResults.value = executionResults.value.slice(0, 50);
254
+ }
255
+
256
+ // 自动滚动到底部(显示最新结果在顶部,所以滚动到0)
257
+ setTimeout(() => {
258
+ if (resultsContentRef.value) {
259
+ resultsContentRef.value.scrollTop = 0;
260
+ }
261
+ }, 100);
262
+ }
263
+
264
+ // 清空执行结果
265
+ function clearResults() {
266
+ executionResults.value = [];
267
+ }
268
+
269
+ // 切换结果展开/收起
270
+ function toggleResult(index: number) {
271
+ const result = executionResults.value[index];
272
+ if (result) {
273
+ result.expanded = !result.expanded;
274
+ }
275
+ }
276
+
277
+ // 格式化错误信息
278
+ function formatError(error: any): any {
279
+ const formatted: any = {
280
+ success: false,
281
+ message: error.msg || error.message || '未知错误'
282
+ };
283
+ if (error.stack) {
284
+ formatted.stack = error.stack;
285
+ }
286
+ return formatted;
287
+ }
288
+
289
+ // JSON 语法高亮
290
+ function highlightJson(data: any): string {
291
+ if (data === null || data === undefined) return '';
292
+ const jsonStr = JSON.stringify(data, null, 2);
293
+ return jsonStr.replace(/("(\\u[a-zA-Z0-9]{4}|\\[^u]|[^\\"])*"(\s*:)?|\b(true|false|null)\b|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?)/g, function (match) {
294
+ let cls = 'json-number';
295
+ if (/^"/.test(match)) {
296
+ if (/:$/.test(match)) {
297
+ cls = 'json-key';
298
+ } else {
299
+ cls = 'json-string';
300
+ }
301
+ } else if (/true|false/.test(match)) {
302
+ cls = 'json-boolean';
303
+ } else if (/null/.test(match)) {
304
+ cls = 'json-null';
305
+ }
306
+ return '<span class="' + cls + '">' + match + '</span>';
307
+ });
308
+ }
309
+
310
+ // 获取结果图标
311
+ function getResultIcon(status: string): string {
312
+ switch (status) {
313
+ case 'success':
314
+ return 'bi bi-check-circle-fill text-success';
315
+ case 'error':
316
+ return 'bi bi-x-circle-fill text-danger';
317
+ case 'info':
318
+ return 'bi bi-info-circle-fill text-info';
319
+ default:
320
+ return 'bi bi-dash-circle-fill text-secondary';
321
+ }
322
+ }
323
+
225
324
  // 数据备份
226
325
  async function backupDatabase() {
326
+ const operation = '备份数据库';
227
327
  try {
228
328
  const res = await databaseService.backupDatabase(props.connection?.id || '', props.database);
229
- if(res.ret === 0) await modal.success('数据库备份成功');
230
- else {
231
- modal.error(res.msg || '备份失败', {
232
- operation: 'BACKUP',
233
- database: props.database,
234
- });
329
+ if(res.ret === 0) {
330
+ addExecutionResult(operation, 'success', res);
331
+ } else {
332
+ modal.error(res.msg || '备份失败');
333
+ addExecutionResult(operation, 'error', formatError(res));
235
334
  }
236
- } catch (error) {
335
+ } catch (error: any) {
237
336
  console.error('备份失败:', error);
238
-
239
- modal.error(error.msg || error.message || '备份失败', {
240
- operation: 'BACKUP',
241
- database: props.database,
242
- //options: backupOptions.value,
243
- stack: error.stack
244
- });
337
+ modal.error(error.msg || error.message || '备份失败');
338
+ addExecutionResult(operation, 'error', formatError(error));
245
339
  }
246
340
  }
247
341
 
248
342
  // 用户管理
249
343
  function showUsersList() {
250
- modal.info('用户列表功能开发中...');
344
+ addExecutionResult('用户列表', 'info', { message: '用户列表功能开发中...' });
251
345
  }
252
346
 
253
347
  function showCreateUserModal() {
254
- modal.info('创建用户功能开发中...');
348
+ addExecutionResult('创建用户', 'info', { message: '创建用户功能开发中...' });
255
349
  }
256
350
 
257
351
  function showPermissionsModal() {
258
- modal.info('权限管理功能开发中...');
352
+ addExecutionResult('权限管理', 'info', { message: '权限管理功能开发中...' });
259
353
  }
260
354
 
261
355
  // 性能监控
262
356
  function showProcessList() {
263
357
  const sql = 'SHOW PROCESSLIST';
358
+ addExecutionResult('进程列表', 'info', { sql: sql, message: '已发送 SQL 查询' });
264
359
  emit('execute-sql', sql);
265
360
  }
266
361
 
267
362
  function showSlowQueries() {
268
363
  const sql = 'SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 10';
364
+ addExecutionResult('慢查询', 'info', { sql: sql, message: '已发送 SQL 查询' });
269
365
  emit('execute-sql', sql);
270
366
  }
271
367
 
272
368
  function showConnectionsList() {
273
369
  const sql = 'SHOW STATUS LIKE "Threads_connected"';
370
+ addExecutionResult('连接数', 'info', { sql: sql, message: '已发送 SQL 查询' });
274
371
  emit('execute-sql', sql);
275
372
  }
276
373
 
277
374
  // 数据库优化
278
375
  async function optimizeDatabase() {
376
+ const operation = '优化数据库';
279
377
  try {
280
- await databaseService.optimizeDatabase(props.connection?.id || '', props.database);
281
- await modal.success('数据库优化完成');
282
- } catch (error) {
378
+ const res = await databaseService.optimizeDatabase(props.connection?.id || '', props.database);
379
+ if(res.ret === 0) {
380
+ addExecutionResult(operation, 'success', res.data);
381
+ } else {
382
+ modal.error(res.msg || '优化失败');
383
+ addExecutionResult(operation, 'error', formatError(res));
384
+ }
385
+ } catch (error: any) {
283
386
  console.error('优化失败:', error);
284
-
285
- modal.error(error.msg || error.message || '优化失败', {
286
- operation: 'OPTIMIZE',
287
- database: props.database,
288
- //options: optimizationOptions.value,
289
- stack: error.stack
290
- });
387
+ modal.error(error.msg || error.message || '优化失败');
388
+ addExecutionResult(operation, 'error', formatError(error));
291
389
  }
292
390
  }
293
391
 
294
392
  async function analyzeTables() {
393
+ const operation = '分析表';
295
394
  try {
296
- await databaseService.analyzeTables(props.connection?.id || '', props.database);
297
- await modal.success('表分析完成');
298
- } catch (error) {
395
+ const res = await databaseService.analyzeTables(props.connection?.id || '', props.database);
396
+ if(res.ret === 0) {
397
+ addExecutionResult(operation, 'success', res.data);
398
+ } else {
399
+ modal.error(res.msg || '分析失败');
400
+ addExecutionResult(operation, 'error', formatError(res));
401
+ }
402
+ } catch (error: any) {
299
403
  console.error('分析失败:', error);
300
-
301
- modal.error(error.msg || error.message || '分析失败', {
302
- operation: 'ANALYZE',
303
- database: props.database,
304
- //options: analysisOptions.value,
305
- stack: error.stack
306
- });
404
+ modal.error(res.msg || error.message || '分析失败');
405
+ addExecutionResult(operation, 'error', formatError(error));
307
406
  }
308
407
  }
309
408
 
310
409
  async function repairTables() {
410
+ const operation = '修复表';
311
411
  try {
312
- await databaseService.repairTables(props.connection?.id || '', props.database);
313
- await modal.success('表修复完成');
314
- } catch (error) {
412
+ const res = await databaseService.repairTables(props.connection?.id || '', props.database);
413
+ if(res.ret === 0) {
414
+ addExecutionResult(operation, 'success', res.data);
415
+ } else {
416
+ modal.error(res.msg || '修复失败');
417
+ addExecutionResult(operation, 'error', formatError(res));
418
+ }
419
+ } catch (error: any) {
315
420
  console.error('修复失败:', error);
316
-
317
- modal.error(error.msg || error.message || '修复失败', {
318
- operation: 'REPAIR',
319
- database: props.database,
320
- //options: repairOptions.value,
321
- stack: error.stack
322
- });
421
+ modal.error(res.msg || error.message || '修复失败');
422
+ addExecutionResult(operation, 'error', formatError(error));
323
423
  }
324
424
  }
325
425
 
326
426
  async function clearLogs() {
427
+ const operation = '清理日志';
327
428
  const logs = [
328
429
  'TRUNCATE TABLE mysql.slow_log',
329
430
  'TRUNCATE TABLE mysql.general_log',
330
431
  'FLUSH LOGS'
331
432
  ];
332
-
333
- logs.forEach(sql => emit('execute-sql', sql));
334
- await modal.success('日志清理完成');
433
+
434
+ logs.forEach(sql => {
435
+ addExecutionResult(`清理日志 - ${sql.split(' ')[1]}`, 'info', { sql, message: '已发送 SQL 查询' });
436
+ emit('execute-sql', sql);
437
+ });
335
438
  }
336
439
 
337
440
  // 数据迁移
338
441
  function showExportModal() {
339
- modal.info('导出结构功能开发中...');
442
+ addExecutionResult('导出结构', 'info', { message: '导出结构功能开发中...' });
340
443
  }
341
444
 
342
445
  function showImportModal() {
343
- modal.info('导入数据功能开发中...');
446
+ addExecutionResult('导入数据', 'info', { message: '导入数据功能开发中...' });
344
447
  }
345
448
 
346
449
  function showSyncModal() {
347
- modal.info('数据同步功能开发中...');
450
+ addExecutionResult('数据同步', 'info', { message: '数据同步功能开发中...' });
348
451
  }
349
452
 
350
453
  // 健康检查
351
454
  async function runHealthCheck() {
352
- healthChecking.value = true;
353
- healthResults.value = [];
354
-
355
- try {
356
- const checks = [
357
- { name: '连接状态', sql: 'SELECT 1 as status' },
358
- { name: '表完整性', sql: 'SELECT COUNT(*) as status FROM information_schema.tables WHERE table_schema = DATABASE() AND table_type = "BASE TABLE"' },
359
- { name: '索引状态', sql: 'SELECT COUNT(*) as status FROM information_schema.statistics WHERE table_schema = DATABASE()' },
360
- { name: '磁盘空间', sql: 'SELECT SUM(data_length + index_length) as status FROM information_schema.tables WHERE table_schema = DATABASE()' }
361
- ];
362
-
363
- for (const check of checks) {
364
- try {
365
- // 这里应该调用实际的数据库查询
366
- healthResults.value.push({
367
- name: check.name,
368
- status: 'healthy',
369
- message: '正常'
370
- });
371
- } catch (error) {
372
- healthResults.value.push({
373
- name: check.name,
374
- status: 'error',
375
- message: error.message
376
- });
377
- }
455
+ const operation = '健康检查';
456
+ const checks = [
457
+ { name: '连接状态', sql: 'SELECT 1 as status' },
458
+ { name: '表完整性', sql: 'SELECT COUNT(*) as status FROM information_schema.tables WHERE table_schema = DATABASE() AND table_type = "BASE TABLE"' },
459
+ { name: '索引状态', sql: 'SELECT COUNT(*) as status FROM information_schema.statistics WHERE table_schema = DATABASE()' },
460
+ { name: '磁盘空间', sql: 'SELECT SUM(data_length + index_length) as status FROM information_schema.tables WHERE table_schema = DATABASE()' }
461
+ ];
462
+
463
+ const results: any[] = [];
464
+ for (const check of checks) {
465
+ try {
466
+ // 这里应该调用实际的数据库查询
467
+ results.push({
468
+ name: check.name,
469
+ status: 'healthy',
470
+ message: '正常'
471
+ });
472
+ } catch (error: any) {
473
+ results.push({
474
+ name: check.name,
475
+ status: 'error',
476
+ message: error.message
477
+ });
378
478
  }
379
-
380
- healthModalVisible.value = true;
381
- } finally {
382
- healthChecking.value = false;
383
479
  }
480
+
481
+ addExecutionResult(operation, 'success', { checks: results });
384
482
  }
385
483
 
386
484
  function showStatistics() {
387
485
  const sql = `
388
- SELECT
486
+ SELECT
389
487
  table_name as '表名',
390
488
  table_rows as '记录数',
391
489
  ROUND(((data_length + index_length) / 1024 / 1024), 2) as '大小(MB)'
392
- FROM information_schema.tables
393
- WHERE table_schema = DATABASE()
490
+ FROM information_schema.tables
491
+ WHERE table_schema = DATABASE()
394
492
  ORDER BY (data_length + index_length) DESC
395
493
  `;
494
+ addExecutionResult('数据统计', 'info', { sql: sql, message: '已发送 SQL 查询' });
396
495
  emit('execute-sql', sql);
397
496
  }
398
497
 
399
498
  function showAuditLog() {
400
499
  const sql = 'SELECT * FROM mysql.general_log ORDER BY event_time DESC LIMIT 100';
500
+ addExecutionResult('审计日志', 'info', { sql: sql, message: '已发送 SQL 查询' });
401
501
  emit('execute-sql', sql);
402
502
  }
403
503
 
@@ -414,55 +514,38 @@ function closeRestoreModal() {
414
514
  function handleFileSelect(event: Event) {
415
515
  const target = event.target as HTMLInputElement;
416
516
  if (target.files && target.files.length > 0) {
417
- selectedFile.value = target.files[0];
517
+ selectedFile.value = target.files[0] as File;
418
518
  }
419
519
  }
420
520
 
421
521
  async function performRestore() {
422
522
  if (!selectedFile.value) return;
423
-
523
+
524
+ const operation = '恢复数据库';
424
525
  try {
425
526
  restoring.value = true;
426
- // 这里简化处理,实际项目中可能需要先上传文件
427
527
  const filePath = selectedFile.value.name;
428
-
429
- await databaseService.restoreDatabase(
528
+
529
+ const res = await databaseService.restoreDatabase(
430
530
  props.connection?.id || '',
431
531
  props.database,
432
532
  filePath,
433
533
  { dropExisting: restoreOptions.value.dropExisting }
434
534
  );
435
-
436
- await modal.success('数据库恢复成功');
535
+
536
+ addExecutionResult(operation, 'success', res);
437
537
  closeRestoreModal();
438
- } catch (error) {
538
+ } catch (error: any) {
439
539
  console.error('恢复失败:', error);
440
-
441
- modal.error(error.msg || error.message || '恢复失败', {
442
- operation: 'RESTORE',
443
- database: props.database,
444
- //file: restoreFile.value,
445
- stack: error.stack
446
- });
540
+ modal.error(error.msg || error.message || '恢复失败');
541
+ addExecutionResult(operation, 'error', formatError(error));
447
542
  } finally {
448
543
  restoring.value = false;
449
544
  }
450
545
  }
451
546
 
452
- // 健康检查模态框
453
- function closeHealthModal() {
454
- healthModalVisible.value = false;
455
- healthResults.value = [];
456
- }
457
-
458
- // 辅助函数
459
- async function fetchTableList(): Promise<string[]> {
460
- // 这里应该调用API获取表列表
461
- return [];
462
- }
463
-
464
547
  function showScheduleModal() {
465
- modal.info('定时备份功能开发中...');
548
+ addExecutionResult('定时备份', 'info', { message: '定时备份功能开发中...' });
466
549
  }
467
550
  </script>
468
551
 
@@ -523,55 +606,212 @@ function showScheduleModal() {
523
606
  gap: 0.5rem;
524
607
  }
525
608
 
526
- .health-results {
527
- max-height: 400px;
528
- overflow-y: auto;
609
+ .modal {
610
+ background-color: rgba(0, 0, 0, 0.5);
611
+ }
612
+
613
+ .modal-dialog {
614
+ max-width: 600px;
615
+ }
616
+
617
+ .modal-header {
618
+ background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
619
+ border-bottom: 1px solid #e2e8f0;
620
+ }
621
+
622
+ .modal-title {
623
+ color: #1e293b;
624
+ font-weight: 600;
625
+ }
626
+
627
+ .modal-footer {
628
+ background: #f8fafc;
629
+ border-top: 1px solid #e2e8f0;
529
630
  }
530
631
 
531
- .health-item {
632
+ /* 执行结果区域 */
633
+ .execution-results {
634
+ border-top: 1px solid #e2e8f0;
635
+ background: #f8fafc;
636
+ }
637
+
638
+ .results-header {
532
639
  display: flex;
640
+ justify-content: space-between;
533
641
  align-items: center;
534
- padding: 0.75rem;
642
+ padding: 1rem 1.5rem;
643
+ background: linear-gradient(135deg, #f1f5f9 0%, #f8fafc 100%);
535
644
  border-bottom: 1px solid #e2e8f0;
536
645
  }
537
646
 
538
- .health-status {
539
- min-width: 150px;
647
+ .results-title {
648
+ margin: 0;
649
+ font-size: 1rem;
540
650
  font-weight: 600;
651
+ color: #1e293b;
541
652
  display: flex;
542
653
  align-items: center;
543
654
  gap: 0.5rem;
544
655
  }
545
656
 
546
- .health-message {
547
- flex: 1;
548
- color: #6b7280;
657
+ .results-content {
658
+ max-height: 400px;
659
+ overflow-y: auto;
660
+ padding: 1rem;
549
661
  }
550
662
 
551
- .modal {
552
- background-color: rgba(0, 0, 0, 0.5);
663
+ .no-results {
664
+ display: flex;
665
+ flex-direction: column;
666
+ align-items: center;
667
+ justify-content: center;
668
+ padding: 3rem 1rem;
669
+ color: #94a3b8;
553
670
  }
554
671
 
555
- .modal-dialog {
556
- max-width: 600px;
672
+ .no-results i {
673
+ font-size: 3rem;
674
+ margin-bottom: 1rem;
557
675
  }
558
676
 
559
- .modal-lg .modal-dialog {
560
- max-width: 800px;
677
+ .no-results p {
678
+ margin: 0;
679
+ font-size: 1rem;
561
680
  }
562
681
 
563
- .modal-header {
564
- background: linear-gradient(135deg, #f8fafc 0%, #f1f5f9 100%);
565
- border-bottom: 1px solid #e2e8f0;
682
+ .result-item {
683
+ margin-bottom: 0.75rem;
684
+ border: 1px solid #e2e8f0;
685
+ border-radius: 8px;
686
+ background: white;
687
+ overflow: hidden;
688
+ transition: box-shadow 0.2s;
566
689
  }
567
690
 
568
- .modal-title {
569
- color: #1e293b;
570
- font-weight: 600;
691
+ .result-item:hover {
692
+ box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
571
693
  }
572
694
 
573
- .modal-footer {
695
+ .result-item.result-success {
696
+ border-left: 4px solid #22c55e;
697
+ }
698
+
699
+ .result-item.result-error {
700
+ border-left: 4px solid #ef4444;
701
+ }
702
+
703
+ .result-item.result-info {
704
+ border-left: 4px solid #3b82f6;
705
+ }
706
+
707
+ .result-header {
708
+ display: flex;
709
+ justify-content: space-between;
710
+ align-items: center;
711
+ padding: 0.75rem 1rem;
712
+ cursor: pointer;
713
+ background: white;
714
+ transition: background 0.2s;
715
+ }
716
+
717
+ .result-header:hover {
574
718
  background: #f8fafc;
719
+ }
720
+
721
+ .result-title {
722
+ display: flex;
723
+ align-items: center;
724
+ gap: 0.75rem;
725
+ flex: 1;
726
+ }
727
+
728
+ .result-title i {
729
+ font-size: 1.1rem;
730
+ }
731
+
732
+ .operation-name {
733
+ font-weight: 600;
734
+ color: #1e293b;
735
+ font-size: 0.95rem;
736
+ }
737
+
738
+ .operation-time {
739
+ color: #64748b;
740
+ font-size: 0.85rem;
741
+ margin-left: auto;
742
+ }
743
+
744
+ .toggle-icon {
745
+ transition: transform 0.2s;
746
+ color: #94a3b8;
747
+ font-size: 0.9rem;
748
+ }
749
+
750
+ .toggle-icon.expanded {
751
+ transform: rotate(180deg);
752
+ }
753
+
754
+ .result-body {
755
+ padding: 1rem;
756
+ background: #fafafa;
575
757
  border-top: 1px solid #e2e8f0;
576
758
  }
759
+
760
+ .result-body pre {
761
+ margin: 0;
762
+ font-size: 0.85rem;
763
+ line-height: 1.5;
764
+ max-height: 300px;
765
+ overflow: auto;
766
+ }
767
+
768
+ .result-body code {
769
+ font-family: 'Fira Code', 'Consolas', 'Monaco', monospace;
770
+ }
771
+
772
+ /* JSON 语法高亮 - 不使用 scoped 以确保 v-html 内容能应用样式 */
773
+ :deep(.json-key) {
774
+ color: #d04255;
775
+ font-weight: 500;
776
+ }
777
+
778
+ :deep(.json-string) {
779
+ color: #22863a;
780
+ }
781
+
782
+ :deep(.json-number) {
783
+ color: #005cc5;
784
+ }
785
+
786
+ :deep(.json-boolean) {
787
+ color: #d73a49;
788
+ }
789
+
790
+ :deep(.json-null) {
791
+ color: #6f42c1;
792
+ }
793
+
794
+ /* 滚动条样式 */
795
+ .results-content::-webkit-scrollbar,
796
+ .result-body pre::-webkit-scrollbar {
797
+ width: 8px;
798
+ height: 8px;
799
+ }
800
+
801
+ .results-content::-webkit-scrollbar-track,
802
+ .result-body pre::-webkit-scrollbar-track {
803
+ background: #f1f5f9;
804
+ border-radius: 4px;
805
+ }
806
+
807
+ .results-content::-webkit-scrollbar-thumb,
808
+ .result-body pre::-webkit-scrollbar-thumb {
809
+ background: #cbd5e1;
810
+ border-radius: 4px;
811
+ }
812
+
813
+ .results-content::-webkit-scrollbar-thumb:hover,
814
+ .result-body pre::-webkit-scrollbar-thumb:hover {
815
+ background: #94a3b8;
816
+ }
577
817
  </style>