详细代码见代码的折叠代码

与这个的不同之处是上传时用的方法不一样: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>
posted on 2025-03-24 17:57  好久不见-库克  阅读(113)  评论(0)    收藏  举报