赞
踩
需求背景:后端配合
,点击"导入"按钮,弹出“导入”弹窗,将电脑本地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-button plain icon="el-icon-upload2" type="primary" size="mini" :disabled="disabled" @click="importFile" >{{ $t('import') }}</el-button> <el-dialog v-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-upload ref="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 = false this.$emit('importSuccess') }, // 上传前校检格式和大小 handleBeforeUpload(file) { // 校检文件类型 if (this.fileType) { const fileName = file.name.split('.') const fileExt = fileName[fileName.length - 1] const isTypeOk = this.fileType.indexOf(fileExt) >= 0 if (!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.fileSize if (!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.downLoadUrl if (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)
}
<FileImport
down-load-url="/client/template/export_template/sec_type"
import-url="/client/sec_type/import"
file-name="导入持仓"
/>
需求背景:纯前端
实现文件导入的情景。例如,一个常规excel文件填写模板,在用户的电脑上,用户上传完后,还可以在预览展示时,在线修改,改完可以下载,也可以将数据给到服务端,但这时,比如这模板数据通常不多,比如是一个团队成员这样的数据,通过文件流的形式传给后端,可能不是很理想,倒不如前端解析传那几行数据就行。
实现思路:importExcel.js
导入excel文件
FileReader
将文件读取为二进制字符串。XLSX插件
的XLSX.read()
方法,将二进制字符串转换成excel文件的工作蒲对象workbook(简写成wb)。XLSX.utils.sheet_to_json()
方法,从wb中获取第一张 Sheets表格数据并将其转换为json数据。/* 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 outdata let reader = new FileReader() reader.onload = function (e) { let bytes = new Uint8Array(reader.result) let length = bytes.byteLength for (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 = tempArrNew outdata.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) } }) }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。