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.
- package/.vscodeignore +45 -0
- package/README.md +24 -85
- package/dist/package.json +115 -0
- package/dist/pnpm-lock.yaml +7447 -0
- package/dist/public/explorer.css +244 -111
- package/dist/public/explorer.js +523 -278
- package/dist/scripts/preinstall.js +112 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +4 -9
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.ts +5 -10
- package/dist/server/service/connection.service.d.ts.map +1 -1
- package/dist/server/service/connection.service.js +1 -0
- package/dist/server/service/connection.service.js.map +1 -1
- package/dist/server/service/connection.service.ts +1 -0
- package/dist/server/service/database/base.service.d.ts +4 -0
- package/dist/server/service/database/base.service.d.ts.map +1 -1
- package/dist/server/service/database/base.service.js +3 -3
- package/dist/server/service/database/base.service.js.map +1 -1
- package/dist/server/service/database/base.service.ts +8 -3
- package/dist/server/service/database/cockroachdb.service.d.ts +5 -0
- package/dist/server/service/database/cockroachdb.service.d.ts.map +1 -1
- package/dist/server/service/database/cockroachdb.service.js +112 -0
- package/dist/server/service/database/cockroachdb.service.js.map +1 -1
- package/dist/server/service/database/cockroachdb.service.ts +123 -0
- package/dist/server/service/database/database.service.d.ts +4 -0
- package/dist/server/service/database/database.service.d.ts.map +1 -1
- package/dist/server/service/database/database.service.js +8 -0
- package/dist/server/service/database/database.service.js.map +1 -1
- package/dist/server/service/database/database.service.ts +9 -0
- package/dist/server/service/database/mongodb.service.d.ts +6 -0
- package/dist/server/service/database/mongodb.service.d.ts.map +1 -1
- package/dist/server/service/database/mongodb.service.js +8 -0
- package/dist/server/service/database/mongodb.service.js.map +1 -1
- package/dist/server/service/database/mongodb.service.ts +9 -0
- package/dist/server/service/database/mssql.service.d.ts +4 -0
- package/dist/server/service/database/mssql.service.d.ts.map +1 -1
- package/dist/server/service/database/mssql.service.js +105 -0
- package/dist/server/service/database/mssql.service.js.map +1 -1
- package/dist/server/service/database/mssql.service.ts +118 -0
- package/dist/server/service/database/mysql.service.d.ts +4 -0
- package/dist/server/service/database/mysql.service.d.ts.map +1 -1
- package/dist/server/service/database/mysql.service.js +116 -0
- package/dist/server/service/database/mysql.service.js.map +1 -1
- package/dist/server/service/database/mysql.service.ts +130 -0
- package/dist/server/service/database/oracle.service.d.ts +4 -0
- package/dist/server/service/database/oracle.service.d.ts.map +1 -1
- package/dist/server/service/database/oracle.service.js +114 -0
- package/dist/server/service/database/oracle.service.js.map +1 -1
- package/dist/server/service/database/oracle.service.ts +128 -0
- package/dist/server/service/database/postgres.service.d.ts +4 -0
- package/dist/server/service/database/postgres.service.d.ts.map +1 -1
- package/dist/server/service/database/postgres.service.js +120 -0
- package/dist/server/service/database/postgres.service.js.map +1 -1
- package/dist/server/service/database/postgres.service.ts +131 -0
- package/dist/server/service/database/sap.service.d.ts +4 -0
- package/dist/server/service/database/sap.service.d.ts.map +1 -1
- package/dist/server/service/database/sap.service.js +107 -0
- package/dist/server/service/database/sap.service.js.map +1 -1
- package/dist/server/service/database/sap.service.ts +120 -0
- package/dist/server/service/database/sqlite.service.d.ts +5 -0
- package/dist/server/service/database/sqlite.service.d.ts.map +1 -1
- package/dist/server/service/database/sqlite.service.js +133 -0
- package/dist/server/service/database/sqlite.service.js.map +1 -1
- package/dist/server/service/database/sqlite.service.ts +150 -0
- package/fdb2.server.pid +1 -0
- package/package.json +18 -9
- package/packages/vscode/.vscodeignore +44 -0
- package/packages/vscode/README.md +62 -0
- package/packages/vscode/out/database-services/base.service.js +236 -0
- package/packages/vscode/out/database-services/base.service.js.map +1 -0
- package/packages/vscode/out/database-services/cockroachdb.service.js +634 -0
- package/packages/vscode/out/database-services/cockroachdb.service.js.map +1 -0
- package/packages/vscode/out/database-services/connection.service.js +346 -0
- package/packages/vscode/out/database-services/connection.service.js.map +1 -0
- package/packages/vscode/out/database-services/database.service.js +571 -0
- package/packages/vscode/out/database-services/database.service.js.map +1 -0
- package/packages/vscode/out/database-services/index.js +18 -0
- package/packages/vscode/out/database-services/index.js.map +1 -0
- package/packages/vscode/out/database-services/model/connection.entity.js +11 -0
- package/packages/vscode/out/database-services/model/connection.entity.js.map +1 -0
- package/packages/vscode/out/database-services/model/database.entity.js +35 -0
- package/packages/vscode/out/database-services/model/database.entity.js.map +1 -0
- package/packages/vscode/out/database-services/mongodb.service.js +458 -0
- package/packages/vscode/out/database-services/mongodb.service.js.map +1 -0
- package/packages/vscode/out/database-services/mssql.service.js +694 -0
- package/packages/vscode/out/database-services/mssql.service.js.map +1 -0
- package/packages/vscode/out/database-services/mysql.service.js +735 -0
- package/packages/vscode/out/database-services/mysql.service.js.map +1 -0
- package/packages/vscode/out/database-services/oracle.service.js +787 -0
- package/packages/vscode/out/database-services/oracle.service.js.map +1 -0
- package/packages/vscode/out/database-services/postgres.service.js +696 -0
- package/packages/vscode/out/database-services/postgres.service.js.map +1 -0
- package/packages/vscode/out/database-services/sap.service.js +695 -0
- package/packages/vscode/out/database-services/sap.service.js.map +1 -0
- package/packages/vscode/out/database-services/sqlite.service.js +532 -0
- package/packages/vscode/out/database-services/sqlite.service.js.map +1 -0
- package/packages/vscode/out/extension.js +93 -0
- package/packages/vscode/out/extension.js.map +1 -0
- package/packages/vscode/out/provider/DatabaseTreeProvider.js +159 -0
- package/packages/vscode/out/provider/DatabaseTreeProvider.js.map +1 -0
- package/packages/vscode/out/provider/WebViewProvider.js +259 -0
- package/packages/vscode/out/provider/WebViewProvider.js.map +1 -0
- package/packages/vscode/out/service/ConnectionManager.js +105 -0
- package/packages/vscode/out/service/ConnectionManager.js.map +1 -0
- package/packages/vscode/out/service/DatabaseServiceBridge.js +395 -0
- package/packages/vscode/out/service/DatabaseServiceBridge.js.map +1 -0
- package/packages/vscode/out/typings/connection.js +3 -0
- package/packages/vscode/out/typings/connection.js.map +1 -0
- package/packages/vscode/package.json +142 -0
- package/packages/vscode/resources/icon.svg +5 -0
- package/packages/vscode/resources/webview/_plugin-vue_export-helper.js +6529 -0
- package/packages/vscode/resources/webview/_plugin-vue_export-helper.js.map +1 -0
- package/packages/vscode/resources/webview/connection.css +69 -0
- package/packages/vscode/resources/webview/connection.js +228 -0
- package/packages/vscode/resources/webview/connection.js.map +1 -0
- package/packages/vscode/resources/webview/database.css +259 -0
- package/packages/vscode/resources/webview/database.js +275 -0
- package/packages/vscode/resources/webview/database.js.map +1 -0
- package/packages/vscode/resources/webview/favicon.ico +0 -0
- package/packages/vscode/resources/webview/index.html +9 -0
- package/packages/vscode/resources/webview/modules/header.tpl +14 -0
- package/packages/vscode/resources/webview/modules/initial_state.tpl +55 -0
- package/packages/vscode/resources/webview/query.css +162 -0
- package/packages/vscode/resources/webview/query.js +198 -0
- package/packages/vscode/resources/webview/query.js.map +1 -0
- package/packages/vscode/src/database-services/base.service.js.map +1 -0
- package/packages/vscode/src/database-services/base.service.ts +363 -0
- package/packages/vscode/src/database-services/cockroachdb.service.js.map +1 -0
- package/packages/vscode/src/database-services/cockroachdb.service.ts +659 -0
- package/packages/vscode/src/database-services/connection.service.ts +341 -0
- package/packages/vscode/src/database-services/database.service.ts +630 -0
- package/packages/vscode/src/database-services/index.ts +7 -0
- package/packages/vscode/src/database-services/model/connection.entity.js +11 -0
- package/packages/vscode/src/database-services/model/connection.entity.js.map +1 -0
- package/packages/vscode/src/database-services/model/connection.entity.ts +66 -0
- package/packages/vscode/src/database-services/model/database.entity.js +35 -0
- package/packages/vscode/src/database-services/model/database.entity.js.map +1 -0
- package/packages/vscode/src/database-services/model/database.entity.ts +246 -0
- package/packages/vscode/src/database-services/mongodb.service.js.map +1 -0
- package/packages/vscode/src/database-services/mongodb.service.ts +454 -0
- package/packages/vscode/src/database-services/mssql.service.js.map +1 -0
- package/packages/vscode/src/database-services/mssql.service.ts +723 -0
- package/packages/vscode/src/database-services/mysql.service.js.map +1 -0
- package/packages/vscode/src/database-services/mysql.service.ts +761 -0
- package/packages/vscode/src/database-services/oracle.service.js.map +1 -0
- package/packages/vscode/src/database-services/oracle.service.ts +832 -0
- package/packages/vscode/src/database-services/postgres.service.js.map +1 -0
- package/packages/vscode/src/database-services/postgres.service.ts +741 -0
- package/packages/vscode/src/database-services/sap.service.js.map +1 -0
- package/packages/vscode/src/database-services/sap.service.ts +713 -0
- package/packages/vscode/src/database-services/sqlite.service.js.map +1 -0
- package/packages/vscode/src/database-services/sqlite.service.ts +559 -0
- package/packages/vscode/src/extension.ts +76 -0
- package/packages/vscode/src/provider/DatabaseTreeProvider.ts +167 -0
- package/packages/vscode/src/provider/WebViewProvider.ts +277 -0
- package/packages/vscode/src/service/DatabaseServiceBridge.ts +414 -0
- package/packages/vscode/src/typings/connection.ts +90 -0
- package/packages/vscode/tsconfig.json +21 -0
- package/public/fdb2.png +0 -0
- package/server/backups/db_ai_breakout_2026-03-11T08-38-48-677Z.sql +0 -0
- package/server/index.ts +5 -10
- package/server/model/connection.entity.js +11 -0
- package/server/model/connection.entity.js.map +1 -0
- package/server/model/database.entity.js +35 -0
- package/server/model/database.entity.js.map +1 -0
- package/server/service/connection.service.ts +1 -0
- package/server/service/database/base.service.ts +8 -3
- package/server/service/database/cockroachdb.service.ts +123 -0
- package/server/service/database/database.service.ts +9 -0
- package/server/service/database/mongodb.service.ts +9 -0
- package/server/service/database/mssql.service.ts +118 -0
- package/server/service/database/mysql.service.ts +130 -0
- package/server/service/database/oracle.service.ts +128 -0
- package/server/service/database/postgres.service.ts +131 -0
- package/server/service/database/sap.service.ts +120 -0
- package/server/service/database/sqlite.service.ts +150 -0
- package/server/tsconfig.json +20 -0
- package/src/components/connection-editor/index.vue +0 -1
- package/src/platform/database/components/db-tools.vue +414 -174
- package/src/platform/database/components/table-detail.vue +3 -2
- package/src/platform/database/components/table-editor.vue +245 -18
- package/src/platform/vscode/bridge.ts +121 -0
- package/src/platform/vscode/components/ConnectionPanel.vue +272 -0
- package/src/platform/vscode/components/DatabasePanel.vue +532 -0
- package/src/platform/vscode/components/QueryPanel.vue +371 -0
- package/src/platform/vscode/entry/connection.ts +13 -0
- package/src/platform/vscode/entry/database.ts +13 -0
- package/src/platform/vscode/entry/query.ts +13 -0
- package/src/platform/vscode/index.ts +5 -0
- package/src/service/database.ts +2 -6
- package/vite.config.ts +46 -6
- 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
|
|
219
|
-
|
|
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)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
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
|
-
|
|
344
|
+
addExecutionResult('用户列表', 'info', { message: '用户列表功能开发中...' });
|
|
251
345
|
}
|
|
252
346
|
|
|
253
347
|
function showCreateUserModal() {
|
|
254
|
-
|
|
348
|
+
addExecutionResult('创建用户', 'info', { message: '创建用户功能开发中...' });
|
|
255
349
|
}
|
|
256
350
|
|
|
257
351
|
function showPermissionsModal() {
|
|
258
|
-
|
|
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
|
-
|
|
282
|
-
|
|
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
|
-
|
|
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
|
-
|
|
298
|
-
|
|
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
|
-
|
|
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
|
-
|
|
314
|
-
|
|
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
|
-
|
|
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 =>
|
|
334
|
-
|
|
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
|
-
|
|
442
|
+
addExecutionResult('导出结构', 'info', { message: '导出结构功能开发中...' });
|
|
340
443
|
}
|
|
341
444
|
|
|
342
445
|
function showImportModal() {
|
|
343
|
-
|
|
446
|
+
addExecutionResult('导入数据', 'info', { message: '导入数据功能开发中...' });
|
|
344
447
|
}
|
|
345
448
|
|
|
346
449
|
function showSyncModal() {
|
|
347
|
-
|
|
450
|
+
addExecutionResult('数据同步', 'info', { message: '数据同步功能开发中...' });
|
|
348
451
|
}
|
|
349
452
|
|
|
350
453
|
// 健康检查
|
|
351
454
|
async function runHealthCheck() {
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
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
|
-
|
|
535
|
+
|
|
536
|
+
addExecutionResult(operation, 'success', res);
|
|
437
537
|
closeRestoreModal();
|
|
438
|
-
} catch (error) {
|
|
538
|
+
} catch (error: any) {
|
|
439
539
|
console.error('恢复失败:', error);
|
|
440
|
-
|
|
441
|
-
|
|
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
|
-
|
|
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
|
-
.
|
|
527
|
-
|
|
528
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
-
.
|
|
539
|
-
|
|
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
|
-
.
|
|
547
|
-
|
|
548
|
-
|
|
657
|
+
.results-content {
|
|
658
|
+
max-height: 400px;
|
|
659
|
+
overflow-y: auto;
|
|
660
|
+
padding: 1rem;
|
|
549
661
|
}
|
|
550
662
|
|
|
551
|
-
.
|
|
552
|
-
|
|
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
|
-
.
|
|
556
|
-
|
|
672
|
+
.no-results i {
|
|
673
|
+
font-size: 3rem;
|
|
674
|
+
margin-bottom: 1rem;
|
|
557
675
|
}
|
|
558
676
|
|
|
559
|
-
.
|
|
560
|
-
|
|
677
|
+
.no-results p {
|
|
678
|
+
margin: 0;
|
|
679
|
+
font-size: 1rem;
|
|
561
680
|
}
|
|
562
681
|
|
|
563
|
-
.
|
|
564
|
-
|
|
565
|
-
border
|
|
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
|
-
.
|
|
569
|
-
|
|
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
|
-
.
|
|
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>
|