From a3ac8a0f86e688f9956ac3356e0e6c566381f5e2 Mon Sep 17 00:00:00 2001 From: zhangkang <57750083+zhangkang1314@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:03:53 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E5=AF=BC=E5=87=BA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 6 +- pnpm-lock.yaml | 6 + src/control/grid/grid/grid.controller.ts | 48 ++++- src/util/xlsx-util/file-saver.d.ts | 1 + src/util/xlsx-util/xlsx-util.ts | 212 +++++++++++++++++++++++ 5 files changed, 270 insertions(+), 3 deletions(-) create mode 100644 src/util/xlsx-util/file-saver.d.ts create mode 100644 src/util/xlsx-util/xlsx-util.ts diff --git a/package.json b/package.json index 16b4453b..87d446b6 100644 --- a/package.json +++ b/package.json @@ -43,7 +43,9 @@ "qx-util": "^0.4.8", "ramda": "^0.29.0", "vue": "^3.3.4", - "vue-router": "^4.2.4" + "vue-router": "^4.2.4", + "file-saver": "^2.0.5", + "xlsx": "^0.18.5" }, "devDependencies": { "@commitlint/cli": "^17.6.6", @@ -102,4 +104,4 @@ "*.ts": "eslint --fix", "*.scss": "stylelint --custom-syntax=postcss-scss" } -} +} \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 68a3c9d9..cb23cdc1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -71,6 +71,12 @@ dependencies: vue-router: specifier: ^4.2.4 version: 4.2.4(vue@3.3.4) + xlsx: + specifier: ^0.18.5 + version: 0.18.5 + file-saver: + specifier: ^2.0.5 + version: 2.0.5 devDependencies: '@commitlint/cli': diff --git a/src/control/grid/grid/grid.controller.ts b/src/control/grid/grid/grid.controller.ts index 6109cc9b..3f986aaa 100644 --- a/src/control/grid/grid/grid.controller.ts +++ b/src/control/grid/grid/grid.controller.ts @@ -4,7 +4,7 @@ import { debounceAndAsyncMerge, RuntimeError, } from '@ibiz-template/core'; -import { IDEGrid, IDEGridColumn, IDEGridGroupColumn } from '@ibiz/model-core'; +import { IDEGrid, IDEGridGroupColumn, IDEGridColumn } from '@ibiz/model-core'; import { calcDeCodeNameById, ControlVO, @@ -463,4 +463,50 @@ export class GridController ); } } + + /** + * 数据导出 + * + * @param {MouseEvent} event 原生js点击事件 + * @returns {*} {Promise} + * @memberof MDControlController + */ + async exportData(_args: { event: MouseEvent }): Promise { + // 前台导出 + const items: { [key: string]: GridFieldColumnController } = + this.fieldColumns; + // 一维数组转成二维数组 + const formatExcelData = (filterVal: string[], tableData: IData[]) => { + return tableData.map(v => { + return filterVal.map(j => { + return v[j]; + }); + }); + }; + // 头部id数组 + const filterVal = Object.keys(items).map( + (key: string) => items[key].model.id!, + ); + // 表头数组 + const header = Object.keys(items).map( + (key: string) => items[key].model.caption, + ); + // 二维数组 + const data = formatExcelData( + filterVal, + this.state.rows.map(row => row.data), + ); + const exportExcel = await import( + '../../../util/xlsx-util/xlsx-util.ts' as string + ); + if (!exportExcel) { + throw new RuntimeError('导出模块加载错误'); + } + exportExcel.exportJsonToExcel({ + header, // 表头内容 数组格式 + data, // 具体数据 这是个二维数组 + filename: this.model.logicName, // 文件名称 + autoWidth: true, // 单元格是否自适应 + }); + } } diff --git a/src/util/xlsx-util/file-saver.d.ts b/src/util/xlsx-util/file-saver.d.ts new file mode 100644 index 00000000..da458fd4 --- /dev/null +++ b/src/util/xlsx-util/file-saver.d.ts @@ -0,0 +1 @@ +declare module 'file-saver'; diff --git a/src/util/xlsx-util/xlsx-util.ts b/src/util/xlsx-util/xlsx-util.ts new file mode 100644 index 00000000..970d3b44 --- /dev/null +++ b/src/util/xlsx-util/xlsx-util.ts @@ -0,0 +1,212 @@ +import { saveAs } from 'file-saver'; +import * as XLSX from 'xlsx'; +import { BookType } from 'xlsx'; + +function dateNum( + v: number | boolean | Date | string, + date1904: boolean = false, +) { + if (date1904) (v as number) += 1462; + const epoch = Date.parse(v as string); + return ( + (epoch - (new Date(Date.UTC(1899, 11, 30)) as unknown as number)) / + (24 * 60 * 60 * 1000) + ); +} + +function sheetFromArrayOfArrays(data: IData[]) { + const ws: IData = {}; + const range = { + s: { + c: 10000000, + r: 10000000, + }, + e: { + c: 0, + r: 0, + }, + }; + for (let R = 0; R !== data.length; ++R) { + for (let C = 0; C !== data[R].length; ++C) { + if (range.s.r > R) range.s.r = R; + if (range.s.c > C) range.s.c = C; + if (range.e.r < R) range.e.r = R; + if (range.e.c < C) range.e.c = C; + const cell: { + v: number | boolean | Date | string; + t?: string; + z?: string; + } = { + v: data[R][C], + }; + // eslint-disable-next-line no-continue + if (cell.v == null) continue; + const cellRef = XLSX.utils.encode_cell({ + c: C, + r: R, + }); + + if (typeof cell.v === 'number') cell.t = 'n'; + else if (typeof cell.v === 'boolean') cell.t = 'b'; + else if (cell.v instanceof Date) { + cell.t = 'n'; + cell.z = XLSX.SSF._table[14]; + cell.v = dateNum(cell.v); + } else cell.t = 's'; + + ws[cellRef] = cell; + } + } + if (range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range); + return ws; +} + +class Workbook { + public SheetNames: string[] = []; + + public Sheets: IData = {}; +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +function s2ab(s: any) { + const buf = new ArrayBuffer(s.length); + const view = new Uint8Array(buf); + for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) && 0xff; + return buf; +} + +/** + * 导出excel文件 + * + * @author zk + * @date 2023-07-18 07:07:36 + * @export + * @param {{ + * multiHeader: []; + * header: string[]; + * data: string[][]; + * filename: string; + * merges: []; + * autoWidth: boolean; + * bookType: BookType; + * }} { + * multiHeader = [], + * header, + * data, + * filename, + * merges = [], + * autoWidth = true, + * bookType = 'xlsx', + * } + */ +export function exportJsonToExcel({ + multiHeader = [], + header, + data, + filename, + merges = [], + autoWidth = true, + bookType = 'xlsx', +}: { + multiHeader: []; + header: string[]; + data: string[][]; + filename: string; + merges: []; + autoWidth: boolean; + bookType: BookType; +}) { + /* original data */ + filename = filename || 'excel-list'; + data = [...data]; + data.unshift(header); + for (let i = multiHeader.length - 1; i > -1; i--) { + data.unshift(multiHeader[i]); + } + const wsName = 'SheetJS'; + const wb = new Workbook(); + const ws = sheetFromArrayOfArrays(data); + + if (merges.length > 0) { + if (!ws['!merges']) ws['!merges'] = []; + merges.forEach(item => { + ws['!merges'].push(XLSX.utils.decode_range(item)); + }); + } + if (autoWidth) { + /* 设置worksheet每列的最大宽度 */ + const colWidth = data.map(row => + row.map(val => { + /* 先判断是否为null/undefined */ + if (val == null) { + return { + wch: 10, + }; + } + if (val.toString().charCodeAt(0) > 255) { + /* 再判断是否为中文 */ + return { + wch: val.toString().length * 2, + }; + } + return { + wch: val.toString().length, + }; + }), + ); + /* 以第一行为初始值 */ + const result = colWidth[0]; + for (let i = 1; i < colWidth.length; i++) { + for (let j = 0; j < colWidth[i].length; j++) { + if (result[j].wch < colWidth[i][j].wch) { + result[j].wch = colWidth[i][j].wch; + } + } + } + ws['!cols'] = result; + } + + /* add worksheet to workbook */ + wb.SheetNames.push(wsName); + wb.Sheets[wsName] = ws; + + const wbOut = XLSX.write(wb, { + bookType, + bookSST: false, + type: 'binary', + }); + saveAs( + new Blob([s2ab(wbOut)], { + type: 'application/octet-stream', + }), + `${filename}.${bookType}`, + ); +} + +/** + * 读取excel文件 + * + * @author zk + * @date 2023-07-18 07:07:26 + * @export + * @param {File} file + * @param {number} sheetIndex + * @return {*} + */ +export async function readExcelFile(file: File, sheetIndex: number) { + const readFile = (_file: File) => { + return new Promise(resolve => { + const reader = new FileReader(); + reader.readAsBinaryString(_file); + reader.onload = ev => { + resolve(ev.target?.result); + }; + }); + }; + let data = await readFile(file); + const workbook: XLSX.WorkBook = XLSX.read(data, { type: 'binary' }); + const worksheet: XLSX.WorkSheet = + workbook.Sheets[workbook.SheetNames[sheetIndex]]; + data = XLSX.utils.sheet_to_json(worksheet); + return data as IData[]; +} -- Gitee From a97475a098b6f0c15aa1d7f3be6e878f08ee4465 Mon Sep 17 00:00:00 2001 From: zhangkang <57750083+zhangkang1314@users.noreply.github.com> Date: Tue, 18 Jul 2023 21:04:55 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E7=9B=B4=E6=8E=A5?= =?UTF-8?q?=E5=86=85=E5=AE=B9=E7=BB=84=E4=BB=B6=E6=8A=A5=E9=94=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/common/rawitem/rawitem.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/common/rawitem/rawitem.tsx b/src/common/rawitem/rawitem.tsx index 1b4f5099..2a452206 100644 --- a/src/common/rawitem/rawitem.tsx +++ b/src/common/rawitem/rawitem.tsx @@ -26,8 +26,12 @@ export const IBizRawItem = defineComponent({ }, setup(props) { const ns = useNamespace('rawitem'); - const { rawItem } = props.rawItem!; - const { contentType } = rawItem!; + let rawItem = null; + let contentType = ''; + if (props.rawItem) { + rawItem = props.rawItem.rawItem!; + contentType = rawItem.contentType!; + } const rawItemType = ref(props.type || contentType || ''); const rawItemContent = ref(props.content); let sysImage = null; -- Gitee From 866def4887b91567c9b582fa1ea9838e66e627ca Mon Sep 17 00:00:00 2001 From: zhangkang <1752553776@qq.com> Date: Tue, 18 Jul 2023 21:26:02 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=AF=BC=E5=87=BA?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=97=A0=E6=B3=95=E6=89=93=E5=BC=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/xlsx-util/xlsx-util.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/util/xlsx-util/xlsx-util.ts b/src/util/xlsx-util/xlsx-util.ts index 970d3b44..fceba17d 100644 --- a/src/util/xlsx-util/xlsx-util.ts +++ b/src/util/xlsx-util/xlsx-util.ts @@ -71,7 +71,8 @@ class Workbook { function s2ab(s: any) { const buf = new ArrayBuffer(s.length); const view = new Uint8Array(buf); - for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) && 0xff; + // eslint-disable-next-line no-bitwise + for (let i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xff; return buf; } -- Gitee