当前位置: 首页 > article >正文

HTML使用 Vue 3 和 Element Plus 实现图片上传功能

在现代 Web 开发中,图片上传功能是一个非常常见的需求。本文将介绍如何使用 Vue 3 和 Element Plus 实现一个功能丰富的图片上传组件。我们将涵盖文件选择、图片预览、文件大小和类型验证、批量上传、以及错误处理等功能。

1. 项目结构

首先,我们需要引入 Vue 3 和 Element Plus 的相关依赖。以下是所有代码:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>图片上传</title>
    <link rel="stylesheet" href="https://unpkg.com/element-plus/dist/index.css">
    <style>
        body {
            margin: 0;
            padding: 0;
            background-color: #f0f2f5;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
            min-height: 100vh;
        }

        .upload-container {
            padding: 24px;
            background: #fff;
            border-radius: 16px;
            margin: 16px;
            box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05);
        }

        .upload-header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 24px;
            padding-bottom: 16px;
            border-bottom: 1px solid #f0f0f0;
        }

        .upload-title {
            margin: 0;
            color: #1a1a1a;
            font-size: 20px;
            font-weight: 600;
        }

        .upload-area {
            margin-bottom: 24px;
        }

        .button-group {
            display: flex;
            gap: 12px;
            margin-top: 24px;
        }

        .upload-btn {
            flex: 1;
            height: 40px;
            font-size: 15px;
        }

        .clear-btn {
            height: 40px;
            font-size: 15px;
        }

        /* 调整上传组件样式 */
        :deep(.el-upload--picture-card) {
            --el-upload-picture-card-size: 90px;
            border-radius: 8px;
            border: 1px dashed #d9d9d9;
            transition: all 0.3s;
        }

        :deep(.el-upload--picture-card:hover) {
            border-color: #409eff;
        }

        :deep(.el-upload-list--picture-card) {
            --el-upload-list-picture-card-size: 90px;
        }

        :deep(.el-upload-list__item) {
            border-radius: 8px;
        }

        :deep(.el-upload-list__item-thumbnail) {
            width: 100%;
            height: 100%;
            object-fit: cover;
            border-radius: 8px;
        }

        .upload-tip {
            margin-top: 8px;
            color: #909399;
            font-size: 13px;
        }

        .file-count {
            color: #606266;
            font-size: 14px;
        }
		
		 /* 添加删除按钮样式 */
        .el-upload-list__item-delete {
			font-size: 16px;
			color: #fff;
			background-color: #ff4d4f;
			border-radius: 50%;
			padding: 4px;
			cursor: pointer;
			z-index: 9999;
			position: absolute;
			right: 0;
			top: 0;
			display: block;
		}



        /* 添加图片错误样式 */
        .upload-error-item {
            position: relative;
            background-color: #fef0f0;
            border: 1px solid #fde2e2;
        }

        .upload-error-text {
            position: absolute;
            bottom: 0;
            left: 0;
            right: 0;
            background: rgba(255, 77, 79, 0.8);
            color: #fff;
            font-size: 12px;
            padding: 2px 4px;
            text-align: center;
        }
    </style>
