一、用户需求
安卓和ios,选择文件(excel、pdf、word等)并且上传
二、技术调查
使用dcloud插件市场里的【文件选择、文件上传组件(图片,视频,文件等)】插件
插件使用:
<!-- 也可以下载示例项目查看使用方法 -->
<template>
<view>
<button @click="handleUploadClick">上传</button>
<xe-upload ref="XeUpload" :options="uploadOptions" @callback="handleUploadCallback"></xe-upload>
</view>
</template>
<script>
export default {
data() {
return {
uploadOptions: {
// url: 'http://192.168.31.185:3000/api/upload', // 不传入上传地址则返回本地链接
},
};
},
methods: {
handleUploadClick() {
// 使用默认配置则不需要传入第二个参数
// type: ['image', 'video', 'file'];
this.$refs.XeUpload.upload('file', {});
// this.$refs.XeUpload.upload('image', {
// count: 6,
// sizeType: ['original', 'compressed'],
// sourceType: ['album'],
// });
},
handleUploadCallback(e) {
// e.type: ['choose', 'success', 'warning']
// choose 是options没有传入url,返回临时链接时触发
// success 是上传成功返回对应的数据时触发
// warning 上传或者选择文件失败触发
// ......
},
},
};
</script>
三、效果图
ios端
安卓端
四、代码实现
<template>
<view>
<my-row v-if="label" type="detailTitle" :title="label" :required="required" :class="labelClass"></my-row>
<view v-if="showTip" class="submit-form-tip mb-20" :class="tipClass">
<view>{{ uploadFileTipPatternText }}</view>
<view>{{ uploadFileTipSizeText }}</view>
</view>
<view class="my-upload-file mb-20" :class="fileBtnClass" @click="chooseFile">
<image class="icon-size-32 mr-10" src="../../static/common/upload-outlined.png" mode=""></image>
<text>上传文件</text>
</view>
<template v-if="isShowFileList">
<template v-if="bindValue.length">
<my-row type="4" :value="bindValue" :file-delete-icon="true" @fileDelete="fileDelete"></my-row>
</template>
</template>
<xe-upload ref="XeUpload" :options="uploadOptions" @callback="handleUploadCallback"></xe-upload>
</view>
</template>
<script>
import {
fileAccept
} from '@/utils/commonFun.js'
import {
batchUpload
} from '@/api/common.js'
export default {
name: 'MyUploadFile',
props: {
value: {
type: Array,
default: () => []
},
required: {
type: Boolean,
default: false
},
// label
label: {
type: String,
default: ''
},
// label类名
labelClass: {
type: String,
default: ''
},
// 是否显示提示
showTip: {
type: Boolean,
default: true
},
// 文件格式提示
uploadFileTipPattern: {
type: String,
default: ''
},
// 文件限制提示
uploadFileTipSize: {
type: String,
default: ''
},
// 文件格式
accept: {
type: Array,
default: () => ([])
},
// 提示类名
tipClass: {
type: String,
default: ''
},
// 是否显示文件列表
isShowFileList: {
type: Boolean,
default: true
},
// 最大上传数量
maxCount: [String, Number],
// 文件大小
fileSize: {
type: Number,
default: 20
},
fileBtnClass: {
type: String,
default: 'file-btn-wieth'
},
type: {
default: 'file', // image, video, file
type: String
}
},
computed: {
bindValue: {
get() {
return this.value
},
set(val) {
this.$emit('input', val)
this.$emit('change', val)
}
},
uploadFileTipPatternText() {
if (this.uploadFileTipPattern) {
return this.uploadFileTipPattern
}
return `请上传"word/excel/pdf/图片/压缩包/文本"等格式文件`
},
uploadFileTipSizeText() {
if (this.uploadFileTipSize) {
return this.uploadFileTipSize
}
let text = `单个文件不超过${this.fileSize}MB`
if (this.maxCount) {
text += `,最多上传${this.maxCount}个`
}
return text
},
uploadFileAccept() {
if (this.accept.length > 0) {
return this.accept
}
return fileAccept
}
},
data() {
return {
// uploadOptions 参数跟uni.uploadFile的参数是一样的(除了类型为Function的属性)
uploadOptions: {}
}
},
methods: {
/**
* 选择文件
*/
chooseFile() {
// App、H5 文件拓展名过滤 { extension: ['.doc', '.docx'] } 或者 { extension: '.doc, .docx' }
this.$refs.XeUpload.upload(this.type)
},
/**
* 选择文件 回调
*/
handleUploadCallback(e) {
if (['choose', 'success'].includes(e.type)) {
const tmpFiles = (e.data || []).map(({ name, size, tempFilePath, fileType }) => {
return {
name: name,
size: size,
type: fileType,
path: tempFilePath
}
})
this.fileUpload(tmpFiles)
}
},
/**
* 文件上传
* @param {Object} files
*/
fileUpload(files) {
let fileSuffixFlag = false
let isFileSizeFlag = false
// 判断数量
if (this.maxCount !== undefined) {
if (this.bindValue.length === 0 && files.length > Number(this.maxCount)) {
uni.$u.toast(`最多上传${this.maxCount}个`)
return
}
if (this.bindValue.length > 0 && this.bindValue.length >= Number(this.maxCount)) {
uni.$u.toast(`最多上传${this.maxCount}个`)
return
}
}
files.forEach(item => {
let fileSuffix = item.name.substring(item.name.lastIndexOf('.')).toLowerCase()
let isFileSize = item.size / 1024 / 1024 < this.fileSize
// 判断格式
if (this.uploadFileAccept.indexOf(fileSuffix) === -1) {
fileSuffixFlag = true
}
// 判断大小
if (!isFileSize) {
isFileSizeFlag = true
}
})
// 判断格式
if (fileSuffixFlag) {
uni.$u.toast(`请上传“${this.uploadFileAccept.join('/').replace(/./g, '')}”等格式文件`)
return
}
// 判断大小
if (isFileSizeFlag) {
uni.$u.toast(`文件大小不能超过 ${this.fileSize}MB!`)
return
}
uni.showLoading({
title: '上传中'
})
let temp = []
files.forEach((item, index) => {
temp.push({
name: 'files' + index,
uri: item.path
})
})
batchUpload(temp).then(res => {
res.forEach(item => {
this.bindValue.push(item)
})
}).finally(_ => {
uni.hideLoading()
})
},
/**
* 文件删除
* @param {Object} data
*/
fileDelete(data) {
this.bindValue.splice(data.index, 1)
}
}
}
</script>
<style lang="scss" scoped>
.my-upload-file {
height: 116rpx;
border-radius: 20rpx;
border: 2rpx solid #FFFFFF;
display: flex;
align-items: center;
justify-content: center;
font-weight: 500;
color: #005AFF;
box-sizing: border-box;
&.file-btn-wieth {
background: rgba(255, 255, 255, 0.3);
}
&.file-btn-blue {
background: #EDF3FF;
}
}
</style>
// 图片格式:jpg/jpeg/png/gif/bmp
export const imgAccept = ['.jpg', '.jpeg', '.png', '.gif', '.bmp']
// office格式:doc/docx/xls/xlsx/xlsm/pdf/ppt
export const officeAccept = ['.doc', '.docx', '.xls', '.xlsx', '.xlsm', '.pdf', '.ppt']
// zip格式:zip/tar/rar/7z
export const zipAccept = ['.zip', '.tar', '.rar', '.7z']
// txt格式:txt
export const txtAccept = ['.txt']
// 文件全格式:word/excel/pdf/图片/压缩包/文本
export const fileAccept = [
...officeAccept,
...imgAccept,
...zipAccept,
...txtAccept
]