封装文件导入组件,含导入进度条
需求
系统中需要有多个文件导入的地方,因此需要把它封装为组件便于复用。
问题是:每次的导入 url
不同,每次封装的导入接口应该在主页面用呢?还是在组件页面用?
解决办法
分析:其实这里进入了一个误区———每个接口 url
都应该封装成一个接口。完全可以不同的接口地址都封装成一个接口,只要它们的请求方法和数据格式相同。
因此,解决办法就是:把不同的导入地址以字符串形式传给组件,组件调用相同的接口,只需在接口中设置不同的 url
即可。
主页面 HTML
<el-col :span="1.5">
<!-- 导入按钮 -->
<import
:para-name="paraName"
:upload-url="uploadUrl"
:show-file-list="showFileList"
:file-type="fileType"
:btn-permi="btnPermi"
@refreshList="loadxxxxList"
:progress-url="progressUrl"
/>
</el-col>
主页面 data 声明
// 是否显示文件列表
showFileList: false,
// 文件类型
fileType: ['zip'],
// 按钮权限
btnPermi: ['xxxx:xxx:xxxx'],
// 上传文件的参数名称
paraName: 'zipFile',
// 上传文件地址
uploadUrl: "/xxxx/uploadFile",
// 查询导入进度地址
progressUrl: "/xxxx/progress",
导入组件 Import/index.vue HTML
<div class="import">
<!-- 导入进度组件 -->
<el-progress
v-if="progressUrl && isUploading"
class="upload-progress"
type="circle"
:percentage="percentage"
:status="percentage === 100 ? 'success' : undefined"
:format="format"
/>
<!-- 导入组件 -->
<el-upload
element-loading-text=""
element-loading-spinner="null"
v-loading.fullscreen.lock="progressUrl && isUploading"
element-loading-background="rgba(0, 0, 0, 0.8)"
class="upload-file"
ref="upload"
:action="uploadUrl"
:http-request="uploadFile"
:show-file-list="showFileList"
multiple
:accept="acceptText"
:on-exceed="handleExceed"
:before-upload="beforeUpload"
:limit="limitNum"
:file-list="fileList"
>
<el-button type="warning" plain icon="el-icon-upload2" size="mini">导入</el-button>
<div slot="tip" class="el-upload__tip">请导入 <b>{{ fileType.join('、') }}</b> 文件</div>
</el-upload>
</div>
导入组件 Import/index.vue JS
import { importFile } from './importFile'
import { queryProgress } from './progress'
export default {
name: 'Import',
data() {
return {
// 导入的文件
file: {},
// 导入成功的文件列表
fileList: [],
// 正在导入状态
isUploading: false,
// 导入进度的百分比数字
percentage: 0,
// 轮询定时器
loopTimer: ''
}
},
props: {
// 导入文件的参数名称
paraName: {
type: String,
default: 'file'
},
// 导入文件的地址
uploadUrl: {
type: String,
required: true
},
// 是否显示文件列表
showFileList: {
type: Boolean,
default: true
},
// 文件类型(小写)
fileType: {
type: Array,
default: ['zip']
},
// 文件大小
fileSize: {
type: Number,
default: 10
},
// 导入文件数量
limitNum: {
type: Number,
default: 20
},
// 按钮权限
btnPermi: {
type: Array
},
// 查询导入进度的地址:若无,表示不加载导入进度
progressUrl: {
type: String
}
},
computed: {
acceptText() {
return this.fileType.map(ele => '.' + ele).join(',')
},
// 上传文件格式(大写)
upperFileType() {
return this.fileType.map((e) => e.toUpperCase());
},
},
methods: {
// 进度条内文字
format(percentage) {
let text = percentage === 100 ? '导入成功' : '导入中'
return percentage + '%\n' + text
},
/* 自定义导入文件 */
async uploadFile(param) {
this.file = param.file
this.isUploading = true
const formData = new FormData()
formData.append(this.paraName, this.file)
if (!this.uploadUrl) { return console.log('uploadUrl 为空') }
const data = await importFile(formData, this.uploadUrl)
if (data.code === 200) {
// 轮询导入进度
if (this.progressUrl) {
await this.loopProgress()
} else {
this.$message.success('上传成功')
this.isUploading = false
this.percentage = 0
this.fileChange(this.file)
this.$emit('refreshList')
}
} else {
this.isUploading = false
this.percentage = 0
this.$message.error('上传失败')
}
},
/* 导入文件改变触发的函数 */
fileChange(file){
this.$refs.upload.clearFiles()
this.fileList = [{name: file.name, zipFile: file}]
},
/* 校验导入文件的数量 */
handleExceed(files, fileList) {
this.$message.warning(`当前限制选择 ${this.limitNum} 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
},
/* 导入文件前校验格式和大小 */
beforeUpload(file) {
const fileSuffix = file.name.substring(file.name.lastIndexOf(".") + 1);
const allFileType = this.fileType.concat(this.upperFileType);
const isType = allFileType.includes(fileSuffix);
const isLimit = file.size / 1024 / 1024 <= this.fileSize;
if (!isType) {
this.$message.error(
`导入文件只能是 ${this.upperFileType.join("/")} 格式`
);
}
if (!isLimit) {
this.$message.error(`导入文件大小不能超过 ${this.fileSize}MB`);
}
return isType && isLimit;
},
/* 查询导入进度 */
async getProgress() {
const data = await queryProgress(this.progressUrl)
if (data.code === 200) {
this.percentage = data.data.percent
} else {
this.$message.error('查询进度失败')
}
},
/* 轮询导入进度 */
loopProgress() {
this.loopTimer = window.setInterval(() => {
setTimeout(async () => {
// 调用接口
this.getProgress()
if (this.percentage === 100) {
clearInterval(this.loopTimer)
this.$message.success('导入成功')
await this.delay(1000)
this.isUploading = false
this.percentage = 0
this.fileChange(this.file)
this.$emit('refreshList')
}
}, 0)
}, 100)
},
/* 延迟 */
delay(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve()
}, num)
})
}
}
}
导入组件 Import/importFile.js
import request from '@/utils/request'
// 导入文件
export const importFile = (data, importUrl) => {
return request({
url: importUrl,
method: 'post',
data,
headers:{"Content-Type" : "multipart/form-data;boundary ="+ new Date().getTime()},
})
}
导入组件 Import/progress.js
export const queryProgress = (progressUrl) => {
return request({
url: progressUrl,
method: 'get'
})
}
总结
- 传递接口地址给组件,提高封装组件的灵活性;
el-upload
组件,采用http-request
自定义上传方式。上传接口是数据格式是multipart/form-data
时,要单独设置Content-Type
,传参也要转为formdata
格式,formData.append(参数名, this.file)
;el-progress
自定义显示数据使用format
属性;- 轮询导入进度:
window.setInterval(() => {setTimeout(() => {// 调用接口}, 0)}, 1000)
。清除定时器:clearInterval(定时器名称)
。
参考链接
本文来自博客园,作者:shayloyuki,转载请注明原文链接:https://www.cnblogs.com/shayloyuki/p/16623692.html
posted on 2022-08-25 11:25 shayloyuki 阅读(117) 评论(0) 编辑 收藏 举报