@ticatec/batch-data-uploader 0.0.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 henryfeng
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,154 @@
1
+ # Excel Batch Data Upload Component
2
+
3
+ [[中文文档](./README_CN.md)]
4
+
5
+ This component is designed to batch import data from Excel files and handle uploads. It supports upload status management, exporting error rows, multilingual adaptation, data preprocessing, and more. By defining template classes and using a unified UI dialog component, it enables quick adaptation for various types of Excel data upload needs.
6
+
7
+ ## Features
8
+
9
+ * Parse `.xls` / `.xlsx` files
10
+ * Custom column mapping and formatting
11
+ * Batch upload with configurable batch size
12
+ * Extensible data preprocessing logic (e.g., merging, grouping)
13
+ * Upload status display: Pending, Uploading, Success, Failed
14
+ * Export error rows to Excel
15
+ * Multilingual support (based on `@ticatec/i18n`)
16
+
17
+ ---
18
+
19
+ ## Usage
20
+
21
+ ### Installation
22
+
23
+ ```bash
24
+ npm i @ticatec/batch-data-uploader
25
+ ```
26
+
27
+ ### 1. Define a Template Class
28
+
29
+ Extend `BaseTemplate`, provide field definitions and upload logic, and optionally override `consolidateData` to process data.
30
+
31
+ ```ts
32
+ import BaseTemplate from '$lib/BaseTemplate';
33
+ import type DataColumn from './DataColumn';
34
+
35
+ class MyDataTemplate extends BaseTemplate {
36
+ constructor(uploadFun: UploadFun) {
37
+ const columns: DataColumn[] = [
38
+ { text: 'Name', field: 'name', pos: 0 },
39
+ { text: 'Email', field: 'email', pos: 1 },
40
+ { text: 'Age', field: 'age', pos: 2, parser: val => parseInt(val) },
41
+ ];
42
+ super(columns, uploadFun, 50);
43
+ }
44
+
45
+ // Optional: Override to implement merge/group logic
46
+ protected consolidateData(rows: Array<any>) {
47
+ return super.consolidateData(rows);
48
+ }
49
+ }
50
+ ```
51
+
52
+ ### 2. Use the Upload Dialog Component
53
+
54
+ ```svelte
55
+ <script lang="ts">
56
+ import UploadDialog from './UploadDialog.svelte';
57
+ import { MyDataTemplate } from './MyDataTemplate';
58
+
59
+ let showDialog = false;
60
+
61
+ function doUpload(rows: any[]): Promise<void> {
62
+ const dataChunk = rows.map(row => row.data);
63
+ return fetch('/api/upload', {
64
+ method: 'POST',
65
+ body: JSON.stringify(dataChunk),
66
+ }).then(res => {
67
+ if (!res.ok) throw new Error('Upload failed');
68
+ // After upload, write result info to row.error if needed
69
+ });
70
+ }
71
+
72
+ const template = new MyDataTemplate(doUpload);
73
+
74
+ const showUploadDialog = () => {
75
+ window.Dialog.showModal(UploadDialog, {
76
+ title: 'Batch Add Employees',
77
+ template,
78
+ });
79
+ }
80
+ </script>
81
+
82
+ <button on:click={() => showUploadDialog()}>Import Data</button>
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Parameter Reference
88
+
89
+ ### `BaseTemplate` Constructor Parameters
90
+
91
+ | Name | Type | Description |
92
+ | ----------- | ------------------------------- | -------------------------------------------- |
93
+ | `columns` | `DataColumn[]` | Defines column position and format |
94
+ | `uploadFun` | `(arr: any[]) => Promise<void>` | Upload function, called in batches |
95
+ | `batchSize` | `number` (default: 50) | Number of rows per upload batch |
96
+ | `rowOffset` | `number` (default: 1) | Row offset for data start (e.g. skip header) |
97
+
98
+ ### `DataColumn` Interface
99
+
100
+ ```ts
101
+ interface DataColumn {
102
+ text: string; // Column display text
103
+ field: string; // Data field path (supports nesting)
104
+ pos: number; // Excel column index (starting from 0)
105
+ parser?: (val: any) => any; // Optional parser function for cell values
106
+ }
107
+ ```
108
+
109
+ ---
110
+
111
+ ## Upload Workflow
112
+
113
+ 1. User selects an Excel file
114
+ 2. Call `BaseTemplate.parseExcelFile(file)` to parse data
115
+ 3. Display preview data table with `Pending` status
116
+ 4. User clicks upload, system calls `uploadFun` in batches
117
+ 5. Mark successful items, retain `error` info for failed items
118
+ 6. Failed rows can be exported to Excel
119
+
120
+ ---
121
+
122
+ ## Exporting Error Data
123
+
124
+ Use `BaseTemplate.exportErrorRowsToExcel(filename: string)` to export rows with errors as an Excel file, including original columns and error messages.
125
+
126
+ ---
127
+
128
+ ## Customization Options
129
+
130
+ * **Custom Column Display**: Define column fields and formatting functions
131
+ * **Custom Status Field**: Built-in `status` column, can be customized per business needs
132
+ * **Data Cleaning & Validation**: Implement in `consolidateData()` method
133
+ * **Multilingual Text**: Use `getI18nText` from `@ticatec/i18n`
134
+
135
+ ---
136
+
137
+ ## Dependencies
138
+
139
+ * [`xlsx`](https://www.npmjs.com/package/xlsx)
140
+ * [`@ticatec/uniface-element`](https://www.npmjs.com/package/@ticatec/uniface-element)
141
+ * [`@ticatec/i18n`](https://www.npmjs.com/package/@ticatec/i18n)
142
+
143
+ ---
144
+
145
+ ## License
146
+
147
+ MIT License.
148
+
149
+ ---
150
+
151
+ ## Author
152
+
153
+ Henry Feng
154
+ [huili.f@gmail.com](mailto:huili.f@gmail.com)
@@ -0,0 +1,54 @@
1
+ import type DataColumn from "./DataColumn";
2
+ import type { DataColumn as TableColumn } from "@ticatec/uniface-element/DataTable";
3
+ export type UploadFun = (arr: Array<any>) => Promise<void>;
4
+ export type UpdateProgressStatus = () => void;
5
+ export default abstract class BaseTemplate {
6
+ protected readonly _columns: Array<DataColumn>;
7
+ protected readonly rowOffset: number;
8
+ protected _list: Array<any>;
9
+ protected uploadFun: UploadFun;
10
+ protected batchSize: number;
11
+ protected updateProgressStatus: UpdateProgressStatus | null;
12
+ /**
13
+ *
14
+ * @param columns
15
+ * @param uploadFun
16
+ * @param batchSize
17
+ * @param rowOffset
18
+ * @protected
19
+ */
20
+ protected constructor(columns: Array<DataColumn>, uploadFun: UploadFun, batchSize?: number, rowOffset?: number);
21
+ /**
22
+ * 状态更新的监听器
23
+ * @param value
24
+ */
25
+ setProgressStatusListener(value: UpdateProgressStatus): void;
26
+ /**
27
+ * 整理数据,在子类可以通过重载完成数据的二次处理
28
+ * @param rows
29
+ * @protected
30
+ */
31
+ protected consolidateData(rows: Array<any>): Array<any>;
32
+ /**
33
+ * 解析一个excel文件
34
+ * @param file
35
+ */
36
+ parseExcelFile(file: File): Promise<void>;
37
+ /**
38
+ * 上传数据
39
+ */
40
+ upload(): Promise<void>;
41
+ /**
42
+ * 获取表格的列定义
43
+ */
44
+ get columns(): Array<TableColumn>;
45
+ /**
46
+ * 获取数据
47
+ */
48
+ get list(): Array<any>;
49
+ /**
50
+ * 导出处理异常的数据
51
+ * @param filename
52
+ */
53
+ exportErrorRowsToExcel(filename: string): void;
54
+ }
@@ -0,0 +1,149 @@
1
+ import * as XLSX from 'xlsx';
2
+ import utils from "./utils";
3
+ import { getI18nText } from "@ticatec/i18n";
4
+ import i18nKeys from "./i18n_resources/i18nKeys";
5
+ const statusColumn = {
6
+ text: "status",
7
+ width: 150,
8
+ resizable: true,
9
+ formatter: row => {
10
+ if (row.status == 'P') {
11
+ return getI18nText(i18nKeys.status.pending);
12
+ }
13
+ else if (row.status == 'U') {
14
+ return getI18nText(i18nKeys.status.uploading);
15
+ }
16
+ else {
17
+ if (row.error) {
18
+ return row.errorText;
19
+ }
20
+ else {
21
+ return getI18nText(i18nKeys.status.successful);
22
+ }
23
+ }
24
+ }
25
+ };
26
+ export default class BaseTemplate {
27
+ _columns;
28
+ rowOffset;
29
+ _list = [];
30
+ uploadFun;
31
+ batchSize;
32
+ updateProgressStatus = null;
33
+ /**
34
+ *
35
+ * @param columns
36
+ * @param uploadFun
37
+ * @param batchSize
38
+ * @param rowOffset
39
+ * @protected
40
+ */
41
+ constructor(columns, uploadFun, batchSize = 50, rowOffset = 1) {
42
+ this._columns = columns;
43
+ this.rowOffset = rowOffset;
44
+ this.uploadFun = uploadFun;
45
+ this.batchSize = batchSize;
46
+ }
47
+ /**
48
+ * 状态更新的监听器
49
+ * @param value
50
+ */
51
+ setProgressStatusListener(value) {
52
+ this.updateProgressStatus = value;
53
+ }
54
+ /**
55
+ * 整理数据,在子类可以通过重载完成数据的二次处理
56
+ * @param rows
57
+ * @protected
58
+ */
59
+ consolidateData(rows) {
60
+ return rows;
61
+ }
62
+ /**
63
+ * 解析一个excel文件
64
+ * @param file
65
+ */
66
+ async parseExcelFile(file) {
67
+ const buffer = await file.arrayBuffer();
68
+ const workbook = XLSX.read(buffer, { type: 'array' });
69
+ const sheet = workbook.Sheets[workbook.SheetNames[0]];
70
+ const range = XLSX.utils.decode_range(sheet['!ref'] || ''); // 获取范围
71
+ const rows = [];
72
+ for (let rowIndex = range.s.r + 1 + this.rowOffset; rowIndex <= range.e.r; rowIndex++) {
73
+ const rowObject = {};
74
+ for (const colDef of this._columns) {
75
+ const cellAddress = { r: rowIndex, c: colDef.pos };
76
+ const cellRef = XLSX.utils.encode_cell(cellAddress);
77
+ const cell = sheet[cellRef];
78
+ const rawValue = cell?.v;
79
+ const formattedValue = colDef.parser ? colDef.parser(rawValue) : rawValue;
80
+ utils.setNestedValue(rowObject, colDef.field, formattedValue);
81
+ }
82
+ rows.push({ data: rowObject, status: 'P' });
83
+ }
84
+ this._list = this.consolidateData(rows);
85
+ }
86
+ /**
87
+ * 上传数据
88
+ */
89
+ async upload() {
90
+ for (let i = 0; i < this.list.length; i += this.batchSize) {
91
+ const chunk = this.list.slice(i, i + this.batchSize);
92
+ chunk.forEach(item => item.status = 'U');
93
+ this.updateProgressStatus?.();
94
+ await this.uploadFun(chunk);
95
+ chunk.forEach(item => item.status = 'D');
96
+ this.updateProgressStatus?.();
97
+ }
98
+ }
99
+ /**
100
+ * 获取表格的列定义
101
+ */
102
+ get columns() {
103
+ let columns = this._columns.map(col => ({ ...col, field: `data.${col.field}` }));
104
+ return [...columns, statusColumn];
105
+ }
106
+ /**
107
+ * 获取数据
108
+ */
109
+ get list() {
110
+ return [...this._list];
111
+ }
112
+ /**
113
+ * 导出处理异常的数据
114
+ * @param filename
115
+ */
116
+ exportErrorRowsToExcel(filename) {
117
+ // 筛选出有错误的行
118
+ const errorRows = this._list.filter(row => row.error != null);
119
+ // 生成 Excel 数据(第一行为标题)
120
+ const header = [...this._columns.map(col => col.text), getI18nText(i18nKeys.errorTitle)];
121
+ const data = errorRows.map(row => {
122
+ const values = this._columns.map(col => {
123
+ return utils.getNestedValue(row.data, col.field);
124
+ });
125
+ return [...values, row.error];
126
+ });
127
+ const worksheetData = [header, ...data];
128
+ const worksheet = XLSX.utils.aoa_to_sheet(worksheetData);
129
+ const workbook = XLSX.utils.book_new();
130
+ XLSX.utils.book_append_sheet(workbook, worksheet, getI18nText(i18nKeys.sheetName));
131
+ const wbout = XLSX.write(workbook, {
132
+ bookType: 'xlsx',
133
+ type: 'array'
134
+ });
135
+ // 创建 Blob 并触发下载
136
+ const blob = new Blob([wbout], {
137
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
138
+ });
139
+ const url = URL.createObjectURL(blob);
140
+ const a = document.createElement('a');
141
+ a.href = url;
142
+ a.download = filename;
143
+ a.style.display = 'none';
144
+ document.body.appendChild(a);
145
+ a.click();
146
+ document.body.removeChild(a);
147
+ URL.revokeObjectURL(url);
148
+ }
149
+ }
@@ -0,0 +1,16 @@
1
+ import type { DataColumn as TableColumn } from "@ticatec/uniface-element/DataTable";
2
+ export type ParserText = (text: string) => any;
3
+ export default interface DataColumn extends TableColumn {
4
+ /**
5
+ * 在excel中的列号
6
+ */
7
+ pos: number;
8
+ /**
9
+ * 对于的字段名
10
+ */
11
+ field: string;
12
+ /**
13
+ * 解析函数
14
+ */
15
+ parser?: ParserText;
16
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,136 @@
1
+ <script lang="ts">
2
+ import Dialog from "@ticatec/uniface-element/Dialog";
3
+ import type {ButtonAction, ButtonActions} from "@ticatec/uniface-element/ActionBar";
4
+ import DataTable, {type IndicatorColumn} from "@ticatec/uniface-element/DataTable";
5
+ import i18n, {getI18nText} from "@ticatec/i18n";
6
+ import Box from "@ticatec/uniface-element/Box"
7
+ import {onMount} from "svelte";
8
+ import type BaseTemplate from "./BaseTemplate";
9
+ import type DataColumn from "@ticatec/uniface-element/DataTable";
10
+ import i18nKeys from "./i18n_resources/i18nKeys";
11
+
12
+ export let title: string;
13
+
14
+ export let width: string = "800px";
15
+ export let height: string = "600px"
16
+
17
+ export let closeHandler: any;
18
+ export let template: BaseTemplate;
19
+
20
+ type ProcessStatus = 'Init' | 'Pending' | 'Uploading' | 'Done'; //初始状态,待上传,上传中,处理完成
21
+
22
+ let status: ProcessStatus = 'Init';
23
+
24
+
25
+ const btnChoose: ButtonAction = {
26
+ label: getI18nText(i18nKeys.button.open),
27
+ type: 'primary',
28
+ handler: () => {
29
+ uploadField.click();
30
+ }
31
+ }
32
+
33
+ const btnUpload: ButtonAction = {
34
+ label: getI18nText(i18nKeys.button.upload),
35
+ type: 'primary',
36
+ handler: async ()=> {
37
+ status = 'Uploading';
38
+ try {
39
+ await template.upload();
40
+ } finally {
41
+ status = 'Done';
42
+ }
43
+ }
44
+ }
45
+
46
+ const btnSave: ButtonAction = {
47
+ label: getI18nText(i18nKeys.button.save),
48
+ type: 'primary',
49
+ handler: async ()=> {
50
+ template.exportErrorRowsToExcel(`error-${filename}`);
51
+ }
52
+ }
53
+
54
+ let actions: ButtonActions = [btnChoose];
55
+ let uploadField: any;
56
+ let list: Array<any> = [];
57
+ let filename: string;
58
+
59
+ const parseExcelFile = async (excelFile: File) => {
60
+ filename = excelFile.name;
61
+ window.Indicator.show(getI18nText(i18nKeys.parsing));
62
+ try {
63
+ await template.parseExcelFile(excelFile);
64
+ list = template.list;
65
+ status = list.length > 0 ? 'Pending' : 'Init';
66
+ } catch (ex) {
67
+ window.Toast.show(getI18nText(i18nKeys.parseFailure, {name: excelFile.name}));
68
+ } finally {
69
+ window.Indicator.hide();
70
+ }
71
+ }
72
+
73
+
74
+ let columns: Array<DataColumn>;
75
+
76
+ onMount(async () => {
77
+ columns = template.columns;
78
+ template.setProgressStatusListener(()=> {
79
+ list = template.list;
80
+ })
81
+ });
82
+
83
+
84
+
85
+ const indicatorColumn: IndicatorColumn = {
86
+ width: 60,
87
+ selectable: false,
88
+ displayNo: true
89
+ }
90
+
91
+
92
+ $: {
93
+ switch (status) {
94
+ case 'Init':
95
+ actions = [btnChoose];
96
+ break;
97
+ case 'Pending':
98
+ actions = [btnUpload, btnChoose];
99
+ break;
100
+ case 'Uploading':
101
+ btnUpload.disabled = true;
102
+ btnChoose.disabled = true;
103
+ actions = [...actions];
104
+ break;
105
+ case 'Done':
106
+ btnUpload.disabled = false;
107
+ btnChoose.disabled = false;
108
+ const hasError = list.filter(item => item.error == null).length != list.length;
109
+ actions = hasError ? [btnSave, btnChoose] : [btnChoose];
110
+ break;
111
+ }
112
+ }
113
+
114
+
115
+ const confirmCloseDialog = async ():Promise<boolean> => {
116
+ if (status == 'Uploading') {
117
+ window.Toast.show(getI18nText(i18nKeys.waitUploading));
118
+ return false;
119
+ } else {
120
+ return true;
121
+ }
122
+ }
123
+
124
+ </script>
125
+
126
+ <Dialog {title} {closeHandler} {actions} closeConfirm={confirmCloseDialog}
127
+ content$style="width: {width}; height: {height}; padding: 12px;">
128
+ <Box style="border: 1px solid var(--uniface-editor-border-color, #F8FAFC); width: 100%; height: 100%; cursor: {status == 'Uploading' ? 'progress' : 'default'}" round>
129
+ <DataTable style="width: 100%; height: 100%" {list} {indicatorColumn} {columns}>
130
+
131
+ </DataTable>
132
+ </Box>
133
+ <input type="file" bind:this={uploadField} on:change={(e) => parseExcelFile(e.target.files?.[0])} style="display: none"
134
+ accept=".xls,.xlsx,application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet">
135
+
136
+ </Dialog>
@@ -0,0 +1,25 @@
1
+ import type BaseTemplate from "./BaseTemplate";
2
+ interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
3
+ new (options: import('svelte').ComponentConstructorOptions<Props>): import('svelte').SvelteComponent<Props, Events, Slots> & {
4
+ $$bindings?: Bindings;
5
+ } & Exports;
6
+ (internal: unknown, props: Props & {
7
+ $$events?: Events;
8
+ $$slots?: Slots;
9
+ }): Exports & {
10
+ $set?: any;
11
+ $on?: any;
12
+ };
13
+ z_$$bindings?: Bindings;
14
+ }
15
+ declare const FileUploadWizard: $$__sveltets_2_IsomorphicComponent<{
16
+ title: string;
17
+ width?: string;
18
+ height?: string;
19
+ closeHandler: any;
20
+ template: BaseTemplate;
21
+ }, {
22
+ [evt: string]: CustomEvent<any>;
23
+ }, {}, {}, string>;
24
+ type FileUploadWizard = InstanceType<typeof FileUploadWizard>;
25
+ export default FileUploadWizard;
@@ -0,0 +1,21 @@
1
+ declare const langRes: {
2
+ batchUploading: {
3
+ status: {
4
+ pending: string;
5
+ uploading: string;
6
+ successful: string;
7
+ fail: string;
8
+ };
9
+ parsing: string;
10
+ parseFailure: string;
11
+ waitUploading: string;
12
+ button: {
13
+ upload: string;
14
+ save: string;
15
+ open: string;
16
+ };
17
+ errorTitle: string;
18
+ sheetName: string;
19
+ };
20
+ };
21
+ export default langRes;
@@ -0,0 +1,21 @@
1
+ const langRes = {
2
+ batchUploading: {
3
+ status: {
4
+ pending: '待上传',
5
+ uploading: "上传中...",
6
+ successful: '处理成功',
7
+ fail: '处理失败'
8
+ },
9
+ parsing: '解析文件...',
10
+ parseFailure: '无法解析文件{{name}}',
11
+ waitUploading: '上传过程中无法退出!',
12
+ button: {
13
+ upload: '上传',
14
+ save: '导出异常',
15
+ open: '文件'
16
+ },
17
+ errorTitle: '异常原因',
18
+ sheetName: '错误数据'
19
+ }
20
+ };
21
+ export default langRes;
@@ -0,0 +1,21 @@
1
+ declare const langRes: {
2
+ batchUploading: {
3
+ status: {
4
+ pending: string;
5
+ uploading: string;
6
+ successful: string;
7
+ fail: string;
8
+ };
9
+ parsing: string;
10
+ parseFailure: string;
11
+ waitUploading: string;
12
+ button: {
13
+ upload: string;
14
+ save: string;
15
+ open: string;
16
+ };
17
+ errorTitle: string;
18
+ sheetName: string;
19
+ };
20
+ };
21
+ export default langRes;
@@ -0,0 +1,21 @@
1
+ const langRes = {
2
+ batchUploading: {
3
+ status: {
4
+ pending: 'To upload',
5
+ uploading: "Uploading...",
6
+ successful: 'Success',
7
+ fail: 'Failure'
8
+ },
9
+ parsing: 'Parsing file...',
10
+ parseFailure: 'Cannot parse file: {{name}}',
11
+ waitUploading: 'Cannot exit on uploading!',
12
+ button: {
13
+ upload: 'Upload',
14
+ save: 'Error data',
15
+ open: 'File'
16
+ },
17
+ errorTitle: 'Error',
18
+ sheetName: 'Abnormal data'
19
+ }
20
+ };
21
+ export default langRes;
@@ -0,0 +1,55 @@
1
+ declare const i18nKeys: {
2
+ status: {
3
+ pending: {
4
+ key: string;
5
+ text: string;
6
+ };
7
+ uploading: {
8
+ key: string;
9
+ text: string;
10
+ };
11
+ successful: {
12
+ key: string;
13
+ text: string;
14
+ };
15
+ fail: {
16
+ key: string;
17
+ text: string;
18
+ };
19
+ };
20
+ parsing: {
21
+ key: string;
22
+ text: string;
23
+ };
24
+ parseFailure: {
25
+ key: string;
26
+ text: string;
27
+ };
28
+ waitUploading: {
29
+ key: string;
30
+ text: string;
31
+ };
32
+ button: {
33
+ upload: {
34
+ key: string;
35
+ text: string;
36
+ };
37
+ save: {
38
+ key: string;
39
+ text: string;
40
+ };
41
+ open: {
42
+ key: string;
43
+ text: string;
44
+ };
45
+ };
46
+ errorTitle: {
47
+ key: string;
48
+ text: string;
49
+ };
50
+ sheetName: {
51
+ key: string;
52
+ text: string;
53
+ };
54
+ };
55
+ export default i18nKeys;
@@ -0,0 +1,56 @@
1
+ import langRes from "./batch_en_resource";
2
+ const i18nKeys = {
3
+ status: {
4
+ pending: {
5
+ key: "batchUploading.status.pending",
6
+ text: langRes.batchUploading.status.pending
7
+ },
8
+ uploading: {
9
+ key: "batchUploading.status.uploading",
10
+ text: langRes.batchUploading.status.uploading
11
+ },
12
+ successful: {
13
+ key: "batchUploading.status.successful",
14
+ text: langRes.batchUploading.status.successful
15
+ },
16
+ fail: {
17
+ key: "batchUploading.status.fail",
18
+ text: langRes.batchUploading.status.fail
19
+ }
20
+ },
21
+ parsing: {
22
+ key: 'batchUploading.parsing',
23
+ text: langRes.batchUploading.parsing
24
+ },
25
+ parseFailure: {
26
+ key: 'batchUploading.parseFailure',
27
+ text: langRes.batchUploading.parseFailure
28
+ },
29
+ waitUploading: {
30
+ key: 'batchUploading.waitUploading',
31
+ text: langRes.batchUploading.waitUploading
32
+ },
33
+ button: {
34
+ upload: {
35
+ key: 'batchUploading.button.upload',
36
+ text: langRes.batchUploading.button.upload
37
+ },
38
+ save: {
39
+ key: 'batchUploading.button.save',
40
+ text: langRes.batchUploading.button.save
41
+ },
42
+ open: {
43
+ key: 'batchUploading.button.open',
44
+ text: langRes.batchUploading.button.open
45
+ }
46
+ },
47
+ errorTitle: {
48
+ key: 'batchUploading.errorTitle',
49
+ text: langRes.batchUploading.errorTitle
50
+ },
51
+ sheetName: {
52
+ key: 'batchUploading.sheetName',
53
+ text: langRes.batchUploading.sheetName
54
+ }
55
+ };
56
+ export default i18nKeys;
@@ -0,0 +1,3 @@
1
+ import cn_resource from "./batch_cn_resource";
2
+ import en_resource from "./batch_en_resource";
3
+ export { cn_resource, en_resource };
@@ -0,0 +1,3 @@
1
+ import cn_resource from "./batch_cn_resource";
2
+ import en_resource from "./batch_en_resource";
3
+ export { cn_resource, en_resource };
@@ -0,0 +1,2 @@
1
+ import FileUploadWizard from "./FileUploadWizard.svelte";
2
+ export default FileUploadWizard;
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ import FileUploadWizard from "./FileUploadWizard.svelte";
2
+ export default FileUploadWizard;
@@ -0,0 +1,5 @@
1
+ declare const _default: {
2
+ setNestedValue: (obj: any, path: string, value: any) => void;
3
+ getNestedValue: (obj: any, path: string) => any;
4
+ };
5
+ export default _default;
package/dist/utils.js ADDED
@@ -0,0 +1,22 @@
1
+ const setNestedValue = (obj, path, value) => {
2
+ const keys = path.split('.');
3
+ let current = obj;
4
+ for (let i = 0; i < keys.length - 1; i++) {
5
+ if (!current[keys[i]])
6
+ current[keys[i]] = {};
7
+ current = current[keys[i]];
8
+ }
9
+ current[keys[keys.length - 1]] = value;
10
+ };
11
+ /**
12
+ * 获取嵌套属性的值
13
+ * @param obj
14
+ * @param path
15
+ */
16
+ const getNestedValue = (obj, path) => {
17
+ return path.split('.').reduce((acc, key) => acc?.[key], obj);
18
+ };
19
+ export default {
20
+ setNestedValue,
21
+ getNestedValue
22
+ };
package/package.json ADDED
@@ -0,0 +1,83 @@
1
+ {
2
+ "name": "@ticatec/batch-data-uploader",
3
+ "version": "0.0.1",
4
+ "description": "A reusable Svelte component for batch uploading Excel data with support for error handling, multi-language, and preprocessing.",
5
+ "scripts": {
6
+ "dev": "vite dev",
7
+ "build": "svelte-kit sync && svelte-package",
8
+ "package": "svelte-kit sync && svelte-package && publint",
9
+ "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
10
+ "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
11
+ "publish:public": "npm run build && npm publish --access public"
12
+ },
13
+ "keywords": [
14
+ "excel",
15
+ "upload",
16
+ "svelte",
17
+ "component",
18
+ "batch",
19
+ "uploader",
20
+ "xlsx",
21
+ "i18n"
22
+ ],
23
+ "files": [
24
+ "dist",
25
+ "!dist/**/*.scss",
26
+ "!dist/**/*.test.*",
27
+ "!dist/**/*.spec.*"
28
+ ],
29
+ "sideEffects": [
30
+ "**/*.css"
31
+ ],
32
+ "main": "dist/index.js",
33
+ "svelte": "./dist/index.js",
34
+ "types": "./dist/index.d.ts",
35
+ "type": "module",
36
+ "exports": {
37
+ ".": {
38
+ "types": "./dist/index.d.ts",
39
+ "import": "./dist/index.js"
40
+ },
41
+ "./BaseTemplate": {
42
+ "types": "./dist/BaseTemplate.d.ts",
43
+ "import": "./dist/BaseTemplate.js"
44
+ },
45
+ "./DataColumn": {
46
+ "types": "./dist/DataColumn.d.ts",
47
+ "import": "./dist/DataColumn.js"
48
+ },
49
+ "./FileUploadWizard.svelte": {
50
+ "types": "./dist/FileUploadWizard.svelte.d.ts",
51
+ "import": "./dist/FileUploadWizard.svelte"
52
+ }
53
+ },
54
+ "peerDependencies": {
55
+ "svelte": "^5.0.0"
56
+ },
57
+ "devDependencies": {
58
+ "@sveltejs/adapter-auto": "^4.0.0",
59
+ "@sveltejs/adapter-static": "^3.0.8",
60
+ "@sveltejs/kit": "^2.0.0",
61
+ "@sveltejs/package": "^2.0.0",
62
+ "@sveltejs/vite-plugin-svelte": "^4.0.0",
63
+ "@ticatec/i18n": "^0.0.8",
64
+ "@ticatec/uniface-element": "^0.1.52",
65
+ "@ticatec/uniface-google-material-icons": "^0.1.2",
66
+ "dayjs": "^1.11.10",
67
+ "publint": "^0.3.2",
68
+ "sass": "^1.57.1",
69
+ "svelte": "^5.0.0",
70
+ "svelte-check": "^4.0.0",
71
+ "svelte-portal": "^2.2.1",
72
+ "svelte-preprocess": "^6.0.3",
73
+ "tslib": "^2.3.1",
74
+ "typescript": "^5.7.3",
75
+ "vite": "^5.4.11"
76
+ },
77
+ "author": "Henry Feng",
78
+ "license": "MIT",
79
+ "private": false,
80
+ "dependencies": {
81
+ "xlsx": "^0.18.5"
82
+ }
83
+ }