需求:将微信小程序云数据库中的数据导出为excel文件,文件按团队分为不同的sheet页,首页汇总每个sheet页的数据总数,并可点击跳转至对应的sheet页。下载时可选择今年某月份进行下载对应的数据。

get到如上需求,记录一下完成时遇到的问题。

问题1:如何生成并下载excel文件?

思路:在云函数端通过 node-xlsx 生成excel文件流,通过 cloud.uploadFile 将本地资源上传至云存储空间,在小程序端通过 cloud.downloadFile 从云存储空间下载文件。

云函数端:

...
async downloadExcel(event, context, db) {
  //获取数据
  let info = await db.collection('数据库表名').get();
  let dataCVS = `excel.xlsx`;
  let alldata = [];
  let row = ['name', 'time','team']; 
  alldata.push(row);  //将此行数据添加到一个向表格中存数据的数组中
  for (let key = 0; key<info.data.length; key++) {
      let arr = [];
      row.forEach(item=>{
        arr.push(info.data[key][item]);
      })
      alldata.push(arr)
      }
    var buffer = await xlsx.build(build);
    //将表格存入到存储库中并返回文件ID
    return await cloud.uploadFile({
      cloudPath: dataCVS,
      fileContent: buffer, //excel二进制文件
    })
  }

小程序端:

downloadExcel() {
    wx.cloud.callFunction({
	name: 'downloadExcel'
	success: res => {
		wx.cloud.getTempFileURL({
			//获取文件下载地址
			fileList: [res.result.fileID],
			success: res => {
				wx.downloadFile({
					url: res.fileList[0].tempFileURL,
					filePath: wx.env.USER_DATA_PATH + '/数据.xlsx', //本地文件路径
					success(res) {
						let data = res.filePath;
						wx.openDocument({ //打开文档
								filePath: data,
								showMenu: true
							});
						},
						fail(res) {
							console.log(res);
						}
					});
				},
				fail(res) {}
			});
		},
		fail(res) {}
	});
}

问题2:如何导出不同的sheet页?

解决:build时,数组的长度是多少则生成多少个sheet页。

例:

import xlsx from 'node-xlsx';

const data1 = [[1, 2, 3], [true, false, null, 'sheetjs'], ['baz', null, 'qux']];
const data2 = [[1, 2, 3], ['foo', 'bar', new Date('2014-02-19T14:30Z'), '0.3'], ['baz', null, 'qux']];

var buffer = xlsx.build([{name: "sheet1", data: data1},{name: "sheet2", data: data2}]);

问题3:如何实现点击首页汇总信息的不同单元格跳转至对应的sheet页?

思路:官方文档只提供了跳转外部链接和跳转同一页单元格的实例,那如何链接跳转至其他sheet页呢?在excel中,可以用 hyperlink 这个超链接函数实现跳转至其他工作表(sheet页),excel中的用法是 hyperlink("#表格名称!单元格",引用目标),在 node-xlsx 中超链接函数也叫 hyperlink,那用法是否也是如此呢?试试将单元格元素的 l 的target 写为 "#其他sheet页名称!A1",点击跳转,果然跳转指向其他sheet页的A1单元格。

例:

...
let indexPage = [
      ['团队', '数量']
    ];
let build = alldata.map((item, index) => {
      indexPage.push([{
        t: "s", //单元格类型string
        v: sheets[index], //值
        l: {
          Target: `#${sheets[index]}!A1` //跳转
        }
      }, item.length])
      item.unshift(row)
      return {
        name: sheets[index], //对应的sheet页名称
        data: item
      }
    })
    build.unshift({
      name: '汇总',
      data: indexPage
})

var buffer = await xlsx.build(build);
...

问题4:下载所有数据时,总数远远少于数据库中的数据量?

原因:统计集合记录数或统计查询语句对应的结果记录数,小程序端与云函数端的表现会有如下差异:

  • 小程序端:如果没有指定 limit,则默认且最多取 20 条记录。
  • 云函数端:如果没有指定 limit,则默认且最多取 100 条记录。

解决:官方提供了解决样例(如下),更多查看这部分的官方文档

const cloud = require('wx-server-sdk')
cloud.init({
  env: cloud.DYNAMIC_CURRENT_ENV
})
const db = cloud.database()
const MAX_LIMIT = 100
exports.main = async (event, context) => {
  // 先取出集合记录总数
  const countResult = await db.collection('todos').count()
  const total = countResult.total
  // 计算需分几次取
  const batchTimes = Math.ceil(total / 100)
  // 承载所有读操作的 promise 的数组
  const tasks = []
  for (let i = 0; i < batchTimes; i++) {
    const promise = db.collection('todos').skip(i * MAX_LIMIT).limit(MAX_LIMIT).get()
    tasks.push(promise)
  }
  // 等待所有
  return (await Promise.all(tasks)).reduce((acc, cur) => {
    return {
      data: acc.data.concat(cur.data),
      errMsg: acc.errMsg,
    }
  })
}

问题5:点击不同的月份,即传参不同,下载下来的excel文件未变化,或有时变化?

思考:

  • 参数未传递过去或是异步导致未及时获取到最新参数?经过验证,接收到了每次传递的参数,排除。
  • 获取的数据库数据不是最新的?经过验证,排除。
  • 上传上去的文件未更新?因为想到我每次上传上去的文件名是一样的,但官方文档明明说:将本地资源上传至云存储空间,如果上传至同一路径则是覆盖  呀?查了查,发现了相似的问题讨论:地址,看来应该是上传后同名文件未覆盖的。

解决:将文件名改为时间戳(不过我自己是把文件名改为对应的月份,这样最多只有12个此文件,因为数据不会经常下载,每次下载间隔时间长,会得到最新文件,所以一般不会有问题),试试,每次下载的文件是对应的数据了。但这种方式不是很好,若有朋友有更好的方法,希望可以提供呀。

 

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