</head>
<body>
    <div id="app">
        <div class="upload-container">
            <div class="upload-header">
                <h2 class="upload-title">图片上传</h2>
                <span class="file-count" v-if="fileList.length > 0">
                    已选择 {{ fileList.length }} 张图片
                </span>
            </div>
            
            <el-upload
                v-model:file-list="fileList"
                class="upload-area"
                action="#"
                :auto-upload="false"
                :on-change="handleFileChange"
                :before-remove="handleBeforeRemove"
                multiple
                accept="image/*"
                :limit="100"
                list-type="picture-card">
                <template #default>
                    <el-icon style="font-size: 24px; color: #909399;"><Plus /></el-icon>
                    <div style="margin-top: 8px; color: #909399;">点击上传</div>
                </template>
                
                <template #file="{ file }">
                    <div :class="{'upload-error-item': file.status === 'error'}">
                        <img 
                            v-if="file.status !== 'error'"
                            class="el-upload-list__item-thumbnail" 
                            :src="file.url" 
                            alt=""
                            @error="handleImageError(file)" 
                        />
                        <div v-else class="upload-error-text">图片加载失败</div>
                        <el-icon 
                            class="el-upload-list__item-delete"
							v-if="file.status !== 'error'"
                            @click.stop="handleRemoveFile(file)">
                            <Close />
                        </el-icon>
                    </div>
                </template>
            </el-upload>

            <p class="upload-tip">支持上传JPG/PNG格式图片,单次最多可上传100张</p>

            <div class="button-group">
                <el-button 
                    type="primary" 
                    class="upload-btn"
                    :disabled="fileList.length === 0"
                    @click="handleUpload">
                    <el-icon style="margin-right: 4px"><Upload /></el-icon>
                    开始上传
                </el-button>
                
                <el-button 
                    type="danger" 
                    class="clear-btn"
                    :disabled="fileList.length === 0"
                    @click="handleClearFiles">
                    <el-icon style="margin-right: 4px"><Delete /></el-icon>
                    清空
                </el-button>
            </div>
        </div>

       <el-loading 
            v-model:visible="loading"
            lock
            text="上传中..."
            background="rgba(255, 255, 255, 0.9)"/>
    </div>

    <!-- 引入依赖 -->
    <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
    <script src="https://unpkg.com/element-plus"></script>
    <script src="https://unpkg.com/@element-plus/icons-vue"></script>
    <script src="https://unpkg.com/axios/dist/axios.min.js"></script>

    <script>
        const { createApp, ref } = Vue
        const { ElMessage, ElMessageBox } = ElementPlus

        const app = createApp({
            setup() {
                // 获取URL参数
                const urlParams = new URLSearchParams(window.location.search)
                const uploadParams = {
                    batchNo: urlParams.get('batchNo') || '',
                    SN: urlParams.get('SN') || ''
                }

                const fileList = ref([])
                const loading = ref(false)


                const handleClearFiles = () => {
                    ElMessageBox.confirm(
                        '确定要清空所有已选择的图片吗?',
                        '提示',
                        {
                            confirmButtonText: '确定',
                            cancelButtonText: '取消',
                            type: 'warning',
                        }
                    ).then(() => {
                        fileList.value = []
                        ElMessage.success('已清空所有图片')
                    }).catch(() => {})
                }

                const handleUpload = async () => {
                    if (fileList.value.length === 0) return

                    try {
                        loading.value = true
                        
                        const uploadPromises = fileList.value.map(file => {
                            const formData = new FormData()
                            formData.append('file', file.raw)
							let url = '你的请求地址'
                            
                            return axios.post(url + uploadParams.batchNo, formData, {
                                headers: {
                                    'Content-Type': 'multipart/form-data',
									'SN': uploadParams.SN
                                }
                            })
                        })
						let flag = true
                        await Promise.all(uploadPromises).then((res) => {
							for (let i in res) {
								if (res[i].data.code !== 1001) {
									flag = false
									 ElMessage({
										type: 'error',
										message: res[i].data.msg,
										duration: 2000
									})
								}
							}
						})
						if (flag) {
							 ElMessage({
								type: 'success',
								message: '上传成功',
								duration: 2000
							})
						}	
                       
                        fileList.value = []
                        
                    } catch (error) {
                        ElMessage({
                            type: 'error',
                            message: '上传失败,请重试',
                            duration: 3000
                        })
                    } finally {
                        loading.value = false
                    }
                }
				
				 // 处理图片加载错误
                const handleImageError = (file) => {
                    file.status = 'error'
                }

                // 处理删除单个文件
                const handleRemoveFile = (file) => {
                    ElMessageBox.confirm(
                        '确定要删除这张图片吗?',
                        '提示',
                        {
                            confirmButtonText: '确定',
                            cancelButtonText: '取消',
                            type: 'warning',
                        }
                    ).then(() => {
                        const index = fileList.value.findIndex(item => item.uid === file.uid)
                        if (index !== -1) {
                            fileList.value.splice(index, 1)
                            ElMessage.success('已删除')
                        }
                    }).catch(() => {})
                }

                // 阻止默认的删除行为
                const handleBeforeRemove = () => {
                    return false
                }

                // 修改文件验证逻辑
                const handleFileChange = (uploadFile) => {
                    // 检查文件类型
                    if (!uploadFile.raw.type.includes('image/')) {
                        ElMessage.error('只能上传图片文件!')
                        const index = fileList.value.findIndex(item => item.uid === uploadFile.uid)
                        if (index !== -1) {
                            fileList.value.splice(index, 1)
                        }
                        return false
                    }
                    // 检查文件大小(限制为5MB)
                    if (uploadFile.raw.size / 1024 / 1024 > 5) {
                        ElMessage.error('图片大小不能超过5MB!')
                        const index = fileList.value.findIndex(item => item.uid === uploadFile.uid)
                        if (index !== -1) {
                            fileList.value.splice(index, 1)
                        }
                        return false
                    }
                    // 验证图片是否可以正常加载
                    const img = new Image()
                    img.src = URL.createObjectURL(uploadFile.raw)
                    img.onerror = () => {
                        ElMessage.error('图片格式错误或已损坏!')
                        const index = fileList.value.findIndex(item => item.uid === uploadFile.uid)
                        if (index !== -1) {
                            fileList.value.splice(index, 1)
                        }
                    }
                }

                return {
                    fileList,
                    loading,
                    handleFileChange,
                    handleBeforeRemove,
                    handleRemoveFile,
                    handleImageError,
                    handleClearFiles,
                    handleUpload
                }
            }
        })

        // 注册Element Plus
        app.use(ElementPlus)
        // 注册需要的图标
        app.component('Plus', ElementPlusIconsVue.Plus)
        app.component('Upload', ElementPlusIconsVue.Upload)
        app.component('Delete', ElementPlusIconsVue.Delete)
        app.component('Close', ElementPlusIconsVue.Close)
        // 挂载应用
        app.mount('#app')
    </script>
