0


Vue前端实现excel的导入、导出、打印功能

目录

一、相关依赖下载

导入导出依赖:

npm install [email protected]
npm install [email protected] --save
  1. 安装xlsx-style,运行报错This relative module was not found: ./cptable in ./node_modules/[email protected]@xlsx-style/dist/cpexcel.js
  2. 解决报错 在\node_modules\xlsx-style\dist\cpexcel.js 807行 的var cpt = require('./cpt' + 'able');改为:var cpt = cptable;

打印依赖:

npm install [email protected] --save

二、excel导入功能

<template><div><el-uploadaction="#":before-upload="beforeUpload":show-file-list="false"accept=".xlsx, .xls"><el-buttonslot="trigger"size="small"type="primary">选取文件</el-button></el-upload><!-- 解析出来的数据 --><el-table:data="tableData"><el-table-columnprop="日期"label="日期"width="180"></el-table-column><el-table-columnprop="姓名"label="姓名"width="180"></el-table-column><el-table-columnprop="地址"label="地址"></el-table-column></el-table></div></template><script>importXLSXfrom'xlsx'exportdefault{name:'importExcel',data(){return{tableData:[],}},methods:{beforeUpload(file){
      console.log(file,'--文件');this.file2XLSX(file).then((res)=>{
        console.log('可以继续对res数据进行二次处理')this.tableData = res[0].sheet
      })returnfalse},// excel导入方法file2XLSX(file){returnnewPromise(function(resolve, reject){// 通过FileReader对象读取文件const reader =newFileReader()// 读取为二进制字符串
        reader.readAsBinaryString(file)
        reader.onload=function(e){
          console.log(e,'读取文件成功的e');// 获取读取文件成功的结果值const data = e.target.result
          // XLSX.read解析数据,按照type 的类型解析let wb =XLSX.read(data,{type:'binary'// 二进制})
          console.log(wb,'---->解析后的数据')// 存储获取到的数据const result =[]// 工作表名称的有序列表
          wb.SheetNames.forEach(sheetName=>{
            result.push({// 工作表名称sheetName: sheetName,// 利用 sheet_to_json 方法将 excel 转成 json 数据sheet:XLSX.utils.sheet_to_json(wb.Sheets[sheetName])})})resolve(result)}})}},}</script>

三、table导出excel表格

1.导出行数据

2.导出table数据(也会导出合并单元格)

3.导出二维数据的table数据

4.导出合并单元格table数据

<template><div><el-buttontype="primary"@click="exportSelectData">导出行数据(json_to_sheet)</el-button><el-buttontype="primary"@click="exportTableData">导出table数据(也会导出合并单元格)(table_to_sheet)</el-button><el-buttontype="primary"@click="exportTableDataFormAoa">导出二维数据的table数据(aoa_to_sheet)</el-button><el-buttontype="primary"@click="exportTableDataCellMerging">导出合并单元格table数据(aoa_to_sheet)</el-button><el-table:data="tableData"@selection-change="handleSelectionChange"ref="tableDataRef"id="table1"><el-table-columntype="selection"width="55"></el-table-column><el-table-columnprop="date"label="日期"width="180"></el-table-column><el-table-columnprop="name"label="姓名"width="180"></el-table-column><el-table-columnprop="address"label="地址"></el-table-column></el-table></div></template><script>importXLSXfrom'xlsx'exportdefault{name:'importExcel',data(){return{selectionList:[],tableData:[{date:'2016-05-02',name:'王小虎',address:'上海市普陀区金沙江路 1518 弄'},{date:'2016-05-04',name:'王小虎',address:'上海市普陀区金沙江路 1517 弄'},{date:'2016-05-01',name:'王小虎',address:'上海市普陀区金沙江路 1519 弄'},{date:'2016-05-03',name:'王小虎',address:'上海市普陀区金沙江路 1516 弄'}]}},methods:{// 获取选择的行数据handleSelectionChange(val){this.selectionList = val;
      console.log(this.selectionList,'--行数据');},// 导出选择的行数据exportSelectData(){// 对选择的表格数据处理:添加标题let arr =this.selectionList.map(item=>{return{日期: item.date,姓名: item.name,地址: item.address
        }})// 将json数据变为sheet数据// json_to_sheet: 将一个由对象组成的数组转成sheet;let sheet =XLSX.utils.json_to_sheet(arr)// 新建表格let book =XLSX.utils.book_new()// 在表格中插入一个sheetXLSX.utils.book_append_sheet(book, sheet,"sheet1")// 通过xlsx的writeFile方法将文件写入XLSX.writeFile(book,`user${newDate().getTime()}.xls`)},// 导出table数据exportTableData(){// 获取dom元素(2种方式)// let table1 = document.querySelector("#table1");  // 原生domlet table =this.$refs.tableDataRef.$el
      // table_to_sheet: 将一个table dom直接转成sheet,会自动识别colspan和rowspan并将其转成对应的单元格合并;let sheet =XLSX.utils.table_to_sheet(table)let book =XLSX.utils.book_new()XLSX.utils.book_append_sheet(book, sheet,"sheet1")XLSX.writeFile(book,`user${newDate().getTime()}.xls`)},// 导出一个二维数组exportTableDataFormAoa(){let aoa =[['姓名','性别','年龄','注册时间'],['张三','男',18,newDate()],['李四','女',22,newDate()]];// 将一个二维数组转成sheet// aoa_to_sheet: 这个工具类最强大也最实用了,将一个二维数组转成sheet,会自动处理number、string、boolean、date等类型数据;let sheet =XLSX.utils.aoa_to_sheet(aoa);let book =XLSX.utils.book_new()XLSX.utils.book_append_sheet(book, sheet,"sheet1")XLSX.writeFile(book,`user${newDate().getTime()}.xls`)},// 导出合并单元格的table数据exportTableDataCellMerging(){let aoa =[['主要信息',null,null,'其它信息'],// 特别注意合并的地方后面预留2个null['姓名','性别','年龄','注册时间'],['张三','男',18,newDate()],['李四','女',22,newDate()]];let sheet =XLSX.utils.aoa_to_sheet(aoa);// 设置合并的单元格
      sheet['!merges']=[// 设置A1-C1的单元格合并{s:{r:0,c:0},e:{r:0,c:2}}];let book =XLSX.utils.book_new()XLSX.utils.book_append_sheet(book, sheet,"sheet1")XLSX.writeFile(book,`user${newDate().getTime()}.xls`)}}}</script>

参考

  1. 对sheet二次处理的参考: https://blog.csdn.net/tian_i/article/details/84327329

四、table导出excel表格(带样式)

1.导出带样式的excel

<template><div><el-button@click="exportExcel()">导出带样式的excel</el-button></div></template><script>importXLSXfrom'xlsx'import XLSXStyle from'xlsx-style';exportdefault{name:'exportExcelStyle',methods:{exportExcel(){let data =[['时间','电压'],['2021-12-01 08:57:12','3.14'],['2021-12-01 08:58:20','3.15']];let titles =['时间','电压']var sheet =XLSX.utils.json_to_sheet(data,{skipHeader:true,});/**设置标题头背景色 */for(const key in sheet){// 第一行,表头if(key.replace(/[^0-9]/ig,'')==='1'){
          sheet[key].s ={fill:{//背景色fgColor:{rgb:'C0C0C0'}},font:{//字体name:'宋体',sz:12,bold:true},border:{//边框bottom:{style:'thin',color:'FF000000'}},alignment:{horizontal:'center'//水平居中}}}// 指定单元格样式if(key ==='A1'){
          sheet[key].s ={...sheet[key].s,fill:{//背景色fgColor:{rgb:'E4DFEC'}}}}// 列宽let colsP = titles.map(item=>{let obj ={'wch':25//列宽}return obj;})
        sheet['!cols']= colsP;//列宽// // 每列的列宽// sheet["!cols"] = [{//   wpx: 70 //单元格列宽// }, {//   wpx: 70// }, {//   wpx: 70// }, {//   wpx: 70// }, {//   wpx: 150// }, {//   wpx: 120// }];}let fileName ='Excel文件.xlsx'let sheetName ='Excel文件'this.openDownload(this.sheet2blob(sheet, sheetName), fileName);},sheet2blob(sheet, sheetName){let wb =XLSX.utils.book_new();
      wb.SheetNames.push(sheetName)
      wb.Sheets[sheetName]= sheet;// 必须使用xlsx-style才能生成指定样式var wbout = XLSXStyle.write(wb,{bookType:'',bookSST:false,type:'binary'})var blob =newBlob([s2ab(wbout)],{type:""}, sheetName);// 字符串转ArrayBufferfunctions2ab(s){var buf =newArrayBuffer(s.length);var view =newUint8Array(buf);for(var i =0; i != s.length;++i) view[i]= s.charCodeAt(i)&0xff;return buf;}return blob;},openDownload(url, saveName){if(typeof url =="object"&& url instanceofBlob){
        url =URL.createObjectURL(url);// 创建blob地址}var aLink = document.createElement("a");
      aLink.href = url;
      aLink.download = saveName ||"";// HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效var event;if(window.MouseEvent) event =newMouseEvent("click");else{
        event = document.createEvent("MouseEvents");
        event.initMouseEvent("click",true,false,
          window,0,0,0,0,0,false,false,false,false,0,null);}
      aLink.dispatchEvent(event);}},}</script>

2. 结合el-table,根据勾选的内容,导出excel表格

html和css就不写了,主要记录下js功能实现的过程

// 导出函数exportasyncfunctionexportBtn(payloadList){// payloadList是table表格勾选的内容数组if(payloadList.length ===0){this.$message({type:'warning',message:'请选择要导出的记录!'})}else{// 最终生成sheet的aoelistlet finalList =[]// 一、准备枚举值// 1.写死let ywlxenum =[{label:'预审选址',value:'YSXZ'},{label:'土地储备',value:'TDCBGM'},{label:'农转用报批',value:'YDBP'},{label:'规划条件',value:'GHTJ'},{label:'行政划拨',value:'XZHB'},{label:'公开出让',value:'GKCR'},{label:'建设用地规划许可',value:'JSYDGHXK'},{label:'建设工程规划类许可证核发',value:'JSGCGHXK'},{label:'建设工程竣工规划核实',value:'JGGHHY'},{label:'竣工验收备案',value:'JGYSBA'}]// 2.通过请求获取let ydxzenum =[]awaitgetEnumByValue({value:'TDLYXZ'}).then(data=>{
      ydxzenum =mapListFunc(data.fieldEnum)})// 二、通过请求获取数据let resList =[]// 保存请求数据for(let i =0; i < payloadList.length; i++){awaitthis.queryInfo({id: payloadList[i].xmguid }).then(res=>{// 这里是判断了返回值里还包含了list数组if(res.data.bizGhtjGhqkList.length !==0){let bizGhtjGhqkList = res.data.bizGhtjGhqkList
          bizGhtjGhqkList.forEach(item=>{
            resList.push({...res.data.bizSlsq,...res.data.bizGhtj,...item })})}else{
          resList.push({...res.data.bizSlsq,...res.data.bizGhtj })}})}// 三、映射关系listlet mappingList =[{field:'xmmc',value:'项目名称',merge:true},// merge代表单元格是否合并{field:'ydxz',value:'用地性质',enum:true,merge:true},// enum代表是否是枚举值{field:'dkbh',value:'地块编号'},]// 四、添加标题let titleList =[]
    mappingList.forEach(item=>{
      titleList.push(item.value)})
    finalList.push(titleList)// 五、添加内容
    resList.forEach(row=>{// 行的listlet ctnList =[]let hyfltemp =[]let ydxztemp =[]
      mappingList.forEach(item=>{if(item.enum){// 带枚举值的处理switch(item.field){case'hyfl':
              hyfltemp = row[item.field]&& row[item.field].split(',')
              ctnList.push(queryEnumVal(hyflenum, hyfltemp && hyfltemp[(hyfltemp.length -1)]))breakcase'ydxz':
              ydxztemp = row[item.field]&& row[item.field].split(',')
              ctnList.push(queryEnumVal(ydxzenum, ydxztemp && ydxztemp[(ydxztemp.length -1)]))breakcase'pzjg':
              ctnList.push(queryEnumVal(pzjgenum, row[item.field]))breakcase'ywlx':
              ctnList.push(queryEnumVal(ywlxenum, row[item.field]))breakcase'cbywlx':
              ctnList.push(queryEnumVal(cbywlxenum, row[item.field]))breakdefault:break}}else{// 常规
          ctnList.push(row[item.field])}})
      finalList.push(ctnList)})// 六、处理合并单元格let mergeArr =[]let{ indices }=unipFunc(resList,'xmguid')
    mappingList.forEach((item, index)=>{if(item.merge){
        indices.forEach(itemlist=>{if(itemlist.length >1){
            mergeArr.push({s:{r: itemlist[0]+1,c: index },e:{r: itemlist[itemlist.length -1]+1,c: index }})}})}})// 七、生成sheetlet sheet =XLSX.utils.aoa_to_sheet(finalList)// 八、合并单元格和添加样式
    sheet['!merges']= mergeArr
    Object.keys(sheet).forEach((item, index)=>{if(sheet[item].t){
        sheet[item].s ={// 对齐方式相关样式alignment:{vertical:'center',// 垂直对齐方式horizontal:'center'// 水平对齐方式// wrapText: true // 自动换行}}}})// 九、导出excelopenDownloadDialog(sheet2blob(sheet),newDate().getTime()+'.xlsx'||'表名.xlsx')}}functionqueryEnumVal(enumList, field){// 获取枚举值对应的keylet enumVal =''
  enumList.forEach(item=>{if(item.value === field){
      enumVal = item.label
    }})return enumVal
}functionmapListFunc(params){// 处理枚举值let list =[]
  list = params.map(item=>{
    item.value = item.enumValue
    item.label = item.enumName
    return item
  })return list
}// 处理数据重复值functionunipFunc(list, objKey){let key ={}// 存储的 key 是type的值,value是在indeces中对应数组的下标let indices =[]// 数组中每一个值是一个数组,数组中的每一个元素是原数组中相同type的下标
  list.map((item, index)=>{// 根据对应字段 分类(type)let itemKey = item[objKey]let _index = key[itemKey]if(_index !==undefined){
      indices[_index].push(index)}else{
      key[itemKey]= indices.length
      indices.push([index])}})// 归类结果let result =[]let resultIndex =[]
  indices.map((item)=>{
    item.map((index)=>{if(item.length >1){
        result.push(list[index])
        resultIndex.push(index)}})})return{ result, resultIndex, indices }}// 下载excelfunctionopenDownloadDialog(url, saveName){if(typeof url ==='object'&& url instanceofBlob){
    url =URL.createObjectURL(url)// 创建blob地址}var aLink = document.createElement('a')
  aLink.href = url
  aLink.download = saveName ||''// HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效var event
  if(window.MouseEvent) event =newMouseEvent('click')else{
    event = document.createEvent('MouseEvents')
    event.initMouseEvent('click',true,false, window,0,0,0,0,0,false,false,false,false,0,null)}
  aLink.dispatchEvent(event)}// 字符串转ArrayBufferfunctions2ab(s){var buf =newArrayBuffer(s.length)var view =newUint8Array(buf)for(var i =0; i !== s.length;++i) view[i]= s.charCodeAt(i)&0xFFreturn buf
}// 将一个sheet转成最终的excel文件的blob对象,然后利用URL.createObjectURL下载functionsheet2blob(sheet, sheetName){
  sheetName = sheetName ||'sheet1'let workbook =XLSX.utils.book_new()
  workbook.SheetNames.push(sheetName)
  workbook.Sheets[sheetName]= sheet
  // 生成excel的配置项var wopts ={bookType:'xlsx',// 要生成的文件类型bookSST:false,// 是否生成Shared String Table,官方解释是,如果开启生成速度会下降,但在低版本IOS设备上有更好的兼容性type:'binary'}var wbout = XLSXStyle.write(workbook, wopts)var blob =newBlob([s2ab(wbout)],{type:'application/octet-stream'})return blob
}

参考

  1. 带样式的导出参考代码: https://blog.csdn.net/weixin_39246975/article/details/121639072
  2. 别人对xlsx-style的二次封装: https://blog.csdn.net/weixin_51947053/article/details/127370479

五、打印功能

1.直接使用window自带的打印功能: window.print()

<template><div><p>点击下面的按钮,可将页面进行打印</p><divid="printDiv"><p>打印内容 </p><p>打印内容 </p><p>打印内容 </p><p>打印内容 </p></div><button@click="print">打印页面内容</button></div></template><script>exportdefault{methods:{print(){
        window.print()}}}</script>

2.使用打印插件:vue-print-nb

1、安装 vue-print-nb:

// vue2.x 版本
npminstall vue-print-nb --save   
// vue3.x 版本
npminstall vue3-print-nb --save

2、在项目中引入 vue-print-nb:

// vue2.x版本 -- 全局引入:在项目中入口文件 main.js 文件中全局引入 vue-print-nbimport Vue from'vue'import Print from'vue-print-nb'
Vue.use(Print)// 局部引入报错,还不知道咋解决,建议是全局引入// vue2.x版本 -- 在需要打印功能的页面引入 vue-print-nbimport print from'vue-print-nb'exportdefault{directives:{ print }}// vue3.x版本 -- 全局引入:在项目中入口文件 main.js 文件中全局引入 vue3-print-nbimport{createApp}from'vue'import App from'./App'import Print from'vue3-print-nb'const app =createApp(App)
app.use(Print)
app.mount('#app')// vue3.x版本 -- 在需要打印功能的页面引入 vue3-print-nbimport print from'vue3-print-nb'exportdefault{directives:{ print }}

3、使用 vue-print-nb 实现打印功能

① 实现方式1:打印区域设置id, 打印按钮绑定此 id

<template><div><p>点击下面的按钮,可将div里的内容区域进行打印</p><divid="printDiv"><p>打印内容 </p><p>打印内容 </p><p>打印内容 </p><p>打印内容 </p></div><buttonv-print="'#printDiv'">打印id为printDiv的div区域内容</button></div></template><script>exportdefault{data(){return{}}}</script>

② 实现方式2:打印区域设置id, 打印按钮进行打印配置

<template><div><p>点击下面的按钮,可将div里的内容区域进行打印</p><divid="printDiv"><p>打印内容 </p><p>打印内容 </p><p>打印内容 </p><p>打印内容 </p></div><buttonv-print="'printSet'">打印id为printDiv的div区域内容</button></div></template><script>exportdefault{data(){return{printSet:{id:'printDiv',extraCss:"https://cdn.bootcdn.net/ajax/libs/animate.css/4.1.1/animate.compat.css, https://cdn.bootcdn.net/ajax/libs/hover.css/2.3.1/css/hover-min.css",extraHead:'<meta http-equiv="Content-Language"content="zh-cn"/>',beforeOpenCallback(vue){
            console.log('打开之前')},openCallback(vue){
            console.log('执行了打印')},closeCallback(vue){
             console.log('关闭了打印工具')}}}}}</script>

③ 打印网址:打印指定url(同一个同源策略)对应的内容

<template><buttonv-print="'printSet'">打印网址</button></template><script>exportdefault{data(){return{printSet:{url:'http://localhost:8080/',beforeOpenCallback(vue){
            console.log('打开之前')},openCallback(vue){
            console.log('执行了打印')},closeCallback(vue){
             console.log('关闭了打印工具')}}}}}</script>

④ 打印预览功能

<template><buttonv-print="'printSet'">打印+预览功能</button></template><script>exportdefault{data(){return{printSet:{url:'http://localhost:8080/',// 打印网页预览  如果想要打印本地预览,那么可以不用提供url,需提供打印区域的id,例如: id: 'printDiv'preview:true,previewTitle:'Test Title',previewBeforeOpenCallback(vue){
            console.log('正在加载预览窗口')},previewOpenCallback(vue){
            console.log('已经加载完预览窗口')},beforeOpenCallback(vue){
            console.log('打开之前')},openCallback(vue){
            console.log('执行了打印')},closeCallback(vue){
             console.log('关闭了打印工具')}}}}}</script>

