上传组件的封装
<template>
<div class="upload-file">
<el-upload multiple :action="uploadFileUrl" :data="fileData" :before-upload="handleBeforeUpload"
:file-list="fileList" :limit="limit" :on-error="handleUploadError" :on-exceed="handleExceed"
:on-success="handleUploadSuccess" :on-progress="handleProgress" :show-file-list="false" :headers="headers"
class="upload-file-uploader" ref="upload">
<!-- 上传按钮 -->
<slot>
<el-button type="primary">选取文件</el-button>
</slot>
</el-upload>
<!-- 上传提示 -->
<div class="el-upload__tip" v-if="showTip">
请上传
<template v-if="fileSize">
大小不超过 <b style="color: #f56c6c">{{ fileSize }}MB</b>
</template>
<template v-if="fileType">
格式为 <b style="color: #f56c6c">{{ fileType.join("/") }}</b>
</template>
的文件
</div>
<!-- 文件列表 -->
<transition-group class="upload-file-list el-upload-list el-upload-list--text" name="el-fade-in-linear" tag="ul" v-if="showFileList">
<li :key="file.uid" class="el-upload-list__item ele-upload-list__item-content" v-for="(file, index) in fileList">
<el-link :href="`${baseUrl}${file.url}`" :underline="false" target="_blank">
<span class="document">
{{ getFileName(file.name) }}
</span>
</el-link>
<div class="ele-upload-list__item-content-action">
<el-link :underline="false" @click="handleDelete(index)" type="danger">删除</el-link>
</div>
</li>
</transition-group>
<div class="flex flex-direction justify-center align-center" v-if="upLoading"
style="position: fixed;top: 0;bottom: 0;left: 0;right: 0;height: 100vh;width: 100vw;z-index: 9999999;background-color: rgba(0,0,0,0.8);">
<div v-loading="upLoading" element-loading-text="" element-loading-background="rgba(0, 0, 0, 0)"
style="width: 10px;height: 10px;"></div>
<span style="font-size: 16px;color: var(--el-color-primary);margin-top: 40px;">上传进度:{{ upProgress }}%</span>
</div>
</div>
</template>
<script setup>
import { genFileId ,ElMessage } from 'element-plus'
import { computed, getCurrentInstance, reactive, ref, watch } from "vue";
import { API_BASE_URL, FILE_FULL_PATH } from '@/config/setting.js'
const props = defineProps({
modelValue: [String, Object, Array],
limit: {
type: Number,
default: 0,
},
fileSize: {
type: Number,
default: 0,
},
fileType: {
type: Array,
default: ['pdf','png','jpg'],
},
// 是否显示提示
isShowTip: {
type: Boolean,
default: false,
},
// 超出上传数量时,是否覆盖继续上传
isExceed: {
type: Boolean,
default: false,
},
showFileList: {
type: Boolean,
default: true,
},
//定义上传附件要传递的字段
source: {
type: String,
default: "",
}
});
const emit = defineEmits(['update:modelValue', 'uploadSuccess']);
const upload = ref()
const num = ref(0);
const uploadList = ref([]);
// 文件访问地址
const baseUrl = FILE_FULL_PATH;
// 上传接口地址
const uploadFileUrl = API_BASE_URL + "api/sys/upload";
// 请求头
const headers = { Authorization: "Bearer " };
const fileList = ref();
const upLoading = ref(false)
const upProgress = ref(0)
const showTip = computed(
() => props.isShowTip && (props.fileType || props.fileSize)
);
const fileData = reactive({
'source': props.source
})
watch(
() => props.modelValue,
(val) => {
if (val) {
let temp = 1;
// 首先将值转为数组
const list = Array.isArray(val)
? val
: (props.modelValue?.toString().split(","));
// 然后将数组转为对象数组
fileList.value = list.map((item) => {
if (typeof item === "string") {
item = { name: item, url: item };
}
item.uid = item.uid || new Date().getTime() + temp++;
return item;
});
} else {
fileList.value = [];
return [];
}
},
{ deep: true, immediate: true }
);
// 上传前校检格式和大小
const handleBeforeUpload = (file) => {
// 校检文件类型
if (props.fileType.length) {
let fileExtension = "";
if (file.name.lastIndexOf(".") > -1) {
fileExtension = file.name.slice(file.name.lastIndexOf(".") + 1);
}
const isTypeOk = props.fileType.some((type) => {
if (file.type.indexOf(type) > -1) return true;
if (fileExtension && fileExtension.indexOf(type) > -1) return true;
return false;
});
if (!isTypeOk) {
ElMessage.error(
`文件格式不正确, 请上传${props.fileType.join("/")}格式文件!`
);
return false;
}
}
// 校检文件大小
if (props.fileSize) {
const isLt = file.size / 1024 / 1024 < props.fileSize;
if (!isLt) {
ElMessage.error(`上传文件大小不能超过 ${props.fileSize} MB!`);
return false;
}
}
// ElMessage.loading("正在上传文件,请稍候...");
upLoading.value = true;
num.value++;
return true;
};
// 文件个数超出
const handleExceed = (files) => {
if (props.isExceed) {
upload.value.clearFiles()
const file = files[0]
file.uid = genFileId()
upload.value.handleStart(file)
upload.value.submit()
} else {
ElMessage.error(`上传文件数量不能超过 ${props.limit} 个!`);
}
};
// 上传失败
const handleUploadError = (err) => {
upload.value = false
ElMessage.error("上传文件失败");
};
// 上传成功回调
const handleUploadSuccess = (res, file) => {
if (res.code === 200) {
upLoading.value = false
//注意下面的数据返回格式 res.data即文件名
uploadList.value.push({ name: res.data, url: res.data });
if (uploadList.value.length === num.value) {
fileList.value = fileList.value
.filter((f) => f.url !== undefined)
.concat(uploadList.value);
uploadList.value = [];
num.value = 0;
emit("update:modelValue", listToString(fileList.value));
emit("uploadSuccess");
}
} else {
ElMessage.error(res.message);
}
};
// 上传进度
const handleProgress = (evt, uploadFile, uploadFiles) => {
upProgress.value = Math.round((evt.percent * 100)) / 100
}
// 删除文件
const handleDelete = (index) => {
fileList.value.splice(index, 1);
emit("update:modelValue", listToString(fileList.value));
};
// 获取文件名称
const getFileName = (name) => {
if (!name) {
console.log('getFileName\'s name is null')
return
}
//console.log('name', name);
if (name.lastIndexOf("/") > -1) {
return name.slice(name.lastIndexOf("/") + 1);
} else {
return name;
}
};
// 对象转成指定字符串分隔
const listToString = (list, separator) => {
let strs = "";
separator = separator || ",";
for (let i in list) {
if (undefined !== list[i].url) {
strs += list[i].url + separator;
}
}
return strs !== "" ? strs.substring(0, strs.length - 1) : "";
};
</script>
<style scoped lang="scss">
.upload-file-uploader {
margin-bottom: 5px;
}
.upload-file-list .el-upload-list__item {
padding: 20px 0;
border: 1px solid #e4e7ed;
line-height: 2;
margin-bottom: 10px;
position: relative;
}
.upload-file-list .ele-upload-list__item-content {
display: flex;
justify-content: space-between;
align-items: center;
color: inherit;
padding: 2px 12px;
}
.ele-upload-list__item-content-action .el-link {
margin-left: 16px;
}
</style>
调用
<el-form-item label="上传图片" prop="thum">
<!--图片上传组件-->
<UploadImage
v-model="form.thum"
:fileType="['png','jpg','jpeg','bmp']"
:isShowTip="true"
:source="'sys_sys_upload_release'" >
<el-button>+ 上传</el-button>
</UploadImage>
</el-form-item>