vue的导入和导出excel(Vue前端实现excel的导入、导出、打印功能)
导读:一、相关依赖下载...
一、相关依赖下载
导入导出依赖:
npm install xlsx@0.16.9
npm install xlsx-style@0.8.13 --save安装xlsx-style,运行报错
This relative module was not found: ./cptable in ./node_modules/xlsx-style@0.8.13@xlsx-style/dist/cpexcel.js 解决报错
在\node_modules\xlsx-style\dist\cpexcel.js 807行 的var cpt = require(./cpt + able);改为:var cpt = cptable;打印依赖: npm install vue-print-nb@1.7.5 --save
二、excel导入功能
<template> <div> <el-upload action="#" :before-upload="beforeUpload" :show-file-list="false" accept=".xlsx, .xls" > <el-button slot="trigger" size="small" type="primary">选取文件</el-button> </el-upload> <!-- 解析出来的数据 --> <el-table :data="tableData"> <el-table-column prop="日期" label="日期" width="180"> </el-table-column> <el-table-column prop="姓名" label="姓名" width="180"> </el-table-column> <el-table-column prop="地址" label="地址"> </el-table-column> </el-table> </div> </template> <script> import XLSX from xlsx export default { name: importExcel, data () { return { tableData: [], } }, methods: { beforeUpload (file) { console.log(file, --文件); this.file2XLSX(file).then((res) => { console.log(可以继续对res数据进行二次处理) this.tableData = res[0].sheet }) return false }, // excel导入方法 file2XLSX (file) { return new Promise(function (resolve, reject) { // 通过FileReader对象读取文件 const reader = new FileReader() // 读取为二进制字符串 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-button type="primary" @click="exportSelectData" >导出行数据(json_to_sheet)</el-button > <el-button type="primary" @click="exportTableData" >导出table数据(也会导出合并单元格)(table_to_sheet)</el-button > <el-button type="primary" @click="exportTableDataFormAoa" >导出二维数据的table数据(aoa_to_sheet)</el-button > <el-button type="primary" @click="exportTableDataCellMerging" >导出合并单元格table数据(aoa_to_sheet)</el-button > <el-table :data="tableData" @selection-change="handleSelectionChange" ref="tableDataRef" id="table1" > <el-table-column type="selection" width="55"> </el-table-column> <el-table-column prop="date" label="日期" width="180"> </el-table-column> <el-table-column prop="name" label="姓名" width="180"> </el-table-column> <el-table-column prop="address" label="地址"> </el-table-column> </el-table> </div> </template> <script> import XLSX from xlsx export default { 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() // 在表格中插入一个sheet XLSX.utils.book_append_sheet(book, sheet, "sheet1") // 通过xlsx的writeFile方法将文件写入 XLSX.writeFile(book, `user${new Date().getTime()}.xls`) }, // 导出table数据 exportTableData () { // 获取dom元素(2种方式) // let table1 = document.querySelector("#table1"); // 原生dom let 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${new Date().getTime()}.xls`) }, // 导出一个二维数组 exportTableDataFormAoa () { let aoa = [ [姓名, 性别, 年龄, 注册时间], [张三, 男, 18, new Date()], [李四, 女, 22, new Date()] ]; // 将一个二维数组转成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${new Date().getTime()}.xls`) }, // 导出合并单元格的table数据 exportTableDataCellMerging () { let aoa = [ [主要信息, null, null, 其它信息], // 特别注意合并的地方后面预留2个null [姓名, 性别, 年龄, 注册时间], [张三, 男, 18, new Date()], [李四, 女, 22, new Date()] ]; 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${new Date().getTime()}.xls`) } } } </script>参考
对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> import XLSX from xlsx import XLSXStyle from xlsx-style; export default { 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 = new Blob([s2ab(wbout)], { type: "" }, sheetName); // 字符串转ArrayBuffer function s2ab (s) { var buf = new ArrayBuffer(s.length); var view = new Uint8Array(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 instanceof Blob) { url = URL.createObjectURL(url); // 创建blob地址 } var aLink = document.createElement("a"); aLink.href = url; aLink.download = saveName || ""; // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效 var event; if (window.MouseEvent) event = new MouseEvent("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功能实现的过程
// 导出函数 export async function exportBtn (payloadList) { // payloadList是table表格勾选的内容数组 if (payloadList.length === 0) { this.$message({ type: warning, message: 请选择要导出的记录! }) } else { // 最终生成sheet的aoelist let 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 = [] await getEnumByValue({ value: TDLYXZ }).then(data => { ydxzenum = mapListFunc(data.fieldEnum) }) // 二、通过请求获取数据 let resList = [] // 保存请求数据 for (let i = 0; i < payloadList.length; i++) { await this.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 }) } }) } // 三、映射关系list let 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 => { // 行的list let 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)])) break case ydxz: ydxztemp = row[item.field] && row[item.field].split(,) ctnList.push(queryEnumVal(ydxzenum, ydxztemp && ydxztemp[(ydxztemp.length - 1)])) break case pzjg: ctnList.push(queryEnumVal(pzjgenum, row[item.field])) break case ywlx: ctnList.push(queryEnumVal(ywlxenum, row[item.field])) break case cbywlx: ctnList.push(queryEnumVal(cbywlxenum, row[item.field])) break default: 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 } }) } }) } }) // 七、生成sheet let 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 // 自动换行 } } } }) // 九、导出excel openDownloadDialog(sheet2blob(sheet), new Date().getTime() + .xlsx || 表名.xlsx) } } function queryEnumVal (enumList, field) { // 获取枚举值对应的key let enumVal = enumList.forEach(item => { if (item.value === field) { enumVal = item.label } }) return enumVal } function mapListFunc (params) { // 处理枚举值 let list = [] list = params.map(item => { item.value = item.enumValue item.label = item.enumName return item }) return list } // 处理数据重复值 function unipFunc (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 } } // 下载excel function openDownloadDialog (url, saveName) { if (typeof url === object && url instanceof Blob) { url = URL.createObjectURL(url) // 创建blob地址 } var aLink = document.createElement(a) aLink.href = url aLink.download = saveName || // HTML5新增的属性,指定保存文件名,可以不要后缀,注意,file:///模式下不会生效 var event if (window.MouseEvent) event = new MouseEvent(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) } // 字符串转ArrayBuffer function s2ab (s) { var buf = new ArrayBuffer(s.length) var view = new Uint8Array(buf) for (var i = 0; i !== s.length; ++i) view[i] = s.charCodeAt(i) & 0xFF return buf } // 将一个sheet转成最终的excel文件的blob对象,然后利用URL.createObjectURL下载 function sheet2blob (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 = new Blob([s2ab(wbout)], { type: application/octet-stream }) return blob }参考
带样式的导出参考代码:
https://blog.csdn.net/weixin_39246975/article/details/121639072 别人对xlsx-style的二次封装:
https://blog.csdn.net/weixin_51947053/article/details/127370479五、打印功能
1.直接使用window自带的打印功能: window.print()
<template> <div> <p>点击下面的按钮,可将页面进行打印</p> <div id="printDiv"> <p>打印内容 </p> <p>打印内容 </p> <p>打印内容 </p> <p>打印内容 </p> </div> <button @click="print">打印页面内容</button> </div> </template> <script> export default{ methods: { print(){ window.print() } } } </script>2.使用打印插件:vue-print-nb
1、安装 vue-print-nb:
// vue2.x 版本 npm install vue-print-nb --save // vue3.x 版本 npm install vue3-print-nb --save2、在项目中引入 vue-print-nb:
// vue2.x版本 -- 全局引入:在项目中入口文件 main.js 文件中全局引入 vue-print-nb import Vue from vue import Print from vue-print-nb Vue.use(Print) // 局部引入报错,还不知道咋解决,建议是全局引入 // vue2.x版本 -- 在需要打印功能的页面引入 vue-print-nb import print from vue-print-nb export default{ directives: { print } } // vue3.x版本 -- 全局引入:在项目中入口文件 main.js 文件中全局引入 vue3-print-nb import {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-nb import print from vue3-print-nb export default{ directives: { print } }3、使用 vue-print-nb 实现打印功能
① 实现方式1:打印区域设置id, 打印按钮绑定此 id <template> <div> <p>点击下面的按钮,可将div里的内容区域进行打印</p> <div id="printDiv"> <p>打印内容 </p> <p>打印内容 </p> <p>打印内容 </p> <p>打印内容 </p> </div> <button v-print="#printDiv">打印id为printDiv的div区域内容</button> </div> </template> <script> export default{ data(){ return{} } } </script> ② 实现方式2:打印区域设置id, 打印按钮进行打印配置 <template> <div> <p>点击下面的按钮,可将div里的内容区域进行打印</p> <div id="printDiv"> <p>打印内容 </p> <p>打印内容 </p> <p>打印内容 </p> <p>打印内容 </p> </div> <button v-print="printSet">打印id为printDiv的div区域内容</button> </div> </template> <script> export default{ 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> <button v-print="printSet">打印网址</button> </template> <script> export default{ data(){ return{ printSet: { url: http://localhost:8080/, beforeOpenCallback (vue) { console.log(打开之前) }, openCallback (vue) { console.log(执行了打印) }, closeCallback (vue) { console.log(关闭了打印工具) } } } } } </script> ④ 打印预览功能 <template> <button v-print="printSet">打印+预览功能</button> </template> <script> export default{ 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> <button v-print="printSet">打印+预览功能</button> </template> <script> export default{ data(){ return{ printSet: { asyncUrl (reslove, vue) { setTimeout(() => { reslove(http://localhost:8080/) }, 2000) }, // 异步url 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> ⑥ 实现区域不打印方式 <template> <div> <div ref="printDiv"> <p>打印内容区域</p> <p>打印内容区域</p> <p>打印内容区域</p> <p>打印内容区域</p> <p>打印内容区域</p> 实现区域不打印方式1:设置class为 no-print 即可实现该区域不打印 <p class="no-print">不要打印的内容区域</p> // 实现区域不打印方式2: 自定义不打印区域的class名 <p class="do-not-print-div">不要打印的内容区域</p> </div> <button @click="printBtnClick">打印按钮</button> </div> </template> <script> export default{ data(){ return {} }, methods:{ printBtnClick(){ // 注意必须使用ref指定打印区域,如果通过id或者class,那么wenpack打包后打印区域会为空 this.$print(this.$refs.printDiv) // 实现区域不打印方式2 this.$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 返回 URL 11、preview: Boolean // 预览 12、previewTitle: String // 预览标题 13、previewPrintBtnLabel: String // 预览按钮的名称 14、zIndex: String,Number // 预览CSS:z-index 15、previewBeforeOpenCallback: Function // 启动预览工具前的回调函数 16、previewOpenCallback: Function // 预览工具完全打开后的回调函数 17、clickMounted: Function //点击打印按钮的回调函数
声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!