</body>
</html>

2. 实现图片上传功能

2.1 初始化 Vue 应用

我们使用 Vue 3 的 createApp 方法来初始化应用,并引入 Element Plus 的组件和图标。

const { createApp, ref } = Vue
const { ElMessage, ElMessageBox } = ElementPlus

const app = createApp({
    setup() {
        // 逻辑部分省略
    }
})

// 注册Element Plus
app.use(ElementPlus)
// 注册需要的图标
app.component('Plus', ElementPlusIconsVue.Plus)
app.component('Upload', ElementPlusIconsVue.Upload)
app.component('Delete', ElementPlusIconsVue.Delete)
app.component('Close', ElementPlusIconsVue.Close)
// 挂载应用
app.mount('#app')

2.2 文件选择与预览

我们使用 Element Plus 的 el-upload 组件来实现文件选择和预览功能。通过 v-model:file-list 绑定文件列表,并使用 list-type="picture-card" 来显示图片预览。

<el-upload
    v-model:file-list="fileList"
    class="upload-area"
    action="#"
    :auto-upload="false"
    :on-change="handleFileChange"
    :before-remove="handleBeforeRemove"
    multiple
    accept="image/*"
    :limit="100"
    list-type="picture-card">
    <template #default>
        <el-icon style="font-size: 24px; color: #909399;"><Plus /></el-icon>
        <div style="margin-top: 8px; color: #909399;">点击上传</div>
    </template>
    
    <template #file="{ file }">
        <div :class="{'upload-error-item': file.status === 'error'}">
            <img 
                v-if="file.status !== 'error'"
                class="el-upload-list__item-thumbnail" 
                :src="file.url" 
                alt=""
                @error="handleImageError(file)" 
            />
            <div v-else class="upload-error-text">图片加载失败</div>
            <el-icon 
                class="el-upload-list__item-delete"
                v-if="file.status !== 'error'"
                @click.stop="handleRemoveFile(file)">
                <Close />
            </el-icon>
        </div>
    </template>
</el-upload>

2.3 文件验证

在上传文件之前,我们需要对文件进行验证,确保文件类型为图片且大小不超过 5MB。

