Vue - 使用Element UI Upload / importExcelJs进行文件导入

1 情景一

需求背景后端配合,点击"导入"按钮,弹出“导入”弹窗,将电脑本地Excel表格数据导入到页面中表格位置(需要调用后端接口),而页面中表格通过后端接口获取最新数据。
实现思路:弹窗嵌入 Element UI Upload 上传组件,获取到文件后调后端接口。

action: 上传的地址
file-list: 上传的文件列表, 例如: [{name: ‘food.jpg’, url: ‘https://xxx.cdn.com/xxx.jpg’}]
headers: 设置上传的请求头部(上传的文件可能是有固定格式的(表头),这个是在后端设定好了的,所以如果上传的文件格式不对可能会引起报错。)
before-upload: 上传文件之前的钩子,可作上传之前校验
on-error: 文件上传失败时的钩子
on-exceed: 文件超出个数限制时的钩子
on-success: 文件上传成功时的钩子
on-change: 文件状态改变时的钩子
this.$refs.fileUpload.submit(): 手动上传文件列表(fileUpload是自定义的名字)

  • 组件代码
<template><span><el-buttonplainicon="el-icon-upload2"type="primary"size="mini":disabled="disabled"@click="importFile">{{ $t('import') }}</el-button><el-dialogv-if="open":title="importTitle":visible.sync="open"width="500px"append-to-body:close-on-click-modal="false":show-close="false"@close="cancel"><div v-if="isShowRecord" class="record-btn"><el-button icon="el-icon-time" type="primary" size="mini" @click="recordBtnClick"><span>{{ recordText }}</span></el-button></div><div style="text-align: center;"><el-uploadref="fileUpload"multiple:auto-upload="false":action="uploadFileUrl":before-upload="handleBeforeUpload":file-list="fileList":limit="limit":on-error="handleUploadError":on-exceed="handleExceed":on-success="handleUploadSuccess":on-change="handleChange":headers="headers"class="upload-file-uploader"><div style="border:1px dashed #e9e9e9"><i class="el-icon-plus avatar-uploader-icon" style="margin:80px;font-size: 25px;" /></div><!-- 上传提示 --><div v-if="showTip" slot="tip" class="el-upload__tip">{{ $t("pleaseUpload") }}<template v-if="fileSize">{{ $t("sizeLess") }}<b style="color: #f56c6c">{{ fileSize }}MB</b></template><template v-if="fileType">{{ $t("formatIs") }}<b style="color: #f56c6c">{{ fileType.join("/") }}</b></template>{{ $t("file") }}</div></el-upload></div><div class="template"><!-- <form v-show="false" id="templateForm" :action="templateUrl" method="get" /> --><span @click="downloadFile">{{ $t("downloadThisTemplate") }}</span></div><div slot="footer" class="dialog-footer"><el-button type="primary" @click="submitForm">{{ $t('confirm') }}</el-button><el-button @click="cancel">{{ $t('cancel') }}</el-button></div></el-dialog></span>
</template>
<script>
import { getToken } from '@/utils/auth'
import { downloadGet } from '@/utils/request'
import moment from 'moment'
import $ from '@/i18n/index'export default {name: 'FileImport',props: {// 是否禁用导入按钮disabled: {type: Boolean,default: false},// 导入弹窗标题importTitle: {type: String,default: () => {return $.t('fileImport')}},// 数量限制limit: {type: Number,default: 1},// 大小限制(MB)fileSize: {type: Number,default: 20},// 文件类型, 例如['png', 'jpg', 'jpeg']fileType: {type: Array,default: () => ['csv']},// 是否显示提示isShowTip: {type: Boolean,default: true},// 下载模板路径downLoadUrl: { type: String, default: null },// 下载的全拼路径fullPathUrl: { type: String, default: null },// 模板名称fileName: { type: String, default: moment().format('yyyyMMDDHHmmss') },// 导入数据路径importUrl: { type: String, default: null },// 是否展示导入导出记录按钮isShowRecord: {type: Boolean,default: false},// 导入导出记录文字recordText: {type: String,default: () => {return $.t('importAndExportRecord')}}},data() {return {open: false,number: 0,baseUrl: process.env.VUE_APP_BASE_API,// templateUrl://   this.fullPathUrl || process.env.VUE_APP_BASE_API + this.downLoadUrl,fileList: []}},computed: {headers() {return { Authorization: getToken() }},// 是否显示提示showTip() {return this.isShowTip && (this.fileType || this.fileSize)},// 上传文件服务器地址uploadFileUrl() {return process.env.VUE_APP_BASE_API + this.importUrl}// templateUrl() {//   return this.fullPathUrl || process.env.VUE_APP_BASE_API + this.downLoadUrl// }},methods: {importFile() {this.open = true},submitForm() {if (this.fileList.length === 0) {this.$modal.msgError(this.$t('pleaseImportFile'))return}this.$refs.fileUpload.submit()},cancel() {this.$refs.fileUpload.clearFiles()this.open = falsethis.$emit('importSuccess')},// 上传前校检格式和大小handleBeforeUpload(file) {// 校检文件类型if (this.fileType) {const fileName = file.name.split('.')const fileExt = fileName[fileName.length - 1]const isTypeOk = this.fileType.indexOf(fileExt) >= 0if (!isTypeOk) {this.$modal.msgError(`${this.$t('fileTypeErrorUpload')}${this.fileType.join('/')}${this.$t('file')}`)return false}}// 校检文件大小if (this.fileSize) {const isLt = file.size / 1024 / 1024 < this.fileSizeif (!isLt) {this.$modal.msgError(`${this.$t('uploadFileSizeLess')} ${this.fileSize} MB!`)return false}}this.$modal.loading(this.$t('waitForUpload'))this.number++return true},// 文件个数超出handleExceed() {this.$modal.msgError(`${this.$t('uploadFileNumberLess')} ${this.limit}`)},// 上传失败handleUploadError() {this.$modal.msgError(this.$t('uploadFail'))this.$modal.closeLoading()},// 上传成功回调handleUploadSuccess(res, file) {if (res.code === 0) {this.$message({ type: 'success', message: this.$t('uploadSuccess') })this.cancel()this.$emit('handleUploadSuccess')} else {this.number--this.$modal.msgError(res.msg)this.$refs.fileUpload.handleRemove(file)}this.$modal.closeLoading()},downloadFile() {// document.getElementById('templateForm').submit()let dowloadUrl = this.downLoadUrlif (this.fullPathUrl) {dowloadUrl = this.fullPathUrl}downloadGet(dowloadUrl, `${this.fileName}.${this.fileType}`, {})},recordBtnClick() {this.$emit('recordBtnClick')},handleChange(file, fileList) {this.fileList = fileList}}
}
</script>
<style lang="scss" scoped>
.template {text-align: center;color: #1890ff;padding: 10px;span:hover {cursor: pointer;border-bottom: 1px solid #1890ff;}
}.record-btn {position: absolute;right: 18px;top: 18px;
}
</style>
// 通用下载方法,blob形式
export function downloadGet(url, filename, config) {downloadLoadingInstance = Loading.service({text: '正在下载数据,请稍候',spinner: 'el-icon-loading',background: 'rgba(0, 0, 0, 0.7)'})return service.get(url, {headers: { 'Content-Type': 'application/x-www-form-urlencoded' },responseType: 'blob',...config}).then(async(data) => {const isLogin = await blobValidate(data)if (isLogin) {const blob = new Blob([data])saveAs(blob, filename)} else {const resText = await data.text()const rspObj = JSON.parse(resText)const errMsg =errorCode[rspObj.code] || rspObj.msg || errorCode['default']Message.error(errMsg)}downloadLoadingInstance.close()}).catch((r) => {console.error(r)Message.error('下载文件出现错误,请联系管理员!')downloadLoadingInstance.close()})
}
import Cookies from 'js-cookie'const TokenKey = 'Admin-Token'export function getToken() {return Cookies.get(TokenKey)
}

在这里插入图片描述

  • 使用组件
 <FileImportdown-load-url="/client/template/export_template/sec_type"import-url="/client/sec_type/import"file-name="导入持仓"
/>

在这里插入图片描述

2 情景二

需求背景纯前端实现文件导入的情景。例如,一个常规excel文件填写模板,在用户的电脑上,用户上传完后,还可以在预览展示时,在线修改,改完可以下载,也可以将数据给到服务端,但这时,比如这模板数据通常不多,比如是一个团队成员这样的数据,通过文件流的形式传给后端,可能不是很理想,倒不如前端解析传那几行数据就行。
实现思路importExcel.js 导入excel文件

  1. 使用html支持上传标签从本地获取文件,例如type为file的input,el-upload等。
  2. 利用FileReader将文件读取为二进制字符串。
  3. 使用XLSX插件XLSX.read()方法,将二进制字符串转换成excel文件的工作蒲对象workbook(简写成wb)。
  4. 通过XLSX.utils.sheet_to_json()方法,从wb中获取第一张 Sheets表格数据并将其转换为json数据。
  5. 重组json数据生成数组,即是根据自己的定义的列字段名,重新组成符合自己需求的json数据。因为从excel中提取的数据是没有字段名或字段名不符合要求的,而我们需要渲染在页面表格中又确实需要合适的字段名。
/* eslint-disable */
/* 导入excel文件 */
/*** @param file 文件流* @param tableTemplate 要导入的表格模板,一个数组,如:* tableTemplate: ['userCode', 'userName', 'department', 'major', 'position'],其中的值* 为表格的字段名,注意字段的顺序应与实际的导入excel一致。*/
export default function importExcel (file, tableTemplate) {return new Promise((resolve, reject) => {let f = file.raw // 获取文件内容// 通过DOM取文件数据let rABS = false // 是否将文件读取为二进制字符串let reader = new FileReader()FileReader.prototype.readAsBinaryString = function (f) {let binary = ''let rABS = false // 是否将文件读取为二进制字符串let wb // 读取完成的数据let outdatalet reader = new FileReader()reader.onload = function (e) {let bytes = new Uint8Array(reader.result)let length = bytes.byteLengthfor (let i = 0; i < length; i++) {binary += String.fromCharCode(bytes[i])}let XLSX = require('xlsx')if (rABS) {wb = XLSX.read(btoa(binary), { // 手动转化type: 'base64'})} else {wb = XLSX.read(binary, {type: 'binary'})}outdata = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]) // outdata就是表格中的值let arr = []// 下面是数据解析提取逻辑if (tableTemplate.length > 0) {let tempArr = Object.keys(outdata[0])let tempArrNew = []for (let i in tempArr) {for (let k in tableTemplate) {if (i === k) {tempArrNew.push({fieldE: tableTemplate[k], fieldC: tempArr[i]})}}}tempArr = tempArrNewoutdata.map(item => {let obj = {}tempArr.map(temp2 => {obj[temp2.fieldE] = item[temp2.fieldC]})arr.push(obj)})}resolve(arr)}reader.readAsArrayBuffer(f)}if (rABS) {reader.readAsArrayBuffer(f)} else {reader.readAsBinaryString(f)}})
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/307727.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Factory Method工厂模式(对象创建)

Factory Method&#xff08;对象创建&#xff09; 链接&#xff1a;工厂模式实例代码 解析 目的 在软件系统中&#xff0c;经常面临着创建对象的工作&#xff1b;由于需求的变化&#xff0c;需要创建的对象的具体类型经常变化。 如何应对这种变化&#xff1f;如何绕过常规的…

深眸科技创新工业AI视觉系统,与机械臂协同工作实现视觉引导功能

工业AI视觉系统&#xff1a;工业AI视觉系统能够在工业环境中进行缺陷检测、视觉分拣、物流供包、拆码垛、工业上料等应用。 随着国内工业企业不断进步和发展&#xff0c;传统机器视觉无法满足企业对复杂操作流程的需求&#xff0c;多数制造企业对于机器视觉系统的需求增长。而…

如何优化旋转花键的装配方式?

花键轴与花键套的装配在工业上广泛应用&#xff0c;装配的质量受花键轴与花键套间的接触状态、对应的受力情况及相对位置关系影响&#xff0c;那么&#xff0c;我们应该如何优化旋转花键的装配方式呢&#xff1f; 确保轴和孔的配合精度是关键&#xff0c;可以采用高精度的加工和…

VM虚拟机及WindowsServer2012安装及激活

一 、虚拟机是什么? 虚拟机&#xff08;VM&#xff09;是一种创建于物理硬件系统&#xff08;位于外部或内部&#xff09;、充当虚拟计算机系统的虚拟环境&#xff0c;它模拟出了自己的整套硬件&#xff0c;包括 CPU、内存、网络接口和存储器。通过名为虚拟机监控程序的软件&…

万物简单AIoT物联网平台快速开始

学物联网&#xff0c;来万物简单IoT物联网&#xff01;&#xff01; 万物简单AIoT物联网提供一站式的AI物联网的学习平台&#xff0c;以及物联网SaaS私有化部署的解决方案。从终端硬件系统、云平台到APP前端的物联网能力&#xff0c;助力企业和开发者的设备具备1分钟快速上云的…

3、Git分支操作与团队协作

Git分支操作 1.什么是分支2. 分支的好处3. 分支的操作3.1 查看分支3.2 创建分支3.3 切换分支3.4 修改分支3.5 合并分支3.6 产生和解决冲突 4. 创建分支和切换分支图解5. Git团队协作机制团队内协作跨团队协作 均在git bash中进行操作。事先建好本地工作库 1.什么是分支 在版本…

javascript实现数据双向绑定

ES5中的双向绑定 ES5中的对象属性类型有两种&#xff1a;分别是数据属性和访问器属性 一&#xff0c;数据属性 数据属性包含一个数据值的位置。在这个位置可以读取和写入值。数据属性有4个描述其行为的特性 1&#xff0c;configurable:表示能否通过delete删除属性而重新定义…

数据结构入门到入土——ArrayList与顺序表

目录 一&#xff0c;线性表 二&#xff0c;顺序表 1.接口实现 三&#xff0c;ArrayList简介 四&#xff0c;ArrayList使用 1.ArrayList的构造 2.ArrayList常见操作 3.ArrayList的遍历 4.ArrayList的扩容机制 五&#xff0c;ArrayLisit的具体使用 杨辉三角 一&#x…

Thinkphp+vue+mysql学生作业管理系统21j0r

运行环境:phpstudy/wamp/xammp等 开发语言&#xff1a;php 后端框架&#xff1a;Thinkphp5 前端框架&#xff1a;vue.js 服务器&#xff1a;apache 数据库&#xff1a;mysql 数据库工具&#xff1a;Navicat/phpmyadmin 为设计一个安全便捷&#xff0c;并且使用户更好获取本学院…

余弦相似度算法

余弦相似度算法 是什么 余弦距离&#xff0c;也称为余弦相似度&#xff0c;是用向量空间中两个向量夹角的余弦值作为衡量两个个体间差异的大小的度量。 余弦值越接近1&#xff0c;就表明夹角越接近0度&#xff0c;也就是两个向量越相似&#xff0c;这就叫"余弦相似性&q…

pd工具箱 Parallels Toolbox

Parallels Toolbox是一款适用于Mac和PC的强大工具箱软件&#xff0c;集成了多种实用的工具和功能&#xff0c;旨在帮助用户提高工作效率和生产力。它提供了文件管理、截图、音频视频处理、清理工具等众多工具&#xff0c;使用户能够方便快捷地完成各种常见任务。Parallels Tool…

C++力扣题目150--逆波兰表达式求值

给你一个字符串数组 tokens &#xff0c;表示一个根据 逆波兰表示法 表示的算术表达式。 请你计算该表达式。返回一个表示表达式值的整数。 注意&#xff1a; 有效的算符为 、-、* 和 / 。每个操作数&#xff08;运算对象&#xff09;都可以是一个整数或者另一个表达式。两个…