前端保留样式导入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端表格编辑,如果需求复杂或者使用频率高,建议使用其他技术或商业库,如:

相关推荐
Abigail_chow4 小时前
EXCEL如何快速批量给两字姓名中间加空格
windows·microsoft·excel·学习方法·政务
xiaohezi15 小时前
Rag chunk 之:Excel 文档解析
excel
weixin_4723394619 小时前
python批量解析提取word内容到excel
python·word·excel
1 天前
Unity与Excel表格交互热更方案
unity·游戏引擎·excel
金融小白数据分析之路1 天前
Excel高级函数使用FILTER、UNIQUE、INDEX
excel
未来之窗软件服务1 天前
Excel表格批量下载 CyberWin Excel Doenlaoder 智能编程-——玄武芯辰
excel·批量下载·仙盟创梦ide·东方仙盟
阿斯加德的IT2 天前
Power Automate: 从Excel 选择列,每200条生成一个CSV文件并保存在sharepoint文档库
低代码·excel
步达硬件2 天前
【转bin】EXCEL数据转bin
excel
wtsolutions2 天前
JSON to Excel 3.0.0 版本发布 - 从Excel插件到Web应用的转变
json·excel·json-to-excel·wtsolutions
cnfelix2 天前
vim&adb&git命令
elasticsearch·vim·excel