react-util-tools 1.0.24 → 1.0.26

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.
@@ -0,0 +1,425 @@
1
+ # Excel 模块使用示例
2
+
3
+ ## React 组件示例
4
+
5
+ ### 1. Excel 文件上传和解析
6
+
7
+ ```tsx
8
+ import React, { useState } from 'react'
9
+ import { readExcelToJSON } from 'react-util-tools'
10
+
11
+ interface UserData {
12
+ name: string
13
+ age: number
14
+ email: string
15
+ }
16
+
17
+ function ExcelUploader() {
18
+ const [data, setData] = useState<UserData[]>([])
19
+ const [loading, setLoading] = useState(false)
20
+
21
+ const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
22
+ const file = e.target.files?.[0]
23
+ if (!file) return
24
+
25
+ setLoading(true)
26
+ try {
27
+ const jsonData = await readExcelToJSON<UserData>(file)
28
+ setData(jsonData)
29
+ console.log('解析成功:', jsonData)
30
+ } catch (error) {
31
+ console.error('解析失败:', error)
32
+ alert('文件解析失败,请检查文件格式')
33
+ } finally {
34
+ setLoading(false)
35
+ }
36
+ }
37
+
38
+ return (
39
+ <div>
40
+ <input
41
+ type="file"
42
+ accept=".xlsx,.xls,.csv"
43
+ onChange={handleFileChange}
44
+ disabled={loading}
45
+ />
46
+ {loading && <p>正在解析...</p>}
47
+ {data.length > 0 && (
48
+ <table>
49
+ <thead>
50
+ <tr>
51
+ <th>姓名</th>
52
+ <th>年龄</th>
53
+ <th>邮箱</th>
54
+ </tr>
55
+ </thead>
56
+ <tbody>
57
+ {data.map((row, index) => (
58
+ <tr key={index}>
59
+ <td>{row.name}</td>
60
+ <td>{row.age}</td>
61
+ <td>{row.email}</td>
62
+ </tr>
63
+ ))}
64
+ </tbody>
65
+ </table>
66
+ )}
67
+ </div>
68
+ )
69
+ }
70
+
71
+ export default ExcelUploader
72
+ ```
73
+
74
+ ### 2. 导出数据为 Excel
75
+
76
+ ```tsx
77
+ import React from 'react'
78
+ import { exportJSONToExcel } from 'react-util-tools'
79
+
80
+ interface Product {
81
+ id: number
82
+ name: string
83
+ price: number
84
+ stock: number
85
+ }
86
+
87
+ function ProductList() {
88
+ const products: Product[] = [
89
+ { id: 1, name: 'iPhone 15', price: 5999, stock: 100 },
90
+ { id: 2, name: 'iPad Pro', price: 6999, stock: 50 },
91
+ { id: 3, name: 'MacBook Pro', price: 12999, stock: 30 }
92
+ ]
93
+
94
+ const handleExport = () => {
95
+ exportJSONToExcel(
96
+ products,
97
+ 'products.xlsx',
98
+ 'ProductList'
99
+ )
100
+ }
101
+
102
+ return (
103
+ <div>
104
+ <button onClick={handleExport}>导出为 Excel</button>
105
+ <table>
106
+ <thead>
107
+ <tr>
108
+ <th>ID</th>
109
+ <th>产品名称</th>
110
+ <th>价格</th>
111
+ <th>库存</th>
112
+ </tr>
113
+ </thead>
114
+ <tbody>
115
+ {products.map(product => (
116
+ <tr key={product.id}>
117
+ <td>{product.id}</td>
118
+ <td>{product.name}</td>
119
+ <td>¥{product.price}</td>
120
+ <td>{product.stock}</td>
121
+ </tr>
122
+ ))}
123
+ </tbody>
124
+ </table>
125
+ </div>
126
+ )
127
+ }
128
+
129
+ export default ProductList
130
+ ```
131
+
132
+ ### 3. 多工作表导出
133
+
134
+ ```tsx
135
+ import React from 'react'
136
+ import { XLSX, utils, exportExcelFile } from 'react-util-tools'
137
+
138
+ function MultiSheetExport() {
139
+ const handleExport = () => {
140
+ // 创建工作簿
141
+ const workbook = utils.book_new()
142
+
143
+ // 用户数据
144
+ const users = [
145
+ { name: '张三', age: 25, department: '技术部' },
146
+ { name: '李四', age: 30, department: '市场部' }
147
+ ]
148
+ const userSheet = utils.json_to_sheet(users)
149
+ utils.book_append_sheet(workbook, userSheet, '员工列表')
150
+
151
+ // 销售数据
152
+ const sales = [
153
+ { month: '1月', revenue: 100000, profit: 20000 },
154
+ { month: '2月', revenue: 120000, profit: 25000 }
155
+ ]
156
+ const salesSheet = utils.json_to_sheet(sales)
157
+ utils.book_append_sheet(workbook, salesSheet, '销售数据')
158
+
159
+ // 导出
160
+ exportExcelFile(workbook, 'company-report.xlsx')
161
+ }
162
+
163
+ return (
164
+ <button onClick={handleExport}>
165
+ 导出多工作表报表
166
+ </button>
167
+ )
168
+ }
169
+
170
+ export default MultiSheetExport
171
+ ```
172
+
173
+ ### 4. 读取指定工作表
174
+
175
+ ```tsx
176
+ import React, { useState } from 'react'
177
+ import { readExcelFile, getSheetNames, workbookToJSON } from 'react-util-tools'
178
+
179
+ function MultiSheetReader() {
180
+ const [sheets, setSheets] = useState<string[]>([])
181
+ const [selectedSheet, setSelectedSheet] = useState<string>('')
182
+ const [data, setData] = useState<any[]>([])
183
+
184
+ const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => {
185
+ const file = e.target.files?.[0]
186
+ if (!file) return
187
+
188
+ try {
189
+ const workbook = await readExcelFile(file)
190
+ const sheetNames = getSheetNames(workbook)
191
+ setSheets(sheetNames)
192
+
193
+ // 默认读取第一个工作表
194
+ if (sheetNames.length > 0) {
195
+ const firstSheet = sheetNames[0]
196
+ setSelectedSheet(firstSheet)
197
+ const jsonData = workbookToJSON(workbook, firstSheet)
198
+ setData(jsonData)
199
+ }
200
+ } catch (error) {
201
+ console.error('读取失败:', error)
202
+ }
203
+ }
204
+
205
+ const handleSheetChange = async (sheetName: string) => {
206
+ setSelectedSheet(sheetName)
207
+ // 重新读取文件并获取指定工作表
208
+ // 实际应用中应该缓存 workbook
209
+ }
210
+
211
+ return (
212
+ <div>
213
+ <input type="file" accept=".xlsx,.xls" onChange={handleFileChange} />
214
+
215
+ {sheets.length > 0 && (
216
+ <div>
217
+ <label>选择工作表:</label>
218
+ <select value={selectedSheet} onChange={e => handleSheetChange(e.target.value)}>
219
+ {sheets.map(sheet => (
220
+ <option key={sheet} value={sheet}>
221
+ {sheet}
222
+ </option>
223
+ ))}
224
+ </select>
225
+ </div>
226
+ )}
227
+
228
+ {data.length > 0 && (
229
+ <pre>{JSON.stringify(data, null, 2)}</pre>
230
+ )}
231
+ </div>
232
+ )
233
+ }
234
+
235
+ export default MultiSheetReader
236
+ ```
237
+
238
+ ### 5. 从 HTML 表格导出
239
+
240
+ ```tsx
241
+ import React, { useRef } from 'react'
242
+ import { tableToSheet, utils, exportExcelFile } from 'react-util-tools'
243
+
244
+ function TableExporter() {
245
+ const tableRef = useRef<HTMLTableElement>(null)
246
+
247
+ const handleExport = () => {
248
+ if (!tableRef.current) return
249
+
250
+ const worksheet = tableToSheet(tableRef.current)
251
+ const workbook = utils.book_new()
252
+ utils.book_append_sheet(workbook, worksheet, 'Sheet1')
253
+
254
+ exportExcelFile(workbook, 'table-export.xlsx')
255
+ }
256
+
257
+ return (
258
+ <div>
259
+ <button onClick={handleExport}>导出表格</button>
260
+
261
+ <table ref={tableRef}>
262
+ <thead>
263
+ <tr>
264
+ <th>姓名</th>
265
+ <th>年龄</th>
266
+ <th>城市</th>
267
+ </tr>
268
+ </thead>
269
+ <tbody>
270
+ <tr>
271
+ <td>张三</td>
272
+ <td>25</td>
273
+ <td>北京</td>
274
+ </tr>
275
+ <tr>
276
+ <td>李四</td>
277
+ <td>30</td>
278
+ <td>上海</td>
279
+ </tr>
280
+ </tbody>
281
+ </table>
282
+ </div>
283
+ )
284
+ }
285
+
286
+ export default TableExporter
287
+ ```
288
+
289
+ ### 6. 高级用法 - 自定义样式和格式
290
+
291
+ ```tsx
292
+ import React from 'react'
293
+ import { XLSX, utils, exportExcelFile } from 'react-util-tools'
294
+
295
+ function AdvancedExport() {
296
+ const handleExport = () => {
297
+ const data = [
298
+ { name: '张三', score: 95, grade: 'A' },
299
+ { name: '李四', score: 87, grade: 'B' },
300
+ { name: '王五', score: 92, grade: 'A' }
301
+ ]
302
+
303
+ // 创建工作表
304
+ const worksheet = utils.json_to_sheet(data)
305
+
306
+ // 设置列宽
307
+ worksheet['!cols'] = [
308
+ { wch: 15 }, // 姓名列宽度
309
+ { wch: 10 }, // 分数列宽度
310
+ { wch: 10 } // 等级列宽度
311
+ ]
312
+
313
+ // 合并单元格(如果需要)
314
+ // worksheet['!merges'] = [
315
+ // { s: { r: 0, c: 0 }, e: { r: 0, c: 2 } } // 合并第一行的 A-C 列
316
+ // ]
317
+
318
+ // 创建工作簿
319
+ const workbook = utils.book_new()
320
+ utils.book_append_sheet(workbook, worksheet, 'Scores')
321
+
322
+ // 导出
323
+ exportExcelFile(workbook, 'student-scores.xlsx', {
324
+ bookType: 'xlsx',
325
+ compression: true
326
+ })
327
+ }
328
+
329
+ return (
330
+ <button onClick={handleExport}>
331
+ 导出成绩单
332
+ </button>
333
+ )
334
+ }
335
+
336
+ export default AdvancedExport
337
+ ```
338
+
339
+ ## 纯 JavaScript 示例
340
+
341
+ ### Node.js 环境
342
+
343
+ ```javascript
344
+ import { readFile, jsonToWorkbook, exportExcelFile } from 'react-util-tools'
345
+ import fs from 'fs'
346
+
347
+ // 读取 Excel 文件
348
+ async function readExcelInNode() {
349
+ const buffer = fs.readFileSync('input.xlsx')
350
+ const workbook = readFile(buffer)
351
+ const data = workbookToJSON(workbook)
352
+ console.log(data)
353
+ }
354
+
355
+ // 导出 Excel 文件
356
+ function exportExcelInNode() {
357
+ const data = [
358
+ { name: 'John', age: 30 },
359
+ { name: 'Jane', age: 25 }
360
+ ]
361
+
362
+ const workbook = jsonToWorkbook(data)
363
+ writeFile(workbook, 'output.xlsx')
364
+ }
365
+ ```
366
+
367
+ ## 常见问题
368
+
369
+ ### 1. 如何处理大文件?
370
+
371
+ 对于大文件,建议分批处理或使用流式读取:
372
+
373
+ ```typescript
374
+ import { readExcelFile, getSheetNames, getSheet, sheetToAOA } from 'react-util-tools'
375
+
376
+ async function processBigFile(file: File) {
377
+ const workbook = await readExcelFile(file)
378
+ const sheet = getSheet(workbook, 'Sheet1')
379
+
380
+ // 转换为数组,逐行处理
381
+ const rows = sheetToAOA(sheet)
382
+
383
+ // 分批处理
384
+ const batchSize = 1000
385
+ for (let i = 0; i < rows.length; i += batchSize) {
386
+ const batch = rows.slice(i, i + batchSize)
387
+ await processBatch(batch)
388
+ }
389
+ }
390
+ ```
391
+
392
+ ### 2. 如何处理日期格式?
393
+
394
+ ```typescript
395
+ import { readExcelFile, workbookToJSON } from 'react-util-tools'
396
+
397
+ async function handleDates(file: File) {
398
+ const workbook = await readExcelFile(file, {
399
+ cellDates: true // 将日期解析为 Date 对象
400
+ })
401
+
402
+ const data = workbookToJSON(workbook)
403
+ console.log(data)
404
+ }
405
+ ```
406
+
407
+ ### 3. 如何导出 CSV?
408
+
409
+ ```typescript
410
+ import { jsonToWorkbook, getSheet, sheetToCSV } from 'react-util-tools'
411
+
412
+ function exportToCSV(data: any[]) {
413
+ const workbook = jsonToWorkbook(data)
414
+ const sheet = getSheet(workbook, 'Sheet1')
415
+ const csv = sheetToCSV(sheet)
416
+
417
+ // 下载 CSV
418
+ const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
419
+ const url = URL.createObjectURL(blob)
420
+ const link = document.createElement('a')
421
+ link.href = url
422
+ link.download = 'data.csv'
423
+ link.click()
424
+ }
425
+ ```
@@ -0,0 +1,245 @@
1
+ /**
2
+ * SheetJS (xlsx) 封装
3
+ * 完整导出 SheetJS 的所有功能
4
+ * 文档:https://docs.sheetjs.com/
5
+ */
6
+
7
+ import * as XLSX from 'xlsx'
8
+
9
+ // 导出整个 XLSX 对象,保留所有功能
10
+ export { XLSX }
11
+
12
+ // 导出常用类型
13
+ export type {
14
+ WorkBook,
15
+ WorkSheet,
16
+ CellObject,
17
+ Range,
18
+ WritingOptions,
19
+ ParsingOptions,
20
+ BookType,
21
+ Sheet2JSONOpts,
22
+ JSON2SheetOpts
23
+ } from 'xlsx'
24
+
25
+ // 导出常用方法(方便直接使用)
26
+ export const {
27
+ read,
28
+ readFile,
29
+ write,
30
+ writeFile,
31
+ writeFileXLSX,
32
+ utils
33
+ } = XLSX
34
+
35
+ /**
36
+ * 读取 Excel 文件(从 File 对象)
37
+ * @param file File 对象
38
+ * @param options 解析选项
39
+ * @returns Promise<WorkBook>
40
+ */
41
+ export function readExcelFile(
42
+ file: File,
43
+ options?: XLSX.ParsingOptions
44
+ ): Promise<XLSX.WorkBook> {
45
+ return new Promise((resolve, reject) => {
46
+ const reader = new FileReader()
47
+
48
+ reader.onload = (e) => {
49
+ try {
50
+ const data = e.target?.result
51
+ const workbook = XLSX.read(data, options)
52
+ resolve(workbook)
53
+ } catch (error) {
54
+ reject(error)
55
+ }
56
+ }
57
+
58
+ reader.onerror = () => {
59
+ reject(new Error('Failed to read file'))
60
+ }
61
+
62
+ reader.readAsArrayBuffer(file)
63
+ })
64
+ }
65
+
66
+ /**
67
+ * 将 WorkBook 转换为 JSON 数据
68
+ * @param workbook WorkBook 对象
69
+ * @param sheetName 工作表名称(可选,默认第一个)
70
+ * @param options 转换选项
71
+ * @returns JSON 数据数组
72
+ */
73
+ export function workbookToJSON<T = any>(
74
+ workbook: XLSX.WorkBook,
75
+ sheetName?: string,
76
+ options?: XLSX.Sheet2JSONOpts
77
+ ): T[] {
78
+ const sheet = sheetName
79
+ ? workbook.Sheets[sheetName]
80
+ : workbook.Sheets[workbook.SheetNames[0]]
81
+
82
+ if (!sheet) {
83
+ throw new Error(`Sheet "${sheetName}" not found`)
84
+ }
85
+
86
+ return XLSX.utils.sheet_to_json<T>(sheet, options)
87
+ }
88
+
89
+ /**
90
+ * 从 JSON 数据创建 WorkBook
91
+ * @param data JSON 数据数组
92
+ * @param sheetName 工作表名称(默认 'Sheet1')
93
+ * @param options 转换选项
94
+ * @returns WorkBook 对象
95
+ */
96
+ export function jsonToWorkbook<T = any>(
97
+ data: T[],
98
+ sheetName = 'Sheet1',
99
+ options?: XLSX.JSON2SheetOpts
100
+ ): XLSX.WorkBook {
101
+ const worksheet = XLSX.utils.json_to_sheet(data, options)
102
+ const workbook = XLSX.utils.book_new()
103
+ XLSX.utils.book_append_sheet(workbook, worksheet, sheetName)
104
+ return workbook
105
+ }
106
+
107
+ /**
108
+ * 导出 Excel 文件(浏览器下载)
109
+ * @param workbook WorkBook 对象
110
+ * @param filename 文件名(默认 'export.xlsx')
111
+ * @param options 写入选项
112
+ */
113
+ export function exportExcelFile(
114
+ workbook: XLSX.WorkBook,
115
+ filename = 'export.xlsx',
116
+ options?: XLSX.WritingOptions
117
+ ): void {
118
+ XLSX.writeFile(workbook, filename, options)
119
+ }
120
+
121
+ /**
122
+ * 从 JSON 数据直接导出 Excel 文件
123
+ * @param data JSON 数据数组
124
+ * @param filename 文件名(默认 'export.xlsx')
125
+ * @param sheetName 工作表名称(默认 'Sheet1')
126
+ * @param options 写入选项
127
+ */
128
+ export function exportJSONToExcel<T = any>(
129
+ data: T[],
130
+ filename = 'export.xlsx',
131
+ sheetName = 'Sheet1',
132
+ options?: XLSX.WritingOptions
133
+ ): void {
134
+ const workbook = jsonToWorkbook(data, sheetName)
135
+ exportExcelFile(workbook, filename, options)
136
+ }
137
+
138
+ /**
139
+ * 读取 Excel 文件并转换为 JSON
140
+ * @param file File 对象
141
+ * @param sheetName 工作表名称(可选,默认第一个)
142
+ * @param parseOptions 解析选项
143
+ * @param jsonOptions JSON 转换选项
144
+ * @returns Promise<JSON 数据数组>
145
+ */
146
+ export async function readExcelToJSON<T = any>(
147
+ file: File,
148
+ sheetName?: string,
149
+ parseOptions?: XLSX.ParsingOptions,
150
+ jsonOptions?: XLSX.Sheet2JSONOpts
151
+ ): Promise<T[]> {
152
+ const workbook = await readExcelFile(file, parseOptions)
153
+ return workbookToJSON<T>(workbook, sheetName, jsonOptions)
154
+ }
155
+
156
+ /**
157
+ * 获取 WorkBook 中所有工作表的名称
158
+ * @param workbook WorkBook 对象
159
+ * @returns 工作表名称数组
160
+ */
161
+ export function getSheetNames(workbook: XLSX.WorkBook): string[] {
162
+ return workbook.SheetNames
163
+ }
164
+
165
+ /**
166
+ * 获取指定工作表
167
+ * @param workbook WorkBook 对象
168
+ * @param sheetName 工作表名称
169
+ * @returns WorkSheet 对象
170
+ */
171
+ export function getSheet(
172
+ workbook: XLSX.WorkBook,
173
+ sheetName: string
174
+ ): XLSX.WorkSheet {
175
+ const sheet = workbook.Sheets[sheetName]
176
+ if (!sheet) {
177
+ throw new Error(`Sheet "${sheetName}" not found`)
178
+ }
179
+ return sheet
180
+ }
181
+
182
+ /**
183
+ * 将 WorkSheet 转换为 CSV 字符串
184
+ * @param worksheet WorkSheet 对象
185
+ * @param options 转换选项
186
+ * @returns CSV 字符串
187
+ */
188
+ export function sheetToCSV(
189
+ worksheet: XLSX.WorkSheet,
190
+ options?: XLSX.Sheet2CSVOpts
191
+ ): string {
192
+ return XLSX.utils.sheet_to_csv(worksheet, options)
193
+ }
194
+
195
+ /**
196
+ * 将 WorkSheet 转换为 HTML 字符串
197
+ * @param worksheet WorkSheet 对象
198
+ * @param options 转换选项
199
+ * @returns HTML 字符串
200
+ */
201
+ export function sheetToHTML(
202
+ worksheet: XLSX.WorkSheet,
203
+ options?: XLSX.Sheet2HTMLOpts
204
+ ): string {
205
+ return XLSX.utils.sheet_to_html(worksheet, options)
206
+ }
207
+
208
+ /**
209
+ * 从 HTML 表格创建 WorkSheet
210
+ * @param table HTML 表格元素或字符串
211
+ * @param options 转换选项
212
+ * @returns WorkSheet 对象
213
+ */
214
+ export function tableToSheet(
215
+ table: HTMLElement | string,
216
+ options?: XLSX.Table2SheetOpts
217
+ ): XLSX.WorkSheet {
218
+ return XLSX.utils.table_to_sheet(table, options)
219
+ }
220
+
221
+ /**
222
+ * 从 AOA (Array of Arrays) 创建 WorkSheet
223
+ * @param data 二维数组
224
+ * @param options 转换选项
225
+ * @returns WorkSheet 对象
226
+ */
227
+ export function aoaToSheet(
228
+ data: any[][],
229
+ options?: XLSX.AOA2SheetOpts
230
+ ): XLSX.WorkSheet {
231
+ return XLSX.utils.aoa_to_sheet(data, options)
232
+ }
233
+
234
+ /**
235
+ * 将 WorkSheet 转换为 AOA (Array of Arrays)
236
+ * @param worksheet WorkSheet 对象
237
+ * @param options 转换选项
238
+ * @returns 二维数组
239
+ */
240
+ export function sheetToAOA(
241
+ worksheet: XLSX.WorkSheet,
242
+ options?: XLSX.Sheet2JSONOpts
243
+ ): any[][] {
244
+ return XLSX.utils.sheet_to_json(worksheet, { ...options, header: 1 })
245
+ }