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
