本文详解Ant Design Vue中Upload组件在表单环境下的自定义上传实现。针对组件change事件频繁触发导致的校验异常,通过getValueFromEvent结合自定义handleChange方法精准控制文件列表状态。文章提供了多文件上传组件的内联处理、customRequest逻辑定义及完整代码示例,解决上传状态与表单提交不同步的问题。
Ant Design Vue Upload组件自定义上传结合表单提交
在表单中内联多个文件上传组件
在使用Ant Design Vue的Upload组件时,会遇到一个现象:文件状态每次变更都会触发change事件。这意味着无论是上传成功、失败还是删除文件,该事件均会被触发。特别需要注意的是,删除文件后也会触发change事件,具体表现可参考下图:


即使返回结果为空,只要触发了change事件,该事件默认会返回如下结构的数据对象:
{
file: { /* ... */ },
fileList: [ /* ... */ ]
}
正是由于上述机制,每次提交表单时,即使没有附件,系统也会进行校验。因此,我们选择放弃使用Upload组件自带的change事件,转而采用表单配置中的getValueFromEvent方法(由于涉及多个上传组件,此处额外传递了一个key参数以区分)。
<a-upload
name="file"
v-decorator="[
'registerCertificatePath',
{
rules: [
{ required: true, message: '请上传公司注册信息证书!' }
],
getValueFromEvent: e =>
handleChange(e, 'registerCertificatePath')
}
]"
:file-list="registerCertificatePathList"
:customRequest="file => uploadFile(file, 'registerCertificate')"
>
<a-button><a-icon type="upload" />上传</a-button>
</a-upload>
我们需要定义两个核心方法:用于重置表单组件的setFileList方法,以及用于处理组件状态变化的handleChange回调(注意:这里并非直接调用Upload组件的change事件)。
setFileList(fileKey) {
// 根据filekey值清空指定的上传文件列表
this[`${fileKey}List`] = [];
// 清除对应的表单组件值
this.lastForm.setFieldsValue({ [fileKey]: "" });
},
handleChange({ file, fileList }, key) {
if (file.status === "removed" || file.status === "error") {
// 当这两种状态时调用setFileList方法
this.setFileList(key);
return "";
}
// 给对应的上传组件文件列表赋值
this[`${key}List`] = fileList;
// 赋值给对应表单
return file;
}
以下是具体的上传实现代码:
uploadRequest(param)
.then(({ success, message, data }) => {
if (success) {
const { fileName, filePath } = data;
this.fileData[`${fileKey}Path`] = filePath;
this.fileData[`${fileKey}Name`] = fileName;
this.$message.success(message);
// 上传成功将状态设置为 done
file.onSuccess();
} else {
this.$message.warning(message);
// 上传成功将状态设置为 error
file.onError();
}
})
.catch(error => {
this.$message.error("上传失败!");
console.log("上传失败:", error);
// 上传成功将状态设置为 error
file.onError();
});
完整代码示例如下:
<template>
<div class="last">
<a-form
:form="lastForm"
:label-col="{ span: 10 }"
:wrapper-col="{ span: 14 }"
@submit="lastSubmit"
>
<a-row>
<a-col :span="8">
<a-form-item label="公司注册信息证书">
<a-upload
name="file"
v-decorator="[
'registerCertificatePath',
{
rules: [
{ required: true, message: '请上传公司注册信息证书!' }
],
getValueFromEvent: e =>
handleChange(e, 'registerCertificatePath')
}
]"
:file-list="registerCertificatePathList"
:customRequest="file => uploadFile(file, 'registerCertificate')"
>
<a-button><a-icon type="upload" />上传</a-button>
</a-upload>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="营业执照附件">
<a-upload
name="file"
v-decorator="[
'businessLicPath',
{
rules: [{ required: true, message: '请上传营业执照附件!' }],
getValueFromEvent: e => handleChange(e, 'businessLicPath')
}
]"
:file-list="businessLicPathList"
:customRequest="file => uploadFile(file, 'businessLic')"
>
<a-button><a-icon type="upload" />上传</a-button>
</a-upload>
</a-form-item>
</a-col>
<a-col :span="8">
<a-form-item label="身份证件附件">
<a-upload
name="file"
v-decorator="[
'idCardCertificatePath',
{
rules: [{ required: true, message: '请上传身份证件附件!' }],
getValueFromEvent: e =>
handleChange(e, 'idCardCertificatePath')
}
]"
:file-list="idCardCertificatePathList"
:customRequest="file => uploadFile(file, 'idCardCertificate')"
>
<a-button><a-icon type="upload" />上传</a-button>
</a-upload>
</a-form-item>
</a-col>
</a-row>
<div class="btn">
<a-button @click="prev">
上一步
</a-button>
<a-button type="primary" html-type="submit">
完成注册
</a-button>
</div>
</a-form>
</div>
</template><script>
import { mapState } from "vuex";
import { uploadRequest } from "../../request/http";
export default {
computed: {
...mapState(["oid", "billKey"])
},
data() {
return {
lastForm: this.$form.createForm(this, { name: "lastForm" }),
fileData: {
registerCertificateName: "", // 公司注册证书
registerCertificatePath: "", // 公司注册证书路径
businessLicName: "", // 营业执照附件
businessLicPath: "", // 营业执照附件路径
idCardCertificateName: "", // 身份证件附件
idCardCertificatePath: "" // 身份证件附件路径
},
registerCertificatePathList: [],
businessLicPathList: [],
idCardCertificatePathList: []
};
},
methods: {
lastSubmit(e) {
e.preventDefault();
this.lastForm.validateFields((err, values) => {
if (!err) {
values = this.fileData;
this.$emit("sub", values);
}
});
},
prev() {
this.$emit("prev");
},
setFileList(fileKey) {
this[`${fileKey}List`] = [];
this.lastForm.setFieldsValue({ [fileKey]: "" });
},
handleChange({ file, fileList }, key) {
if (file.status === "removed" || file.status === "error") {
this.setFileList(key);
return "";
}
this[`${key}List`] = fileList;
return file;
},
uploadFile(file, fileKey) {
const formData = new FormData();
formData.append("file", file.file);
const param = {
billKey: this.billKey,
billId: this.oid,
data: formData
};
uploadRequest(param)
.then(({ success, message, data }) => {
if (success) {
const { fileName, filePath } = data;
this.fileData[`${fileKey}Path`] = filePath;
this.fileData[`${fileKey}Name`] = fileName;
this.$message.success(message);
file.onSuccess();
} else {
this.$message.warning(message);
file.onError();
}
})
.catch(error => {
this.$message.error("上传失败!");
console.log("上传失败:", error);
file.onError();
});
}
}
};
</script><style lang="less">
.last {
.ant-upload {
width: 100%;
text-align: left;
.ant-btn{
width: 230px;
}
}
.ant-upload-list {
text-align: left;
}
}
</style>Ant Design Vue自定义上传逻辑实现
实际上,使用Ant Design自带的上传逻辑也是可行的,当然也可以采用自定义上传逻辑。由于个人感觉某些功能通过自带逻辑难以实现或实现过程较为繁琐,因此这里重点记录自定义上传逻辑的实现方式。
<a-upload
:action="$rootUrl+'BillAudit/BillFile/UploadFile'"
:multiple="true"
:file-list="fileList"
name="files"
:customRequest="customRequest"
:data="{type:3}"
@change="handleChange"
>
<a-button> <a-icon type="upload" /> 上传附件 </a-button>
</a-upload>customRequest方法的逻辑处理如下:
customRequest(data) {
const formData = new FormData()
formData.append('files', data.file)
formData.append('type', 3)
// 这里的token根据自身情况修改
// formData.append('token', 'dfjdgfdgskdfkaslfdskf')
this.saveFile(formData)
},
saveFile(data) {
axios({
method: 'post',
url: this.$rootUrl+'BillAudit/BillFile/UploadFile',
data: data
}).then(res=>{
let fileList = JSON.parse(JSON.stringify(this.fileList))
if(res.data.code == 1) {
fileList.map(file=>{
file.url = res.data.data
file.status = 'done'
})
this.$message.success('上传成功')
}else {
fileList.map(file=>{
file.status = 'error'
})
this.$message.error(res.data.message)
}
this.fileList = fileList
}).catch(err=>{
let fileList = JSON.parse(JSON.stringify(this.fileList))
fileList.map(file=>{
file.status = 'error'
})
this.fileList = fileList
this.$message.error('服务器内部错误')
})
},最终效果展示:

以上为个人经验分享,希望能为大家提供参考,也希望大家多多支持本站。