【前端实践系列之二】不用云函数,小程序也能直接解析Excel

1,492 阅读3分钟

这是我参与更文挑战的第2天,活动详情查看: 更文挑战 !

👽概论

小程序解析Excel表格文件并非不可能的任务,搭配SheetJs可以轻松完成任务,但在此过程中也有一些值得注意与分享的地方,下面就和大家一起来探讨一下。

此次实践演示以UNIAPP开发环境为例。

👽SheetJs介绍

SheetJs是一款框架无关的excel操作库,其可以再web环境下或者node环境下完成excel文件读写、新建、导出等诸多功能,此次实践的核心工具就是它啦。

👽模块引入

👻导入工具文件

在github的SheetJS中找到dist目录下的构建文件,官方提供了很多版本可供选择:

xlsx.full.min.js:包含全部特性及功能的版本;
xlsx.core.min.js:仅具备核心功能的版本;

因为此次项目仅涉及到了excel文件的解析,且小程序包体积又有大小要求,所以选择了xlsx.mini.min.js精简版本。

xlsx.mini.min.js文件下载后放入项目的utils文件夹中备用。

👻获取Excel文件

因为小程序本身只支持上传图像等媒体文件,所以我们选择从服务器请求获取Excel文件。

<script>
import XLSX from '../../utils/xlsx.mini.min.js';//导入SheetJS
export default {
  data() {
    return {
      fileLocation:'https://xxx.xxx.com/tagetFile.xlsx',//文件地址
     fileData:null//存放解析后的数据
     }
   },
  created() {
    this.getFileData();
  },  
  methods: {
    getFileData() {
      uni.request({
        url: this.fileLocation,
        data: {
          v: parseInt(Math.random() * 1000000000),//此处作防止服务器缓存用
        },
        method: 'GET',
        responseType: 'arraybuffer',//定义响应类型
        success:res=> {
          console.log(res.data)//此处即为获取到的二进制数据
          /*-----
          此处作如下几点解释:
              1. 解析后的原始数据我们一般不去改动它,为优化性能将其冻结;
              2. { type: 'array' }:定义解析后的数据格式为Array;
              3. 我们只关心其中的Sheets工作簿,故直接获取到它。
          -----*/
          this.fileData = Object.freeze(
              XLSX.read(new Uint8Array(res.data), { type: 'array' }).Sheets
          )
          console.log('文件获取失败', this.fileData);
        },
        fail:res=> {
          console.log('文件获取失败', res);
        },
      });
    },
  }
}

👻组织单元格数据

经过上一步操作处理后,我们成功获取到了excel表格中的数据,但同时我们也能清晰的发现,这些数据都是以单元格为单位零散地在一个数组中,这些数据往往需要进一步处理后才能使用。



<script>
···

export default {
···
  data(){
    return {
      cookedSheetAData:null//用于存储处理后的SheetA数据
    }
  },
  methods:{
  
    /* 原始表格数据处理函数
      param:{
        sheetName,//表名
        columnSortMap,//列与字段名之间的映射结构
        target,//待填充的目标
     }
    */
    processData(param) {
      let tableData = this.fileData[param.sheetName];

      let uselessKeys = Object.keys(tableData).filter(key => key[0] == '!'); //筛选非数据属性
      uselessKeys.forEach(key => delete tableData[key]); //删除非数据属性

      let usefulKeys = Object.keys(tableData);

      let titleRow = [1, 2]; //标题行行号
      let dataLength = usefulKeys.filter(key => key[0] == 'A').length - titleRow.length; //定义内容区数组长度

      let emptyItem = {};
      Object.values(param.columnSortMap).forEach(value => {
        emptyItem[value] = null;
      });
      this[param.target] = JSON.parse(JSON.stringify(Array(dataLength).fill(emptyItem))); //制作模板数据

      usefulKeys.forEach(key => {
        //解析单元格坐标:如将A3解析为'A'和'3'
        //因为此表数据列数较少,对于AZ31这种坐标的情况,此处不做介绍。大家自行开动脑筋
        let columSort = key.match(/[A-Z]/g).join(''); //'A'
        let columIndex = key.match(/\d/g).join(''); //'3'
        if (!titleRow.includes(+columIndex)) {
          //将解析后的值赋如data中
          this[param.target][columIndex - titleRow.length - 1][param.columnSortMap[columSort]] =
            param.columnSortMap[columSort] == 'loanDate' ? tableData[key].w : tableData[key].v;
        }
      });
      Object.freeze(this[param.target]);//依旧做冻结处理
    },
    
    //调用处理函数,处理相应工作簿
    processSheetA() {
      let target = 'cookedSheetAData';//data中用于存放对应数据的字段
      let sheetName = '一级渠道放款列表';//工作簿名
      let columnSortMap = {
        A: 'region',
        B: 'rank',
        C: 'name',
        D: 'channelQuality2nd',
        E: 'loanAmount',
        F: 'finishedCount',
        G: 'unfinishedCount',
        H: 'refusedCount',
        I: 'passingRate',
        J: 'loanDate',
        K: 'incrementRate',
      };//列坐标与实体字段之间的映射结构(即A列下的数据对应cookedSheetAData数组中的region字段)
      this.processData({ target, sheetName, columnSortMap });
    },
  }
}

接下来的工作就是调用相应方法对数据处理,再在页面上展示啦。大功告成!

👽结语

对于Excel解析来说,代码开发阶段固然很重要,但更为重要的其实是Excel内容结构的定义。一份合理明晰的excel表格可以为开发工作省下不少事情,诸君切记!!🙊🙊🙊