Vue实现在线编辑excel、导入、导出(转)
* 博客文章部分截图及内容来自于学习的书本及相应培训课程以及网络其他博客,仅做学习讨论之用,不做商业用途。
* 如有侵权,马上联系我,我立马删除对应链接。
* @author Alan
* @Email no008@foxmail.com
正文
原文:
概要
Vue实现在线编辑excel、导入、导出
整体架构流程
1.npm安装依赖
npm i exceljs file-saver luckyexcel
2.index.html中引入
Vue引入luckysheet,index.html头部标签head中引入(luckysheet文件在node_modules中)
<link rel='stylesheet' href='./luckysheet/plugins/plugins.css' /> <link rel='stylesheet' href='./luckysheet/css/luckysheet.css' /> <link rel='stylesheet' href='./luckysheet/assets/iconfont/iconfont.css' /> <script src="./luckysheet/plugins/js/plugin.js"></script> <script src="<%= BASE_URL %>./luckysheet/luckysheet.umd.js"></script>
3.组件实现
<template> <div class="test2"> <div class="mb-md"> <input type="file" @change="chageFile" ref="inputFile" /> <button class="blueBtn" @click="exportExcelBtn">导出xlsx</button> <button v-if="bgbsm" class="blueBtn" @click="zxClicked">最新任务清单</button> <button v-if="!bgbsm" class="blueBtn" @click="editClicked">编辑</button> <button v-if="!bgbsm" class="blueBtn" @click="save">保存</button> </div> <!--web spreadsheet组件--> <div class="excel"> <div id="luckysheetDom" style="margin: 0px; padding: 0px; width: 100%; height: 100%"></div> </div> </div> </template> <script> //引入依赖包 import LuckyExcel from 'luckyexcel' const luckysheet = window.luckysheet //代码见下 import { exportExcel } from './export' export default { name: 'XspreadsheetDemo', props:{ bgbsm:{ type:String }, }, data() { return { xs: null, FormData:{}, } }, mounted() { this.init() }, methods: { init() { console.log(this.bgbsm); // let options = localStorage.getItem('excel2') //默认空excel // if (this.FormData={}) { // this.FormData = { // container: 'luckysheetDom', // title: '', // lang: 'zh', // data: [ // { // name: 'Sheet1' // } // ], // showinfobar: false // } // luckysheet.create(this.FormData) // } // 可开启只读模式allowEdit // options.allowEdit = false // if (this.bgbsm) { // } let url = GLOBAL_CONFIG.stxf + "/hzqd/getBybsm"; this.http.longPost(url,{bsm:this.bgbsm}).then((res) => { if (res) { this.FormData = JSON.parse(res.data) // console.log('this.FormData',res.data); console.log('this.FormData',this.FormData); this.FormData.allowEdit = false this.FormData.showinfobar = false this.FormData.showtoolbar = false this.FormData.lang = 'zh' luckysheet.create(this.FormData) } }); }, save() { let data = luckysheet.toJson() // console.log(JSON.stringify(data)) // localStorage.setItem('excel2', JSON.stringify(data)) let url = GLOBAL_CONFIG.stxf + "/hzqd/save"; this.http.post(url, {json:JSON.stringify(data)}).then((res) => { if (res && res.data) { this.$message.success('保存成功'); this.init() this.$emit('getlsTable') } }); }, editClicked(){ this.FormData.showtoolbar = true this.FormData.allowEdit = true luckysheet.create(this.FormData) }, zxClicked(){ this.bgbsm='' this.init() }, exportExcelBtn() { // console.log(luckysheet.getluckysheetfile()) exportExcel(luckysheet.getluckysheetfile(), '下载') }, chageFile() { this.importExcel(this.$refs.inputFile.files[0]) // let data = luckysheet.toJson() // console.log(JSON.stringify(data)) }, importExcel(file) { let name = file.name //获取文件后缀 let suffixArr = name.split('.'), suffix = suffixArr[suffixArr.length - 1] if (suffix !== 'xlsx') { alert('目前只能导入xlsx类型的文件') return } LuckyExcel.transformExcelToLucky(file, this.fileCb, this.errorCb) }, fileCb(exportJson, luckysheetfile) { // 转换后获取工作表数据 if (exportJson.sheets === null || exportJson.sheets.length === 0) { alert('无法读取excel文件的内容,当前不支持xls文件!') return } // console.log('exportJson', exportJson) // console.log('luckysheetfile', luckysheetfile) luckysheet.destroy() luckysheet.create({ container: 'luckysheetDom', //luckysheet is the container id showinfobar: false, data: exportJson.sheets, title: exportJson.info.name, userInfo: exportJson.info.name.creator }) }, errorCb(error) { console.log(error) } } } </script> <style scoped lang="less"> .test2 { width: 100%; height: 100%; display: flex; flex-direction: column; .excel { flex: 1; } .mb-md{ display: flex; justify-content: flex-end; margin-bottom: 10px; button { margin-left: 20px; } } } </style>
4.export.js
exportJs分解Dom为excel文件流
// import { createCellPos } from './translateNumToLetter' import Excel from 'exceljs' import FileSaver from 'file-saver' const exportExcel = function (luckysheet, value) { // 参数为luckysheet.getluckysheetfile()获取的对象 // 1.创建工作簿,可以为工作簿添加属性 const workbook = new Excel.Workbook() // 2.创建表格,第二个参数可以配置创建什么样的工作表 if (Object.prototype.toString.call(luckysheet) === '[object Object]') { luckysheet = [luckysheet] } luckysheet.forEach(function (table) { if (table.data.length === 0) { return true } // ws.getCell('B2').fill = fills. const worksheet = workbook.addWorksheet(table.name) const merge = (table.config && table.config.merge) || {} const borderInfo = (table.config && table.config.borderInfo) || {} // 3.设置单元格合并,设置单元格边框,设置单元格样式,设置值 setStyleAndValue(table.data, worksheet) setMerge(merge, worksheet) setBorder(borderInfo, worksheet) return true }) // return // 4.写入 buffer const buffer = workbook.xlsx.writeBuffer().then(data => { // console.log('data', data) const blob = new Blob([data], { type: 'application/vnd.ms-excel;charset=utf-8' }) console.log('导出成功!') FileSaver.saveAs(blob, `${value}.xlsx`) }) return buffer } var setMerge = function (luckyMerge = {}, worksheet) { const mergearr = Object.values(luckyMerge) mergearr.forEach(function (elem) { // elem格式:{r: 0, c: 0, rs: 1, cs: 2} // 按开始行,开始列,结束行,结束列合并(相当于 K10:M12) worksheet.mergeCells(elem.r + 1, elem.c + 1, elem.r + elem.rs, elem.c + elem.cs) }) } var setBorder = function (luckyBorderInfo, worksheet) { if (!Array.isArray(luckyBorderInfo)) { return } // console.log('luckyBorderInfo', luckyBorderInfo) luckyBorderInfo.forEach(function (elem) { // 现在只兼容到borderType 为range的情况 // console.log('ele', elem) if (elem.rangeType === 'range') { let border = borderConvert(elem.borderType, elem.style, elem.color) let rang = elem.range[0] // console.log('range', rang) let row = rang.row let column = rang.column for (let i = row[0] + 1; i < row[1] + 2; i++) { for (let y = column[0] + 1; y < column[1] + 2; y++) { worksheet.getCell(i, y).border = border } } } if (elem.rangeType === 'cell') { // col_index: 2 // row_index: 1 // b: { // color: '#d0d4e3' // style: 1 // } const { col_index, row_index } = elem.value const borderData = Object.assign({}, elem.value) delete borderData.col_index delete borderData.row_index let border = addborderToCell(borderData, row_index, col_index) // console.log('bordre', border, borderData) worksheet.getCell(row_index + 1, col_index + 1).border = border } // console.log(rang.column_focus + 1, rang.row_focus + 1) // worksheet.getCell(rang.row_focus + 1, rang.column_focus + 1).border = border }) } var setStyleAndValue = function (cellArr, worksheet) { if (!Array.isArray(cellArr)) { return } cellArr.forEach(function (row, rowid) { row.every(function (cell, columnid) { if (!cell) { return true } let fill = fillConvert(cell.bg) let font = fontConvert(cell.ff, cell.fc, cell.bl, cell.it, cell.fs, cell.cl, cell.ul) let alignment = alignmentConvert(cell.vt, cell.ht, cell.tb, cell.tr) let value = '' if (cell.f) { value = { formula: cell.f, result: cell.v } } else if (!cell.v && cell.ct && cell.ct.s) { // xls转为xlsx之后,内部存在不同的格式,都会进到富文本里,即值不存在与cell.v,而是存在于cell.ct.s之后 // value = cell.ct.s[0].v cell.ct.s.forEach(arr => { value += arr.v }) } else { value = cell.v } // style 填入到_value中可以实现填充色 let letter = createCellPos(columnid) let target = worksheet.getCell(letter + (rowid + 1)) // console.log('1233', letter + (rowid + 1)) for (const key in fill) { target.fill = fill break } target.font = font target.alignment = alignment target.value = value return true }) }) } var fillConvert = function (bg) { if (!bg) { return {} } // const bgc = bg.replace('#', '') let fill = { type: 'pattern', pattern: 'solid', fgColor: { argb: bg.replace('#', '') } } return fill } var fontConvert = function (ff = 0, fc = '#000000', bl = 0, it = 0, fs = 10, cl = 0, ul = 0) { // luckysheet:ff(样式), fc(颜色), bl(粗体), it(斜体), fs(大小), cl(删除线), ul(下划线) const luckyToExcel = { 0: '微软雅黑', 1: '宋体(Song)', 2: '黑体(ST Heiti)', 3: '楷体(ST Kaiti)', 4: '仿宋(ST FangSong)', 5: '新宋体(ST Song)', 6: '华文新魏', 7: '华文行楷', 8: '华文隶书', 9: 'Arial', 10: 'Times New Roman ', 11: 'Tahoma ', 12: 'Verdana', num2bl: function (num) { return num !== 0 } } // 出现Bug,导入的时候ff为luckyToExcel的val let font = { name: typeof ff === 'number' ? luckyToExcel[ff] : ff, family: 1, size: fs, color: { argb: fc.replace('#', '') }, bold: luckyToExcel.num2bl(bl), italic: luckyToExcel.num2bl(it), underline: luckyToExcel.num2bl(ul), strike: luckyToExcel.num2bl(cl) } return font } var alignmentConvert = function (vt = 'default', ht = 'default', tb = 'default', tr = 'default') { // luckysheet:vt(垂直), ht(水平), tb(换行), tr(旋转) const luckyToExcel = { vertical: { 0: 'middle', 1: 'top', 2: 'bottom', default: 'top' }, horizontal: { 0: 'center', 1: 'left', 2: 'right', default: 'left' }, wrapText: { 0: false, 1: false, 2: true, default: false }, textRotation: { 0: 0, 1: 45, 2: -45, 3: 'vertical', 4: 90, 5: -90, default: 0 } } let alignment = { vertical: luckyToExcel.vertical[vt], horizontal: luckyToExcel.horizontal[ht], wrapText: luckyToExcel.wrapText[tb], textRotation: luckyToExcel.textRotation[tr] } return alignment } var borderConvert = function (borderType, style = 1, color = '#000') { // 对应luckysheet的config中borderinfo的的参数 if (!borderType) { return {} } const luckyToExcel = { type: { 'border-all': 'all', 'border-top': 'top', 'border-right': 'right', 'border-bottom': 'bottom', 'border-left': 'left' }, style: { 0: 'none', 1: 'thin', 2: 'hair', 3: 'dotted', 4: 'dashDot', // 'Dashed', 5: 'dashDot', 6: 'dashDotDot', 7: 'double', 8: 'medium', 9: 'mediumDashed', 10: 'mediumDashDot', 11: 'mediumDashDotDot', 12: 'slantDashDot', 13: 'thick' } } let template = { style: luckyToExcel.style[style], color: { argb: color.replace('#', '') } } let border = {} if (luckyToExcel.type[borderType] === 'all') { border['top'] = template border['right'] = template border['bottom'] = template border['left'] = template } else { border[luckyToExcel.type[borderType]] = template } // console.log('border', border) return border } function addborderToCell(borders, row_index, col_index) { let border = {} const luckyExcel = { type: { l: 'left', r: 'right', b: 'bottom', t: 'top' }, style: { 0: 'none', 1: 'thin', 2: 'hair', 3: 'dotted', 4: 'dashDot', // 'Dashed', 5: 'dashDot', 6: 'dashDotDot', 7: 'double', 8: 'medium', 9: 'mediumDashed', 10: 'mediumDashDot', 11: 'mediumDashDotDot', 12: 'slantDashDot', 13: 'thick' } } // console.log('borders', borders) for (const bor in borders) { // console.log(bor) if (borders[bor].color.indexOf('rgb') === -1) { border[luckyExcel.type[bor]] = { style: luckyExcel.style[borders[bor].style], color: { argb: borders[bor].color.replace('#', '') } } } else { border[luckyExcel.type[bor]] = { style: luckyExcel.style[borders[bor].style], color: { argb: borders[bor].color } } } } return border } function createCellPos(n) { let ordA = 'A'.charCodeAt(0) let ordZ = 'Z'.charCodeAt(0) let len = ordZ - ordA + 1 let s = '' while (n >= 0) { s = String.fromCharCode((n % len) + ordA) + s n = Math.floor(n / len) - 1 } return s } export { exportExcel }
小结
主要运用
两个插件来完成
为人:谦逊、激情、博学、审问、慎思、明辨、 笃行
学问:纸上得来终觉浅,绝知此事要躬行
为事:工欲善其事,必先利其器。
态度:道阻且长,行则将至;行而不辍,未来可期
.....................................................................
------- 桃之夭夭,灼灼其华。之子于归,宜其室家。 ---------------
------- 桃之夭夭,有蕡其实。之子于归,宜其家室。 ---------------
------- 桃之夭夭,其叶蓁蓁。之子于归,宜其家人。 ---------------
=====================================================================
* 博客文章部分截图及内容来自于学习的书本及相应培训课程以及网络其他博客,仅做学习讨论之用,不做商业用途。
* 如有侵权,马上联系我,我立马删除对应链接。 * @author Alan -liu * @Email no008@foxmail.com
转载请标注出处! ✧*꧁一品堂.技术学习笔记꧂*✧. ---> https://www.cnblogs.com/ios9/
学问:纸上得来终觉浅,绝知此事要躬行
为事:工欲善其事,必先利其器。
态度:道阻且长,行则将至;行而不辍,未来可期
.....................................................................
------- 桃之夭夭,灼灼其华。之子于归,宜其室家。 ---------------
------- 桃之夭夭,有蕡其实。之子于归,宜其家室。 ---------------
------- 桃之夭夭,其叶蓁蓁。之子于归,宜其家人。 ---------------
=====================================================================
* 博客文章部分截图及内容来自于学习的书本及相应培训课程以及网络其他博客,仅做学习讨论之用,不做商业用途。
* 如有侵权,马上联系我,我立马删除对应链接。 * @author Alan -liu * @Email no008@foxmail.com
转载请标注出处! ✧*꧁一品堂.技术学习笔记꧂*✧. ---> https://www.cnblogs.com/ios9/
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET10 - 预览版1新功能体验(一)
2021-07-13 比骨头更适合孩子长高的高钙汤
2020-07-13 Oracle主键自增
2020-07-13 Oracle实现主键自增的几种方式
2018-07-13 Oracle 之 外部表
2018-07-13 创建外部用户_外部表
2018-07-13 Oracle Blob查询和插入
2018-07-13 oracle中Blob和Clob类型的区别