Luckysheet + Exceljs:H5实现Excel在线编辑、导入、导出及上传服务器的示例代码(完整版demo)

 

创建xeditor.html 

<!DOCTYPE html>
<html><head><meta charset="UTF-8" /><title>Hello World!</title><!-- <link rel='stylesheet' href='./luckysheet/plugins/css/pluginsCss.css' /><link rel='stylesheet' href='./luckysheet/plugins/plugins.css' /><link rel='stylesheet' href='./luckysheet/css/luckysheet.css' /><link rel='stylesheet' href='./luckysheet/assets/iconfont/iconfont.css' /><script src="./luckysheet/plugins/js/plugin.js"></script><script src="./luckysheet/luckysheet.umd.js"></script> --><!-- 引入luckysheet,用于渲染表格 --><link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/css/pluginsCss.css' /><link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/plugins.css' /><link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/css/luckysheet.css' /><link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/assets/iconfont/iconfont.css' /><script src="https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/js/plugin.js"></script><script src="https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/luckysheet.umd.js"></script><!-- 引入exceljs、FileSaver,用于luckysheet表格转xlsx文件 --><script src="https://cdn.bootcdn.net/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script><script src="https://cdn.bootcdn.net/ajax/libs/exceljs/4.3.0/exceljs.js"></script><script>$(function () {//Configuration itemvar options = {container: 'luckysheet', //luckysheet is the container idshowinfobar: false,title: '在线表格', // 设定表格名称lang: 'zh' // 设定表格语言}luckysheet.create(options)});</script>
</head><body><div id="lucky-mask-demo"style="position: absolute;z-index: 1000000;left: 0px;top: 0px;bottom: 0px;right: 0px; background: rgba(255, 255, 255, 0.8); text-align: center;font-size: 40px;align-items:center;justify-content: center;display: none;">download...</div><p style="text-align:center;"> <input style="font-size:16px;" type="file" id="Luckyexcel-demo-file"name="Luckyexcel-demo-file" change="demoHandler" /> 或加载远程 xlsx 文件:<select style="height: 27px;top: -2px;position: relative;" id="Luckyexcel-select-demo"><option value="">选择网络文件</option><option value="https://minio.cnbabylon.com/public/luckysheet/money-manager-2.xlsx">Money Manager.xlsx</option><option value="https://minio.cnbabylon.com/public/luckysheet/Activity%20costs%20tracker.xlsx">Activity costs tracker.xlsx</option><option value="https://minio.cnbabylon.com/public/luckysheet/House%20cleaning%20checklist.xlsx">House cleaning checklist.xlsx</option><option value="https://minio.cnbabylon.com/public/luckysheet/Student%20assignment%20planner.xlsx">Student assignment planner.xlsx</option><option value="https://minio.cnbabylon.com/public/luckysheet/Credit%20card%20tracker.xlsx">Credit card tracker.xlsx</option><option value="https://minio.cnbabylon.com/public/luckysheet/Blue%20timesheet.xlsx">Blue timesheet.xlsx</option><option value="https://minio.cnbabylon.com/public/luckysheet/Student%20calendar%20%28Mon%29.xlsx">Student calendar (Mon).xlsx</option><option value="https://minio.cnbabylon.com/public/luckysheet/Blue%20mileage%20and%20expense%20report.xlsx">Blue mileage and expense report.xlsx</option></select><a href="javascript:void(0)" id="Luckyexcel-downlod-file">下载 xlsx 文件</a><a href="javascript:void(0)" id="Luckyexcel-upload-file">上传到服务器</a></p><div id="luckysheet"style="margin:0px;padding:0px;position:absolute;width:100%;left: 0px;top: 50px;bottom: 0px;outline: none;"></div><!-- <script src="luckyexcel.umd.js"></script> --><script src="https://cdn.jsdelivr.net/npm/luckyexcel/dist/luckyexcel.umd.js"></script><!-- <script type="module">import l from './luckyexcel.js';console.info('=====',l)// window.onload = () => {//     let upload = document.getElementById("file");//     upload.addEventListener("change", function(evt){//         var files = evt.target.files;   //         importFile(files[0]);//     });// }</script> --><script>let fullName=''; // 正在编辑的Excel文件名,包含后缀名function demoHandler() {let upload = document.getElementById("Luckyexcel-demo-file");let selectADemo = document.getElementById("Luckyexcel-select-demo");let downlodDemo = document.getElementById("Luckyexcel-downlod-file");let uploadDemo = document.getElementById("Luckyexcel-upload-file");let mask = document.getElementById("lucky-mask-demo");if (upload) {window.onload = () => {upload.addEventListener("change", function (evt) {var files = evt.target.files;if (files == null || files.length == 0) {alert("No files wait for import");return;}let name = files[0].name;let suffixArr = name.split("."), suffix = suffixArr[suffixArr.length - 1];if (suffix != "xlsx") {alert("Currently only supports the import of xlsx files");return;}fullName=name;LuckyExcel.transformExcelToLucky(files[0], function (exportJson, luckysheetfile) {if (exportJson.sheets == null || exportJson.sheets.length == 0) {alert("Failed to read the content of the excel file, currently does not support xls files!");return;}window.luckysheet.destroy();window.luckysheet.create({container: 'luckysheet', //luckysheet is the container idshowinfobar: false,data: exportJson.sheets,title: '在线表格',userInfo: exportJson.info.name.creator,lang: 'zh'});});});selectADemo.addEventListener("change", function (evt) {var obj = selectADemo;var index = obj.selectedIndex;var value = obj.options[index].value;var name = obj.options[index].innerHTML;if (value == "") {return;}fullName=name;mask.style.display = "flex";LuckyExcel.transformExcelToLuckyByUrl(value, name, function (exportJson, luckysheetfile) {if (exportJson.sheets == null || exportJson.sheets.length == 0) {alert("Failed to read the content of the excel file, currently does not support xls files!");return;}console.log(exportJson, luckysheetfile);mask.style.display = "none";window.luckysheet.destroy();window.luckysheet.create({container: 'luckysheet', //luckysheet is the container idshowinfobar: false,data: exportJson.sheets,title: '在线表格',userInfo: exportJson.info.name.creator,lang: 'zh'});});});uploadDemo.addEventListener("click", function (evt) {uploadExcel(window.luckysheet.getAllSheets(), fullName)  // 上传到服务器});downlodDemo.addEventListener("click", function (evt) {exportExcelFront(window.luckysheet.getAllSheets(), fullName) // 下载Excel// var obj = selectADemo;// var index = obj.selectedIndex;// var value = obj.options[index].value;// if (value.length == 0) {//     alert("Please select a demo file");//     return;// }// var elemIF = document.getElementById("Lucky-download-frame");// if (elemIF == null) {//     elemIF = document.createElement("iframe");//     elemIF.style.display = "none";//     elemIF.id = "Lucky-download-frame";//     document.body.appendChild(elemIF);// }// elemIF.src = value;});}}}demoHandler();/*** 上传到服务器* @param luckysheet    -> luckysheet的所有sheet* @param name          -> 保存文件名(如:a.xlsx)* @param excelType     -> office/wps*/var uploadExcel = function(luckysheet, name, excelType) {// 1.创建工作簿,可以为工作簿添加属性const workbook = new ExcelJS.Workbook()// 2.创建表格,第二个参数可以配置创建什么样的工作表luckysheet.forEach(function (table) {// debuggerif (table.data.length === 0) return trueconst worksheet = workbook.addWorksheet(table.name)const merge = (table.config && table.config.merge) || {}        //合并单元格const borderInfo = (table.config && table.config.borderInfo) || {}      //边框const columnWidth = (table.config && table.config.columnlen) || {}    //列宽const rowHeight = (table.config && table.config.rowlen) || {}      //行高const frozen = table.frozen || {}       //冻结const rowhidden = (table.config && table.config.rowhidden) || {}    //行隐藏const colhidden = (table.config && table.config.colhidden) || {}    //列隐藏const filterSelect = table.filter_select || {}    //筛选const images = table.images || {}   //图片// console.log(table)const hide = table.hide;    //工作表 sheet 1隐藏if (hide === 1) {// 隐藏工作表worksheet.state = 'hidden';}setStyleAndValue(table.data, worksheet)setMerge(merge, worksheet)setBorder(borderInfo, worksheet)setImages(images, worksheet, workbook)setColumnWidth(columnWidth, worksheet)//行高设置50导出后在ms-excel中打开显示25,在wps-excel中打开显示50这个bug不会修复setRowHeight(rowHeight, worksheet, excelType)setFrozen(frozen, worksheet)setRowHidden(rowhidden, worksheet)setColHidden(colhidden, worksheet)setFilter(filterSelect, worksheet)return true})// 4.写入 bufferconst buffer = workbook.xlsx.writeBuffer().then(data => {const blob = new Blob([data], {type: 'application/vnd.ms-excel;charset=utf-8'})// 创建FormData对象const formData = new FormData();formData.append('file', blob, `${name}`);// 创建XMLHttpRequest对象const xhr = new XMLHttpRequest();// 配置请求xhr.open('POST', 'http://127.0.0.1:3000/upload', true);// 设置请求完成的回调函数xhr.onload = function () {if (xhr.status === 200) {alert('已上传成功!')console.log('Success:', xhr.responseText);} else {alert('上传失败!'+xhr.statusText)console.error('Error:', xhr.statusText);}};// 设置请求失败的回调函数xhr.onerror = function () {console.error('Network error occurred');};// 设置请求超时的回调函数(可选)xhr.ontimeout = function (e) {console.error('Request timed out');};// 设置请求超时时间(可选)xhr.timeout = 5000; // 5秒// 发送FormData对象xhr.send(formData);// 设置请求头(可选,某些浏览器可能不需要)xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');})return buffer}/*** 下载Excel* @param luckysheet    -> luckysheet的所有sheet* @param name          -> 保存文件名(如:a.xlsx)* @param excelType     -> office/wps*/var exportExcelFront = function(luckysheet, name, excelType) {// 1.创建工作簿,可以为工作簿添加属性const workbook = new ExcelJS.Workbook()// 2.创建表格,第二个参数可以配置创建什么样的工作表luckysheet.forEach(function (table) {// debuggerif (table.data.length === 0) return trueconst worksheet = workbook.addWorksheet(table.name)const merge = (table.config && table.config.merge) || {}        //合并单元格const borderInfo = (table.config && table.config.borderInfo) || {}      //边框const columnWidth = (table.config && table.config.columnlen) || {}    //列宽const rowHeight = (table.config && table.config.rowlen) || {}      //行高const frozen = table.frozen || {}       //冻结const rowhidden = (table.config && table.config.rowhidden) || {}    //行隐藏const colhidden = (table.config && table.config.colhidden) || {}    //列隐藏const filterSelect = table.filter_select || {}    //筛选const images = table.images || {}   //图片// console.log(table)const hide = table.hide;    //工作表 sheet 1隐藏if (hide === 1) {// 隐藏工作表worksheet.state = 'hidden';}setStyleAndValue(table.data, worksheet)setMerge(merge, worksheet)setBorder(borderInfo, worksheet)setImages(images, worksheet, workbook)setColumnWidth(columnWidth, worksheet)//行高设置50导出后在ms-excel中打开显示25,在wps-excel中打开显示50这个bug不会修复setRowHeight(rowHeight, worksheet, excelType)setFrozen(frozen, worksheet)setRowHidden(rowhidden, worksheet)setColHidden(colhidden, worksheet)setFilter(filterSelect, worksheet)return true})// 4.写入 bufferconst buffer = workbook.xlsx.writeBuffer().then(data => {const blob = new Blob([data], {type: 'application/vnd.ms-excel;charset=utf-8'})// 浏览器下载文件的示例代码console.log("导出成功!")saveAs(blob, `${name}`)})return buffer}/*** 列宽* @param columnWidth* @param worksheet*/var setColumnWidth = function (columnWidth, worksheet) {for (let key in columnWidth) {worksheet.getColumn(parseInt(key) + 1).width = columnWidth[key] / 7.5}}/*** 行高* @param rowHeight* @param worksheet* @param excelType*/var setRowHeight = function (rowHeight, worksheet, excelType) {//导出的文件用wps打开和用excel打开显示的行高大一倍if (excelType == "wps") {for (let key in rowHeight) {worksheet.getRow(parseInt(key) + 1).height = rowHeight[key] * 0.75}}if (excelType == "office" || excelType == undefined) {for (let key in rowHeight) {worksheet.getRow(parseInt(key) + 1).height = rowHeight[key] * 1.5}}}/*** 合并单元格* @param luckyMerge* @param worksheet*/var 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)})}/*** 设置边框* @param luckyBorderInfo* @param worksheet*/var setBorder = function (luckyBorderInfo, worksheet) {if (!Array.isArray(luckyBorderInfo)) return//合并边框信息var mergeCellBorder = function (border1, border2) {if (undefined === border1 || Object.keys(border1).length === 0) return border2;return Object.assign({}, border1, border2)}// console.log('luckyBorderInfo', luckyBorderInfo)luckyBorderInfo.forEach(function (elem) {// 现在只兼容到borderType 为range的情况// console.log('ele', elem)if (elem.rangeType === 'range') {let border = borderConvert(elem.borderType, elem.style, elem.color)let rang = elem.range[0]let row = rang.rowlet column = rang.columnlet rowBegin = row[0]let rowEnd = row[1]let colBegin = column[0]let colEnd = column[1]//处理外边框的情况 没有直接对应的外边框 需要转换成上下左右if (border.all) {//全部边框let b = border.allfor (let i = row[0] + 1; i <= row[1] + 1; i++) {for (let y = column[0] + 1; y <= column[1] + 1; y++) {let border = {}border['top'] = b;border['bottom'] = b;border['left'] = b;border['right'] = b;worksheet.getCell(i, y).border = border// console.log(i, y, worksheet.getCell(i, y).border)}}} else if (border.top) {//上边框let b = border.toplet i = row[0] + 1;for (let y = column[0] + 1; y <= column[1] + 1; y++) {let border = {}border['top'] = b;worksheet.getCell(i, y).border = border// console.log(i, y, worksheet.getCell(i, y).border)}} else if (border.right) {//右边框let b = border.rightfor (let i = row[0] + 1; i <= row[1] + 1; i++) {let y = column[1] + 1;let border = {}border['right'] = b;worksheet.getCell(i, y).border = border// console.log(i, y, worksheet.getCell(i, y).border)}} else if (border.bottom) {//下边框let b = border.bottomlet i = row[1] + 1;for (let y = column[0] + 1; y <= column[1] + 1; y++) {let border = {}border['bottom'] = b;worksheet.getCell(i, y).border = border// console.log(i, y, worksheet.getCell(i, y).border)}} else if (border.left) {//左边框let b = border.leftfor (let i = row[0] + 1; i <= row[1] + 1; i++) {let y = column[0] + 1;let border = {}border['left'] = b;worksheet.getCell(i, y).border = border// console.log(i, y, worksheet.getCell(i, y).border)}} else if (border.outside) {//外边框let b = border.outsidefor (let i = row[0] + 1; i <= row[1] + 1; i++) {for (let y = column[0] + 1; y <= column[1] + 1; y++) {let border = {}if (i === rowBegin + 1) {border['top'] = b}if (i === rowEnd + 1) {border['bottom'] = b}if (y === colBegin + 1) {border['left'] = b}if (y === colEnd + 1) {border['right'] = b}let border1 = worksheet.getCell(i, y).borderworksheet.getCell(i, y).border = mergeCellBorder(border1, border)// console.log(i, y, worksheet.getCell(i, y).border)}}} else if (border.inside) {//内边框let b = border.insidefor (let i = row[0] + 1; i <= row[1] + 1; i++) {for (let y = column[0] + 1; y <= column[1] + 1; y++) {let border = {}if (i !== rowBegin + 1) {border['top'] = b}if (i !== rowEnd + 1) {border['bottom'] = b}if (y !== colBegin + 1) {border['left'] = b}if (y !== colEnd + 1) {border['right'] = b}let border1 = worksheet.getCell(i, y).borderworksheet.getCell(i, y).border = mergeCellBorder(border1, border)// console.log(i, y, worksheet.getCell(i, y).border)}}} else if (border.horizontal) {//内侧水平边框let b = border.horizontalfor (let i = row[0] + 1; i <= row[1] + 1; i++) {for (let y = column[0] + 1; y <= column[1] + 1; y++) {let border = {}if (i === rowBegin + 1) {border['bottom'] = b} else if (i === rowEnd + 1) {border['top'] = b} else {border['top'] = bborder['bottom'] = b}let border1 = worksheet.getCell(i, y).borderworksheet.getCell(i, y).border = mergeCellBorder(border1, border)// console.log(i, y, worksheet.getCell(i, y).border)}}} else if (border.vertical) {//内侧垂直边框let b = border.verticalfor (let i = row[0] + 1; i <= row[1] + 1; i++) {for (let y = column[0] + 1; y <= column[1] + 1; y++) {let border = {}if (y === colBegin + 1) {border['right'] = b} else if (y === colEnd + 1) {border['left'] = b} else {border['left'] = bborder['right'] = b}let border1 = worksheet.getCell(i, y).borderworksheet.getCell(i, y).border = mergeCellBorder(border1, border)// console.log(i, y, worksheet.getCell(i, y).border)}}} else if (border.none) {//当luckysheet边框为border-none的时候表示没有边框 则将对应的单元格border清空for (let i = row[0] + 1; i <= row[1] + 1; i++) {for (let y = column[0] + 1; y <= column[1] + 1; y++) {worksheet.getCell(i, y).border = {}// console.log(i, y, worksheet.getCell(i, y).border)}}}}if (elem.rangeType === 'cell') {// col_index: 2// row_index: 1// b: {//   color: '#d0d4e3'//   style: 1// }const { col_index, row_index } = elem.valueconst borderData = Object.assign({}, elem.value)delete borderData.col_indexdelete borderData.row_indexlet border = addborderToCell(borderData, row_index, col_index)let border1 = worksheet.getCell(row_index + 1, col_index + 1).border;worksheet.getCell(row_index + 1, col_index + 1).border = mergeCellBorder(border1, border)// console.log(row_index + 1, col_index + 1, worksheet.getCell(row_index + 1, col_index + 1).border)}})}/*** 设置带样式的值* @param cellArr* @param worksheet*/var setStyleAndValue = function (cellArr, worksheet) {if (!Array.isArray(cellArr)) returncellArr.forEach(function (row, rowid) {row.every(function (cell, columnid) {if (!cell) return truelet fill = fillConvert(cell.bg)let font = fontConvert(cell.ff,cell.fc,cell.bl,cell.it,cell.fs,cell.cl,cell.un)let 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之后let richText = [];let cts = cell.ct.sfor (let i = 0; i < cts.length; i++) {let rt = {text: cts[i].v,font: fontConvert(cts[i].ff, cts[i].fc, cts[i].bl, cts[i].it, cts[i].fs, cts[i].cl, cts[i].un)}richText.push(rt)}value = {richText: richText};} else {//设置值为数字格式if (cell.v !== undefined && cell.v !== '') {var v = +cell.v;if (isNaN(v)) v = cell.vvalue = v}}//  style 填入到_value中可以实现填充色let letter = createCellPos(columnid)let target = worksheet.getCell(letter + (rowid + 1))// console.log('1233', letter + (rowid + 1))for (const key in fill) {target.fill = fillbreak}target.font = fonttarget.alignment = alignmenttarget.value = valuetry {//设置单元格格式target.numFmt = cell.ct.fa;} catch (e) {console.warn(e)}return true})})}/*** 设置图片* @param images* @param worksheet* @param workbook*/var setImages = function (images, worksheet, workbook) {if (typeof images != "object") return;for (let key in images) {// console.log(images[key]);// "data:image/png;base64,iVBORw0KG..."// 通过 base64  将图像添加到工作簿const myBase64Image = images[key].src;//位置const tl = { col: images[key].default.left / 72, row: images[key].default.top / 19 }// 大小const ext = { width: images[key].default.width, height: images[key].default.height }const imageId = workbook.addImage({base64: myBase64Image,//extension: 'png',});worksheet.addImage(imageId, {tl: tl,ext: ext});}}/*** 冻结行列* @param frozen* @param worksheet*/var setFrozen = function (frozen = {}, worksheet) {switch (frozen.type) {// 冻结首行case 'row': {worksheet.views = [{ state: 'frozen', xSplit: 0, ySplit: 1 }];break}// 冻结首列case 'column': {worksheet.views = [{ state: 'frozen', xSplit: 1, ySplit: 0 }];break}// 冻结行列case 'both': {worksheet.views = [{ state: 'frozen', xSplit: 1, ySplit: 1 }];break}// 冻结行到选区case 'rangeRow': {let row = frozen.range.row_focus + 1worksheet.views = [{ state: 'frozen', xSplit: 0, ySplit: row }];break}// 冻结列到选区case 'rangeColumn': {let column = frozen.range.column_focus + 1worksheet.views = [{ state: 'frozen', xSplit: column, ySplit: 0 }];break}// 冻结行列到选区case 'rangeBoth': {let row = frozen.range.row_focus + 1let column = frozen.range.column_focus + 1worksheet.views = [{ state: 'frozen', xSplit: column, ySplit: row }];}}}/*** 行隐藏* @param rowhidden* @param worksheet*/var setRowHidden = function (rowhidden = {}, worksheet) {for (const key in rowhidden) {//如果当前行没有内容则隐藏不生效const row = worksheet.getRow(parseInt(key) + 1)row.hidden = true;}}/*** 列隐藏* @param colhidden* @param worksheet*/var setColHidden = function (colhidden = {}, worksheet) {for (const key in colhidden) {const column = worksheet.getColumn(parseInt(key) + 1)column.hidden = true;}}/*** 自动筛选器* @param filter* @param worksheet*/var setFilter = function (filter = {}, worksheet) {if (Object.keys(filter).length === 0) returnconst from = {row: filter.row[0] + 1,column: filter.column[0] + 1}const to = {row: filter.row[1] + 1,column: filter.column[1] + 1}worksheet.autoFilter = {from: from,to: to}}var fillConvert = function (bg) {if (!bg) {return {}}// const bgc = bg.replace('#', '')let fill = {type: 'pattern',pattern: 'solid',fgColor: { argb: bg.startsWith("#") ? bg.replace('#', '') : colorRGBtoHex(bg).replace("#", "") },}return fill;}var 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}}// 出现Bug,导入的时候ff为luckyToExcel的vallet font = {name: typeof ff === 'number' ? luckyToExcel[ff] : ff,family: 1,size: fs,color: { argb: fc.startsWith("#") ? fc.replace('#', '') : colorRGBtoHex(fc).replace("#", "") },bold: luckyToExcel.num2bl(bl),italic: luckyToExcel.num2bl(it),underline: luckyToExcel.num2bl(ul),strike: luckyToExcel.num2bl(cl)}return font}var 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: 'middle'},horizontal: {0: 'center',1: 'left',2: 'right',default: 'center'},wrapText: {0: false,1: false,2: true,default: false},textRotation: {0: 0,1: 45,2: -45,3: 'vertical',4: 90,5: -90,default: 0}}let alignment = {vertical: luckyToExcel.vertical[vt],horizontal: luckyToExcel.horizontal[ht],wrapText: luckyToExcel.wrapText[tb],textRotation: luckyToExcel.textRotation[tr]}return alignment}var 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','border-outside': 'outside','border-inside': 'inside','border-horizontal': 'horizontal','border-vertical': 'vertical','border-none': 'none',},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'}}let border = {}border[luckyToExcel.type[borderType]] = {style: luckyToExcel.style[style],color: { argb: color.replace('#', '') }}return border}function addborderToCell(borders, row_index, col_index) {let 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) {let ordA = 'A'.charCodeAt(0)let ordZ = 'Z'.charCodeAt(0)let len = ordZ - ordA + 1let s = ''while (n >= 0) {s = String.fromCharCode((n % len) + ordA) + sn = Math.floor(n / len) - 1}return s}//rgb(255,255,255)转16进制 #fffffffunction colorRGBtoHex(color) {color = color.replace("rgb", "").replace("(", "").replace(")", "")var rgb = color.split(',');var r = parseInt(rgb[0]);var g = parseInt(rgb[1]);var b = parseInt(rgb[2]);return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1);}</script>
</body></html>

