Skip to content

ImageUpload图片上传组件

(1)搭建组件

这部分我们简单封装一个图片上传组件,方便我们上传图片

☞ 新建 src => components => ImageUpload => index.vue

简单搭建一个图片上传组件

JS
<template>
	<div>ImageUpload</div>
</template>

(2)注册组件

☞ 在main.ts注册组件

JS
import ImageUpload from '@/components/ImageUpload/index.vue' //组件-图片上传
app.component('ImageUpload', ImageUpload) //组件-图片上传

(3)功能实现

接下来我们先在这里写一个上传头像的功能组件,然后我们再进行优化

  • 实现上传头像

先调用一下我们的上传头像,注意我们上传时候要更改上传的请求头格式

🌤 关于请求headers可以查看这篇文章

JS
// 上传图片
export function uploadImage(data) {
  return request({
    url: '/upload/image', 
    method: 'post',
    data: data,
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  })
}
  • 引入接口
JS
import { uploadImage} from "@/api/common/common";
  • 实现上传功能
JS
<template>
    <div>
        <el-upload class="avatar-uploader" action="#" :show-file-list="false" :before-upload="beforeAvatarUpload" :http-request="uploadPicImg" :on-success="handleAvatarSuccess">
            <img v-if="imageUrl" :src="imageUrl" class="imgavatar" />
            <el-icon v-else class="avatar-uploader-icon">
                <Plus />
            </el-icon>
        </el-upload>
    </div>
</template>
<script setup>
import { ref, reactive, toRefs, onMounted } from 'vue'

import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'

import { uploadImage} from "@/api/common/common";

const imageUrl = ref('')


// 文件上传前的验证
const beforeAvatarUpload = (file) => {
    const isImage = file.type.startsWith('image/');
    const isLt2M = file.size / 1024 / 1024 < 10; // 限制文件大小为10MB
    if (!isImage) {
        this.$message.error('只能上传图片文件!');
    }
    if (!isLt2M) {
        this.$message.error('上传图片大小不能超过 2MB!');
    }
    return isImage && isLt2M;
};


// 上传图片接口
const uploadPicImg = async (query) => {
    // 确保传入的是文件对象
    let file = query.file;
    if (!file) {
        alert('请选择文件');
        return;
    }
    const formData = new FormData();
    formData.append('img', file);
    try {
        // 调用上传图片的API函数
        const res = await uploadImage(formData);
        console.log('上传', res);
        if (res.code === 200) {
            console.log('图片上传成功', res);
            ElMessage.success(res.message);
            imageUrl.value = res.imgurl; // 假设返回的数据包含图片 URL
        } else {
            ElMessage.error(res.message);
            console.error('上传失败', res);
        }
    } catch (error) {
        console.error('上传失败,网络错误', error);
    }
};
</script>
<style>
.imgavatar {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    text-align: center;
    object-fit: cover;
}
</style>

到这里我们就实现了我们上传图片的功能!

(4)组件封装

接下来我们就简单封装一下这个组件,方便我们使用,定义一下传递参数以及提交参数

JS
// Props
const props = defineProps({
  action: {
    type: String,
    required: true
  },
  maxSize: {
    type: Number,
    default: 10 // 默认最大文件大小为10MB
  }
});

// Emits
const emit = defineEmits(['update:imageUrl']);


成功以后提交给父组件
emit('update:imageUrl', res.imgurl); // 将图片URL通过事件传递给父组件
  • 父组件之中使用子组件

父组件使用 UploadImage 组件,我们可以在其中自定义上传接口地址和最大文件大小,然后在子组件成功上传后将图片URL通过事件传递给父组件。

JS
<template>
  <div>
    <h2>头像上传</h2>
    <!-- 使用封装好的 UploadImage 组件 -->
    <UploadImage 
      :action="'/upload/image'" 
      :maxSize="5" 
      @update:imageUrl="handleImageUrlUpdate" />
    <div v-if="imageUrl">
      <p>上传的图片 URL: {{ imageUrl }}</p>
      <img :src="imageUrl" alt="上传的头像" class="uploaded-image" />
    </div>
  </div>
</template>

<script setup>
import { ref } from 'vue'
import UploadImage from '@/components/UploadImage.vue'

const imageUrl = ref('')

// 更新图片URL
const handleImageUrlUpdate = (url) => {
  imageUrl.value = url;
}
</script>

<style scoped>
.uploaded-image {
  width: 150px;
  height: 150px;
  object-fit: cover;
}
</style>

(5)组件优化

但是我们这种组件方法使用起来感觉并不好用,因为我们习惯v-model双向绑定数值,所以我们可以进行一下优化

  • 更新组件初始状态
