
详细代码见代码的折叠代码
与这个的不同之处是上传时用的方法不一样:https://www.cnblogs.com/duocaishijie/p/18789861
点击查看代码
<template>
<div class="upload-container1">
<!-- 如果readOnly为false且multipleFlag为true或者multipleFlag为false且fileList的长度为0,则显示el-upload组件 -->
<el-upload v-if="!readOnly && (multipleFlag || !multipleFlag && fileList.length == 0)" :disabled="readOnly"
class="avatar1-uploader1" :limit="limit" :multiple="multipleFlag"
:accept="'.png,.jpg,.jpeg,.doc,.docx,.txt,.xls,.xlsx,.pdf'" element-loading-text="正在上传"
:show-file-list="false" ref="imageUp" :http-request="doUpload" :before-upload="beforeUpload">
<!-- 设置上传按钮的样式 -->
<el-icon class="avatar1-uploader1-icon">
<Plus />
</el-icon>
</el-upload>
<!-- 修改预览区域 -->
<div v-if="fileList.length" class="previews-container">
<!-- 单/多文件预览模式 -->
<div v-for="(file, index) in fileList" :key="index" class="multi-preview-item">
<div @click="previewImage(file)">
<img v-if="isImage(file)" :src="file.url" class="avatar1" />
<!-- 如果是图片,则显示图片 -->
<div v-else class="file-preview">
<!-- 否则显示文件预览 -->
<el-icon class="document-icon">
<component :is="file.icon" />
</el-icon>
<!-- 显示文件图标 -->
<span class="filename">{{ file.name }}</span>
<!-- 显示文件名 -->
</div>
</div>
<div class="down-file">
<el-button type="primary" @click="handleDownload(file)">下载</el-button>
<!-- 点击下载文件 -->
</div>
<el-icon v-if="!readOnly" class="close-icon" @click.stop="removeFile(index)">
<Close />
</el-icon>
<!-- 如果不是只读模式,则显示关闭按钮 -->
</div>
</div>
<el-dialog v-model="previewVisible" width="60%" title="预览">
<div class="preview-image">
<img v-if="isImage" :src="previewImageUrl" />
</div>
</el-dialog>
</div>
</template>
<script setup>
import api from '@/utils/interface'
import {
Document,
Plus,
Close
} from '@element-plus/icons-vue'
import { ref, computed, markRaw } from 'vue';
import { staticUrl } from '@/utils/function'
import CryptoJS from "crypto-js"
const props = defineProps({
// 图片上传的url
url: {
type: String,
default: ''
},
// 是否只读
readOnly: {
type: Boolean,
default: false
},
// 是否支持多选
multipleFlag: {
type: Boolean,
default: false
},
// 限制上传数量
limit: {
type: Number,
default: 1
},
// 媒体名称
mediaName: {
type: String,
default: ''
}
})
// 判断props.url是否为空,如果为空,则kongflag为true,否则为false
let kongflag = props.url == '' ? true : false
// 定义一个空数组
let defaultArray = []
// 如果kongflag为false,则执行以下代码
if (!kongflag) {
// 判断props.url的后缀是否为.doc,.docx,.txt,.xls,.xlsx,.pdf中的一个,如果是,则extFlag为true,否则为false
let extFlag = '.doc,.docx,.txt,.xls,.xlsx,.pdf'.includes(props.url.split('.').pop()) ? true : false
// 将props.url按逗号分隔,得到一个数组
let urlArrary = props.url.split(',')
// 遍历urlArrary数组
urlArrary.forEach((item, index) => {
// 将数组中的每个元素添加到defaultArray数组中
defaultArray.push({
name: props.mediaName,
type: props.url.split('.').pop(),
url: item,
icon: extFlag ? markRaw(Document) : ''
})
})
}
// 修改为数组存储多个文件
const fileList = ref(Array.isArray(props.url) ? props.url : kongflag ? [] : defaultArray)
let multipleFlag = ref(props.multipleFlag)
let loading = ref(false)
const imageUp = ref();
const emit = defineEmits(["closeInvoice"]);
// 新增响应式数据和计算属性
const isImage = (file) => ['png', 'jpg', 'jpeg'].includes(file.type)
const previewVisible = ref(false)
const previewImageUrl = ref('')
// 预览图片
const previewImage = (file) => {
// 判断文件是否为图片
if (!isImage(file)) {
return
}
// 设置预览图片的url
previewImageUrl.value = file.url
// 设置预览图片的可见性为true
previewVisible.value = true
}
// 新增删除文件方法
const removeFile = (index) => {
fileList.value.splice(index, 1)
emit("closeInvoice", fileList.value)
}
const handleDownload = (item) => {
// 图片文件直接下载
// 创建一个a标签
const link = document.createElement('a')
// 设置a标签的href属性为item的url
link.href = item.url;
// 设置a标签的download属性为item的name
link.setAttribute('download', item.name)
// 将a标签添加到body中
document.body.appendChild(link)
// 模拟点击a标签
link.click()
document.body.removeChild(link)
}
function doUpload(option) {
// 设置loading状态为true
loading.value = true
// 获取文件
const file = option.file;
// 创建FileReader对象
let reader = new FileReader();
// 文件读取完成后的回调函数
reader.onload = (e) => {
// 获取文件内容
let fileContent = e.target.result;
// 将文件内容转换为WordArray对象
let wordArray = CryptoJS.lib.WordArray.create(fileContent);
// 计算文件的MD5哈希值
let md5Hash = CryptoJS.MD5(wordArray).toString();
// 获取文件扩展名
let ext = file.name.split(".").pop();
// 生成新的文件名
let newFileName = `${md5Hash}.${ext}`;
// 创建FormData对象
const formData = new FormData();
// 将文件名和文件添加到FormData对象中
formData.append("filename", newFileName);
formData.append("file", file);
// 调用api.upload方法上传文件
api.upload(formData, "/base/oss/upload").then((res) => {
// 判断文件扩展名是否为.doc,.docx,.txt,.xls,.xlsx,.pdf
let extFlag = '.doc,.docx,.txt,.xls,.xlsx,.pdf'.includes(ext) ? true : false
// 创建新的文件对象
const newFile = {
name: file.name,
type: ext,
url: staticUrl(newFileName),
icon: extFlag ? markRaw(Document) : ''
}
// 将新的文件对象添加到文件列表中
fileList.value.push(newFile)
// 清空文件上传组件中的文件
imageUp.value.clearFiles();
emit("closeInvoice", fileList.value) // 返回整个文件列表
loading.value = false
}).catch(err => {
console.log(err)
})
};
reader.readAsArrayBuffer(file);
}
function beforeUpload(file) {
const allowedTypes = ['png', 'jpg', 'jpeg', 'doc', 'docx', 'txt', 'xls', 'xlsx', 'pdf']
const fileType = file.name.split('.').pop().toLowerCase()
const isValidType = allowedTypes.includes(fileType)
if (!isValidType) {
ElMessage.error('仅支持上传:' + allowedTypes.join(', '))
return false
}
const isLt2M = file.size / 1024 / 1024 < 20;
if (!isLt2M) {
ElMessage.error('上传图片大小不能超过 20MB!');
}
return isLt2M;
}
</script>
<style scoped>
.down-file {
display: flex;
justify-content: center;
margin-top: 5px;
}
.preview-image {
width: 100%;
height: 70vh;
display: flex;
justify-content: center;
align-items: center;
overflow: auto;
}
.preview-image img {
max-width: 100%;
max-height: 100%;
object-fit: contain;
border-radius: 4px;
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
}
.upload-container1 {
margin-left: 35px;
width: 170px;
min-height: 170px;
}
.avatar1 {
width: 100%;
height: 135px;
display: block;
object-fit: contain;
}
:deep(.avatar1-uploader1 .el-upload) {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
:deep(.el-icon.avatar1-uploader1-icon) {
font-size: 28px;
color: #8c939d;
width: 150px;
height: 150px;
text-align: center;
}
.file-preview {
display: flex;
margin-top: 10px;
height: 125px;
flex-direction: column;
align-items: center;
padding: 10px;
}
.document-icon {
font-size: 60px;
color: #409eff;
margin-bottom: 8px;
}
.filename {
font-size: 12px;
color: #666;
word-break: break-all;
text-align: center;
max-width: 140px;
}
/* 新增多文件预览样式 */
.previews-container {
display: flex;
flex-wrap: wrap;
gap: 10px;
width: 586px;
background: #f5f7fa;
padding: 10px;
margin-top: 10px;
}
.multi-preview-item {
position: relative;
width: 180px;
border: 1px solid #eee;
border-radius: 4px;
padding: 6px 0;
background: #e1e1e1;
&:hover .close-icon {
display: block;
}
}
.thumb-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.file-thumb {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
.close-icon {
position: absolute;
width: 25px;
height: 25px;
line-height: 25px;
text-align: center;
right: 4px;
top: 4px;
color: red;
font-size: 24px;
background: white;
border-radius: 50%;
display: none;
cursor: pointer;
}
.document-icon-small {
font-size: 32px;
}
</style>
浙公网安备 33010602011771号