上传组件封装需求分析

在基于elementUI库做的商城后台管理中,需求最大的是商品管理表单这块,因为需要录入各种各样的商品图片信息。加上后台要求要传递小于2M的图片,因此封装了一个upload.vue组件作为上传页面的子组件,它用于管理图片上传逻辑。

upload.vue解析

upload主要用于实现表单上传图片的需求,主要由input +img 构成当没有图片的时候显示默认图片,有图片则显示上传图片,因为input样式不太符合需求所以只是将起设置为不可见,不能将其设置为display:none。否则将将无法触发input的change事件

upload.vue代码如下:

<template>
	<div>
		<div class="upload-box" :style="imgStyle">
			 <!-- 用户改变图片按钮的点击 触发上传图片事件  -->
			<input type="file" :ref="imgType$1"   @change="upload(formVal$1,imgType$1)" class="upload-input" />
			<!-- img 的 src 用于渲染一个 图片路径  传入图片路径 渲染出图片  -->
			<img :src="formVal$1[imgType$1]?formVal$1[imgType$1]:'static/img/upload.jpg'" />
		</div>
	</div>
</template>
<script>
/* 
  该组件因为要上传多个属性的图片  主图(mainImg) 详细图(detailImg)  规格图 (plusImg)  
	该组件基于压缩插件lrz,所以下方打入该组件
	npm install lrz --save 即可
*/
import lrz from 'lrz';
export default {
    name: 'uploadImg', //组件名字
    props: {
        formVal: {
            type: Object, //props接受对象类型数据(表单对象也可以是纯对象类型)
            required: true,
            default: {}
        },
        imgType: {                //表单对象中的图片属性 example:mainImg
            type: String,
            required: true,
            default: ''
        },
        imgStyle: {
            type: Object,        //  用于显示的图片的样式 
            required: true //必须传递
        }
    },
    created: function() {
        //生命周期函数 
    },
    data: function() {
      /*
         因为该组件需要改变父组件传递过来的值,
         所以将起拷贝一份
      */
        let formVal$1 = this.formVal;
        let imgType$1 = this.imgType;
        return {
            formVal$1,
            imgType$1,
            uploadUrl: url,//你的服务器url地址
        };
    },
    methods: {
        upload: function(formVal, imgType) {
            var self = this;
            //图片上传加载我们在这里加入提示,下方需要主动关闭,防止页面卡死
            var loadingInstance = this.$loading({
                text: '上传中'
            });
            var that = this.$refs[imgType].files[0]; //文件压缩file
            //图片上传路径
            var testUrl = this.uploadUrl; //图片上传路径
            try {
                //lrz用法和上一个一样也是一个压缩插件来的
                lrz(that)
                    .then(function(message) {
                        var formData = message.formData; //压缩之后我们拿到相应的formData上传
                        self.$axios
                            .post(testUrl, formData)
                            .then(function(res) {
                                console.log(res);
                                if (res && res.data.iRet == 0) {
                                    formVal[imgType] = res.data.objData.sUrl;
                                    //上传成功之后清掉数据防止下次传相同图片的时候不触发change事件 
                                    self.$refs[imgType].value = '';
                                    /*
	                                    这里因为使用elementUI中的表单验证,
	                                    当上传图片完成之后还会提示没有上传图片
	                                    所以需要通知父组件清除该验证标记 
                                     */
                                    self.$emit('clearValidate', imgType);
                                    self.$nextTick(() => {
                                        // 以服务的方式调用的 Loading 需要异步关闭
                                        loadingInstance.close();
                                    });
                                } else {
                                    throw res.data.sMsg;
                                }
                            })
                            .catch(function(err) {
                                self.$nextTick(() => {
                                    // 以服务的方式调用的 Loading 需要异步关闭
                                    loadingInstance.close();
                                });
                                //接口报错弹出提示
                                alert(err);
                            });
                    })
                    .catch(function(err) {
                        self.$nextTick(() => {
                            loadingInstance.close();
                        });
                    });
            } catch (e) {
                //关闭加载动画实例
                self.$nextTick(() => {
                    loadingInstance.close();
                });
            }
        }
    },
    mounted: function() {},
    watch: {
       /*
        这里需要注意当父组件上传一个图片然后通过重置按钮重置的时候.
         我们需要监听一下,防止上传同一张图片上传失败
        */
        formVal: {
            handle: function(newVal, oldVal) {
                var imgType = this.imgType;
                if (newVal[imgType] == '') {
                   //这里使用了原生js写法当然也可以通过ref引用找到,后者更好
                    document.getElementsByClassName('upload-input')[0].value = '';
                }
            }
        }
    }
};
</script>
<style scoped>
/*
 这里是默认的设置图片的尺寸。可以通过父组件传值将其覆盖
*/
.upload-box {
    position: relative;
    height: 100px;
    width: 100px;
    overflow: hidden;
}

.upload-box img {
    width: 100%;
    height: 100%;
}

.upload-box .upload-input {
    position: absolute;
    left: 0;
    opacity: 0;
    width: 100%;
    height: 100%;
}
</style>

商品页中使用upload组件

good.vue中我们引入upload组件。并且传递相应表单对象,需上传的图片类型的属性,以及图片显示样式给子组件

good.vue核心代码:

<template>
   <el-form ref="form" :model="form" label-width="80px" label-position="top" :rules="rules">
   	<!-- 无关代码略 -->
   	<el-form-item label="详情图" prop="sDetailImg" ref="sDetailImg">
   		<uploadImg :form-val="form" :img-type="'sDetailImg'" :img-style="detailImgStl" @clearValidate="clearValidate"></uploadImg>
   	</el-form-item>
   	<el-form-item>
   		<el-row style="text-align:center;">
   			<el-button type="primary" size="medium" @click.stop="submit('form')" v-if="!form.ID">保存</el-button>
   			<el-button type="primary" size="medium" @click.stop="submit('form')" v-else-if="form.ID">修改</el-button>
   			<el-button size="medium" @click.stop="resetForm('form')">重置</el-button>
   		</el-row>
   	</el-form-item>
   </el-form>
   <!--  略 -->
</template>
<script>
   import uploadImg from "../common/uploadImg"; //图片上传
   export default {
   	name: "good", //组件名字用户缓存 
   	data: function() {
   		return {
   			form: {
   				ID: NULL,
   				//其他字段略
   				sDetailImg: "" //商品详细图
   			},
   			detailImgStl: {
   				width: "350px",
   				height: "150px"
   			},
   			rules: {
   				sDetailImg: [{
   					required: true,
   					message: "请填写详细图信息",
   					trigger: "change"
   				}],
   			}
   		}
   	},
   	methods: {
   		//这里监听子组件回写的信息,用户清除上传成功之后还显示图片未上传的bug
   		clearValidate: function(imgName) {
   			//清空图片上传成功提示图片没有上传的验证字段
   			this.$refs[imgName].clearValidate();
   		},
   		//重置表单
   		resetForm: function(formName) {
   			this.confirm("确认重置表单", function(self) {
   				self.$refs[formName].resetFields();
   			})

   		}
   	},
   }
</script>

写在最后

关于图片上传之前我也写过一个小程序版本,总体看来pc端的图片上传相对于小程序 要复杂一点,这个封装只能满足当下单图上传的需求也有他的不足之处。当然也可以扩展为多图上传,关于多图上传的网上也有很多例子。这里不再一一赘述。

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