菜单 学习猿地 - LMONKEY

VIP

开通学习猿地VIP

尊享10项VIP特权 持续新增

知识通关挑战

打卡带练!告别无效练习

接私单赚外块

VIP优先接,累计金额超百万

学习猿地私房课免费学

大厂实战课仅对VIP开放

你的一对一导师

每月可免费咨询大牛30次

领取更多软件工程师实用特权

入驻
0
0

前端实现excel导出和导入(react项目)

原创
05/13 14:22
阅读数 196

课程推荐:web全栈就业班--融职教育精品课程

最近公司有个新需求,需要前端导出规定模板的excel,导入的时候前端解析excel文件,数据处理一下传给后台,下面分享下纯前端实现excel的导出和导入:

excel导出

handleClick = () => {
// 定制化改动地方
let excelData = this.state.excelData // 数据是后台返回渲染导出的excel数据的
let data = []
if (excelData.length) {
for (let i = 0; i < excelData.length; i++) {
let obj = {
'监控面编号': excelData[i].taskName,
'监控点编号': excelData[i].monitorPointNumber,
'高程(m)': '',
};
data.push(obj);
}
} else {
let obj = {
'监控面编号': '',
'监控点编号': '',
'高程(m)': '',
};
data.push(obj);
}
// 表格标题

const options = {
  dataTitle: '表名:哈哈哈',
  reportCompany: '说明:XXXX',
  date: `日期`,
  reportType: `施工工况`,
}
// 配置文件类型
const wopts = { bookType: 'xlsx', bookSST: true, type: 'binary', cellStyles: true };
this.recordExcelDown(data, wopts, options);

}
recordExcelDown = (json, type, options) => {
var tmpdata = json[0];
// 定制化改动地方
json.unshift({}, {}, {}, {}); // 向表格数据中插入4行位置(规定的模板)
const keyMap = []; // 获取keys
for (const k in tmpdata) { // 为插入的4行位置添加数据
keyMap.push(k);
// 定制化改动地方
json[3][k] = k; // json[3][k] = k 3为插入4行的最后一行索引,用于展示列头
}
var tmpdata = []; // 用来保存转换好的json
json
.map((v, i) => {
const data = keyMap.map((k, j) => {
return Object.assign(
{},
{
v: v[k],
position: (j > 25 ? this.getCharCol(j) : String.fromCharCode(65 + j)) + (i + 2), // 表格数据的位置
}
);
});
return data;
})
.reduce((prev, next) => prev.concat(next))
.forEach(
(v, i) =>
(tmpdata[v.position] = {
v: v.v,
})
);
let outputPos = Object.keys(tmpdata); // 设置区域,比如表格从A1到D10
// 定制化改动地方
tmpdata.A1 = { v: options.dataTitle }; // A1-A4区域的内容
tmpdata.A2 = { v: options.reportCompany };
tmpdata.A3 = { v: options.date };
tmpdata.A4 = { v: options.reportType };
tmpdata.C3 = { v: '2020-1-1' };
tmpdata.C4 = { v: '1号梁段挂篮就位' };
tmpdata.C6 = { v: '1.001' };
tmpdata.C1 = { v: 'tid' };
tmpdata.D1 = { v: this.props.taskId };
tmpdata.E1 = { v: 'bid' };
tmpdata.F1 = { v: this.props.bridgeRecord.id };
// 定制化改动地方
outputPos = ['A1'].concat(['A2'], ['A3'], ['A4'], ['C1'], ['C4'], ['C5'], ['C6'], ['D1'], ['D3'], ['D4'], ['D6'], ['E1'], ['F1'], outputPos);
// 定制化改动地方
tmpdata.A1.s = {
font: { sz: 14, bold: true, vertAlign: true },
alignment: { vertical: 'center', horizontal: 'center' }, // 垂直、水平
fill: { bgColor: { rgb: 'E8E8E8' }, fgColor: { rgb: 'E8E8E8' } },
}; // <====设置xlsx单元格样式
tmpdata.A2.s = {
font: { sz: 12, bold: true, vertAlign: true },
alignment: { vertical: 'center', horizontal: 'bottom' },
}; // <====设置xlsx单元格样式
tmpdata.A3.s = {
font: { sz: 12, bold: true, vertAlign: true },
alignment: { vertical: 'center', horizontal: 'bottom' },
}; // <====设置xlsx单元格样式
tmpdata.A4.s = {
font: { sz: 12, bold: true, vertAlign: true },
alignment: { vertical: 'center', horizontal: 'bottom' },
}; // <====设置xlsx单元格样式
// s-e 代表区域 c-r 代表列-行的索引
// 定制化改动地方
tmpdata['!merges'] = [
{
s: { c: 0, r: 0 },
e: { c: 1, r: 0 },
},
{
s: { c: 0, r: 1 },
e: { c: 9, r: 1 },
},
{
s: { c: 0, r: 2 },
e: { c: 1, r: 2 },
},
{
s: { c: 0, r: 3 },
e: { c: 1, r: 3 },
},
]; // <====合并单元格
let dataArrWidth = []
// 定制化改动地方
json.forEach(item => {
dataArrWidth.push({ wpx: 100 })
})
tmpdata['!cols'] = dataArrWidth;// <====设置一列宽度, 代表20列都是300宽
const tmpWB = {
SheetNames: ['mySheet'], // 保存的表标题
Sheets: {
mySheet: Object.assign(
{},
tmpdata, // 内容
{
'!ref': ${outputPos[0]}:${outputPos[outputPos.length - 1]}, // 设置填充区域(表格渲染区域)
}
),
},
};
const tmpDown = new Blob(
[
this.s2ab(
XLSX.write(
tmpWB,
{ bookType: type == undefined ? 'xlsx' : type.bookType, bookSST: false, type: 'binary' } // 这里的数据是用来定义导出的格式类型
)
),
],
{
type: '',
}
);
// 定制化改动地方
this.saveAs(tmpDown, ${'记录模板' + '.'}${type.bookType == 'biff2' ? 'xls' : type.bookType});
}

