@svadmin/core 0.31.0 → 0.32.1
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/package.json +3 -3
- package/src/auth-hooks.svelte.ts +42 -25
- package/src/context.svelte.ts +185 -46
- package/src/data-transfer.svelte.ts +9 -62
- package/src/export-format.ts +35 -31
- package/src/form-hooks.svelte.ts +322 -58
- package/src/helpers.ts +11 -7
- package/src/hook-utils.svelte.ts +13 -12
- package/src/hooks.svelte.ts +29 -23
- package/src/http.ts +114 -33
- package/src/i18n.svelte.ts +181 -35
- package/src/index.ts +7 -6
- package/src/mutation-hooks.svelte.ts +27 -16
- package/src/query-hooks.svelte.ts +16 -13
- package/src/router.ts +28 -8
- package/src/routing-hooks.svelte.ts +13 -18
- package/src/table-hooks.svelte.ts +5 -3
- package/src/table-state.svelte.ts +6 -3
- package/src/task-hooks.svelte.ts +27 -14
- package/src/theme.svelte.ts +248 -154
- package/src/toast.svelte.ts +1 -2
- package/src/types.ts +26 -4
- package/src/unsaved-changes.svelte.ts +4 -38
- package/src/url-sync.ts +47 -8
- package/src/useParsed.svelte.ts +16 -11
- package/src/utility-hooks.svelte.ts +8 -7
package/src/export-format.ts
CHANGED
|
@@ -5,24 +5,28 @@
|
|
|
5
5
|
export type ExportFormat = 'csv' | 'json' | 'xlsx';
|
|
6
6
|
|
|
7
7
|
/** CSV 转义,含公式注入防护 */
|
|
8
|
-
export function escapeCsvField(
|
|
9
|
-
let
|
|
10
|
-
if (/^[=+@\t\r]/.test(
|
|
11
|
-
return
|
|
12
|
-
? `"${
|
|
13
|
-
:
|
|
8
|
+
export function escapeCsvField(fieldValue: string): string {
|
|
9
|
+
let escapedValue = fieldValue;
|
|
10
|
+
if (/^[=+@\t\r]/.test(escapedValue)) escapedValue = "'" + escapedValue;
|
|
11
|
+
return escapedValue.includes(',') || escapedValue.includes('"') || escapedValue.includes('\n') || escapedValue.includes('\r')
|
|
12
|
+
? `"${escapedValue.replace(/"/g, '""')}"`
|
|
13
|
+
: escapedValue;
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
/** 将记录数组转为 CSV 字符串 */
|
|
17
17
|
export function toCsv(records: Record<string, unknown>[]): string {
|
|
18
18
|
if (records.length === 0) return '';
|
|
19
|
-
const
|
|
20
|
-
const header =
|
|
19
|
+
const fieldNames = Object.keys(records[0]);
|
|
20
|
+
const header = fieldNames.map(escapeCsvField).join(',');
|
|
21
21
|
const rows = records.map(record =>
|
|
22
|
-
|
|
23
|
-
const
|
|
24
|
-
const
|
|
25
|
-
|
|
22
|
+
fieldNames.map(fieldName => {
|
|
23
|
+
const rawFieldValue = record[fieldName];
|
|
24
|
+
const serializedFieldValue = rawFieldValue === null || rawFieldValue === undefined
|
|
25
|
+
? ''
|
|
26
|
+
: typeof rawFieldValue === 'object'
|
|
27
|
+
? JSON.stringify(rawFieldValue)
|
|
28
|
+
: String(rawFieldValue);
|
|
29
|
+
return escapeCsvField(serializedFieldValue);
|
|
26
30
|
}).join(',')
|
|
27
31
|
);
|
|
28
32
|
return [header, ...rows].join('\n');
|
|
@@ -34,8 +38,8 @@ export function toJson(records: Record<string, unknown>[]): string {
|
|
|
34
38
|
}
|
|
35
39
|
|
|
36
40
|
/** 转义 XML 特殊字符 */
|
|
37
|
-
export function escapeXml(
|
|
38
|
-
return
|
|
41
|
+
export function escapeXml(xmlValue: string): string {
|
|
42
|
+
return xmlValue.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');
|
|
39
43
|
}
|
|
40
44
|
|
|
41
45
|
/**
|
|
@@ -43,17 +47,17 @@ export function escapeXml(s: string): string {
|
|
|
43
47
|
*/
|
|
44
48
|
export function toXlsx(records: Record<string, unknown>[]): string {
|
|
45
49
|
if (records.length === 0) return '';
|
|
46
|
-
const
|
|
50
|
+
const fieldNames = Object.keys(records[0]);
|
|
47
51
|
|
|
48
|
-
const headerCells =
|
|
52
|
+
const headerCells = fieldNames.map(fieldName => `<Cell><Data ss:Type="String">${escapeXml(fieldName)}</Data></Cell>`).join('');
|
|
49
53
|
const dataRows = records.map(record =>
|
|
50
|
-
`<Row>${
|
|
51
|
-
const
|
|
52
|
-
if (
|
|
53
|
-
if (typeof
|
|
54
|
-
if (typeof
|
|
55
|
-
const
|
|
56
|
-
return `<Cell><Data ss:Type="String">${escapeXml(
|
|
54
|
+
`<Row>${fieldNames.map(fieldName => {
|
|
55
|
+
const fieldValue = record[fieldName];
|
|
56
|
+
if (fieldValue === null || fieldValue === undefined) return '<Cell><Data ss:Type="String"></Data></Cell>';
|
|
57
|
+
if (typeof fieldValue === 'number') return `<Cell><Data ss:Type="Number">${fieldValue}</Data></Cell>`;
|
|
58
|
+
if (typeof fieldValue === 'boolean') return `<Cell><Data ss:Type="Boolean">${fieldValue ? 1 : 0}</Data></Cell>`;
|
|
59
|
+
const serializedValue = typeof fieldValue === 'object' ? JSON.stringify(fieldValue) : String(fieldValue);
|
|
60
|
+
return `<Cell><Data ss:Type="String">${escapeXml(serializedValue)}</Data></Cell>`;
|
|
57
61
|
}).join('')}</Row>`
|
|
58
62
|
).join('\n');
|
|
59
63
|
|
|
@@ -84,32 +88,32 @@ export function downloadData(
|
|
|
84
88
|
|
|
85
89
|
let content: string;
|
|
86
90
|
let mimeType: string;
|
|
87
|
-
let
|
|
91
|
+
let extension: string;
|
|
88
92
|
|
|
89
93
|
switch (format) {
|
|
90
94
|
case 'json':
|
|
91
95
|
content = toJson(records);
|
|
92
96
|
mimeType = 'application/json;charset=utf-8;';
|
|
93
|
-
|
|
97
|
+
extension = 'json';
|
|
94
98
|
break;
|
|
95
99
|
case 'xlsx':
|
|
96
100
|
content = toXlsx(records);
|
|
97
101
|
mimeType = 'application/vnd.ms-excel;charset=utf-8;';
|
|
98
|
-
|
|
102
|
+
extension = 'xls';
|
|
99
103
|
break;
|
|
100
104
|
case 'csv':
|
|
101
105
|
default:
|
|
102
106
|
content = toCsv(records);
|
|
103
107
|
mimeType = 'text/csv;charset=utf-8;';
|
|
104
|
-
|
|
108
|
+
extension = 'csv';
|
|
105
109
|
break;
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
const blob = new Blob(['\uFEFF' + content], { type: mimeType });
|
|
109
113
|
const url = URL.createObjectURL(blob);
|
|
110
|
-
const
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
const downloadAnchor = document.createElement('a');
|
|
115
|
+
downloadAnchor.href = url;
|
|
116
|
+
downloadAnchor.download = `${resource}_export.${extension}`;
|
|
117
|
+
downloadAnchor.click();
|
|
114
118
|
setTimeout(() => URL.revokeObjectURL(url), 10000);
|
|
115
119
|
}
|