⑤ 打印异步url

<template><buttonv-print="'printSet'">打印+预览功能</button></template><script>exportdefault{data(){return{printSet:{asyncUrl(reslove, vue){setTimeout(()=>{reslove('http://localhost:8080/')},2000)},// 异步urlpreview:true,previewTitle:'Test Title',previewBeforeOpenCallback(vue){
            console.log('正在加载预览窗口')},previewOpenCallback(vue){
            console.log('已经加载完预览窗口')},beforeOpenCallback(vue){
            console.log('打开之前')},openCallback(vue){
            console.log('执行了打印')},closeCallback(vue){
             console.log('关闭了打印工具')}}}}}</script>

⑥ 实现区域不打印方式

<template><div><divref="printDiv"><p>打印内容区域</p><p>打印内容区域</p><p>打印内容区域</p><p>打印内容区域</p><p>打印内容区域</p>
    

            实现区域不打印方式1:设置class为 no-print 即可实现该区域不打印
            <pclass="no-print">不要打印的内容区域</p>

            // 实现区域不打印方式2: 自定义不打印区域的class名
            <pclass="do-not-print-div">不要打印的内容区域</p></div><button@click="printBtnClick">打印按钮</button></div></template><script>exportdefault{data(){return{}},methods:{printBtnClick(){// 注意必须使用ref指定打印区域,如果通过id或者class,那么wenpack打包后打印区域会为空this.$print(this.$refs.printDiv)// 实现区域不打印方式2this.$print(this.$refs.print,{noPrint:'.do-not-print-div'})},}}</script>

⑦ vue-print-nb的API配置如下

1、id: String // 范围打印 ID,必填值2、standard: String   // 文档类型(仅打印本地范围)3、extraHead: String // <head></head>在节点中添加DOM节点,并用,(Print local range only)分隔多个节点4、extraCss: String  // <link>新的 CSS 样式表,并使用,(仅打印本地范围)分隔多个节点5、popTitle: String // <title></title> 标签内容(仅打印局部范围)6、openCallback: Function // 调用打印工具成功回调函数7、closeCallback: Function // 关闭打印工具成功回调函数8、beforeOpenCallback: Function // 调用打印工具前的回调函数9、url: String // 打印指定的 URL。(不允许同时设置ID)10、asyncUrl: Function // 异步网址:通过 'resolve()' 和 Vue 返回 URL11、preview: Boolean // 预览12、previewTitle: String // 预览标题13、previewPrintBtnLabel: String // 预览按钮的名称14、zIndex: String,Number // 预览CSS:z-index15、previewBeforeOpenCallback: Function // 启动预览工具前的回调函数16、previewOpenCallback: Function // 预览工具完全打开后的回调函数17、clickMounted: Function //点击打印按钮的回调函数
标签: vue.js 前端 excel

本文转载自: https://blog.csdn.net/Litt_White/article/details/128167901
版权归原作者 Litt_White 所有, 如有侵权,请联系我们删除。

“Vue前端实现excel的导入、导出、打印功能”的评论:

还没有评论