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

相关推荐
专注VB编程开发20年12 小时前
Power Query 是 Excel 和 Power BI 中强大的数据获取、转换和加载工具
数据库·excel·vba·csv·导入数据
silenci2 天前
vscode配置vim
vscode·vim·excel
杜子腾dd2 天前
16.Excel:打印技巧
数据分析·excel
就叫年华吧丶3 天前
Apache POI实现Excel的基本写入、导出操作
java·maven·apache·excel
杜子腾dd3 天前
12.Excel:查找替换
excel
CodeJourney.3 天前
基于DeepSeek与HTML的可视化图表创新研究
数据库·人工智能·信息可视化·excel
龙虎榜小红牛系统3 天前
Python项目源码57:数据格式转换工具1.0(csv+json+excel+sqlite3)
python·json·excel
wtsolutions4 天前
在Excel中轻松处理嵌套JSON数据:json-to-excel插件使用指南
json·excel·nested·嵌套·json-to-excel
課代表4 天前
Excel VBA 自定义函数
excel·vba·自定义函数
vortex54 天前
深入理解 Bash 中的 $‘...‘ 字符串语法糖
开发语言·bash·excel