表格导出,填写数据,导入表格
- 需求:表格导出,填写数据,导入数据
- 表格文件存储在前端
- 表格文件不存储
需求:表格导出,填写数据,导入数据
分析需求:
(1)关于表格导出
在前端页面导出表格,有三种情况:①表格文件存储在后端,后端返给前端文件流,前端调用后端接口,下载文件;②表格文件存储在前端,前端下载文件;③表格文件不存储,前端根据tableData的某些字段导出相应表格。
(2)填写数据
Excel里填写数据,便不再多说;
(3)导入数据
读取Excel表格,得到所需的数据,可能需要进行数据处理,总之,导入数据是给后端交一个对象数组
表格文件存储在前端
如图,表格文件template.xlsx存储在public下的static文件夹下
页面布局和用户需填写的excel如下
注意: 涉及到金额的保留2位不是四舍五入,而是直接保留两位,其他去掉。如:200.098保留2位,得到的结果为200.09。因此,在导入数据时,需要对数值进行处理,处理办法如下:
num=Math.floor(num*100)/100
代码实现
1.template
<div class="buttons"><el-button type="primary" size="small"@click="DownloadTemplate">下载模板</el-button><el-uploadaction=""class="upload"accept=".xlsx,.xls":auto-upload="false":show-file-list="false":on-change="importData"><el-button type="primary" size="small">导入数据</el-button></el-upload></div><el-table:data="newData"style="width: 100%"><el-table-columnprop="name"label="姓名"width="180"/><el-table-columnprop="date"label="出生日期"/><el-table-columnprop="last"label="上月"/><el-table-columnprop="next"label="下月"/><el-table-columnprop="sum"label="合计"/></el-table></div>
2.js
import * as XLSX from 'xlsx'
//相当于一个字典对应关系
const tempObj = {姓名: 'name',出生日期: 'date',上月: 'last',下月: 'next'
}data () {return {newData: []}
},
methods: {// 点击按钮触发a下载文件DownloadTemplate () {let a = document.createElement('a')a.href = './static/template.xlsx'a.download = '近期消费统计表.xlsx'a.style.display = 'none'document.body.appendChild(a)a.click()a.remove()},ImportData (file) { // 参数为file,表示要上传的文件const types = file.name.slice(file.name.lastIndexOf('.')) // 获取文件后缀名const fileType = ['.xlsx', '.xls'].some(item => item === types) // 判断文件类型是否为xlsx或xlsif (!fileType) { // 如果文件类型不正确,提示用户重新上传this.$message.warning('文件格式错误!请重新上传')return // 如果文件类型不正确 结束函数}this.newData = [] // 初始化newData,用于存储读取到的数据const reader = new FileReader() // 创建文件读取器reader.readAsBinaryString(file.raw) // 将文件内容读取成二进制字符串reader.onload = () => { // 当文件读取成功时const binary = reader.result // 获取文件内容的二进制字符串const wb = XLSX.read(binary, { // 将二进制字符串转换成workbook对象type: 'binary'})// 将workbook对象中的第一个sheet转换成JSON格式(跳过前两行)const outdata = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { range: 2 })console.log(outdata)outdata.map((item, index) => {// 对读取的字段进行必要的判断this.JudgeEmpty(item['出生日期'], '出生日期', index)this.JudgeEmpty(item['姓名'], '姓名', index)this.JudgeEmptyAndNumber(item['上月'], '上月', index)this.JudgeEmptyAndNumber(item['下月'], '下月', index)let obj = {}for (let key in tempObj) {if (!item.hasOwnProperty(key)) { continue }if (key === '上月' || key === '下月') {obj[tempObj[key]] = Math.floor(item[key] * 100) / 100} else if (key === '出生日期') {obj[tempObj[key]] = this.$util.formatExcelDate(item[key])} else { obj[tempObj[key]] = item[key] }}this.newData.push(obj)})this.DataProcess()}},DataProcess () {// 涉及需要系统自动计算处理的this.newData.forEach(ele => {this.$set(ele, 'sum', Math.floor((ele.last + ele.next) * 100) / 100)})console.log(this.newData)// 处理完后,接下来调用接口,将newData提交给后端},// Excel里字段判空JudgeEmpty (a, str, i) {i++if (typeof a === 'undefined') {this.$message.error('Excel中第' + i + '行<' + str + '>列数据未填写完整,请补充!')this.newData = []throw Error}},// Excel里字段判空且填写类型必须是数字JudgeEmptyAndNumber (a, str, i) {i++if (typeof a === 'undefined') {this.$message.error('Excel中第' + i + '行<' + str + '>列数据未填写完整,请补充!')this.newData = []throw Error}if (isNaN(a)) {this.$message.error('Excel中第' + i + '行<' + str + '>列数据类型不正确,请修正!')this.newData = []throw Error}}
}
表格文件不存储
看图:
excel的一些填写注意点与前端存储excel模板时一样
依赖下载:
npm install moment
npm install vue-json-excel?
npm install xlsx?
代码实现
1.template
<download-excelclass="export-excel-wrapper":data="tableData":fields="json_fields"header="近期消费统计模板表"type="xls"worksheet="My Worksheet"name="近期消费统计模板表"
><el-button type="primary" size="small">导出模板</el-button>
</download-excel>
<!-- 导入数据按钮同前端存储excel -->
<el-table:data="tableData"style="width: 100%"
><el-table-columnprop="name"label="姓名"width="180"/><el-table-columnprop="gender"label="性別"width="180"/><el-table-columnprop="date"label="出生日期"/><el-table-columnprop="last"label="上月"/><el-table-columnprop="next"label="下月"/><el-table-columnprop="sum"label="合计"/><el-table-columnprop="remarks"label="备注"/>
</el-table>
2.js
import * as XLSX from 'xlsx'
let tempObj = {出生日期: 'date',上月: 'last',下月: 'next',备注: 'remarks'
}data () {return {// 导出的字段(前两个字段有值,后4个无值)json_fields: {姓名: 'name',性别: 'gender',出生日期: 'xxx',上月: 'xxx',下月: 'xxx',备注: 'xxx'},// 后端返给前端的初始假数据tableData: [{name: 'yxx',gender: '女'},{name: 'wx',gender: '男'}],// 新导入的数据newData: []}},
methods: {ImportData (file) {const types = file.name.slice(file.name.lastIndexOf('.')) // 获取文件后缀名const fileType = ['.xlsx', '.xls'].some(item => item === types) // 判断文件类型是否为xlsx或xlsif (!fileType) { // 如果文件类型不正确,提示用户重新上传this.$message.warning('文件格式错误!请重新上传')return // 如果文件类型不正确 结束函数}this.newData = [] // 初始化newData,用于存储读取到的数据const reader = new FileReader() // 创建文件读取器reader.readAsBinaryString(file.raw) // 将文件内容读取成二进制字符串reader.onload = () => { // 当文件读取成功时执行以下操作const binary = reader.result // 获取文件内容的二进制字符串const wb = XLSX.read(binary, { // 将二进制字符串转换成workbook对象type: 'binary'})// 将workbook对象中的第一个sheet转换成JSON格式,跳过1行(标题行)const outdata = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]], { range: 1 })console.log(outdata)outdata.map((item, index) => {this.JudgeEmpty(item['出生日期'], '出生日期', index)this.JudgeEmptyAndNumber(item['上月'], '上月', index)this.JudgeEmptyAndNumber(item['下月'], '下月', index)this.JudgeEmpty(item['备注'], '备注', index)let obj = {}// eslint-disable-next-line guard-for-infor (let key in tempObj) {// eslint-disable-next-line no-prototype-builtinsif (!item.hasOwnProperty(key)) { return }if (key === '上月' || key === '下月') {obj[tempObj[key]] = Math.floor(item[key] * 100) / 100} else if (key === '出生日期') {obj[tempObj[key]] = this.$util.formatExcelDate(item[key])} else { obj[tempObj[key]] = item[key] }}this.newData.push(obj)})this.DataProcess()}},DataProcess () {this.tableData.forEach((ele, i) => {// eslint-disable-next-line guard-for-infor (let key in this.newData[i]) { // 将新旧数据对象拼接起来this.$set(ele, key, this.newData[i][key])}// 涉及需要系统自动计算处理的this.$set(ele, 'sum', Math.floor((ele.last + ele.next) * 100) / 100)})// 处理完后,接下来调用接口,将newData提交给后端}
}
处理excel输入为日期时的方法,放在了公共代码util.js里,如下:
import * as moment from 'moment'
const START_TIME = '1900/01/01'
const FORMAT = 'YYYY/MM/DD'
export default {// 日期转换:将1999年8月27日或1999/8/27 统一转为1996/08/27formatExcelDate (num) {let duration = num - 1// 1900/2/29的num为60if (num > 60) {// 对于num大于60的需解析日期,要减去多的1900/2/29日的那一天duration = num - 2}return moment(START_TIME).add(moment.duration({ days: duration })).format(FORMAT)},install (innerVue) {innerVue.prototype.$util = this}
}