const handleFileChange = (uploadFile) => {
    // 检查文件类型
    if (!uploadFile.raw.type.includes('image/')) {
        ElMessage.error('只能上传图片文件!')
        const index = fileList.value.findIndex(item => item.uid === uploadFile.uid)
        if (index !== -1) {
            fileList.value.splice(index, 1)
        }
        return false
    }
    // 检查文件大小(限制为5MB)
    if (uploadFile.raw.size / 1024 / 1024 > 5) {
        ElMessage.error('图片大小不能超过5MB!')
        const index = fileList.value.findIndex(item => item.uid === uploadFile.uid)
        if (index !== -1) {
            fileList.value.splice(index, 1)
        }
        return false
    }
    // 验证图片是否可以正常加载
    const img = new Image()
    img.src = URL.createObjectURL(uploadFile.raw)
    img.onerror = () => {
        ElMessage.error('图片格式错误或已损坏!')
        const index = fileList.value.findIndex(item => item.uid === uploadFile.uid)
        if (index !== -1) {
            fileList.value.splice(index, 1)
        }
    }
}

2.4 批量上传

我们使用 axios 来发送上传请求,并通过 Promise.all 实现批量上传。

const handleUpload = async () => {
    if (fileList.value.length === 0) return

    try {
        loading.value = true
        
        const uploadPromises = fileList.value.map(file => {
            const formData = new FormData()
            formData.append('file', file.raw)
            let url = '你的请求地址'
            
            return axios.post(url + uploadParams.batchNo, formData, {
                headers: {
                    'Content-Type': 'multipart/form-data',
                    'SN': uploadParams.SN
                }
            })
        })
        let flag = true
        await Promise.all(uploadPromises).then((res) => {
            for (let i in res) {
                if (res[i].data.code !== 1001) {
                    flag = false
                    ElMessage({
                        type: 'error',
                        message: res[i].data.msg,
                        duration: 2000
                    })
                }
            }
        })
        if (flag) {
            ElMessage({
                type: 'success',
                message: '上传成功',
                duration: 2000
            })
        }	
        
        fileList.value = []
        
    } catch (error) {
        ElMessage({
            type: 'error',
            message: '上传失败,请重试',
            duration: 3000
        })
    } finally {
        loading.value = false
    }
}

2.5 错误处理

在上传过程中,可能会遇到各种错误,如网络错误、文件格式错误等。我们通过 ElMessage 组件来显示错误信息。

const handleImageError = (file) => {
    file.status = 'error'
}

3. 总结

通过本文的介绍,我们实现了一个功能丰富的图片上传组件。该组件支持文件选择、图片预览、文件大小和类型验证、批量上传以及错误处理等功能。使用 Vue 3 和 Element Plus 可以大大简化开发过程,提高开发效率。

希望本文对你有所帮助,欢迎在评论区留言讨论!如果对你有帮助,请帮忙点个👍


http://www.kler.cn/a/560278.html

相关文章:

  • 《渗透测试方法论:从信息搜集到报告输出的死亡行军》
  • python学习一
  • Spring Boot 项目启动命令大全:参数详解与高阶用法
  • (六)趣学设计模式 之 代理模式!
  • Hyperledger Fabric 入门笔记(十九)Fabric V2.5 杂项 - 在开发模式下运行链码
  • OpenCV计算摄影学Computational Photography
  • 【嵌入式Linux应用开发基础】网络编程(1):TCP/IP协议栈
  • 【备赛】点亮LED
  • 【信息系统项目管理师-案例真题】2010下半年案例分析答案和详解
  • UE5实现角色二段跳
  • DIP的实际举例
  • 垂类大模型微调(一):认识LLaMA-Factory
  • clickhouse--本地表和分布式表,副本机制,分片集群
  • DeepSeek-R1蒸馏模型与其他模型的区别
  • 【Linux知识】Linux上从源码编译到软件安装全过程详细说明
  • 冒泡排序:简单又易于实现的排序算法
  • 智能生成ER图工具。使用 SQL 生成 ER 图:让数据库设计更高效
  • C/C++高性能Web开发框架全解析:2025技术选型指南
  • 快手弹幕 websocket 分析
  • 用Deepseek直接在word中完成论文的润色(中-中,中-英, 英-中)