Node.js 接收前端上传文件的示例代码

首先,确保您已经安装了multer和express:

npm install multer express


然后,您可以使用以下代码来设置一个Express服务器,该服务器能够接收Blob格式的文件上传:

在项目一级目录,创建uploads文件夹和server.js

const express = require('express');
const multer = require('multer');
const app = express();// 配置存储选项
const storage = multer.diskStorage({destination: function (req, file, cb) {cb(null, 'uploads/'); // 保存的路径,确保这个目录存在},filename: function (req, file, cb) {cb(null, file.originalname); // 使用原始文件名作为保存的文件名}
});// 创建multer实例
const upload = multer({ storage: storage });// 设置允许跨域请求,如果需要的话
app.use((req, res, next) => {res.header('Access-Control-Allow-Origin', '*');res.header('Access-Control-Allow-Methods', 'GET, POST');next();
});// 定义上传路由
app.post('/upload', upload.single('file'), (req, res) => {// req.file 包含了上传文件的信息if (!req.file) {return res.status(400).send('No file uploaded');}// 文件上传成功res.send(`File uploaded successfully. ${req.file.filename}`);
});// 启动服务器
const port = 3000;
app.listen(port, () => {console.log(`Server running on port ${port}`);
});

启动服务

node server.js

参考内容:

1. Luckyexcel/README-zh.md

2.使用exceljs导出luckysheet表格

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/750540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

idea找不到或无法加载主类

前言 今天在运行项目的时候突然出了这样一个错误&#xff1a;IDEA 错误 找不到或无法加载主类,相信只要是用过IDEA的朋友都 遇到过它吧&#xff0c;但是每次遇到都是一顿焦头烂额、抓耳挠腮、急赤白咧&#xff01;咋整呢&#xff1f;听我给你吹~ 瞧我这张嘴~ 问题报错 找不…

vscode通过多个跳板机连接目标机(两种方案亲测成功)

1、ProxyJump&#xff08;推荐使用&#xff09; 需要OpenSSH 7.3以上版本才可使用&#xff0c;可用下列命令查看&#xff1a; ssh -V ProxyJump命令行使用方法 ssh -J [email protected]:port1,[email protected]:port2 一层跳板机&#xff1a; ssh dst_usernamedst_ip -…

ARP和DDOS攻击防御介绍

学习目标&#xff1a; 1. 如何利用ARP漏洞进行攻击&#xff1f; 2. 怎样有效地防御ARP攻击&#xff1f; 3. 如何应对DDOS攻击&#xff1f; ARP攻击如何产生&#xff1f; ARP如何进行有效防御&#xff1f; ARP基础工作原理&#xff1a; 交换机会根据mac地址表&#xff0c;进行转…

pytorch 入门基础知识一(Pytorch 01)

一 深度学习基础相关 深度学习三个主要的方向&#xff1a;计算机视觉&#xff0c;自然语言&#xff0c;语音识别。 机器学习核心组件&#xff1a;1 数据集(data)&#xff0c;2 前向传播的model(net)&#xff0c;3 目标函数(loss)&#xff0c; 4 调整模型参数和优化函数的算法…

【STM32定时器(一)内部时钟定时与外部时钟 TIM小总结】

STM32 TIM详解 TIM介绍定时器类型基本定时器通用定时器高级定时器常用名词时序图预分频时序计数器时序图 定时器中断配置图定时器定时 代码调试代码案例1代码案例2 TIM介绍 定时器&#xff08;Timer&#xff09;是微控制器中的一个重要模块&#xff0c;用于生成定时和延时信号…

mybatis源码阅读系列(一)

源码下载 mybatis 初识mybatis MyBatis 是一个优秀的持久层框架&#xff0c;它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解用于配置和原始映射&#xff0c;将接口和 Java 的…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的自动驾驶目标检测系统详解(深度学习+Python代码+PySide6界面+训练数据集)

摘要&#xff1a;开发自动驾驶目标检测系统对于提高车辆的安全性和智能化水平具有至关重要的作用。本篇博客详细介绍了如何运用深度学习构建一个自动驾驶目标检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLO…

相机与相机模型(针孔/鱼眼/全景相机)

本文旨在较为直观地介绍相机成像背后的数学模型&#xff0c;主要的章节组织如下&#xff1a; 第1章用最简单的针孔投影模型为例讲解一个三维点是如何映射到图像中的一个像素 第2章介绍除了针孔投影模型外其他一些经典投影模型&#xff0c;旨在让读者建立不同投影模型之间的建模…

RabbitMQ高级-高级特性

1.消息可靠性传递 在使用RabbitMQ的时候&#xff0c;作为消息发送方希望杜绝任何消息丢失或者投递失败场景。RabbitMQ为我们提供了两种方式来控制消息的投递可靠性模式 1.confirm 确认模式 确认模式是由exchange决定的 2.return 退回模式 回退模式是由routing…

力扣热题100_矩阵_240_搜索二维矩阵 II

文章目录 题目链接解题思路解题代码 题目链接 240. 搜索二维矩阵 II 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a; 每行的元素从左到右升序排列。 每列的元素从上到下升序排列。 示例 1&#xff1a; 输入&#xf…

GAMES104-现代游戏引擎 1

主要学习重点还是面向就业&#xff0c;重点复习八股和算法 每天早上八点到九点用来学习这个课程 持续更新中... 第一节 游戏引擎导论 第二节 引擎架构分层

橡胶工厂5G智能制造数字孪生可视化平台,推进橡胶工业数字化转型

橡胶5G智能制造工厂数字孪生可视化平台&#xff0c;推进橡胶工业数字化转型。随着信息技术的迅猛发展和智能制造的不断推进&#xff0c;数字化转型已成为制造业转型升级的重要方向。橡胶工业作为传统制造业的重要领域&#xff0c;正面临着产业升级和转型的迫切需求。橡胶5G智能…

软考79-上午题-【面向对象技术3-设计模式】-结构型设计模式02

一、组合模式 1-1、意图 将对象组合成树型结构&#xff0c;以表示"部分-整体"的层次结构。Composite使得用户对单个对象和组 合对象的使用具有一致性。 示例&#xff1a;对象&#xff1a;文件、文件夹 1-2、结构 Component 为组合中的对象声明接口&#xff1b;在适…

决策树 | 分类树回归树:算法逻辑

目录 一. 决策树(Decision Tree)1. 决策树的构建1.1 信息熵(Entropy)1.1.1 信息量&信息熵 定义1.1.2 高信息熵&低信息熵 定义1.1.3 信息熵 公式 1.2 信息增益(Information Gain)1.2.1 信息增益的计算1.2.2 小节 2. 小节2.1 算法分类2.2 决策树算法分割选择2.3 决策树算…

提升物流效率,快递平台实战总结与分享

随着电商行业的蓬勃发展&#xff0c;物流配送服务变得愈发重要。快递平台作为连接电商企业和消费者的桥梁&#xff0c;扮演着至关重要的角色。本篇博客将分享快递平台实战经验&#xff0c;总结关键要点&#xff0c;帮助物流从业者提升物流效率、优化服务质量。 ### 快递平台实…

汽车网络基础知识 要点

在以太网开发中&#xff0c;常常会听到一些专业名词&#xff0c;例如PHY&#xff0c;MAC&#xff0c;MII&#xff0c;switch&#xff0c;下面是解释 PHY PHY 是物理接口收发器&#xff0c;它实现物理层。包括 MII/GMII (介质独立接口) 子层、PCS (物理编码子层) 、PMA (物理介…

SQLiteC/C++接口详细介绍之sqlite3类(十四)

返回目录&#xff1a;SQLite—免费开源数据库系列文章目录 上一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;十三&#xff09; 下一篇&#xff1a;SQLiteC/C接口详细介绍之sqlite3类&#xff08;十五&#xff09; 43.sqlite3_preupdate_hook sqlite3_preup…

基于springboot的高校化学试剂仓储管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

VMware NSX Advanced Load Balancer (NSX ALB) 22.1.6 - 多云负载均衡平台

VMware NSX Advanced Load Balancer (NSX ALB) 22.1.6 - 多云负载均衡平台 应用交付&#xff1a;多云负载均衡、Web 应用防火墙和容器 Ingress 服务 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-nsx-alb-22/&#xff0c;查看最新版。原创作品&#xff0c;转载请…

194 基于matlab的日历GUI制作

基于matlab的日历GUI制作&#xff0c;可实时显示当前的日期和时间&#xff0c;精确到秒。非常漂亮&#xff0c;也很基础&#xff0c;学习GUI的不错程序&#xff0c;程序已调通&#xff0c;可直接运行。 194 matlab 日历制作 GUI可视化 - 小红书 (xiaohongshu.com)