因为我的项目中有需要要展现 excel(内容只有一些简略的地址 / 地区等信息,没有简单的款式,只是蕴含了一些合并的表格),所以利用 XLSX 的读写性能,对读取后的数据进行二次解决并展现。
成果截图在最下边
先上代码(间接从我的项目中摘抄出一部分,并附上了正文):
// 过后我的项目用的是 vue,为了省事只将有用的局部筛选了进去
<template>
<div class="about">
<button @click="createBook"> 生成 excel</button>
<br>
<input type="file" id="inputFile" @change="fileChange">
<table id="tableView" style="min-width: 100%; border-collapse:collapse; border: 1px solid;">
<tr v-for="(item, index) in dataSource" :key="index" style="height: 28px;">
<template v-for="(sub, subIndex) in item" :key="subIndex">
<td v-if="sub.rowspan !== 0 && sub.colspan !== 0"
:rowspan="sub.rowspan || 1" :colspan="sub.colspan || 1"
style="width: auto; min-width: 120px; padding: 3px 5px; border: 1px solid;">
{{sub.name}}
</td>
</template>
</tr>
</table>
</div>
</template>
<script>
import {axios} from '@/utils/request'
const XLSX = require('@/utils/xlsx.full.min')
export default {data() {
return {dataSource: [],
}
},
methods: {
// 获取本地文件
fileChange() {let files = document.getElementById('inputFile').files
this.fileReader(files[0])
},
// // 获取网络文件
// getNetworkFile(url) {
// axios({
// url, // 文件地址
// method: 'get',
// responseType: 'blob'
// }).then(blobData => {// console.log(blobData)
// // 将 blob 转为 file 类型
// let file = new File([blobData], '样例', {type: blobData.type})
// this.fileReader(file)
// })
// },
fileReader(file) {let reader = new FileReader()
// 读入 file
reader.readAsBinaryString(file)
reader.onload = e => {
let data = e.target.result
// 读取 file, 提取数据
let workbook = XLSX.read(data, {type: 'binary', cellStyles: true})
console.log(workbook)
// workbook.SheetNames 是工作表名称的有序列表
// workbook.Sheets 是一个对象,其键是工作表名称,其值是工作表对象
this.sheetNames = workbook.SheetNames
this.sheets = workbook.Sheets
// 默认显示第一个 sheet
this.parsingTable(this.sheets[this.sheetNames[0]])
}
},
// 对数据进行解决,实现表格合并展现的性能
parsingTable(table) {let header = [] // 表格列
let keys = Object.keys(table)
let maxRowIndex = 0 // 最大行数
//!ref 工作表范畴
if (table['!ref'] && table['!ref'].includes(':')) {let refs = table['!ref'].split(':')
maxRowIndex = refs[1].replace(/[A-Z]/g, '')
}
for (let [i, h] of keys.entries()) {
// 提取 key 中的英文字母
let col = h.replace(/[^A-Z]/g, '')
// 单元格是以 A - 1 的模式展现的,所以排除蕴含! 的 key
h.indexOf('!') === -1 && header.indexOf(col) === -1 && header.push(col)
// 如果!ref 不存在时,设置某一列最初一个单元格的索引为最大行数
if ((!table['!ref'] || !table['!ref'].includes(':')) && header.some(c => table[`${c}${i}`])) {maxRowIndex = i > maxRowIndex ? i : maxRowIndex}
}
header = header.sort((a, b) => a.localeCompare(b)) // 按字母程序排序 [A, B, ..., E, F]
// console.log(header)
// console.log(maxRowIndex)
let dataSource = [] // 表格数据
//excel 的行示意为 1, 2, 3, ......, 所以 index 起始为 1
for (let index = 1; index <= maxRowIndex; index++) {let row = [] // 行
// 每行的单元格汇合, 例: [A1, ..., F1]
row = header.map(item => {let key = `${item}${index}`
let cell = table[key]
return {
key,
name: cell ? cell.v : '',
// style: cell ? cell.s : '', // 单元格的款式 / 主题, 有些不实用
}
})
dataSource.push(row)
}
// 合并单元格
if (table['!merges']) {for (let item of table['!merges']) {// s 开始 e 完结 c 列 r 行 ( 行、列的索引都是从 0 开始的)
for (let r = item.s.r; r <= item.e.r; r++) {for (let c = item.s.c; c <= item.e.c; c++) {// console.log('=======', r, c)
// 查找单元格时须要 r +1
// 例:单元格 A1 的地位是 {c: 0, r:0}
let rowIndex = r + 1
if (!dataSource[r]) {dataSource.splice(r, 0, header.map(col => ({key: `${col}${rowIndex}`})))
}
let cell = dataSource[r].find(a => a.key === `${header}${rowIndex}`)
cell.rowspan = 0
cell.colspan = 0
}
}
// 合并时保留范畴内左上角的单元格
let start = `${header[item.s.c]}${item.s.r + 1}`
// let end = `${header[item.e.c]}${item.e.r + 1}`
// console.log(start)
let cell = dataSource[item.s.r].find(a => a.key === start)
cell.rowspan = item.s.r !== item.e.r ? item.e.r - item.s.r + 1 : 1 // 纵向合并
cell.colspan = item.s.c !== item.e.c ? item.e.c - item.s.c + 1 : 1 // 横向合并
}
}
console.log(dataSource)
this.dataSource = dataSource
},
createBook() {
// 应用 table_to_sheet 或 table_to_book 其中一种办法
//table_to_sheet 的用法
let worksheet = XLSX.utils.table_to_sheet(document.getElementById('tableView'))
let workbook = {SheetNames: [],
Sheets: {}}
workbook.SheetNames.push('sheet1')
worksheet['!cols'] = [{wch: 20}] // 设置第一列的列宽
workbook.Sheets['sheet1'] = worksheet
//table_to_book 的用法
// let workbook = XLSX.utils.table_to_book(document.getElementById('tableView'));
let data = XLSX.write(workbook, {
bookType: 'xlsx', // 要生成的文件类型
type: 'array'
})
let blobData = new Blob([data], {type: 'application/octet-stream'})
this.downFile(blobData)
},
downFile(blobData) {if (typeof window.navigator.msSaveBlob !== 'undefined') {window.navigator.msSaveBlob(new Blob([blobData]), new Date().getTime() + '.xlsx')
} else {let url = window.URL.createObjectURL(new Blob([blobData]))
let link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.setAttribute('download', new Date().getTime() + '.xlsx')
document.body.appendChild(link)
link.click()
document.body.removeChild(link)
window.URL.revokeObjectURL(url)
}
}
},
}
</script>
本地文件(款式是我本人加的,读取或写入款式须要将 cellStyles 设置为 true):
页面渲染的 table(只实现了表格合并,没有对其余款式进行解决):
导出后的文件: