发布时间:2025-06-24 18:00:44  作者:北方职教升学中心  阅读量:411


')+'数据为空,请重新输入后上传'console.log(errorMessage)this.$confirm(errorMessage,'系统提示',{confirmButtonText:'确定',showCancelButton:false,type:'warning',}).then(()=>{console.log('确定')})return}else{console.log('所有字段均有值,可以上传')}this.tableHeader =tableHeader this.excelData =formatExcelDataA(excelData,dayjs)this.dealExcelData()//处理成表格的数据模板}},XLSX,dayjs )},//处理数据dealExcelData(){// 获取第一个对象的键数组作为表头,并确保顺序与对象中属性的顺序相同this.tableHeader =transformTableHeader(this.tableHeader)console.log('表头:',this.tableHeader)this.tableData =transformTableData(this.tableHeader,this.excelData)// 转换数据格式console.log('这里是表数据tableData:',this.tableData)this.tableHeader =processTableHeaderLabel(this.tableHeader)this.$refs.singleTable.doLayout()},//监听下拉框变化handleSelectChange(number){constdateTimeRegex =/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/switch(number){case1:this.stationCodeTemp =this.tableData.map((row)=>row[this.formLabelTop.stationCode])console.log('🚀 ~ handleSelectChange ~ this.stationCodeTemp :',this.stationCodeTemp)breakcase2:this.curveNameTemp =this.tableData.map((row)=>row[this.formLabelTop.curveName])console.log('🚀 ~ handleSelectChange ~ this.curveNameTemp:',this.curveNameTemp)breakcase3:for(leti =0;i <this.tableData.length;i++){constrow =this.tableData[i]constvalue =row[this.formLabelTop.enableTime]console.log('🚀 ~ handleSelectChange ~ value:',value)// 判断值是否不是 'YYYY-MM-DD HH:mm:ss' 格式if(!dateTimeRegex.test(value)){console.log('value不是YYYY-MM-DD HH:mm:ss格式')this.formLabelTop.enableTime =''this.$modal.msgWarning('BGTM字段需为日期,字段类型不匹配,请重新选择')return}}this.enableTimeTemp =this.tableData.map((row)=>row[this.formLabelTop.enableTime])console.log('🚀 ~ handleSelectChange ~ this.enableTimeTemp :',this.enableTimeTemp)breakcase4:for(leti =0;i <this.tableData.length;i++){constrow =this.tableData[i]constvalue =row[this.formLabelTop.pointNum]console.log('🚀 ~ handleSelectChange ~ value:',value)// 在这里放置对 value 值的判断逻辑if(typeofvalue !=='number'||!Number.isInteger(value)){console.log('value不是整数')this.formLabelTop.pointNum =''this.$modal.msgWarning('PTNO字段需为整型,字段类型不匹配,请重新选择')return}}this.pointNumTemp =this.tableData.map((row)=>row[this.formLabelTop.pointNum])console.log('🚀 ~ handleSelectChange ~ this.pointNumTemp:',this.pointNumTemp)breakcase5:for(leti =0;i <this.tableData.length;i++){constrow =this.tableData[i]constvalue =row[this.formLabelTop.waterLevel]console.log('🚀 ~ handleSelectChange ~ value:',value)// 在这里放置对 value 值的判断逻辑if(typeofvalue !=='number'){console.log('waterLevel中有数据不是number')this.formLabelTop.waterLevel =''this.$modal.msgWarning('Z字段需为浮点型,字段类型不匹配,请重新选择')return}}this.waterLevelTemp =this.tableData.map((row)=>row[this.formLabelTop.waterLevel])console.log('🚀 ~ handleSelectChange ~ this.waterLevelTemp:',this.waterLevelTemp)breakcase6:for(leti =0;i <this.tableData.length;i++){constrow =this.tableData[i]constvalue =row[this.formLabelTop.flow]console.log('🚀 ~ handleSelectChange ~ value:',value)// 在这里放置对 value 值的判断逻辑if(typeofvalue !=='number'){console.log('flow中有数据不是number')this.formLabelTop.flow =''this.$modal.msgWarning('Q字段需为浮点型,字段类型不匹配,请重新选择')return}}console.log('flow++++++')this.flowTemp =this.tableData.map((row)=>row[this.formLabelTop.flow])console.log('🚀 ~ handleSelectChange ~ this.flowTemp:',this.flowTemp)breakcase7:this.columnCommentsTemp =this.tableData.map((row)=>row[this.formLabelTop.columnComments])console.log('🚀 ~ handleSelectChange ~ this.columnCommentsTemp:',this.columnCommentsTemp)breakcase8:for(leti =0;i <this.tableData.length;i++){constrow =this.tableData[i]constvalue =row[this.formLabelTop.columnModitime]console.log('🚀 ~ handleSelectChange ~ value:',value)// 判断值是否不是 'YYYY-MM-DD HH:mm:ss' 格式if(!dateTimeRegex.test(value)){console.log('value不是YYYY-MM-DD HH:mm:ss格式')this.formLabelTop.columnModitime =''this.$modal.msgWarning('MODITIME字段需为日期,字段类型不匹配,请重新选择')return}}this.columnModitimeTemp =this.tableData.map((row)=>row[this.formLabelTop.columnModitime])console.log('🚀 ~ handleSelectChange ~ this.columnModitimeTemp:',this.columnModitimeTemp)breakcase9:console.log('🚀 ~ handleSelectChange ~ this.tableData:',this.tableData)this.columnExkeyTemp =this.tableData.map((row)=>row[this.formLabelTop.columnExkey])console.log('🚀 ~ handleSelectChange ~ this.columnExkeyTemp:',this.columnExkeyTemp)breakdefault:console.log('number 参数不正确')break}},//处理下拉框的曲线数据dealData2(){// 定义属性数组letpropArrays =[this.stationCodeTemp,this.curveNameTemp,this.enableTimeTemp,this.pointNumTemp,this.waterLevelTemp,this.flowTemp,this.columnCommentsTemp,this.columnModitimeTemp,this.columnExkeyTemp,]// 遍历属性数组for(leti =0;i <Math.max(...propArrays.map((arr)=>arr.length));i++){// 创建新的对象letnewObj ={formulaId:null,stationCode:'',curveName:'',enableTime:'',pointNum:'',waterLevel:'',flow:'',columnComments:'',columnModitime:'',columnExkey:'',}// 遍历属性数组并赋值propArrays.forEach((arr,index)=>{if(i <arr.length){newObj[Object.keys(newObj)[index +1]]=arr[i]}})// 将对象添加到结果数组中this.curveInfoTemp.push(newObj)}constobj ={tableHeader:this.tableHeader,fileName:this.responseName,stationCode:this.formLabelTop.stationCode,curveName:this.formLabelTop.curveName,enableTime:this.formLabelTop.enableTime,pointNum:this.formLabelTop.pointNum,waterLevel:this.formLabelTop.waterLevel,flow:this.formLabelTop.flow,columnComments:this.formLabelTop.columnComments,columnModitime:this.formLabelTop.columnModitime,columnExkey:this.formLabelTop.columnExkey,}this.curveColumnMappingTemp =JSON.stringify(obj)console.log(this.curveInfoTemp)},//移除文件beforeRemove(file,fileList){this.isUpload =falsethis.tableHeader =[]this.tableData =[]this.responseName =''this.formLabelTop ={stationCode:'',curveName:'',enableTime:'',pointNum:'',waterLevel:'',flow:'',columnComments:'',columnModitime:'',columnExkey:'',}this.$nextTick(()=>{this.$refs['formLabelTop'].clearValidate()})},},}</script><style></style>

