github: https://github.com/stardew516...javascript
以往作excel表格下載功能的時候,都是後端生成好表格後,存儲在某個地方,而後給前端一個連接,前端使用a標籤加download下載,或者使用node。其實純前端也是能夠作表格下載的,有一個很好用的javascript插件叫js-xlsx。前端
github:https://github.com/SheetJS/js...
使用js-xlsx時,前端能夠將後端返回的json數據拼接成本身須要導出的格式,下載到電腦中,徹底不依賴後端。導入只需像平時同樣選擇文件,而後解析excel表格數據,轉換成json格式。vue
目前js-xlsx對各瀏覽器的支持狀況以下圖所示:java
以vue使用爲例node
npm install xlsx --save
代碼實現(全)git
<template> <div class="index" v-loading.fullscreen.lock="fullscreenLoading" element-loading-text="拼命加載中..."> <input type="file" @change="importFile(this)" id="imFile" style="display: none" accept="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet, application/vnd.ms-excel"/> <a id="downlink"></a> <el-button class="button" @click="uploadFile()">導入</el-button> <el-button class="button" @click="downloadFile(excelData)">導出</el-button> <!--錯誤信息提示--> <el-dialog title="提示" v-model="errorDialog" size="tiny"> <span>{{errorMsg}}</span> <span slot="footer" class="dialog-footer"> <el-button type="primary" @click="errorDialog=false">確認</el-button> </span> </el-dialog> <!--展現導入信息--> <el-table :data="excelData" tooltip-effect="dark"> <el-table-column label="名稱" prop="name" show-overflow-tooltip></el-table-column> <el-table-column label="份量" prop="size" show-overflow-tooltip></el-table-column> <el-table-column label="口味" prop="taste" show-overflow-tooltip></el-table-column> <el-table-column label="單價(元)" prop="price" show-overflow-tooltip></el-table-column> <el-table-column label="剩餘(份)" prop="remain" show-overflow-tooltip></el-table-column> </el-table> </div> </template> <script> // 引入xlsx var XLSX = require('xlsx') export default { name: 'Index', data () { return { fullscreenLoading: false, // 加載中 imFile: '', // 導入文件el outFile: '', // 導出文件el errorDialog: false, // 錯誤信息彈窗 errorMsg: '', // 錯誤信息內容 excelData: [ // 測試數據 { name: '紅燒魚', size: '大', taste: '微辣', price: '40', remain: '100' }, { name: '麻辣小龍蝦', size: '大', taste: '麻辣', price: '138', remain: '200' }, { name: '清蒸小龍蝦', size: '大', taste: '清淡', price: '138', remain: '200' }, { name: '香辣小龍蝦', size: '大', taste: '特辣', price: '138', remain: '200' }, { name: '十三香小龍蝦', size: '大', taste: '中辣', price: '138', remain: '108' }, { name: '蒜蓉小龍蝦', size: '大', taste: '中辣', price: '138', remain: '100' }, { name: '涼拌牛肉', size: '中', taste: '中辣', price: '48', remain: '60' }, { name: '蝦仁壽司', size: '大', taste: '清淡', price: '29', remain: '無限' }, { name: '海苔壽司', size: '大', taste: '微辣', price: '26', remain: '無限' }, { name: '金針菇壽司', size: '大', taste: '清淡', price: '23', remain: '無限' }, { name: '泡菜壽司', size: '大', taste: '微辣', price: '24', remain: '無限' }, { name: '鰻魚壽司', size: '大', taste: '清淡', price: '28', remain: '無限' }, { name: '肉鬆壽司', size: '大', taste: '清淡', price: '22', remain: '無限' }, { name: '三文魚壽司', size: '大', taste: '清淡', price: '30', remain: '無限' }, { name: '蛋黃壽司', size: '大', taste: '清淡', price: '20', remain: '無限' } ] } }, mounted () { this.imFile = document.getElementById('imFile') this.outFile = document.getElementById('downlink') }, methods: { uploadFile: function () { // 點擊導入按鈕 this.imFile.click() }, downloadFile: function (rs) { // 點擊導出按鈕 let data = [{}] for (let k in rs[0]) { data[0][k] = k } data = data.concat(rs) this.downloadExl(data, '菜單') }, importFile: function () { // 導入excel this.fullscreenLoading = true let obj = this.imFile if (!obj.files) { this.fullscreenLoading = false return } var f = obj.files[0] var reader = new FileReader() let $t = this reader.onload = function (e) { var data = e.target.result if ($t.rABS) { $t.wb = XLSX.read(btoa(this.fixdata(data)), { // 手動轉化 type: 'base64' }) } else { $t.wb = XLSX.read(data, { type: 'binary' }) } let json = XLSX.utils.sheet_to_json($t.wb.Sheets[$t.wb.SheetNames[0]]) console.log(typeof json) $t.dealFile($t.analyzeData(json)) // analyzeData: 解析導入數據 } if (this.rABS) { reader.readAsArrayBuffer(f) } else { reader.readAsBinaryString(f) } }, downloadExl: function (json, downName, type) { // 導出到excel let keyMap = [] // 獲取鍵 for (let k in json[0]) { keyMap.push(k) } console.info('keyMap', keyMap, json) let tmpdata = [] // 用來保存轉換好的json json.map((v, i) => keyMap.map((k, j) => Object.assign({}, { v: v[k], position: (j > 25 ? this.getCharCol(j) : String.fromCharCode(65 + j)) + (i + 1) }))).reduce((prev, next) => prev.concat(next)).forEach(function (v) { tmpdata[v.position] = { v: v.v } }) let outputPos = Object.keys(tmpdata) // 設置區域,好比表格從A1到D10 let tmpWB = { SheetNames: ['mySheet'], // 保存的表標題 Sheets: { 'mySheet': Object.assign({}, tmpdata, // 內容 { '!ref': outputPos[0] + ':' + outputPos[outputPos.length - 1] // 設置填充區域 }) } } let tmpDown = new Blob([this.s2ab(XLSX.write(tmpWB, {bookType: (type === undefined ? 'xlsx' : type), bookSST: false, type: 'binary'} // 這裏的數據是用來定義導出的格式類型 ))], { type: '' }) // 建立二進制對象寫入轉換好的字節流 var href = URL.createObjectURL(tmpDown) // 建立對象超連接 this.outFile.download = downName + '.xlsx' // 下載名稱 this.outFile.href = href // 綁定a標籤 this.outFile.click() // 模擬點擊實現下載 setTimeout(function () { // 延時釋放 URL.revokeObjectURL(tmpDown) // 用URL.revokeObjectURL()來釋放這個object URL }, 100) }, analyzeData: function (data) { // 此處能夠解析導入數據 return data }, dealFile: function (data) { // 處理導入的數據 console.log(data) this.imFile.value = '' this.fullscreenLoading = false if (data.length <= 0) { this.errorDialog = true this.errorMsg = '請導入正確信息' } else { this.excelData = data } }, s2ab: function (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 }, getCharCol: function (n) { // 將指定的天然數轉換爲26進製表示。映射關係:[0-25] -> [A-Z]。 let s = '' let m = 0 while (n > 0) { m = n % 26 + 1 s = String.fromCharCode(m + 64) + s n = (n - m) / 26 } return s }, fixdata: function (data) { // 文件流轉BinaryString var o = '' var l = 0 var w = 10240 for (; l < data.byteLength / w; ++l) { o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w, l * w + w))) } o += String.fromCharCode.apply(null, new Uint8Array(data.slice(l * w))) return o } } } </script> <!-- Add "scoped" attribute to limit CSS to this component only --> <style> .el-table th>.cell { text-align: center; } .button { margin-bottom: 20px; } </style>
npm run dev