vue项目使用xlsx-style导出自定义格式excel

一.遇到问题

1.给自定义格式的excel中的合并单元格加边框的时候,出现边框不完整的情况.

解决:将该合并单元格拆分成最小单位的单元格,设置其边框属性. 直接赋值的话不可行,因为子单元格并没有边框属性,只有合并单元格才有, 这里使用的方法是:记录合并单元格的属性信息, 然后循环对子单元格赋值.
代码:

function addRangeBorder(range,ws){
	let arr = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
    
	range.forEach(item=>{
		let startColNumber = Number(item.s.r), endColNumber = Number(item.e.r);
		let startRowNumber = Number(item.s.c), endRowNumber = Number(item.e.c);
		const test = ws[arr[startRowNumber] + (startColNumber + 1)];
		for(let col = startColNumber ; col <= endColNumber ; col++)
		{
			for(let row = startRowNumber; row <= endRowNumber ; row++)
			{
				//const test = ws[arr[j] + i];
				//console.log(arr[row] + (col + 1));
				ws[arr[row] + (col + 1)] = test;
				//{s:{border:{top:{style:'thin'}, left:{style:'thin'},bottom:{style:'thin'},right:{style:'thin'}}}};
			}
		}
	})
	return ws;
}

二.DEMO

一个完整的例子:

import FileSaver from 'file-saver'
import XLSX from 'xlsx-style'
 
 // 设置表格中cell默认的字体,居中,颜色等

var defaultCellStyle = {
 			font: {
 				name: "宋体", sz: 11, color: { auto: 1 } ,
 			},
 			border: {
 				color: { auto: 1 },
				top: { style: 'thin' },
				bottom: { style: 'thin'},
				left: { style: 'thin' },
				right: { style: 'thin' }
 			},
 			alignment: {
 				/// 自动换行
 				wrapText: 1,
 					// 居中
 				horizontal: "center",
 				vertical: "center",
 				indent: 0
 			}
 		};
// 从json转化为sheet,xslx中没有aoaToSheet的方法,该方法摘自官方test
function sheet_from_array_of_arrays(data) {
  const ws = {};
  const range = {s: {c:10000000, r:10000000}, e: {c:0, r:0 }};
    for(let R = 0; R !== data.length; ++R) {
    for(let C = 0; C !== data[R].length; ++C) {
      if(range.s.r > R) range.s.r = R;
      if(range.s.c > C) range.s.c = C;
      if(range.e.r < R) range.e.r = R;
      if(range.e.c < C) range.e.c = C;
      /// 这里生成cell的时候,使用上面定义的默认样式
      const cell = {v: data[R][C], s: defaultCellStyle};
      if(cell.v == null) continue;
      const cell_ref = XLSX.utils.encode_cell({c:C,r:R});

      /* TEST: proper cell types and value handling */
      if(typeof cell.v === 'number') cell.t = 'n';
      else if(typeof cell.v === 'boolean') cell.t = 'b';
      else if(cell.v instanceof Date) {
      cell.t = 'n'; cell.z = XLSX.SSF._table[14];
      cell.v = this.dateNum(cell.v);
      }
      else cell.t = 's';
      ws[cell_ref] = cell;
    }
  }

  /* TEST: proper range */
  if(range.s.c < 10000000) ws['!ref'] = XLSX.utils.encode_range(range);
  return ws;
}


