fdb2 1.0.8 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dockerignore +21 -21
- package/.editorconfig +11 -11
- package/.eslintrc.cjs +14 -14
- package/.eslintrc.json +7 -7
- package/.prettierrc.js +3 -3
- package/.tpl.env +21 -21
- package/.vscodeignore +45 -45
- package/README.md +312 -312
- package/bin/build.sh +28 -28
- package/bin/deploy.sh +8 -8
- package/bin/dev.sh +10 -10
- package/bin/docker/dev-docker-compose.yml +43 -43
- package/bin/docker/dev.Dockerfile +24 -24
- package/bin/docker/prod-docker-compose.yml +17 -17
- package/bin/docker/prod.Dockerfile +29 -29
- package/bin/fdb2.js +220 -220
- package/dist/package.json +29 -29
- package/dist/pnpm-lock.yaml +1042 -354
- package/dist/public/explorer.css +1464 -1437
- package/dist/public/explorer.js +759 -223
- package/dist/public/index.css +1026 -1026
- package/dist/public/index.js +15 -9
- package/dist/public/layout.css +221 -221
- package/dist/public/layout.js +1 -1
- package/dist/public/vue.js +8 -2
- package/dist/scripts/preinstall.js +112 -112
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +8 -0
- package/dist/server/index.js.map +1 -1
- package/dist/server/index.ts +680 -671
- package/dist/server/model/connection.entity.ts +65 -65
- package/dist/server/model/database.entity.ts +245 -245
- package/dist/server/service/connection.service.d.ts +6 -1
- package/dist/server/service/connection.service.d.ts.map +1 -1
- package/dist/server/service/connection.service.js +15 -0
- package/dist/server/service/connection.service.js.map +1 -1
- package/dist/server/service/connection.service.ts +356 -341
- package/dist/server/service/database/base.service.d.ts +27 -0
- package/dist/server/service/database/base.service.d.ts.map +1 -1
- package/dist/server/service/database/base.service.js +17 -0
- package/dist/server/service/database/base.service.js.map +1 -1
- package/dist/server/service/database/base.service.ts +406 -367
- package/dist/server/service/database/cockroachdb.service.d.ts +16 -0
- package/dist/server/service/database/cockroachdb.service.d.ts.map +1 -1
- package/dist/server/service/database/cockroachdb.service.js +220 -154
- package/dist/server/service/database/cockroachdb.service.js.map +1 -1
- package/dist/server/service/database/cockroachdb.service.ts +871 -782
- 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 +123 -0
- package/dist/server/service/database/database.service.js.map +1 -1
- package/dist/server/service/database/database.service.ts +775 -638
- package/dist/server/service/database/index.ts +6 -6
- package/dist/server/service/database/mongodb.service.d.ts +16 -0
- package/dist/server/service/database/mongodb.service.d.ts.map +1 -1
- package/dist/server/service/database/mongodb.service.js +35 -0
- package/dist/server/service/database/mongodb.service.js.map +1 -1
- package/dist/server/service/database/mongodb.service.ts +39 -1
- package/dist/server/service/database/mssql.service.d.ts +16 -0
- package/dist/server/service/database/mssql.service.d.ts.map +1 -1
- package/dist/server/service/database/mssql.service.js +168 -96
- package/dist/server/service/database/mssql.service.js.map +1 -1
- package/dist/server/service/database/mssql.service.ts +931 -840
- package/dist/server/service/database/mysql.service.d.ts +16 -0
- package/dist/server/service/database/mysql.service.d.ts.map +1 -1
- package/dist/server/service/database/mysql.service.js +189 -80
- package/dist/server/service/database/mysql.service.js.map +1 -1
- package/dist/server/service/database/mysql.service.ts +1025 -890
- package/dist/server/service/database/oracle.service.d.ts +16 -0
- package/dist/server/service/database/oracle.service.d.ts.map +1 -1
- package/dist/server/service/database/oracle.service.js +182 -120
- package/dist/server/service/database/oracle.service.js.map +1 -1
- package/dist/server/service/database/oracle.service.ts +1035 -959
- package/dist/server/service/database/postgres.service.d.ts +16 -0
- package/dist/server/service/database/postgres.service.d.ts.map +1 -1
- package/dist/server/service/database/postgres.service.js +154 -88
- package/dist/server/service/database/postgres.service.js.map +1 -1
- package/dist/server/service/database/postgres.service.ts +960 -871
- package/dist/server/service/database/sap.service.d.ts +16 -0
- package/dist/server/service/database/sap.service.d.ts.map +1 -1
- package/dist/server/service/database/sap.service.js +66 -0
- package/dist/server/service/database/sap.service.js.map +1 -1
- package/dist/server/service/database/sap.service.ts +89 -0
- package/dist/server/service/database/sqlite.service.d.ts +16 -0
- package/dist/server/service/database/sqlite.service.d.ts.map +1 -1
- package/dist/server/service/database/sqlite.service.js +77 -18
- package/dist/server/service/database/sqlite.service.js.map +1 -1
- package/dist/server/service/database/sqlite.service.ts +787 -708
- package/dist/server/service/session.service.ts +158 -158
- package/dist/view/index.html +38 -38
- package/env.d.ts +1 -1
- package/package.json +1 -1
- package/packages/vscode/.vscodeignore +44 -44
- package/packages/vscode/README.md +62 -62
- package/packages/vscode/out/database-services/cockroachdb.service.js +154 -154
- package/packages/vscode/out/database-services/mssql.service.js +96 -96
- package/packages/vscode/out/database-services/mysql.service.js +80 -80
- package/packages/vscode/out/database-services/oracle.service.js +120 -120
- package/packages/vscode/out/database-services/postgres.service.js +88 -88
- package/packages/vscode/out/database-services/sqlite.service.js +18 -18
- package/packages/vscode/out/provider/WebViewProvider.js +32 -32
- package/packages/vscode/package.json +142 -142
- package/packages/vscode/resources/icon.svg +5 -5
- package/packages/vscode/resources/webview/connection.css +41 -41
- package/packages/vscode/resources/webview/database.css +163 -163
- package/packages/vscode/resources/webview/index.html +9 -9
- package/packages/vscode/resources/webview/modules/header.tpl +13 -13
- package/packages/vscode/resources/webview/modules/initial_state.tpl +54 -54
- package/packages/vscode/resources/webview/query.css +104 -104
- package/packages/vscode/src/database-services/base.service.ts +362 -362
- package/packages/vscode/src/database-services/cockroachdb.service.ts +659 -659
- package/packages/vscode/src/database-services/connection.service.ts +340 -340
- package/packages/vscode/src/database-services/database.service.ts +629 -629
- package/packages/vscode/src/database-services/index.ts +6 -6
- package/packages/vscode/src/database-services/model/connection.entity.ts +65 -65
- package/packages/vscode/src/database-services/model/database.entity.ts +245 -245
- package/packages/vscode/src/database-services/mssql.service.ts +722 -722
- package/packages/vscode/src/database-services/mysql.service.ts +760 -760
- package/packages/vscode/src/database-services/oracle.service.ts +831 -831
- package/packages/vscode/src/database-services/postgres.service.ts +740 -740
- package/packages/vscode/src/database-services/sqlite.service.ts +558 -558
- package/packages/vscode/src/extension.ts +76 -76
- package/packages/vscode/src/provider/DatabaseTreeProvider.ts +167 -167
- package/packages/vscode/src/provider/WebViewProvider.ts +277 -277
- package/packages/vscode/src/service/DatabaseServiceBridge.ts +414 -414
- package/packages/vscode/src/typings/connection.ts +90 -90
- package/packages/vscode/tsconfig.json +21 -21
- package/public/index.html +9 -9
- package/public/modules/header.tpl +13 -13
- package/public/modules/initial_state.tpl +54 -54
- package/scripts/preinstall.js +112 -112
- package/server/index.ts +680 -671
- package/server/model/connection.entity.ts +65 -65
- package/server/model/database.entity.ts +245 -245
- package/server/service/connection.service.ts +356 -341
- package/server/service/database/base.service.ts +406 -367
- package/server/service/database/cockroachdb.service.ts +871 -782
- package/server/service/database/database.service.ts +775 -638
- package/server/service/database/index.ts +6 -6
- package/server/service/database/mongodb.service.ts +39 -1
- package/server/service/database/mssql.service.ts +931 -840
- package/server/service/database/mysql.service.ts +1025 -890
- package/server/service/database/oracle.service.ts +1035 -959
- package/server/service/database/postgres.service.ts +960 -871
- package/server/service/database/sap.service.ts +89 -0
- package/server/service/database/sqlite.service.ts +787 -708
- package/server/service/session.service.ts +158 -158
- package/server/tsconfig.json +20 -20
- package/server.js +149 -149
- package/server.pid +1 -0
- package/src/adapter/ajax.ts +135 -135
- package/src/assets/base.css +1 -1
- package/src/assets/database.css +949 -949
- package/src/assets/images/svg/illustrations/illustration-1.svg +1 -1
- package/src/assets/images/svg/illustrations/illustration-2.svg +2 -2
- package/src/assets/images/svg/illustrations/illustration-3.svg +50 -50
- package/src/assets/images/svg/illustrations/illustration-4.svg +1 -1
- package/src/assets/images/svg/illustrations/illustration-5.svg +73 -73
- package/src/assets/images/svg/illustrations/illustration-6.svg +89 -89
- package/src/assets/images/svg/illustrations/illustration-7.svg +39 -39
- package/src/assets/images/svg/separators/curve-2.svg +3 -3
- package/src/assets/images/svg/separators/curve.svg +3 -3
- package/src/assets/images/svg/separators/line.svg +3 -3
- package/src/assets/logo.svg +73 -73
- package/src/assets/main.css +1 -1
- package/src/base/config.ts +20 -20
- package/src/base/detect.ts +134 -134
- package/src/base/entity.ts +92 -92
- package/src/base/eventBus.ts +36 -36
- package/src/components/connection-editor/index.vue +588 -588
- package/src/components/dataGrid/index.vue +104 -104
- package/src/components/dataGrid/pagination.vue +105 -105
- package/src/components/loading/index.vue +42 -42
- package/src/components/modal/index.ts +180 -180
- package/src/components/modal/index.vue +560 -560
- package/src/components/toast/index.ts +43 -43
- package/src/components/toast/toast.vue +57 -57
- package/src/components/user/name.vue +103 -103
- package/src/components/user/selector.vue +416 -416
- package/src/domain/SysConfig.ts +74 -74
- package/src/platform/App.vue +7 -7
- package/src/platform/database/components/connection-detail.vue +1153 -1154
- package/src/platform/database/components/data-editor.vue +477 -477
- package/src/platform/database/components/database-detail.vue +1173 -1172
- package/src/platform/database/components/database-monitor.vue +1085 -1085
- package/src/platform/database/components/db-tools.vue +1264 -816
- package/src/platform/database/components/query-history.vue +1348 -1348
- package/src/platform/database/components/sql-executor.vue +737 -737
- package/src/platform/database/components/sql-query-editor.vue +1045 -1045
- package/src/platform/database/components/table-detail.vue +1375 -1376
- package/src/platform/database/components/table-editor.vue +916 -916
- package/src/platform/database/explorer.vue +1839 -1839
- package/src/platform/database/index.vue +1192 -1192
- package/src/platform/database/layout.vue +366 -366
- package/src/platform/database/router.ts +36 -36
- package/src/platform/database/styles/common.scss +601 -601
- package/src/platform/database/types/common.ts +444 -444
- package/src/platform/database/utils/export.ts +231 -231
- package/src/platform/database/utils/helpers.ts +436 -436
- package/src/platform/index.ts +32 -32
- package/src/platform/router.ts +40 -40
- package/src/platform/vscode/bridge.ts +121 -121
- package/src/platform/vscode/components/ConnectionPanel.vue +272 -272
- package/src/platform/vscode/components/DatabasePanel.vue +532 -532
- package/src/platform/vscode/components/QueryPanel.vue +371 -371
- package/src/platform/vscode/entry/connection.ts +13 -13
- package/src/platform/vscode/entry/database.ts +13 -13
- package/src/platform/vscode/entry/query.ts +13 -13
- package/src/platform/vscode/index.ts +5 -5
- package/src/service/base.ts +133 -127
- package/src/service/database.ts +505 -495
- package/src/service/login.ts +120 -120
- package/src/shims-vue.d.ts +6 -6
- package/src/stores/connection.ts +266 -266
- package/src/stores/session.ts +87 -87
- package/src/typings/database-types.ts +412 -412
- package/src/typings/database.ts +363 -363
- package/src/typings/global.d.ts +58 -58
- package/src/typings/pinia.d.ts +7 -7
- package/src/utils/clipboard.ts +29 -29
- package/src/utils/database-types.ts +242 -242
- package/src/utils/modal.ts +123 -123
- package/src/utils/request.ts +55 -55
- package/src/utils/sleep.ts +3 -3
- package/src/utils/toast.ts +73 -73
- package/src/utils/util.ts +171 -171
- package/src/utils/xlsx.ts +228 -228
- package/tsconfig.json +33 -33
- package/view/index.html +9 -9
- package/view/modules/header.tpl +13 -13
- package/view/modules/initial_state.tpl +19 -19
- package/vite.config.ts +424 -424
- package/vite.config.vscode.ts +47 -47
- package/fdb2.server.pid +0 -1
- package/server/backups/db_ai_breakout_2026-03-11T08-38-48-677Z.sql +0 -0
|
@@ -1,232 +1,232 @@
|
|
|
1
|
-
// 数据导出工具函数
|
|
2
|
-
import { exportToExcel } from '@/utils/xlsx';
|
|
3
|
-
|
|
4
|
-
// 导出为Excel
|
|
5
|
-
export async function exportDataToExcel(
|
|
6
|
-
data: Array<any>,
|
|
7
|
-
headers: Record<string, string> = {},
|
|
8
|
-
filename: string = 'export.xlsx'
|
|
9
|
-
) {
|
|
10
|
-
if (data.length === 0) {
|
|
11
|
-
console.warn('没有数据可导出');
|
|
12
|
-
return;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// 如果没有提供headers,则使用第一行的键作为header
|
|
16
|
-
if (Object.keys(headers).length === 0 && data.length > 0) {
|
|
17
|
-
const firstRow = data[0];
|
|
18
|
-
Object.keys(firstRow).forEach(key => {
|
|
19
|
-
headers[key] = key;
|
|
20
|
-
});
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const blob = await exportToExcel(data, headers);
|
|
24
|
-
downloadFile(blob, filename);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// 导出为CSV
|
|
28
|
-
export function exportDataToCSV(
|
|
29
|
-
data: Array<any>,
|
|
30
|
-
headers: Record<string, string> = {},
|
|
31
|
-
filename: string = 'export.csv'
|
|
32
|
-
) {
|
|
33
|
-
if (data.length === 0) {
|
|
34
|
-
console.warn('没有数据可导出');
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// 如果没有提供headers,则使用第一行的键作为header
|
|
39
|
-
if (Object.keys(headers).length === 0 && data.length > 0) {
|
|
40
|
-
const firstRow = data[0];
|
|
41
|
-
Object.keys(firstRow).forEach(key => {
|
|
42
|
-
headers[key] = key;
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// 构建CSV内容
|
|
47
|
-
const headerRow = Object.values(headers);
|
|
48
|
-
const rows = data.map(row =>
|
|
49
|
-
Object.keys(headers).map(key => {
|
|
50
|
-
const value = row[key];
|
|
51
|
-
if (value === null || value === undefined) return '';
|
|
52
|
-
if (typeof value === 'object') return JSON.stringify(value);
|
|
53
|
-
// 处理包含逗号、引号或换行符的字段
|
|
54
|
-
const stringValue = String(value);
|
|
55
|
-
if (stringValue.includes(',') || stringValue.includes('"') || stringValue.includes('\n')) {
|
|
56
|
-
return `"${stringValue.replace(/"/g, '""')}"`;
|
|
57
|
-
}
|
|
58
|
-
return stringValue;
|
|
59
|
-
})
|
|
60
|
-
);
|
|
61
|
-
|
|
62
|
-
const csvContent = [
|
|
63
|
-
headerRow.join(','),
|
|
64
|
-
...rows.map(row => row.join(','))
|
|
65
|
-
].join('\n');
|
|
66
|
-
|
|
67
|
-
const blob = new Blob(['\ufeff' + csvContent], {
|
|
68
|
-
type: 'text/csv;charset=utf-8;'
|
|
69
|
-
});
|
|
70
|
-
downloadFile(blob, filename);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// 导出为JSON
|
|
74
|
-
export function exportDataToJSON(
|
|
75
|
-
data: Array<any>,
|
|
76
|
-
filename: string = 'export.json'
|
|
77
|
-
) {
|
|
78
|
-
if (data.length === 0) {
|
|
79
|
-
console.warn('没有数据可导出');
|
|
80
|
-
return;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const jsonContent = JSON.stringify(data, null, 2);
|
|
84
|
-
const blob = new Blob([jsonContent], {
|
|
85
|
-
type: 'application/json;charset=utf-8;'
|
|
86
|
-
});
|
|
87
|
-
downloadFile(blob, filename);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// 导出为SQL
|
|
91
|
-
export function exportDataToSQL(
|
|
92
|
-
data: Array<any>,
|
|
93
|
-
tableName: string,
|
|
94
|
-
headers: Record<string, string> = {},
|
|
95
|
-
filename: string = 'export.sql'
|
|
96
|
-
) {
|
|
97
|
-
if (data.length === 0) {
|
|
98
|
-
console.warn('没有数据可导出');
|
|
99
|
-
return;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
// 如果没有提供headers,则使用第一行的键作为header
|
|
103
|
-
if (Object.keys(headers).length === 0 && data.length > 0) {
|
|
104
|
-
const firstRow = data[0];
|
|
105
|
-
Object.keys(firstRow).forEach(key => {
|
|
106
|
-
headers[key] = key;
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
const columns = Object.keys(headers);
|
|
111
|
-
const values = data.map(row =>
|
|
112
|
-
'(' + columns.map(col => {
|
|
113
|
-
const value = row[col];
|
|
114
|
-
if (value === null || value === undefined) return 'NULL';
|
|
115
|
-
if (typeof value === 'string') return `'${value.replace(/'/g, "''")}'`;
|
|
116
|
-
if (typeof value === 'number') return value.toString();
|
|
117
|
-
if (typeof value === 'boolean') return value ? '1' : '0';
|
|
118
|
-
if (value instanceof Date) return `'${value.toISOString().slice(0, 19).replace('T', ' ')}'`;
|
|
119
|
-
return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
|
|
120
|
-
}).join(', ') + ')'
|
|
121
|
-
).join(',\n');
|
|
122
|
-
|
|
123
|
-
const sqlContent = `-- Generated at ${new Date().toLocaleString()}\n`;
|
|
124
|
-
const sqlInsert = `INSERT INTO ${tableName} (${columns.join(', ')})\nVALUES\n${values};`;
|
|
125
|
-
|
|
126
|
-
const blob = new Blob([sqlContent + sqlInsert], {
|
|
127
|
-
type: 'text/plain;charset=utf-8;'
|
|
128
|
-
});
|
|
129
|
-
downloadFile(blob, filename);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// 通用的文件下载函数
|
|
133
|
-
function downloadFile(blob: Blob, filename: string) {
|
|
134
|
-
const url = URL.createObjectURL(blob);
|
|
135
|
-
const link = document.createElement('a');
|
|
136
|
-
link.href = url;
|
|
137
|
-
link.download = filename;
|
|
138
|
-
link.style.display = 'none';
|
|
139
|
-
document.body.appendChild(link);
|
|
140
|
-
link.click();
|
|
141
|
-
document.body.removeChild(link);
|
|
142
|
-
URL.revokeObjectURL(url);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// 格式化文件名,添加时间戳
|
|
146
|
-
export function formatFileName(baseName: string, extension: string): string {
|
|
147
|
-
const timestamp = new Date().toISOString()
|
|
148
|
-
.replace(/[:.]/g, '-')
|
|
149
|
-
.slice(0, 19);
|
|
150
|
-
return `${baseName}_${timestamp}.${extension}`;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// 获取导出文件的内容类型
|
|
154
|
-
export function getExportMimeType(format: 'excel' | 'csv' | 'json' | 'sql'): string {
|
|
155
|
-
const mimeTypes = {
|
|
156
|
-
excel: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
157
|
-
csv: 'text/csv;charset=utf-8;',
|
|
158
|
-
json: 'application/json;charset=utf-8;',
|
|
159
|
-
sql: 'text/plain;charset=utf-8;'
|
|
160
|
-
};
|
|
161
|
-
return mimeTypes[format];
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
// 验证导出数据
|
|
165
|
-
export function validateExportData(data: any[]): { valid: boolean; error?: string } {
|
|
166
|
-
if (!Array.isArray(data)) {
|
|
167
|
-
return { valid: false, error: '导出数据必须是数组' };
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
if (data.length === 0) {
|
|
171
|
-
return { valid: false, error: '没有数据可导出' };
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// 检查数据结构是否一致
|
|
175
|
-
if (data.length > 1) {
|
|
176
|
-
const firstKeys = Object.keys(data[0]);
|
|
177
|
-
for (let i = 1; i < data.length; i++) {
|
|
178
|
-
const currentKeys = Object.keys(data[i]);
|
|
179
|
-
if (JSON.stringify(firstKeys.sort()) !== JSON.stringify(currentKeys.sort())) {
|
|
180
|
-
return {
|
|
181
|
-
valid: false,
|
|
182
|
-
error: `数据结构不一致,第1行有 ${firstKeys.length} 个字段,第${i + 1}行有 ${currentKeys.length} 个字段`
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return { valid: true };
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
// 自动检测并生成表头映射
|
|
192
|
-
export function generateHeaders(data: any[]): Record<string, string> {
|
|
193
|
-
if (data.length === 0) return {};
|
|
194
|
-
|
|
195
|
-
const firstRow = data[0];
|
|
196
|
-
const headers: Record<string, string> = {};
|
|
197
|
-
|
|
198
|
-
Object.keys(firstRow).forEach(key => {
|
|
199
|
-
// 将驼峰命名转换为更友好的显示名称
|
|
200
|
-
const displayName = key
|
|
201
|
-
.replace(/_/g, ' ')
|
|
202
|
-
.replace(/([A-Z])/g, ' $1')
|
|
203
|
-
.replace(/^./, str => str.toUpperCase())
|
|
204
|
-
.trim();
|
|
205
|
-
|
|
206
|
-
headers[key] = displayName;
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
return headers;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
// 分批导出大数据集
|
|
213
|
-
export async function exportDataInBatches(
|
|
214
|
-
data: Array<any>,
|
|
215
|
-
exportFunction: (batch: any[], batchIndex: number) => Promise<void>,
|
|
216
|
-
batchSize: number = 1000
|
|
217
|
-
) {
|
|
218
|
-
const totalBatches = Math.ceil(data.length / batchSize);
|
|
219
|
-
|
|
220
|
-
for (let i = 0; i < totalBatches; i++) {
|
|
221
|
-
const start = i * batchSize;
|
|
222
|
-
const end = Math.min(start + batchSize, data.length);
|
|
223
|
-
const batch = data.slice(start, end);
|
|
224
|
-
|
|
225
|
-
await exportFunction(batch, i);
|
|
226
|
-
|
|
227
|
-
// 添加延迟以避免浏览器卡顿
|
|
228
|
-
if (i < totalBatches - 1) {
|
|
229
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
230
|
-
}
|
|
231
|
-
}
|
|
1
|
+
// 数据导出工具函数
|
|
2
|
+
import { exportToExcel } from '@/utils/xlsx';
|
|
3
|
+
|
|
4
|
+
// 导出为Excel
|
|
5
|
+
export async function exportDataToExcel(
|
|
6
|
+
data: Array<any>,
|
|
7
|
+
headers: Record<string, string> = {},
|
|
8
|
+
filename: string = 'export.xlsx'
|
|
9
|
+
) {
|
|
10
|
+
if (data.length === 0) {
|
|
11
|
+
console.warn('没有数据可导出');
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// 如果没有提供headers,则使用第一行的键作为header
|
|
16
|
+
if (Object.keys(headers).length === 0 && data.length > 0) {
|
|
17
|
+
const firstRow = data[0];
|
|
18
|
+
Object.keys(firstRow).forEach(key => {
|
|
19
|
+
headers[key] = key;
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const blob = await exportToExcel(data, headers);
|
|
24
|
+
downloadFile(blob, filename);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 导出为CSV
|
|
28
|
+
export function exportDataToCSV(
|
|
29
|
+
data: Array<any>,
|
|
30
|
+
headers: Record<string, string> = {},
|
|
31
|
+
filename: string = 'export.csv'
|
|
32
|
+
) {
|
|
33
|
+
if (data.length === 0) {
|
|
34
|
+
console.warn('没有数据可导出');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 如果没有提供headers,则使用第一行的键作为header
|
|
39
|
+
if (Object.keys(headers).length === 0 && data.length > 0) {
|
|
40
|
+
const firstRow = data[0];
|
|
41
|
+
Object.keys(firstRow).forEach(key => {
|
|
42
|
+
headers[key] = key;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 构建CSV内容
|
|
47
|
+
const headerRow = Object.values(headers);
|
|
48
|
+
const rows = data.map(row =>
|
|
49
|
+
Object.keys(headers).map(key => {
|
|
50
|
+
const value = row[key];
|
|
51
|
+
if (value === null || value === undefined) return '';
|
|
52
|
+
if (typeof value === 'object') return JSON.stringify(value);
|
|
53
|
+
// 处理包含逗号、引号或换行符的字段
|
|
54
|
+
const stringValue = String(value);
|
|
55
|
+
if (stringValue.includes(',') || stringValue.includes('"') || stringValue.includes('\n')) {
|
|
56
|
+
return `"${stringValue.replace(/"/g, '""')}"`;
|
|
57
|
+
}
|
|
58
|
+
return stringValue;
|
|
59
|
+
})
|
|
60
|
+
);
|
|
61
|
+
|
|
62
|
+
const csvContent = [
|
|
63
|
+
headerRow.join(','),
|
|
64
|
+
...rows.map(row => row.join(','))
|
|
65
|
+
].join('\n');
|
|
66
|
+
|
|
67
|
+
const blob = new Blob(['\ufeff' + csvContent], {
|
|
68
|
+
type: 'text/csv;charset=utf-8;'
|
|
69
|
+
});
|
|
70
|
+
downloadFile(blob, filename);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// 导出为JSON
|
|
74
|
+
export function exportDataToJSON(
|
|
75
|
+
data: Array<any>,
|
|
76
|
+
filename: string = 'export.json'
|
|
77
|
+
) {
|
|
78
|
+
if (data.length === 0) {
|
|
79
|
+
console.warn('没有数据可导出');
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
const jsonContent = JSON.stringify(data, null, 2);
|
|
84
|
+
const blob = new Blob([jsonContent], {
|
|
85
|
+
type: 'application/json;charset=utf-8;'
|
|
86
|
+
});
|
|
87
|
+
downloadFile(blob, filename);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 导出为SQL
|
|
91
|
+
export function exportDataToSQL(
|
|
92
|
+
data: Array<any>,
|
|
93
|
+
tableName: string,
|
|
94
|
+
headers: Record<string, string> = {},
|
|
95
|
+
filename: string = 'export.sql'
|
|
96
|
+
) {
|
|
97
|
+
if (data.length === 0) {
|
|
98
|
+
console.warn('没有数据可导出');
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// 如果没有提供headers,则使用第一行的键作为header
|
|
103
|
+
if (Object.keys(headers).length === 0 && data.length > 0) {
|
|
104
|
+
const firstRow = data[0];
|
|
105
|
+
Object.keys(firstRow).forEach(key => {
|
|
106
|
+
headers[key] = key;
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const columns = Object.keys(headers);
|
|
111
|
+
const values = data.map(row =>
|
|
112
|
+
'(' + columns.map(col => {
|
|
113
|
+
const value = row[col];
|
|
114
|
+
if (value === null || value === undefined) return 'NULL';
|
|
115
|
+
if (typeof value === 'string') return `'${value.replace(/'/g, "''")}'`;
|
|
116
|
+
if (typeof value === 'number') return value.toString();
|
|
117
|
+
if (typeof value === 'boolean') return value ? '1' : '0';
|
|
118
|
+
if (value instanceof Date) return `'${value.toISOString().slice(0, 19).replace('T', ' ')}'`;
|
|
119
|
+
return `'${JSON.stringify(value).replace(/'/g, "''")}'`;
|
|
120
|
+
}).join(', ') + ')'
|
|
121
|
+
).join(',\n');
|
|
122
|
+
|
|
123
|
+
const sqlContent = `-- Generated at ${new Date().toLocaleString()}\n`;
|
|
124
|
+
const sqlInsert = `INSERT INTO ${tableName} (${columns.join(', ')})\nVALUES\n${values};`;
|
|
125
|
+
|
|
126
|
+
const blob = new Blob([sqlContent + sqlInsert], {
|
|
127
|
+
type: 'text/plain;charset=utf-8;'
|
|
128
|
+
});
|
|
129
|
+
downloadFile(blob, filename);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 通用的文件下载函数
|
|
133
|
+
function downloadFile(blob: Blob, filename: string) {
|
|
134
|
+
const url = URL.createObjectURL(blob);
|
|
135
|
+
const link = document.createElement('a');
|
|
136
|
+
link.href = url;
|
|
137
|
+
link.download = filename;
|
|
138
|
+
link.style.display = 'none';
|
|
139
|
+
document.body.appendChild(link);
|
|
140
|
+
link.click();
|
|
141
|
+
document.body.removeChild(link);
|
|
142
|
+
URL.revokeObjectURL(url);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// 格式化文件名,添加时间戳
|
|
146
|
+
export function formatFileName(baseName: string, extension: string): string {
|
|
147
|
+
const timestamp = new Date().toISOString()
|
|
148
|
+
.replace(/[:.]/g, '-')
|
|
149
|
+
.slice(0, 19);
|
|
150
|
+
return `${baseName}_${timestamp}.${extension}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// 获取导出文件的内容类型
|
|
154
|
+
export function getExportMimeType(format: 'excel' | 'csv' | 'json' | 'sql'): string {
|
|
155
|
+
const mimeTypes = {
|
|
156
|
+
excel: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
|
157
|
+
csv: 'text/csv;charset=utf-8;',
|
|
158
|
+
json: 'application/json;charset=utf-8;',
|
|
159
|
+
sql: 'text/plain;charset=utf-8;'
|
|
160
|
+
};
|
|
161
|
+
return mimeTypes[format];
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 验证导出数据
|
|
165
|
+
export function validateExportData(data: any[]): { valid: boolean; error?: string } {
|
|
166
|
+
if (!Array.isArray(data)) {
|
|
167
|
+
return { valid: false, error: '导出数据必须是数组' };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (data.length === 0) {
|
|
171
|
+
return { valid: false, error: '没有数据可导出' };
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// 检查数据结构是否一致
|
|
175
|
+
if (data.length > 1) {
|
|
176
|
+
const firstKeys = Object.keys(data[0]);
|
|
177
|
+
for (let i = 1; i < data.length; i++) {
|
|
178
|
+
const currentKeys = Object.keys(data[i]);
|
|
179
|
+
if (JSON.stringify(firstKeys.sort()) !== JSON.stringify(currentKeys.sort())) {
|
|
180
|
+
return {
|
|
181
|
+
valid: false,
|
|
182
|
+
error: `数据结构不一致,第1行有 ${firstKeys.length} 个字段,第${i + 1}行有 ${currentKeys.length} 个字段`
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return { valid: true };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// 自动检测并生成表头映射
|
|
192
|
+
export function generateHeaders(data: any[]): Record<string, string> {
|
|
193
|
+
if (data.length === 0) return {};
|
|
194
|
+
|
|
195
|
+
const firstRow = data[0];
|
|
196
|
+
const headers: Record<string, string> = {};
|
|
197
|
+
|
|
198
|
+
Object.keys(firstRow).forEach(key => {
|
|
199
|
+
// 将驼峰命名转换为更友好的显示名称
|
|
200
|
+
const displayName = key
|
|
201
|
+
.replace(/_/g, ' ')
|
|
202
|
+
.replace(/([A-Z])/g, ' $1')
|
|
203
|
+
.replace(/^./, str => str.toUpperCase())
|
|
204
|
+
.trim();
|
|
205
|
+
|
|
206
|
+
headers[key] = displayName;
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
return headers;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// 分批导出大数据集
|
|
213
|
+
export async function exportDataInBatches(
|
|
214
|
+
data: Array<any>,
|
|
215
|
+
exportFunction: (batch: any[], batchIndex: number) => Promise<void>,
|
|
216
|
+
batchSize: number = 1000
|
|
217
|
+
) {
|
|
218
|
+
const totalBatches = Math.ceil(data.length / batchSize);
|
|
219
|
+
|
|
220
|
+
for (let i = 0; i < totalBatches; i++) {
|
|
221
|
+
const start = i * batchSize;
|
|
222
|
+
const end = Math.min(start + batchSize, data.length);
|
|
223
|
+
const batch = data.slice(start, end);
|
|
224
|
+
|
|
225
|
+
await exportFunction(batch, i);
|
|
226
|
+
|
|
227
|
+
// 添加延迟以避免浏览器卡顿
|
|
228
|
+
if (i < totalBatches - 1) {
|
|
229
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
232
|
}
|