这边也是借鉴了百度的大佬,合并单元格可以生效的,不过样式的修改并没有生效,有需要的可以自己研究下。

excel导入

我选择的是原始的input type=file 来实现文件的上传解析,因为初始的这个标签效果并不是很好看,右边还默认带着一个未选择任何文件的字样,又改不掉样式,这边我用了个小技巧分享出来:
原理:input的样式设置display: none 用过div绝对定位在这儿,取原始input的id 并添加点击事件,从而实现点击div实现文件上传
html部分代码:

    <Col span={20} className={style.btnSpec}>
    <input
      type='file' accept='.xlsx, .xls' onChange={this.excelImport} style={{ height: '30px', lineHeight: '26px', display: 'none' }} id="batchUpload" />
      <div onClick={this.centerUploadBox} className={style.centerUploadBox}>
        文件上传
      </div>
    </Col>

js部分代码:

// div按钮实现上传
centerUploadBox = () => {
var me = this;
let batchUpload = document.querySelector('#batchUpload');
batchUpload.click();
let filesList = document.querySelector('#batchUpload').files;
batchUpload.addEventListener('change', function () {
filesList = document.querySelector('#batchUpload').files;
if (filesList.length == 0) {
return;
}
})
}
//导入excel
dataImport = file => {
const { dispatch } = this.props
// 获取上传的文件对象
const { files } = file.target;
// 通过FileReader对象读取文件
const fileReader = new FileReader();
fileReader.onload = event => {
// try {
const { result } = event.target;
// 以二进制流方式读取得到整份excel表格对象
const workbook = XLSX.read(result, { type: 'binary' });
// 存储获取到的数据
let params = []
let listRow = []; // 多少列的数组
let maxHeight = 0;
let indexK = 0

  console.log(workbook)
  let oriData = workbook.Sheets.mySheet;
  // 这边就是读取了excel的所有内容
  let excelFilterData = []

  for (let k in oriData) {
    ++indexK
    if (indexK == 1) {
      maxHeight = oriData[k].substring(oriData[k].lastIndexOf(":") + 2)
    } else {
      listRow.push(k.substring(0, 1))
    }

  }

  listRow = Array.from(new Set(listRow))
  listRow.splice(listRow.length - 1, 1)

  // 字母排序
  listRow = listRow.sort((s, t) => {
    let a = s.toLowerCase();
    let b = t.toLowerCase();
    if (a < b) return -1;
    if (a > b) return 1;
    return 0;
  })
  //找到最大列 eg.9
  //找到最大行 eg.39
  //这边主要是处理后台需要的数据
  for (let i = 2; i <= listRow.length - 1; i++) {
    //循环行
    if (oriData[listRow[i] + '3']) {
      if (oriData[listRow[i] + '3'].h) {
        excelFilterData.push(oriData[listRow[i] + '3'].h)
      } else if (oriData[listRow[i] + '3'].v) {
        excelFilterData.push(this.formatDate(oriData[listRow[i] + '3'].v, '-'))
      }
    }


    for (let j = 6; j <= Number(maxHeight); j++) {
      let reg = /^[0-9,.]*$/, item = {}

      if (oriData[listRow[i] + j] && oriData[listRow[i] + j].h && reg.test(oriData[listRow[i] + j].h)) {
        item = {
          calculationElevation: oriData[listRow[i] + j] ? oriData[listRow[i] + j].h : "",
          constructionMonitorPoint: {
            monitorPointNumber: oriData[`B${j}`] ? oriData[`B${j}`].h : "",
          },
          constructionMonitorTask: {
            taskName: oriData[`A${j}`] ? oriData[`A${j}`].h : "",
          },
          constructionMonitorTaskCollection: {
            checkTime: oriData[listRow[i] + '3'] ? oriData[listRow[i] + '3'].h ? oriData[listRow[i] + '3'].h : this.formatDate(oriData[listRow[i] + '3'].v, '-') : "",
            constructCondition: oriData[listRow[i] + '4'] ? oriData[listRow[i] + '4'].h : "",
          }
        }
        params.push(item)
      }
    }
  }
  this.setState({ excelFilterData: excelFilterData })
  console.log(params)
  this.setState({ filterVisible: true, excelParams: params })


};
// 以二进制方式打开文件
fileReader.readAsBinaryString(files[0]);

}

div按钮的样式:

.centerUploadBox {
margin-top: 8px;
height: 30px;
line-height: 26px;
width: 89px;
float: right;
margin-right: 623px;
cursor: pointer;
background: #fafafa;
padding: 0 10px;
padding-top: 1px;
font-size: 16px;
border-radius: 0px;
color: #000000e6;
border: solid 1px #000;
border-color: rgba(0, 0, 0, 0.31);
}

file
按钮就是这样的 哈哈哈哈哈哈 也不是很好看 根据需求和页面布局自行画吧 ~
以上就是纯前端实现excel导出和导入!!! 不足之处就是设置样式没有生效 ,导入的时候不能做到识别合并单元格,所以说很复杂或者规定样式的excel可能还得后台画,也有很大可能我是不会的-_-zzzzzzzz~

原文来自:https://blog.csdn.net/qq_37642495/article/details/109327705?utm_medium=distribute.pc_category.none-task-blog-hot-3.nonecase&depth_1-utm_source=distribute.pc_category.none-task-blog-hot-3.nonecase

发表评论

0/200
0 点赞
0 评论
收藏
为你推荐 换一批