前端保留样式导入Excel编辑并导出

用x-spreadsheet开发时需要用到导入导出功能,找了一堆只找到sheetJs的demo(https://docs.sheetjs.com/docs/demos/grid/xs),在线体验(https://docs.sheetjs.com/xspreadsheet/);

但是sheetJs要保留样式需要升级pro版本,所以用Excel.js 做了一个简单的实现,基本满足目前的需求, 原理是导入时把excel的数据格式转换成x-spreadsheet的数据格式,导出时在反过来转换。

因为搜到的其他人的文章给了我很大的帮助,所以用sheetJs的demo代码整理修改成这个单文件的示例,如果有人遇到同样的问题,希望能帮你节省一两个小时的时间

效果如图:

仓库地址:github.com/changgeee/x...

在线体验:changgeee.github.io/x-spreadshe...

主要代码:

scss 复制代码
// 导入
let workbookData = [];
      wb.eachSheet((sheet, sheetIndex) => {
        // 构造x-data-spreadsheet 的 sheet 数据源结构
        let sheetData = { name: sheet.name, styles: [], rows: {}, merges: [] }
        // 收集合并单元格信息
        let mergeAddressData = []
        for (let mergeRange in sheet._merges) {
          sheetData.merges.push(sheet._merges[mergeRange].shortRange)
          let mergeAddress = {}
          // 合并单元格起始地址
          mergeAddress.startAddress = sheet._merges[mergeRange].tl
          // 合并单元格终止地址
          mergeAddress.endAddress = sheet._merges[mergeRange].br
          // Y轴方向跨度
          mergeAddress.YRange = sheet._merges[mergeRange].model.bottom - sheet._merges[mergeRange].model.top
          // X轴方向跨度
          mergeAddress.XRange = sheet._merges[mergeRange].model.right - sheet._merges[mergeRange].model.left
          mergeAddressData.push(mergeAddress)
        }
        sheetData.cols = {}
        for (let i = 0; i < sheet.columns.length; i++) {
          sheetData.cols[i.toString()] = {}
          if (sheet.columns[i].width) {
            sheetData.cols[i.toString()].width = sheet.columns[i].width * 8
          } else {
            // 默认列宽
            sheetData.cols[i.toString()].width = 100
          }
        }

        // 遍历行
        sheet.eachRow((row, rowIndex) => {
          sheetData.rows[(rowIndex - 1).toString()] = { cells: {} }
          if (row.height) {
            sheetData.rows[(rowIndex - 1).toString()].height = row.height * 1.5;
          }
          row.eachCell({ includeEmpty: true }, function (cell, colNumber) {
            let cellText = ''
            if (cell.value && cell.value.result) {
              // Excel 单元格有公式
              cellText = cell.value.result
            } else if (cell.value && cell.value.richText) {
              // Excel 单元格是多行文本
              for (let text in cell.value.richText) {
                // 多行文本做累加
                cellText += cell.value.richText[text].text
              }
            }
            else {
              // Excel 单元格无公式
              cellText = cell.value
            }

            // 背景色
            let backGroundColor = null
            if (cell.style.fill && cell.style.fill.fgColor && cell.style.fill.fgColor.argb) {
              backGroundColor = argb2hex(cell.style.fill.fgColor.argb)
            }

            if (backGroundColor) {
              cell.style.bgcolor = backGroundColor
            }
            // 字体颜色
            let fontColor = null
            if (cell.style.font && cell.style.font.color && cell.style.font.color.argb) {
              fontColor = argb2hex(cell.style.font.color.argb)
            }
            if (fontColor) {
              //console.log(fontColor)
              cell.style.color = fontColor
            }

            // 对齐 
            if (cell.style.alignment && cell.style.alignment.horizontal) {
              cell.style.align = cell.style.alignment.horizontal
              cell.style.valign = cell.style.alignment.vertical || "middle";
            }
            if (cell.style.border) {
              // excel border
              // {
              //   "left": {
              //       "style": "thin",
              //       "color": {
              //           "argb": "FF000000"
              //       }
              //   },
              //   "right": {
              //       "style": "thin",
              //       "color": {
              //           "argb": "FF000000"
              //       }
              //   },
              //   "top": {
              //       "style": "thin",
              //       "color": {
              //           "argb": "FF000000"
              //       }
              //   },
              //   "bottom": {
              //       "style": "thin",
              //       "color": {
              //           "argb": "FF000000"
              //       }
              //   }
              // }

              // xs border
              // {
              //   "bottom": [
              //       "thin",
              //       "#000"
              //   ],
              //   "top": [
              //       "thin",
              //       "#000"
              //   ],
              //   "left": [
              //       "thin",
              //       "#000"
              //   ],
              //   "right": [
              //       "thin",
              //       "#000"
              //   ]
              // }
              const { left, right, top, bottom } = cell.style.border;
              let bd = {
                top: _.isArray(top) ? top : [top.style, argb2hex(top.color.argb)],
                bottom: _.isArray(bottom) ? bottom : [bottom.style, argb2hex(bottom.color.argb)],
                left: _.isArray(left) ? left : [left.style, argb2hex(left.color.argb)],
                right: _.isArray(right) ? right : [right.style, argb2hex(right.color.argb)]
              }
              cell.style.border = bd;
            }
            //处理合并单元格
            let mergeAddress = _.find(mergeAddressData, function (o) { return o.startAddress == cell._address })
            if (mergeAddress) {
              // 遍历的单元格属于合并单元格
              if (cell.master.address != mergeAddress.startAddress) {
                // 不是合并单元格中的第一个单元格不需要计入数据源
                return
              }
              // 说明是合并单元格区域的起始单元格
              sheetData.rows[(rowIndex - 1).toString()].cells[(colNumber - 1).toString()] = { text: cellText, style: 0, merge: [mergeAddress.YRange, mergeAddress.XRange] }
              sheetData.styles.push(cell.style)
              //对应的style存放序号
              sheetData.rows[(rowIndex - 1).toString()].cells[(colNumber - 1).toString()].style = sheetData.styles.length - 1
            }
            else {
              // 非合并单元格
              sheetData.rows[(rowIndex - 1).toString()].cells[(colNumber - 1).toString()] = { text: cellText, style: 0 }
              //解析单元格,包含样式
              sheetData.styles.push(cell.style)
              //对应的style存放序号
              sheetData.rows[(rowIndex - 1).toString()].cells[(colNumber - 1).toString()].style = sheetData.styles.length - 1
            }
          });
        })
        workbookData.push(sheetData)
      })
      return workbookData;

这种方式只适合轻量化的web端表格编辑,如果需求复杂或者使用频率高,建议使用其他技术或商业库,如:

相关推荐
开开心心_Every13 小时前
强制打字练习工具:打够百字才可退出
java·游戏·微信·eclipse·pdf·excel·语音识别
开开心心_Every13 小时前
多端免费远程控制工具:4K流畅同账号直连
游戏·macos·微信·pdf·excel·语音识别·phpstorm
hhzz1 天前
Springboot项目中使用POI操作Excel(详细教程系列3/3)
spring boot·后端·excel·poi·easypoi
Excel工作圈1 天前
凭证助手一键匹配已勾选抵扣发票与全量发票明细
数据库·excel
qq_150841991 天前
32位的CVI2010基于ExcelReport库无法正常访问64位EXCEL的解决方案
excel
weixin_431822401 天前
办公自动化:通过字符串相似度算法找出Excel 中的重复数据
excel·零售
寄思~1 天前
Excel 数据匹配工具 -笔记
笔记·python·学习·excel
222you2 天前
智慧社区:居民信息Excel导入数据库
excel
霍格沃兹测试开发学社测试人社区2 天前
Playwright数据驱动测试:从Excel与JSON获取测试数据指南
excel·playwright
关于不上作者榜就原神启动那件事2 天前
Java中大量数据Excel导入导出的实现方案
java·开发语言·excel