JS

// Emits
const emit = defineEmits(['update:modelValue']); // 触发更新 modelValue 的事件


// 组件状态
const imageUrl = ref(props.modelValue) // 使用传入的 modelValue 初始化
JS
// 监听 props.modelValue 的变化,保持父子组件同步
watch(() => props.modelValue, (newVal) => {
  imageUrl.value = newVal;
});
  • 在父组件之中使用
JS
<template>
    <div>
        <h1>我是admin主页</h1>
        <div>
            <ImageUpload v-model="imgurl" :maxSize="5" />
            <div v-if="imgurl">
                <p>上传的图片 URL: {{ imgurl }}</p>
                <img :src="imgurl" alt="上传的头像" class="uploaded-image" />
            </div>
        </div>
    </div>
</template>
<script setup>
import { ref,watch } from 'vue'

// 存储上传的图片 URL
const imgurl = ref('');

// 监听 imgurl 的变化,并在变化时输出新值
watch(imgurl, (newUrl) => {
  console.log('图片 URL 已更新:', newUrl);
});

</script>
<style scoped>
.uploaded-image {
    width: 150px;
    height: 150px;
    object-fit: cover;
}
</style>

好了,接下来就可以快乐的使用我们的上传图片组件了

(6)组件重编

之前我们采用的方式都是利用属性http-request方法之中自己写接口的方式实现的,接下来我们改写成更简单的,直接利用官方给我们的action属性来实现

👉 第一版完整版

JS
<template>
    <div>
        <el-upload 
        class="avatar-uploader" 
        action="#" 
        :show-file-list="false" 
        :before-upload="beforeAvatarUpload" 
        :http-request="uploadPicImg" 
        :on-success="handleAvatarSuccess">
            <img v-if="imageUrl" 
            :src="config.baseURL+imageUrl" 
            class="imgavatar"/>
            <el-icon v-else class="avatar-uploader-icon">
                <Plus />
            </el-icon>
        </el-upload>
    </div>
</template>
<script setup>
import { ref, reactive, toRefs, onMounted,watch} from 'vue'
import config from '@/utils/config.js';
// config.baseURL+config.prefixAPI
import { ElMessage } from 'element-plus'
import { Plus } from '@element-plus/icons-vue'

import { uploadImage} from "@/api/common/common";
const props = defineProps({
  modelValue: { // 通过 v-model 绑定的 prop
    type: String,
    default: ''
  },
  action: {
    type: String,
    required: false,
  default:"#",
  },
  maxSize: {
    type: Number,
    default: 10 // 默认最大文件大小为 10MB
  }
});

// 文件上传前的验证
const beforeAvatarUpload = (file) => {
    const isImage = file.type.startsWith('image/');
    const isLt2M = file.size / 1024 / 1024 < props.maxSize; // 限制文件大小为10MB
    if (!isImage) {
        ElMessage.error('只能上传图片文件!');
    }
    if (!isLt2M) {
        ElMessage.error('上传图片大小不能超过 2MB!');
    }
    return isImage && isLt2M;
};

// 上传图片接口
const uploadPicImg = async (query) => {
    // 确保传入的是文件对象
    let file = query.file;
    if (!file) {
        alert('请选择文件');
        return;
    }
    const formData = new FormData();
    formData.append('file', file);
    try {
        // 调用上传图片的API函数
        const res = await uploadImage(formData);
        console.log('上传', res);
        if (res.code === 200) {
            // console.log('图片上传成功', res);
            ElMessage.success(res.message);
            emit('update:modelValue', res.imgurl); // 更新父组件的数据
            // imageUrl.value = res.imgurl; // 假设返回的数据包含图片 URL
        } else {
            ElMessage.error(res.message);
            console.error('上传失败', res);
        }
    } catch (error) {
        console.error('上传失败,网络错误', error);
    }
};

// 上传成功后的处理函数
const handleAvatarSuccess = (response, file, fileList) => {
   console.log('文件上传成功', response, file, fileList);
};

// Emits
const emit = defineEmits(['update:modelValue']); // 触发更新 modelValue 的事件

// 组件状态
const imageUrl = ref(props.modelValue) // 使用传入的 modelValue 初始化

// 监听 props.modelValue 的变化,保持父子组件同步
watch(() => props.modelValue, (newVal) => {
  imageUrl.value = newVal;
});
</script>

<style>
.imgavatar {
    font-size: 28px;
    color: #8c939d;
    width: 178px;
    height: 178px;
    text-align: center;
    object-fit: cover;
}
</style>

👉 第二版 重编以后代码如下:

JS

Released under the MIT License.