Appearance
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可以查看这篇文章
TIP
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