From d66e6e37660a6eb5f2779356e21cdff428038fe9 Mon Sep 17 00:00:00 2001 From: wangshaoping Date: Thu, 16 Jan 2025 09:42:44 +0800 Subject: [PATCH] =?UTF-8?q?1.=20=E4=BF=AE=E5=A4=8D=20"=E4=BD=8E=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E5=B9=B3=E5=8F=B0"=20=E4=B8=AD=20"=E6=8A=A5=E8=A1=A8?= =?UTF-8?q?=E6=A8=A1=E7=89=88=E7=AE=A1=E7=90=86"=20=E5=89=8D=E7=AB=AF?= =?UTF-8?q?=E7=9A=84=20bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../views/excel-template/TemplateConfig.vue | 11 + .../excel-template/TemplateConfigDialog.vue | 358 ++++++++++++++ .../src/views/excel-template/TemplateGrid.vue | 131 +++++ .../excel-template/TemplateParamsDialog.vue | 464 ++++++++++++++++++ .../TemplateParamsSelectDialog.vue | 234 +++++++++ .../excel-template/TemplateReportDialog.vue | 34 ++ .../TemplateSetColumnParamsDialog.vue | 91 ++++ .../TemplateSetForeachPropsDialog.vue | 250 ++++++++++ .../excel-template/luckysheet/LuckySheet.vue | 339 +++++++++++++ .../excel-template/luckysheet/exportExcel.ts | 319 ++++++++++++ .../src/views/excel-template/template.ts | 27 + 11 files changed, 2258 insertions(+) create mode 100644 io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateConfig.vue create mode 100644 io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateConfigDialog.vue create mode 100644 io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateGrid.vue create mode 100644 io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateParamsDialog.vue create mode 100644 io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateParamsSelectDialog.vue create mode 100644 io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateReportDialog.vue create mode 100644 io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateSetColumnParamsDialog.vue create mode 100644 io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateSetForeachPropsDialog.vue create mode 100644 io.sc.platform.lcdp.frontend/src/views/excel-template/luckysheet/LuckySheet.vue create mode 100644 io.sc.platform.lcdp.frontend/src/views/excel-template/luckysheet/exportExcel.ts create mode 100644 io.sc.platform.lcdp.frontend/src/views/excel-template/template.ts diff --git a/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateConfig.vue b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateConfig.vue new file mode 100644 index 00000000..6a884d05 --- /dev/null +++ b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateConfig.vue @@ -0,0 +1,11 @@ + + + + + diff --git a/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateConfigDialog.vue b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateConfigDialog.vue new file mode 100644 index 00000000..2d3e6df8 --- /dev/null +++ b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateConfigDialog.vue @@ -0,0 +1,358 @@ + + + + + diff --git a/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateGrid.vue b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateGrid.vue new file mode 100644 index 00000000..1d6b6587 --- /dev/null +++ b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateGrid.vue @@ -0,0 +1,131 @@ + + + diff --git a/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateParamsDialog.vue b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateParamsDialog.vue new file mode 100644 index 00000000..f6424126 --- /dev/null +++ b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateParamsDialog.vue @@ -0,0 +1,464 @@ + + + diff --git a/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateParamsSelectDialog.vue b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateParamsSelectDialog.vue new file mode 100644 index 00000000..c4a04514 --- /dev/null +++ b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateParamsSelectDialog.vue @@ -0,0 +1,234 @@ + + + diff --git a/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateReportDialog.vue b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateReportDialog.vue new file mode 100644 index 00000000..197a3368 --- /dev/null +++ b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateReportDialog.vue @@ -0,0 +1,34 @@ + + + + + diff --git a/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateSetColumnParamsDialog.vue b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateSetColumnParamsDialog.vue new file mode 100644 index 00000000..714ac7c3 --- /dev/null +++ b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateSetColumnParamsDialog.vue @@ -0,0 +1,91 @@ + + + diff --git a/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateSetForeachPropsDialog.vue b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateSetForeachPropsDialog.vue new file mode 100644 index 00000000..e7284e72 --- /dev/null +++ b/io.sc.platform.lcdp.frontend/src/views/excel-template/TemplateSetForeachPropsDialog.vue @@ -0,0 +1,250 @@ + + + diff --git a/io.sc.platform.lcdp.frontend/src/views/excel-template/luckysheet/LuckySheet.vue b/io.sc.platform.lcdp.frontend/src/views/excel-template/luckysheet/LuckySheet.vue new file mode 100644 index 00000000..953031ce --- /dev/null +++ b/io.sc.platform.lcdp.frontend/src/views/excel-template/luckysheet/LuckySheet.vue @@ -0,0 +1,339 @@ + + + + + diff --git a/io.sc.platform.lcdp.frontend/src/views/excel-template/luckysheet/exportExcel.ts b/io.sc.platform.lcdp.frontend/src/views/excel-template/luckysheet/exportExcel.ts new file mode 100644 index 00000000..eb4cf872 --- /dev/null +++ b/io.sc.platform.lcdp.frontend/src/views/excel-template/luckysheet/exportExcel.ts @@ -0,0 +1,319 @@ +import Excel from 'exceljs'; +import FileSaver from 'file-saver'; + +const exportExcel = function (luckysheet, value) { + // 参数为luckysheet.getluckysheetfile()获取的对象 + // 1.创建工作簿,可以为工作簿添加属性 + const workbook = new Excel.Workbook(); + // 2.创建表格,第二个参数可以配置创建什么样的工作表 + if (Object.prototype.toString.call(luckysheet) === '[object Object]') { + luckysheet = [luckysheet]; + } + luckysheet.forEach(function (table) { + if (table.data.length === 0) return true; + // ws.getCell('B2').fill = fills. + const worksheet = workbook.addWorksheet(table.name); + const merge = (table.config && table.config.merge) || {}; + const borderInfo = (table.config && table.config.borderInfo) || {}; + // 3.设置单元格合并,设置单元格边框,设置单元格样式,设置值 + setStyleAndValue(table.data, worksheet); + setMerge(merge, worksheet); + setBorder(borderInfo, worksheet); + return true; + }); + + // return + // 4.写入 buffer + const buffer = workbook.xlsx.writeBuffer().then((data) => { + // console.log('data', data) + const blob = new Blob([data], { + type: 'application/vnd.ms-excel;charset=utf-8', + }); + console.log('导出成功!'); + FileSaver.saveAs(blob, `${value}.xlsx`); + }); + return buffer; +}; + +const setMerge = function (luckyMerge = {}, worksheet) { + const mergearr = Object.values(luckyMerge); + mergearr.forEach(function (elem) { + // elem格式:{r: 0, c: 0, rs: 1, cs: 2} + // 按开始行,开始列,结束行,结束列合并(相当于 K10:M12) + worksheet.mergeCells(elem.r + 1, elem.c + 1, elem.r + elem.rs, elem.c + elem.cs); + }); +}; + +const setBorder = function (luckyBorderInfo, worksheet) { + if (!Array.isArray(luckyBorderInfo)) return; + // console.log('luckyBorderInfo', luckyBorderInfo) + luckyBorderInfo.forEach(function (elem) { + // 现在只兼容到borderType 为range的情况 + // console.log('ele', elem) + if (elem.rangeType === 'range') { + const border = borderConvert(elem.borderType, elem.style, elem.color); + const rang = elem.range[0]; + // console.log('range', rang) + const row = rang.row; + const column = rang.column; + for (let i = row[0] + 1; i < row[1] + 2; i++) { + for (let y = column[0] + 1; y < column[1] + 2; y++) { + worksheet.getCell(i, y).border = border; + } + } + } + if (elem.rangeType === 'cell') { + // col_index: 2 + // row_index: 1 + // b: { + // color: '#d0d4e3' + // style: 1 + // } + const { col_index, row_index } = elem.value; + const borderData = Object.assign({}, elem.value); + delete borderData.col_index; + delete borderData.row_index; + const border = addborderToCell(borderData, row_index, col_index); + // console.log('bordre', border, borderData) + worksheet.getCell(row_index + 1, col_index + 1).border = border; + } + // console.log(rang.column_focus + 1, rang.row_focus + 1) + // worksheet.getCell(rang.row_focus + 1, rang.column_focus + 1).border = border + }); +}; +const setStyleAndValue = function (cellArr, worksheet) { + if (!Array.isArray(cellArr)) return; + cellArr.forEach(function (row, rowid) { + row.every(function (cell, columnid) { + if (!cell) return true; + const fill = fillConvert(cell.bg); + + const font = fontConvert(cell.ff, cell.fc, cell.bl, cell.it, cell.fs, cell.cl, cell.ul); + const alignment = alignmentConvert(cell.vt, cell.ht, cell.tb, cell.tr); + let value = ''; + + if (cell.f) { + value = { formula: cell.f, result: cell.v }; + } else if (!cell.v && cell.ct && cell.ct.s) { + // xls转为xlsx之后,内部存在不同的格式,都会进到富文本里,即值不存在与cell.v,而是存在于cell.ct.s之后 + // value = cell.ct.s[0].v + cell.ct.s.forEach((arr) => { + value += arr.v; + }); + } else { + value = cell.v; + } + // style 填入到_value中可以实现填充色 + const letter = createCellPos(columnid); + const target = worksheet.getCell(letter + (rowid + 1)); + // console.log('1233', letter + (rowid + 1)) + for (const key in fill) { + target.fill = fill; + break; + } + target.font = font; + target.alignment = alignment; + target.value = value; + + return true; + }); + }); +}; + +const fillConvert = function (bg) { + if (!bg) { + return {}; + } + // const bgc = bg.replace('#', '') + const fill = { + type: 'pattern', + pattern: 'solid', + fgColor: { argb: bg.replace('#', '') }, + }; + return fill; +}; + +const fontConvert = function (ff = 0, fc = '#000000', bl = 0, it = 0, fs = 10, cl = 0, ul = 0) { + // luckysheet:ff(样式), fc(颜色), bl(粗体), it(斜体), fs(大小), cl(删除线), ul(下划线) + const luckyToExcel = { + 0: '微软雅黑', + 1: '宋体(Song)', + 2: '黑体(ST Heiti)', + 3: '楷体(ST Kaiti)', + 4: '仿宋(ST FangSong)', + 5: '新宋体(ST Song)', + 6: '华文新魏', + 7: '华文行楷', + 8: '华文隶书', + 9: 'Arial', + 10: 'Times New Roman ', + 11: 'Tahoma ', + 12: 'Verdana', + num2bl: function (num) { + return num === 0 ? false : true; + }, + }; + // 出现Bug,导入的时候ff为luckyToExcel的val + + const font = { + name: typeof ff === 'number' ? luckyToExcel[ff] : ff, + family: 1, + size: fs, + color: { argb: fc.replace('#', '') }, + bold: luckyToExcel.num2bl(bl), + italic: luckyToExcel.num2bl(it), + underline: luckyToExcel.num2bl(ul), + strike: luckyToExcel.num2bl(cl), + }; + + return font; +}; + +const alignmentConvert = function (vt = 'default', ht = 'default', tb = 'default', tr = 'default') { + // luckysheet:vt(垂直), ht(水平), tb(换行), tr(旋转) + const luckyToExcel = { + vertical: { + 0: 'middle', + 1: 'top', + 2: 'bottom', + default: 'top', + }, + horizontal: { + 0: 'center', + 1: 'left', + 2: 'right', + default: 'left', + }, + wrapText: { + 0: false, + 1: false, + 2: true, + default: false, + }, + textRotation: { + 0: 0, + 1: 45, + 2: -45, + 3: 'vertical', + 4: 90, + 5: -90, + default: 0, + }, + }; + + const alignment = { + vertical: luckyToExcel.vertical[vt], + horizontal: luckyToExcel.horizontal[ht], + wrapText: luckyToExcel.wrapText[tb], + textRotation: luckyToExcel.textRotation[tr], + }; + return alignment; +}; + +const borderConvert = function (borderType, style = 1, color = '#000') { + // 对应luckysheet的config中borderinfo的的参数 + if (!borderType) { + return {}; + } + const luckyToExcel = { + type: { + 'border-all': 'all', + 'border-top': 'top', + 'border-right': 'right', + 'border-bottom': 'bottom', + 'border-left': 'left', + }, + style: { + 0: 'none', + 1: 'thin', + 2: 'hair', + 3: 'dotted', + 4: 'dashDot', // 'Dashed', + 5: 'dashDot', + 6: 'dashDotDot', + 7: 'double', + 8: 'medium', + 9: 'mediumDashed', + 10: 'mediumDashDot', + 11: 'mediumDashDotDot', + 12: 'slantDashDot', + 13: 'thick', + }, + }; + const template = { + style: luckyToExcel.style[style], + color: { argb: color.replace('#', '') }, + }; + const border = {}; + if (luckyToExcel.type[borderType] === 'all') { + border['top'] = template; + border['right'] = template; + border['bottom'] = template; + border['left'] = template; + } else { + border[luckyToExcel.type[borderType]] = template; + } + // console.log('border', border) + return border; +}; + +function addborderToCell(borders, row_index, col_index) { + const border = {}; + const luckyExcel = { + type: { + l: 'left', + r: 'right', + b: 'bottom', + t: 'top', + }, + style: { + 0: 'none', + 1: 'thin', + 2: 'hair', + 3: 'dotted', + 4: 'dashDot', // 'Dashed', + 5: 'dashDot', + 6: 'dashDotDot', + 7: 'double', + 8: 'medium', + 9: 'mediumDashed', + 10: 'mediumDashDot', + 11: 'mediumDashDotDot', + 12: 'slantDashDot', + 13: 'thick', + }, + }; + // console.log('borders', borders) + for (const bor in borders) { + // console.log(bor) + if (borders[bor].color.indexOf('rgb') === -1) { + border[luckyExcel.type[bor]] = { + style: luckyExcel.style[borders[bor].style], + color: { argb: borders[bor].color.replace('#', '') }, + }; + } else { + border[luckyExcel.type[bor]] = { + style: luckyExcel.style[borders[bor].style], + color: { argb: borders[bor].color }, + }; + } + } + + return border; +} + +function createCellPos(n) { + const ordA = 'A'.charCodeAt(0); + + const ordZ = 'Z'.charCodeAt(0); + const len = ordZ - ordA + 1; + let s = ''; + while (n >= 0) { + s = String.fromCharCode((n % len) + ordA) + s; + + n = Math.floor(n / len) - 1; + } + return s; +} + +export { exportExcel }; diff --git a/io.sc.platform.lcdp.frontend/src/views/excel-template/template.ts b/io.sc.platform.lcdp.frontend/src/views/excel-template/template.ts new file mode 100644 index 00000000..246690d7 --- /dev/null +++ b/io.sc.platform.lcdp.frontend/src/views/excel-template/template.ts @@ -0,0 +1,27 @@ +const cellFormatMap = new Map(); +// 纯文本 +cellFormatMap.set('1', { fa: '@', t: 's' }); +// 数字格式-整数 +cellFormatMap.set('2', { fa: '0', t: 'n' }); +// 数字格式-两位小数 +cellFormatMap.set('3', { fa: '0.00', t: 'n' }); +// 数字格式-四位小数 +cellFormatMap.set('4', { fa: '0.0000', t: 'n' }); +// 百分比整数 +cellFormatMap.set('5', { fa: '0%', t: 'n' }); +// 百分比两位小数 +cellFormatMap.set('6', { fa: '0.00%', t: 'n' }); +// 万元两位小数(示例:12万3456.00) +cellFormatMap.set('7', { fa: 'w0.00', t: 'n' }); +// 千位符整数(示例:1,235) +cellFormatMap.set('8', { fa: '#,##0', t: 'n' }); +// 千位符两位小数(示例:1,234.56) +cellFormatMap.set('9', { fa: '#,##0.00', t: 'n' }); + +/** + * 根据下拉框所选值获取单元格格式化对象 + * @param index + */ +export function getExcelCellFormatObject(index: string) { + return cellFormatMap.get(index); +}