1.场景
工作场景中,需要一个上传图片的功能 效果如下
PC端
mob端
2.业务逻辑
PC端和Mob端最多可以上传3张照片 每张大小不超过3M 格式支持bmp,gif,jpg,png,jpeg PC端上传的图片会展示在添加图片按钮和上传图片文案描述中间 mob端默认是一个添加图片的图 如果上传了图片 会出现在添加图片这张图之前 上传到第三张的时候 添加图片这张图就隐藏掉了 当前行显示3张正方形的上传图。 PC和Mob端每一个上传好的图上面都有一个x按钮 点击x可以删除掉上传的图片 并且这个块是封装成了组件 组件复用在了查看详情页 如果是查看详情 进入这个组件 我这里会传递进来一个resonDisabled 让input框隐藏掉 只能查看当前内容 不能点击上传 代码里的:class="{ 'component-order-return-detail__apply-about-hidden': resonDisabled }"就是控制input隐藏的
3.前后端约定
form-data方式上传 key value格式 上传 这里的baseUrl就是接口的请求地址公共url 做了全局配置而已 可以忽略 这个截图主要看传参格式
4.css部分代码
css实现中 先写了个父元素 包含input和上面的上传图片按钮 父元素 position:relative;子元素 position:absolute;input大小和上面的添加图片按钮大小一致 然后设置了opacity: 0;并且input的z-index层级设置的高一点 透明的浮在按钮之上
layout-mobile-only 是手机端的样式适配
layout-desktop 是PC端的样式适配
layout-responsive 是手机端和PC端分别是什么样式
如果需要写单端 可以手动把这些代码分离出来
这里写的有点多哈 大家可以不看这块 只看js业务逻辑部分 这里思路上面已经写出来了 业务逻辑也不一定一致 大家按照思路自己写就好
<!-- 上传图片按钮相关代码 -->
<div class="component-order-return-detail__apply-about-outer">
<div class="component-order-return-detail__apply-about-reason">图片信息</div>
<div class="component-order-return-detail__apply-about-position layout-desktop-only">
<div class="component-order-return-detail__apply-about-upload">+ 添加图片</div>
<input class="component-order-return-detail__apply-about-file component-order-return-detail__apply-about-upload" :class="{ 'component-order-return-detail__apply-about-hidden': resonDisabled }" type="file" multiple accept="image/png,image/jpeg,image/jpg,image/gif,image/bmp" @change="uploadApplyImage" />
</div>
</div>
<!-- PC端展示上传的图相关代码 以及mob端展示默认上传按钮以及上传图片以后 不够3张 默认图片往后推移相关逻辑 -->
<div v-if="showUploadImage" class="component-order-return-detail__apply-about-static">
<div v-for="(item, index) of returnImageInfo" :key="index" class="component-order-return-detail__apply-about-static-outer">
<img class="component-order-return-detail__apply-about-img" :src="item.url" alt="" />
<svg-icon name="icon-delete-img" class="component-order-return-detail__delete-svg" @click="deleteImage"></svg-icon>
</div>
<img v-if="returnImageInfo.length < 3" class="component-order-return-detail__apply-about-img" :src="addMobileImage" alt="" />
</div>
<style lang="scss">
$MQMobile: 1024px !default;
@mixin layout-mobile-only {
@media (max-width: calc(#{$MQMobile - 1px})) {
@content;
}
}
@mixin layout-desktop {
@media (min-width: $MQMobile) {
@content;
}
}
// RESPONSIVE
@mixin layout-responsive($property, $mobile: null, $desktop: null, $lage-desktop: null) {
@if ($mobile) {
@include layout-mobile {
#{$property}: $mobile;
}
}
@if ($desktop) {
@include layout-desktop {
#{$property}: $desktop;
}
}
}
.component-order-return-detail__apply-about-outer {
width: 100%;
@include layout-desktop {
display: flex;
align-items: center;
}
.block-input {
@include layout-mobile-only {
background-color: transparent !important;
}
}
@include layout-responsive(margin-top, vw(24), 26px);
.component-order-return-detail__apply-about-reason {
@include layout-responsive(width, 100%, 100px);
@include layout-mobile-only {
margin-bottom: vw(6);
}
@include layout-desktop {
margin-right: 49px;
}
}
.component-order-return-detail__reason-red {
color: red;
}
.component-order-return-detail__apply-about-input {
color: #888;
@include layout-responsive(font-size, vw(13), 13px);
@include layout-responsive(width, 100%, 335px);
@include layout-responsive(height, vw(36), 36px);
.block-input {
color: #888;
}
.is-black {
border-color: #c0c0c0 !important;
}
}
.component-order-return-detail__apply-about-textarea {
box-sizing: border-box;
color: #888;
@include layout-responsive(width, 100%, 642px);
@include layout-responsive(height, vw(100), 117px);
@include layout-responsive(line-height, vw(20), 20px);
@include layout-responsive(padding, vw(9) vw(5), 8px 11px);
}
.component-order-return-detail__apply-about-position {
position: relative;
width: 335px;
height: 36px;
.component-order-return-detail__apply-about-hidden {
display: none !important;
}
.component-order-return-detail__apply-about-file {
position: absolute;
z-index: 9;
opacity: 0;
}
.component-order-return-detail__apply-about-upload {
@include layout-desktop {
position: absolute;
width: 335px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
color: #888;
font-size: 13px;
border: 1px solid #c0c0c0;
}
}
}
}
</style>
样式基本上就写好了
5.js部分代码
下面是js部分逻辑 看起来内容有点多 但是注释的很详细 可以耐心看一下 按需贴到自己代码中直接使用 这里的axios是我单独自己写进去的 原项目中 是有封装的 避免看起来麻烦 改写了 如果不小心有问题 大家改一下哈
// 上传图片 点击input的时候 获取到图片信息
uploadApplyImage(e) {
const files = e.target.files
// 如果获取到了上传的内容 才进行以下业务逻辑操作
if (files && files.length > 0) {
// 如果上传多个图片或者文件 对其进行循环 对每个文件的格式都进行校验
for (let i = 0; i < files.length; i++) {
const file = files[i]
// 对支持上传的图片格式 进行判断
if (!/\.(jpg|jpeg|png|bmp|gif|JPG|PNG|BMP|GIF)$/.test(file.name)) {
this.$toast.show('请上传.png /.jpg /.jpeg格式的图片')
} else if (file.size > 1024 * 1024 * 3) {
// 弹出不满足上传格式的弹窗
this.$toast.show('单张图片不可以超过3M,请重新上传')
return
} else {
// 图片超过三张 弹出提示
if (this.returnImageInfo.length === 3) {
this.setToast('最多可上传3张图片')
return
}
console.log('满足上传条件', file)
// 转base64格式 这里约定的格式是key value 直接传file就好 就没用到转base64
// const reader = new FileReader()
// // 因为这两个组件的fils文件不一样所有一个判断
// if (file?.originFileObj) {
// reader.readAsDataURL(file.originFileObj)
// } else {
// reader.readAsDataURL(file)
// }
// reader.addEventListener('load', () => {
// console.log('reader.result', reader.result)
// })
// 转base64格式结束
// 上传图片 form-data方式进行参数序列化 和后端约定的参数是uploadFile
const formData = new FormData()
formData.append('uploadFile', file)
axios.post('/upload', formData,
{headers: { 'Content-Type': 'application/form-data' }})
.then(function (response) {
// 上传成功 执行对应操作
console.log(response);
})
.catch(function (error) {
// 上传失败 执行对应操作
console.log(error);
});
}
}
}
}
好了 就写到这里吧 希望可以对你有帮助~ 如果大家有什么问题 可以私信我 或者我们评论区见