xe-upload地址:文件选择、文件上传组件(图片,视频,文件等) - DCloud 插件市场
致敬开发者!!!
感觉好用的话,给xe-upload的作者一个好评
背景:开发中经常会有上传附件的情况,但是找了一圈没有满意的组件,无意间看到xe-upload,满足我需要上传图片和视频的需求,直接使用示例项目开始二次开发my-file-upload。
修改内容:
1.文件的点击事件
2.对video类型文件的处理
3.根据文件名称创建file文件内容
javascript
<template>
<view>
<view class="upload-wrap">
<view class="cu-form-group1">
<view class="label">{{label}}:</view>
<view class="btn-click mgb-16 upload-btn" @click="handleUploadClick" v-show="!disabled">
<image :src="icons.upload" mode="aspectFill" class="upload-icon" />
<text class="upload-text">上传{{ label }}</text>
</view>
</view>
<view class="mgb-16 file-wrap" v-for="(item, index) in fileList" :key="index">
<view class="btn-click file-line" @click="handlePreview(item)">
<!-- <view class="btn-click file-line" @click="handleUploadFile(item)"> -->
<view class="file-info">
<image :src="icons[item.fileType || 'file']" mode="aspectFill" class="file-icon" />
<text class="file-name">{{ item.name || title[type] }}</text>
</view>
<image :src="icons.close" mode="aspectFill" class="file-icon"
@click.stop="handleDeleteFile(index)" />
</view>
</view>
<view class="mgb-16 file-wrap" v-if="fileList.length === 0 && disabled">
<view class="file-line">
<text class="file-empty">暂无数据</text>
</view>
</view>
<view>
<video id="myVideo" :src="videoUrl" autoplay loop muted objectFit="cover" v-if="showVideo"></video>
</view>
</view>
<xe-upload ref="XeUpload" :options="uploadOptions" @callback="handleUploadCallback"></xe-upload>
</view>
</template>
<script>
export default {
name: 'MyFileUpload',
components: {},
props: {
type: {
default: 'image', // image, video, file
type: String,
},
list: {
default: () => ([]),
type: Array,
},
// disabled: {
// default: false,
// type: Boolean,
// },
value: {
type: String, // 或者是 File[],取决于你的需求
default: null
},
maxFile: {
type: Number, //最大上传数量
default: 1
},
label: {
type: String, // 或者是 File[],取决于你的需求
default: '附件'
},
},
data() {
return {
// uploadOptions 参数跟uni.uploadFile的参数是一样的(除了类型为Function的属性)
uploadOptions: {
// url: 'http://192.168.31.185:3000/api/upload', // 不传入上传地址则返回本地链接
},
uploadUrl: "/sys/common/upload",
staticUrl: "/sys/common/static/",
fileList: [],
title: {
image: '图片',
video: '视频',
file: '文件',
},
icons: {
upload: '/static/xeUpload/icon_upload.png',
close: '/static/xeUpload/icon_close.png',
image: '/static/xeUpload/icon_image.png',
video: '/static/xeUpload/icon_video.png',
file: '/static/xeUpload/icon_file.png',
},
disabled: false,
showVideo: false,
videoUrl: ''
};
},
watch: {
value: {
async handler(val) {
if (val && val !== null && val !== undefined) {
const url = this.$config.apiUrl + this.staticUrl + val;
const file = this.urlToFile(url);
this.fileList = [file];
if (this.fileList.length === this.maxFile) {
this.disabled = true;
}
}
},
immediate: true,
deep: true,
},
},
onLoad: function() {
this.videoContext = uni.createVideoContext('myVideo', this)
},
methods: {
handleUploadClick() {
// 使用默认配置则不需要传入第二个参数
// App、H5 文件拓展名过滤 { extension: ['.doc', '.docx'] } 或者 { extension: '.doc, .docx' }
this.$refs.XeUpload.upload(this.type);
// 可以根据当前的平台,传入选择文件的参数,例如
// 注意 当chooseMedia可用时,会优先使用chooseMedia
// // uni.chooseImage
// this.$refs.XeUpload.upload(type, {
// count: 6,
// sizeType: ['original', 'compressed'],
// sourceType: ['album'],
// });
// // uni.chooseVideo
// this.$refs.XeUpload.upload(type, {
// sourceType: ['camera', 'album'],
// });
// // uni.chooseMedia (微信小程序2.10.0+;抖音小程序、飞书小程序;京东小程序支持)
// this.$refs.XeUpload.upload(type, {
// count: 9,
// sourceType: ['album', 'camera'],
// });
},
handleUploadCallback(e) {
console.log('UploadCallback', e);
if (['choose', 'success'].includes(e.type)) {
// 根据接口返回修改对应的response相关的逻辑
const tmpFiles = (e.data || []).map(({
response,
tempFilePath,
name,
fileType
}) => {
// 当前测试服务返回的数据结构如下
// {
// "result": {
// "fileName": "fileName",
// "filePath": `http://192.168.1.121:3000/static/xxxxx.png`,
// },
// "success": true,
// }
const res = response?.result || {};
const tmpUrl = res.filePath ?? tempFilePath;
const tmpName = res.fileName ?? name;
return {
...res,
url: tmpUrl,
name: tmpName,
fileType,
};
});
this.fileList.push(...tmpFiles);
this.handleUploadFile(e.data[0].tempFilePath);
}
},
// 自定义上传
handleUploadFile(url) {
var that = this;
console.log('UploadFile', url);
if (url != undefined) {
that.$http.upload(that.$config.apiUrl + that.uploadUrl + '?biz=temp', {
filePath: url,
name: 'file',
}).then(res => {
console.log('handleUpload success', res);
//回传至表单
that.$emit('input', res.data.message);
const tmpData = JSON.parse(res.data);
uni.showToast({
title: tmpData.success ? '上传成功' : '上传失败',
icon: 'none'
});
})
}
},
// 预览
handlePreview(item) {
console.log('PreviewFile', item);
const fileType = this.getFileType(item.name);
const url = item.url;
if (fileType === 'image') {
return uni.previewImage({
current: url,
urls: [url],
});
}else if (fileType === 'video') {
this.videoUrl = url;
this.showVideo = !this.showVideo;
//全屏显示
// this.videoContext.requestFullScreen();
}
// #ifndef H5
else if (fileType === 'office') {
return uni.openDocument({
filePath: url,
fail: (err) => {
console.log(err);
uni.showToast({
icon: 'none',
title: '文件预览失败'
});
},
});
}
// #endif
else{
uni.showModal({
title: '提示',
content: url,
showCancel: false,
});
}
},
handleDeleteFile(index) {
this.fileList.splice(index, 1);
if (this.fileList.length < this.maxFile) {
this.disabled = false;
}
},
urlToFile(url) {
// 获取URL的最后一部分
const lastPart = url.split('/').pop();
// 获取文件名(不包括扩展名)
const filenameWithoutExtension = lastPart.split('_').slice(0, -1).join('_');
// 获取文件扩展名
const extension = lastPart.split('.').pop();
// 组合文件名和扩展名
const filename = filenameWithoutExtension + '.' + extension;
const fileType = this.getFileType(url);
const file = {
"fileName": filename,
"fileKey": lastPart,
"filePath": url,
"url": url,
"name": filename,
"fileType": fileType
}
return file;
},
/**
* 获取文件类型
* @param {String} fileName 文件链接
* @returns {String} fileType => '', image, video, audio, office, unknown
*/
getFileType(fileName = '') {
const fileType = fileName.split('.').pop();
// let suffix = flieArr[flieArr.length - 1];
// if (!suffix) return '';
// suffix = suffix.toLocaleLowerCase();
const image = ['png', 'jpg', 'jpeg', 'bmp', 'gif', 'webp'];
if (image.includes(fileType)) return 'image';
const video = ['mp4', 'm4v'];
if (video.includes(fileType)) return 'video';
const audio = ['mp3', 'm4a', 'wav', 'aac'];
if (audio.includes(fileType)) return 'audio';
const office = ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'plain'];
if (office.includes(fileType)) return 'office';
return 'unknown';
},
},
};
</script>
<style lang="scss" scoped>
view {
box-sizing: border-box;
}
.label{
text-align: justify;
padding-right: 15px;
white-space: nowrap;
font-size: 15px;
position: relative;
height: 30px;
line-height: 30px;
}
.cu-form-group1 {
background-color: #ffffff;
padding: 1px 15px 1px 0px;
display: flex;
align-items: center;
min-height: 50px;
// justify-content: space-between;
}
.btn-click {
transition: all 0.3s;
opacity: 1;
}
.btn-click:active {
opacity: 0.5;
}
.mgb-16 {
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
}
.upload-wrap {
width: 100%;
border-radius: 16rpx;
background: white;
padding: 32rpx;
.upload-btn {
width: 60%;
height: 120rpx;
border: 2rpx dashed #AAAAAA;
background: #FAFAFA;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
.upload-icon {
width: 48rpx;
height: 48rpx;
margin-bottom: 8rpx;
}
.upload-text {
font-size: 26rpx;
color: #9E9E9E;
line-height: 40rpx;
}
}
.file-wrap {
.file-line {
width: 100%;
background: #F5F5F5;
border-radius: 8rpx;
padding: 16rpx;
font-size: 26rpx;
color: #1A1A1A;
line-height: 40rpx;
display: flex;
align-items: center;
justify-content: space-between;
.file-info {
width: 90%;
display: flex;
align-items: center;
.file-name {
max-width: 80%;
padding-left: 16rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.file-icon {
width: 40rpx;
height: 40rpx;
flex-shrink: 0;
}
.file-empty {
color: #999999;
}
}
}
}
</style>
父组件中引入使用
javascript
<my-file-upload type="file" v-model="model.attachment" label="附件"></my-file-upload>
import myFileUpload from '@/components/my-componets/my-file-upload.vue';
export default {
name: "Test",
components:{ myFileUpload },
props:{
formData:{
type:Object,
default:()=>{},
required:false
}
},
data(){
return {
model: {},
}
},
onLoad: function (option) {
}
},
created(){
},
methods:{
}
}