// 将一个sheet转成最终的excel文件的blob对象,然后利用URL.createObjectURL下载
function sheet2blob(sheet, sheetName) {
  sheetName = sheetName || 'sheet1';
  const workbook = {
    SheetNames: [sheetName],
    Sheets: {}
  };
  workbook.Sheets[sheetName] = sheet

  window.console.log(workbook)
  // 生成excel的配置项
  const wopts = {
    bookType: 'xlsx', // 要生成的文件类型
    bookSST: false, // 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性
    type: 'binary'
  };

  const wbout = XLSX.write(workbook, wopts, { defaultCellStyle: defaultCellStyle });
  const blob = new Blob([s2ab(wbout)], {type: "application/octet-stream"});
  // 字符串转ArrayBuffer
  function s2ab(s) {
    const buf = new ArrayBuffer(s.length);
    const view = new Uint8Array(buf);
    for (let i=0; i!==s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF;
    return buf;
  }
  return blob;
}


function addRangeBorder(range,ws){
	let arr = ["A","B","C","D","E","F","G","H","I","J","K","L","M","N","O","P","Q","R","S","T","U","V","W","X","Y","Z"];
    
	range.forEach(item=>{
		let startColNumber = Number(item.s.r), endColNumber = Number(item.e.r);
		let startRowNumber = Number(item.s.c), endRowNumber = Number(item.e.c);
		const test = ws[arr[startRowNumber] + (startColNumber + 1)];
		for(let col = startColNumber ; col <= endColNumber ; col++)
		{
			for(let row = startRowNumber; row <= endRowNumber ; row++)
			{
				//const test = ws[arr[j] + i];
				//console.log(arr[row] + (col + 1));
				ws[arr[row] + (col + 1)] = test;
				//{s:{border:{top:{style:'thin'}, left:{style:'thin'},bottom:{style:'thin'},right:{style:'thin'}}}};
			}
		}
	})
	return ws;
}

function funtransformF() {
	 
      // 表头信息 要合并的字段用null代替
	  let aoa = [
        ['江苏省多式联运示范工程项目2019年第二季度动态监测信息表'],
        ['序号', '市别', '示范工程项目名称', '示范工程项目运行情况'],
        [null, null, null, '联运组织',null, null, null, null, null, null, '企业总货物运输量(万吨/万标箱)', null, '枢纽场站建设投资额(万元)', '装备配备建设投资额(万元)', '信息化建设投资额(万元)', '其他'],
        [null, null, null, '联运线路(条)','联运线路运营情况', null, null, null, null, null],
        [null, null, null, null, '线路', '联运路线', '联运模式', '联运量', null, '联运周转量(万吨公里)', null, null, null, null, null, null],
        [null, null, null, null, null, null, null, '万吨', '万标箱', null, '万吨', '万标箱', null, null, null]
      ]
      
      // json => sheet
      const sheet = sheet_from_array_of_arrays(aoa);
      
      // 表头合并: r: row 行;c:column 列
      const mergeTitle = [
        // 江苏省多式联运示范工程项目2019年第二季度动态监测信息表
        {
          s: {r: 0, c: 0},
          e: {r: 0, c: 15}
        },
        // 序号
        {
          s: {r: 1, c: 0},
          e: {r: 5, c: 0}
        },
        // 市别
        {
          s: {r: 1, c: 1},
          e: {r: 5, c: 1}
        },
        // 示范工程名称
        {
          s: {r: 1, c: 2},
          e: {r: 5, c: 2}
        },
        // 示范工程项目运行情况
        {
          s: {r: 1, c: 3},
          e: {r: 1, c: 15}
        },
        // 联运组织
        {
          s: {r: 2, c: 3},
          e: {r: 2, c: 9}
        },
        // 联运线路(条)
        {
          s: {r: 3, c: 3},
          e: {r: 5, c: 3}
        },
        // 联运线路运营情况
        {
          s: {r: 3, c: 4},
          e: {r: 3, c: 9}
        },
        // 线路
        {
          s: {r: 4, c: 4},
          e: {r: 5, c: 4}
        },
        // 联运路线
        {
          s: {r: 4, c: 5},
          e: {r: 5, c: 5}
        },
        // 联运模式
        {
          s: {r: 4, c: 6},
          e: {r: 5, c: 6}
        },
        // 联运量
        {
          s: {r: 4, c: 7},
          e: {r: 4, c: 8}
        },
        // 联运周转量(万吨公里)
        {
          s: {r: 4, c: 9},
          e: {r: 5, c: 9}
        },

        // 企业总货物运输量(万吨/万标箱)
        {
          s: {r: 2, c: 10},
          e: {r: 4, c: 11}
        },
        // 枢纽场站建设投资额(万元)
        {
          s: {r: 2, c: 12},
          e: {r: 5, c: 12}
        },
        //装备配备建设投资额(万元)
        {
          s: {r: 2, c: 13},
          e: {r: 5, c: 13}
        },
        // 信息化建设投资额(万元)
        {
          s: {r: 2, c: 14},
          e: {r: 5, c: 14}
        },
        // 其他
        {
          s: {r: 2, c: 15},
          e: {r: 5, c: 15}
        },
				]
				
      //sheet['!merges'] = mergeTitle.concat(mergeContent);
      sheet['!merges'] = mergeTitle;
      // 冻结前6行和第一列,右下可以滑动
      sheet["!freeze"] = {
        xSplit: "1",
        ySplit: "6",
        topLeftCell: "B7",
        activePane: "bottomRight",
        state: "frozen",
      }
      sheet["!margins"] = { left: 1.0, right: 1.0, top: 1.0, bottom: 1.0, header: 0.5, footer: 0.5 }
      // 列宽 使用的不是像素值
      const sheetCols = [
        { wch: 8} , // 序号
        { wch: 10 }, // 市别
        { wch: 20 }, // 示范工程项目名称
        { wch: 9 }, // 联运线路(条)
        { wch: 8 }, // 线路
        { wch: 18 }, // 联运路线
        { wch: 15 }, // 联运模式
        { wch: 9 }, // 联运量-万吨
        { wch: 9 }, // 联运量-万标箱
        { wch: 12 }, // 联运周转量
        { wch: 9 }, // 企业万吨
        { wch: 9 }, // 企业 万标箱
        { wch: 10 }, // 枢纽站
        { wch: 10 }, // 装备
        { wch: 10 }, // 信息化
        { wch: 27 }, // 备注
      ];
      sheet['!cols'] = sheetCols;
			
			addRangeBorder(mergeTitle, sheet);
			
      const wbBlob = sheet2blob(sheet, '1')
      // 保存下载
      FileSaver.saveAs(wbBlob, 'd.xlsx')
    }
	
	export {
		funtransformF
	}

效果图:
在这里插入图片描述

你可能感兴趣的:(前端)