antd vue 中,在 form 表单中的自定义组件使用 v-decorator
问题描述
项目需要,在表单中上传图片,所以要自己定以一个上传图片的组件,直接在 form 中使用,但是普通的自定义组件无法使用表单的 v-decorator。
分析
this.form.getFieldDecorator(id, options) 和 v-decorator="[id, options]"
经过 getFieldDecorator 或 v-decorator 包装的控件,表单控件会自动添加 value(或 valuePropName 指定的其他属性) onChange(或 trigger 指定的其他属性),数据同步将被 Form 接管,这会导致以下结果:
你不再需要也不应该用 onChange 来做同步,但还是可以继续监听 onChange 等事件。
你不能用控件的 value defaultValue 等属性来设置表单域的值,默认值可以用 getFieldDecorator 或 v-decorator 里的 initialValue。
你不应该用 v-model,可以使用 this.form.setFieldsValue 来动态改变表单值。
大概描述一下就是,一旦在 form 下使用了 v-decorator 之后,就不需要使用 v-model,其实实现上也有所相同,在自定义组件中需要自己定以 model 的东西,详细可以查阅官网。
简单说明
通俗来说,想使用 v-decorator,就必须要有个 value 想子组件传递数据。和一个 change 方法将子组件的数据变动告诉父组件,下面看部分代码
model: { prop: 'value', event: 'change' }, props: { value: { type: String // 这个参数是v-decorator给子组件传值用的 // 这里不要给默认值, 在form下使用会爆警告 Warning: SquareUpload `default value` can not collect, please use `option.initialValue` to set default value. } } watch: { // 监听数据变化,及时提交给父组件 fileList: { deep: true, immediate: true, handler: function (newV, oldV) { // 向父组件更新 this.$emit('change', temp) } } }
注意:value 不要给默认值,不然会爆警告default value can not collect, please use option.initialValue to set default value.
例子,封装一个上传图片的组件,在 form 中使用
子组件
<template> <div> <!-- one --> <div class="clearfix"> <a-upload name="upfile" :action="apiUrl" listType="picture-card" :fileList="imageList" :before-upload="beforeUpload" @preview="handlePreview" @change="imgChange" > <div v-if="imageList.length < maxListSize "> <a-icon type="plus"/> <div class="ant-upload-text">上传图片</div> </div> </a-upload> <a-modal :visible="previewVisible" :footer="null" @cancel="previewCancel"> <img alt="example" style="width: 100%" :src="previewImage"/> </a-modal> </div> </div> </template> <script> import {randomUUID, isJson} from "@util/functionBox"; export default { name: "imgUpload", props: { value: { type: String // 这个参数是v-decorator给子组件传值用的 // 这里不要给默认值, 在form下使用会爆警告 Warning: SquareUpload `default value` can not collect, please use `option.initialValue` to set default value. }, format: {type: Boolean, default: false}, maxListSize: {type: Number, default: 1}, }, model: { prop: 'value', event: 'change' }, data() { return { apiUrl: this.apiBaseURL + "/base/upload/upload_image", imageList: [], previewVisible: false, previewImage: "", } }, watch: { // 监听父组件传递过来的图片列表信息 value: { deep: true, immediate: true, handler: function (newV,oldV) { // 向父组件更新 if (newV === null || newV === '' || newV === undefined) { this.imageList = [] return } // 需要清空原有的文件列表,否则会出现多个🙅 this.imageList = [] if (Array.isArray(newV)) { //以防万一,避免出错 } else { if (this.format && isJson(newV)) { //需要格式化且是JSON格式才执行,否则会死循环输出 let caList = JSON.parse(newV) if (caList && caList.length && caList.length != 0) { // var newImgList = []; for (let i = 0; i < caList.length; i++) { this.pushImg(caList[i]) } // this.imageList = newImgList } } else { this.pushImg(newV) } } // this.$emit('change', newV) } }, //监听数据变化,及时提交给父组件 imageList: { deep: true, immediate: true, handler: function (fileList, oldV) { // 向父组件更新 if (this.imageList.length === 0) { return } // this.imageList = fileList; let urlInfo = null; if ( fileList.length != 0 && fileList[fileList.length - 1].status === "done") { // 处理数据 if (!this.format) { urlInfo = fileList[0].response.data } else { let imgs = []; for (let i = 0; i < fileList.length; i++) { if (fileList[i].response) { imgs.push(fileList[i].response.data); } // if (fileList[i].url) { // imgs.push(fileList[i].url); // } } urlInfo = JSON.stringify(imgs); } this.$emit('change', urlInfo) } } } }, methods: { imgChange({fileList}) { this.imageList = fileList; if ( fileList.length != 0 && fileList[fileList.length - 1].status === "done") { this.$message.success("上传成功!"); // this.$emit('change', this.imageList) } }, /**取消阅览 **/ previewCancel() { this.previewVisible = false; }, handlePreview(file) { this.previewImage = file.url || file.thumbUrl; this.previewVisible = true; }, beforeUpload(file) { // let file = el.file; const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'image/gif'; if (!isJpgOrPng) { this.$message.error("您只能上传 图片 文件!"); } const isLt2M = file.size / 1024 / 1024 < 2; if (!isLt2M) { this.$message.error('图片大小必须小于2mb!'); } return isJpgOrPng && isLt2M; }, pushImg(imgUrl) { if (!imgUrl) { return } //维护统一的格式 this.imageList.push({ uid: randomUUID(), name: imgUrl, status: "done", url: imgUrl, response: { data: imgUrl }, }) return this.imageList; }, getList() { return this.imageList; }, getFirst() { for (const info in this.imageList) { if (!info) { return info; } } }, } } </script> <style scoped> </style>
父组件使用
<imgUpload ref="imgUploadHead" v-decorator="['headImage',{ rules: [{ required: true,message: '请上传照片!' }] }]"></imgUpload>
主要针对单图片和多图片进行了封装📦
多图片后台返回和传入的格式:
“http://media.shixun365.com/image/31dc3ee8e4974c4d8257c9c3d1ece085.png”,“http://media.shixun365.com/image/17e2faf8d13e4e2b857cfde6748534b2.png”]
单图片的格式:
http://media.shixun365.com/image/31dc3ee8e4974c4d8257c9c3d1ece085.png