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