diff --git a/package.json b/package.json index 5a6c663..d92eabc 100644 --- a/package.json +++ b/package.json @@ -24,16 +24,19 @@ "clipboard": "2.0.11", "dayjs": "1.11.11", "echarts": "5.5.0", + "file-saver": "^2.0.5", "lodash-es": "4.17.21", "nprogress": "0.2.0", "pinia": "2.1.7", "vue": "3.4.27", "vue-draggable-plus": "0.5.0", "vue-i18n": "9.13.1", - "vue-router": "4.3.2" + "vue-router": "4.3.2", + "xlsx": "^0.18.5" }, "devDependencies": { "@elegant-router/vue": "0.3.7", + "@types/file-saver": "^2.0.7", "@iconify/json": "2.2.217", "@sa/uno-preset": "workspace:*", "@soybeanjs/eslint-config": "1.3.6", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2c4ab8c..3d77312 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: echarts: specifier: 5.5.0 version: 5.5.0 + file-saver: + specifier: ^2.0.5 + version: 2.0.5 lodash-es: specifier: 4.17.21 version: 4.17.21 @@ -65,6 +68,9 @@ importers: vue-router: specifier: 4.3.2 version: 4.3.2(vue@3.4.27(typescript@5.4.5)) + xlsx: + specifier: ^0.18.5 + version: 0.18.5 devDependencies: '@elegant-router/vue': specifier: 0.3.7 @@ -78,6 +84,9 @@ importers: '@soybeanjs/eslint-config': specifier: 1.3.6 version: 1.3.6(@types/eslint@8.56.10)(@unocss/eslint-config@0.60.4(eslint@9.4.0)(typescript@5.4.5))(eslint-plugin-vue@9.26.0(eslint@9.4.0))(eslint@9.4.0)(typescript@5.4.5)(vue-eslint-parser@9.4.3(eslint@9.4.0)) + '@types/file-saver': + specifier: ^2.0.7 + version: 2.0.7 '@types/lodash-es': specifier: 4.17.12 version: 4.17.12 @@ -913,6 +922,9 @@ packages: '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + '@types/file-saver@2.0.7': + resolution: {integrity: sha512-dNKVfHd/jk0SkR/exKGj2ggkB45MAkzvWCaqLUUgkyjITkGNzH8H+yUwr+BLJUBjZOe9w8X3wgmXhZDRg1ED6A==} + '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} @@ -1254,6 +1266,10 @@ packages: engines: {node: '>=0.4.0'} hasBin: true + adler-32@1.3.1: + resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==} + engines: {node: '>=0.8'} + ajv@6.12.6: resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} @@ -1465,6 +1481,10 @@ packages: caniuse-lite@1.0.30001629: resolution: {integrity: sha512-c3dl911slnQhmxUIT4HhYzT7wnBK/XYpGnYLOj4nJBaRiw52Ibe7YxlDaAeRECvA786zCuExhxIUJ2K7nHMrBw==} + cfb@1.2.2: + resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==} + engines: {node: '>=0.8'} + chai@4.4.1: resolution: {integrity: sha512-13sOfMv2+DWduEU+/xbun3LScLoqN17nBeTLUsmDfKdoiC1fr0n9PU4guu4AhRcOVFk/sW8LyZWHuhWtQZiF+g==} engines: {node: '>=4'} @@ -1526,6 +1546,10 @@ packages: resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} engines: {node: '>=0.8'} + codepage@1.15.0: + resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==} + engines: {node: '>=0.8'} + collection-visit@1.0.0: resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==} engines: {node: '>=0.10.0'} @@ -1604,6 +1628,11 @@ packages: resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} engines: {node: '>= 0.10'} + crc-32@1.2.2: + resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} + engines: {node: '>=0.8'} + hasBin: true + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -2124,6 +2153,9 @@ packages: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} + file-saver@2.0.5: + resolution: {integrity: sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA==} + fill-range@4.0.0: resolution: {integrity: sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==} engines: {node: '>=0.10.0'} @@ -2175,6 +2207,10 @@ packages: resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} engines: {node: '>= 6'} + frac@1.1.2: + resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==} + engines: {node: '>=0.8'} + fragment-cache@0.2.1: resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==} engines: {node: '>=0.10.0'} @@ -3536,6 +3572,10 @@ packages: resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==} engines: {node: '>=0.10.0'} + ssf@0.11.2: + resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==} + engines: {node: '>=0.8'} + stable@0.1.8: resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' @@ -4063,10 +4103,18 @@ packages: engines: {node: '>=8'} hasBin: true + wmf@1.0.2: + resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==} + engines: {node: '>=0.8'} + word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} + word@0.3.0: + resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==} + engines: {node: '>=0.8'} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -4082,6 +4130,11 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + xlsx@0.18.5: + resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==} + engines: {node: '>=0.8'} + hasBin: true + xml-name-validator@4.0.0: resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==} engines: {node: '>=12'} @@ -4737,6 +4790,8 @@ snapshots: '@types/estree@1.0.5': {} + '@types/file-saver@2.0.7': {} + '@types/json-schema@7.0.15': {} '@types/json5@0.0.29': {} @@ -5233,6 +5288,8 @@ snapshots: acorn@8.11.3: {} + adler-32@1.3.1: {} + ajv@6.12.6: dependencies: fast-deep-equal: 3.1.3 @@ -5481,6 +5538,11 @@ snapshots: caniuse-lite@1.0.30001629: {} + cfb@1.2.2: + dependencies: + adler-32: 1.3.1 + crc-32: 1.2.2 + chai@4.4.1: dependencies: assertion-error: 1.1.0 @@ -5566,6 +5628,8 @@ snapshots: clone@2.1.2: {} + codepage@1.15.0: {} + collection-visit@1.0.0: dependencies: map-visit: 1.0.0 @@ -5626,6 +5690,8 @@ snapshots: object-assign: 4.1.1 vary: 1.1.2 + crc-32@1.2.2: {} + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -6373,6 +6439,8 @@ snapshots: dependencies: flat-cache: 4.0.1 + file-saver@2.0.5: {} + fill-range@4.0.0: dependencies: extend-shallow: 2.0.1 @@ -6426,6 +6494,8 @@ snapshots: combined-stream: 1.0.8 mime-types: 2.1.35 + frac@1.1.2: {} + fragment-cache@0.2.1: dependencies: map-cache: 0.2.2 @@ -7842,6 +7912,10 @@ snapshots: dependencies: extend-shallow: 3.0.2 + ssf@0.11.2: + dependencies: + frac: 1.1.2 + stable@0.1.8: {} stackback@0.0.2: {} @@ -8448,8 +8522,12 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 + wmf@1.0.2: {} + word-wrap@1.2.5: {} + word@0.3.0: {} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -8470,6 +8548,16 @@ snapshots: wrappy@1.0.2: {} + xlsx@0.18.5: + dependencies: + adler-32: 1.3.1 + cfb: 1.2.2 + codepage: 1.15.0 + crc-32: 1.2.2 + ssf: 0.11.2 + wmf: 1.0.2 + word: 0.3.0 + xml-name-validator@4.0.0: {} y18n@5.0.8: {} diff --git a/src/utils/execl-utils.ts b/src/utils/execl-utils.ts new file mode 100644 index 0000000..cc1ad49 --- /dev/null +++ b/src/utils/execl-utils.ts @@ -0,0 +1,87 @@ +import { JSON2SheetOpts, read, utils, write } from 'xlsx'; + +// 静态资源路径 +const baseUrl = import.meta.env.VITE_HISTORY_BASE_URL; +export const xlsxUrl = `${ + baseUrl.length === 1 && baseUrl.indexOf('/') === 0 + ? '' + : baseUrl.indexOf('/') === -1 + ? '/' + baseUrl + : baseUrl +}/alarmHelp`; + +/** + * 读取本地文件 + * @param id 表格ID + * @returns 数据数组 + * @example + * readLoalXlsx('20001').then(res=>{ + * console.log(res) + * }); + */ +export async function readLoalXlsx( + lang: string, + id: string +): Promise[]> { + let result = await fetch(`${xlsxUrl}/${lang}/${id}.xlsx`); + let fileBuffer = await result.arrayBuffer(); + // 判断是否xlsx文件 + const data = new Uint8Array(fileBuffer); + const isXlsxFile = + data[0] === 0x50 && + data[1] === 0x4b && + data[2] === 0x03 && + data[3] === 0x04; + if (!isXlsxFile) { + result = await fetch(`${xlsxUrl}/${lang}/all.xlsx`); + fileBuffer = await result.arrayBuffer(); + } + return readSheet(fileBuffer, 0); +} + +/** + * 读取表格数据 工作表 + * @param fileBolb 文件对象 + * @param index 文件保存路径 + * @return 表格对象列表 + */ +export async function readSheet( + fileBolb: Blob | ArrayBuffer, + index: number = 0 +): Promise[]> { + const workBook = read(fileBolb); + let workSheet = workBook.Sheets[workBook.SheetNames[index]]; + return utils.sheet_to_json>(workSheet); +} + +/** + * 写入表格数据,一般用于导出 + * @param filePath 文件路径 + * @param sheetName 工作表名称 + * @return xlsx文件流, 使用saveAs函数保存 + * @example + * writeSheet(res.data, from.logType).then(fileBlob => + * saveAs(fileBlob, `${from.logType}_${Date.now()}.xlsx`) + * ); + * + */ +export async function writeSheet( + data: any[], + sheetName: string, + opts?: JSON2SheetOpts + ) { + if (data.length === 0) { + return new Blob([], { type: 'application/octet-stream' }); + } + + const workSheet = utils.json_to_sheet(data, opts); + + // 设置列宽度,单位厘米 + workSheet['!cols'] = Object.keys(data[0]).map(() => { + return { wch: 20 }; + }); + const workBook = utils.book_new(); + utils.book_append_sheet(workBook, workSheet, sheetName); + const excelBuffer = write(workBook, { type: 'array', bookType: 'xlsx' }); + return new Blob([excelBuffer], { type: 'application/octet-stream' }); +}