流量')}else{if(item['*曲线名称']==='-'){emptyFields.add('曲线名称')}if(item['*水位(m)']==='-'){emptyFields.add('水位')}if(item['*流量(m³/s)']==='-'){emptyFields.add('流量')}if(item['*测站编码']==='-'){emptyFields.add('测站编码')}}})if(emptyFields.size >0){this.beforeRemove()consterrorMessage =[...emptyFields].join('、水位、

模板存放位置 模板限定xls后缀,xlsx会有时间问题
src/assets/excelTemplate/模板.xls
安装模块包

npminstallfile-loader --save-dev    //开发,Webpack 配置中使用它来处理文件加载npmi xlsx --save//生产,解析和处理 Excel 文件的库

新增配置,在vue.config.js中,自己比较一下,最后一段新增的chainWebpack

module.exports =defineConfig({transpileDependencies:true,assetsDir:'static',//打包配置文件parallel:false,publicPath:'./',devServer:{port:port,open:true,proxy:{'/api':{target:process.env.VUE_APP_BASE_URL,changeOrigin:true,ws:true,pathRewrite:{'^/api':'',},},},},configureWebpack:{name:name,resolve:{alias:{'@':resolve('src'),},},},chainWebpack(config){config.module      .rule('excel').test(/\.(xls|xlsx)$/).use('file-loader').loader('file-loader').options({name:'[name].[ext]',}).end()},})

即可将模板下载到本地

<template><el-button @click="downloadFile"icon="el-icon-download">下载配置模板</el-button></template><script>importexcelFile from'@/assets/excelTemplate/模板.xls'exportdefault{data(){return{}},methods:{//下载downloadFile(){constlink =document.createElement('a')link.href =excelFile      link.download ='模板.xls'link.style.display ='none'// 隐藏元素document.body.appendChild(link)// 添加到文档中link.click()document.body.removeChild(link)// 点击后移除},},}</script><style></style>

我使用XLSX的场景,在我上传excel的时候,我需要获取它的表头以及里面的数据进行渲染到表格中,在我编辑的时候需要请求Excel的地址,将返回流转JSON也拿里面的表格数据

封装公共的方法

/** * xlsx自身插件有bug,比如excel里17:00:00,从流获取会少了43s,跟时区有关,用下面2个无用,最好方法直接限制上传文件后最为xls即可,就不会出现时间偏差了 * @param  date * @returns */functionfixPrecisionLoss(date){constimportBugHotfixDiff =(()=>{constbasedate =newDate(1899,11,30,0,0,0);constdnthreshAsIs =(newDate().getTimezoneOffset()-basedate.getTimezoneOffset())*60000-1000;constdnthreshToBe =getTimezoneOffsetMS(newDate())-getTimezoneOffsetMS(basedate);returndnthreshAsIs -dnthreshToBe;})();returnnewDate(date.getTime()-importBugHotfixDiff);}functiongetTimezoneOffsetMS(date){vartime =date.getTime();varutcTime =Date.UTC(date.getFullYear(),date.getMonth(),date.getDate(),date.getHours(),date.getMinutes(),date.getSeconds(),date.getMilliseconds());returntime -utcTime;}/** * 核心处理excel流,读取里面的数据 * @param  file * @param  callback * @param  XLSX * @param  dayjs */exportfunctionreadExcelFile(file,callback,XLSX,dayjs){constreader =newFileReader();reader.onload=event=>{constdata =event.target.result;letworkBook =XLSX.read(data,{type:'array',cellDates:true});try{constworksheet =workBook.Sheets[workBook.SheetNames[0]];console.log('🚀 ~ readExcelFile ~ worksheet:',worksheet);consttableHeader =getHeaderRowA(worksheet,XLSX);console.log('🚀 ~ readExcelFile ~ tableHeader:',tableHeader);constexcelData =XLSX.utils.sheet_to_json(worksheet,{raw:true,defval:'-'});excelData.forEach(obj=>{Object.keys(obj).forEach(v=>{if(obj[v]instanceofDate){obj[v]=dayjs((obj[v])).format('YYYY-MM-DD HH:mm:ss');}});});callback(null,{tableHeader,excelData });}catch(error){callback(error);}};reader.onerror=event=>{callback(event.error);};reader.readAsArrayBuffer(file);}/** * 获取excel的表头数组 * @param  sheet  工作簿 查看excel就知道有很多的sheet了一般读取[0]也就是第一个 * @param  XLSX 工具包 * @returns */functiongetHeaderRowA(sheet,XLSX){constheaders =[];// 定义数组,用于存放解析好的数据constrange =XLSX.utils.decode_range(sheet['!ref']);// 读取sheet的单元格数据letC;constR=range.s.r;/* start in the first row */for(C=range.s.c;C<=range.e.c;++C){/* walk every column in the range */constcell =sheet[XLSX.utils.encode_cell({c:C,r:R})];/* find the cell in the first row */lethdr ='UNKNOWN '+C;// <-- replace with your desired defaultif(cell &&cell.t)hdr =XLSX.utils.format_cell(cell);headers.push(hdr);}returnheaders;// 经过上方一波操作遍历,得到最终的第一行头数据}/** * 将excel的 Sun Feb 18 2024 11:53:44 GMT+0800日期格式化成这种格式YYYY-MM-DD HH:mm:ss * @param {} excelData * @returns */exportfunctionformatExcelDataA(excelData,dayjs){returnexcelData.map(obj=>{letnewObj ={};Object.keys(obj).forEach(v=>{if(obj[v]instanceofDate){newObj[v]=dayjs((obj[v])).format('YYYY-MM-DD HH:mm:ss');}else{newObj[v]=obj[v];}});returnnewObj;});}// 定义一个方法来转换表头数据格式,符合elementUi里的tableexportfunctiontransformTableHeader(tableHeader){returntableHeader.map((item,index)=>({label:item,prop:`propTable${index +1}`}));}// 定义一个方法来转换表格数据格式,符合elementUi里的dataexportfunctiontransformTableData(tableHeader,excelData){returnexcelData.map(item=>{letrowData ={};Object.keys(item).forEach(value=>{constpropItem =tableHeader.find(p=>p.label ===value);rowData[propItem.prop]=item[value];});return{...rowData };});}// 定义一个方法来处理表头标签,因为上传的带有*的必填,去掉展示exportfunctionprocessTableHeaderLabel(tableHeader){returntableHeader.map(obj=>{constlabel =obj.label.startsWith('*')?obj.label.slice(1):obj.label;return{...obj,label };});}

下面是调用,抽离核心需要的,仅供参考

<template><div><el-button @click="downloadFile"icon="el-icon-download">下载配置模板</el-button><el-upload ref="upload":limit="1"accept=".xls,.xlsx"class="upload-demo":action="upload.url":file-list="fileList":on-success="handleFileSuccess"><el-button type="primary"icon="el-icon-upload">上传xls文件</el-button></el-upload><el-card class="box-card"style="height: 250px"><div slot="header"class="clearfix"><span>数据展示</span></div><el-table ref="singleTable":data="tableData"highlight-current-row height="170"@current-change="handleCurrentChange"><el-table-column v-for="(item, index) in tableHeader":prop="item.prop":key="index":label="item.label"show-overflow-tooltip></el-table-column></el-table></el-card></div></template><script>// npm install file-loader --save-dev    //开发,Webpack 配置中使用它来处理文件加载// npm i xlsx --save					          //生产,解析和处理 Excel 文件的库importexcelFile from'@/assets/excelTemplate/模板.xls'import*asXLSXfrom'xlsx'importdayjs from'dayjs'import{readExcelFile,formatExcelDataA,transformTableHeader,transformTableData,processTableHeaderLabel }from'@/views/common/index.js'//算法模块公用方法lUCKY封装exportdefault{data(){return{tableHeader:'',tableData:'',responseName:'',responseUrl:'',//上传的列表fileList:[],// 上传参数upload:{url:'http://192.168.15.115:3737/luckyNwa/uploadPicLocal',// 请求地址},}},methods:{//上传之前的限制beforeUpload(file){console.log('🚀 ~ beforeUpload ~ file:',file)constvalidFormats =['.xls','.xlsx']constfileFormat =file.name.slice(file.name.lastIndexOf('.')).toLowerCase()if(!validFormats.includes(fileFormat)){this.$message.error('只能上传xls或xlsx文件')returnfalse// 阻止文件上传}returntrue// 允许文件上传},//下载downloadFile(){constlink =document.createElement('a')link.href =excelFile      link.download ='模板.xls'link.style.display ='none'// 隐藏元素document.body.appendChild(link)// 添加到文档中link.click()document.body.removeChild(link)// 点击后移除},/** 文件上传成功处理 */handleFileSuccess(response,f,fileList){this.isUpload =truethis.$refs.upload.clearFiles()this.responseUrl =f.response.data      console.log('🚀 ~ handleFileSuccess ~ this.responseUrl:',this.responseUrl)this.responseName =f.raw.name      constfile =f.raw //获取上传的文件console.log('🚀 ~ handleFileSuccess ~ file:',file)// this.$modal.msgSuccess('上传成功');if(file){this.isFileDeal(file)}},//处理excelisFileDeal(file){readExcelFile(file,(error,data)=>{if(error){console.log('里面的tryCatch捕获的异常:'+error)this.beforeRemove()this.$modal.msgWarning('表格数据不能为空!')}else{consttableHeader =data.tableHeader ||[]constexcelData =data.excelData ||[]if(excelData.length ===0||excelData ===null){this.beforeRemove()this.$modal.msgWarning('表格数据不能为空!')return}console.log('读取的excel表头数据(第一行)',tableHeader)console.log('读取所有excel数据',excelData)//这里进一步验证里面的数据,必填的里面不能为空也就是-letemptyFields =newSet()excelData.forEach((item)=>{if(item['*曲线名称']==='-'&&item['*水位(m)']==='-'&&item['*流量(m³/s)']==='-'&&item['*测站编码']==='-'){emptyFields.add('测站编码、曲线名称、