1 情景一
需求背景:后端配合
,點擊"導入"按鈕,彈出“導入”彈窗,將電腦本地Excel表格數(shù)據(jù)導入
到頁面中表格位置(需要調(diào)用后端接口),而頁面中表格通過后端接口獲取最新數(shù)據(jù)。
實現(xiàn)思路:彈窗嵌入 Element UI Upload 上傳組件,獲取到文件后調(diào)后端接口。
action
: 上傳的地址file-list
: 上傳的文件列表, 例如: [{name: ‘food.jpg’, url: ‘https://xxx.cdn.com/xxx.jpg’}]headers
: 設(shè)置上傳的請求頭部(上傳的文件可能是有固定格式的(表頭),這個是在后端設(shè)定好了的,所以如果上傳的文件格式不對可能會引起報錯。)before-upload
: 上傳文件之前的鉤子,可作上傳之前校驗on-error
: 文件上傳失敗時的鉤子on-exceed
: 文件超出個數(shù)限制時的鉤子on-success
: 文件上傳成功時的鉤子on-change
: 文件狀態(tài)改變時的鉤子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')
}
},
// 數(shù)量限制
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') },
// 導入數(shù)據(jù)路徑
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)
},
// 上傳文件服務(wù)器地址
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
},
// 文件個數(shù)超出
handleExceed() {
this.$modal.msgError(`${this.$t('uploadFileNumberLess')} ${this.limit}`)
},
// 上傳失敗
handleUploadError() {
this.$modal.msgError(this.$t('uploadFail'))
this.$modal.closeLoading()
},
// 上傳成功回調(diào)
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: '正在下載數(shù)據(jù),請稍候',
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('下載文件出現(xiàn)錯誤,請聯(lián)系管理員!')
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="導入持倉"
/>
文章來源:http://www.zghlxwxcb.cn/news/detail-769552.html
2 情景二
需求背景:純前端
實現(xiàn)文件導入的情景。例如,一個常規(guī)excel文件填寫模板,在用戶的電腦上,用戶上傳完后,還可以在預覽展示時,在線修改,改完可以下載,也可以將數(shù)據(jù)給到服務(wù)端,但這時,比如這模板數(shù)據(jù)通常不多,比如是一個團隊成員這樣的數(shù)據(jù),通過文件流的形式傳給后端,可能不是很理想,倒不如前端解析傳那幾行數(shù)據(jù)就行。
實現(xiàn)思路:importExcel.js
導入excel文件文章來源地址http://www.zghlxwxcb.cn/news/detail-769552.html
- 使用html支持上傳標簽從本地獲取文件,例如type為file的input,el-upload等。
- 利用
FileReader
將文件讀取為二進制字符串。 - 使用
XLSX插件
的XLSX.read()
方法,將二進制字符串轉(zhuǎn)換成excel文件的工作蒲對象workbook(簡寫成wb)。 - 通過
XLSX.utils.sheet_to_json()
方法,從wb中獲取第一張 Sheets表格數(shù)據(jù)并將其轉(zhuǎn)換為json數(shù)據(jù)。 - 重組json數(shù)據(jù)生成數(shù)組,即是根據(jù)自己的定義的列字段名,重新組成符合自己需求的json數(shù)據(jù)。因為從excel中提取的數(shù)據(jù)是沒有字段名或字段名不符合要求的,而我們需要渲染在頁面表格中又確實需要合適的字段名。
/* eslint-disable */
/* 導入excel文件 */
/**
* @param file 文件流
* @param tableTemplate 要導入的表格模板,一個數(shù)組,如:
* tableTemplate: ['userCode', 'userName', 'department', 'major', 'position'],其中的值
* 為表格的字段名,注意字段的順序應(yīng)與實際的導入excel一致。
*/
export default function importExcel (file, tableTemplate) {
return new Promise((resolve, reject) => {
let f = file.raw // 獲取文件內(nèi)容
// 通過DOM取文件數(shù)據(jù)
let rABS = false // 是否將文件讀取為二進制字符串
let reader = new FileReader()
FileReader.prototype.readAsBinaryString = function (f) {
let binary = ''
let rABS = false // 是否將文件讀取為二進制字符串
let wb // 讀取完成的數(shù)據(jù)
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), { // 手動轉(zhuǎn)化
type: 'base64'
})
} else {
wb = XLSX.read(binary, {
type: 'binary'
})
}
outdata = XLSX.utils.sheet_to_json(wb.Sheets[wb.SheetNames[0]]) // outdata就是表格中的值
let arr = []
// 下面是數(shù)據(jù)解析提取邏輯
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)
}
})
}
到了這里,關(guān)于Vue - 使用Element UI Upload / importExcelJs進行文件導入的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!