Ant Design <a-upload>上传时只有Uploading状态,无法获取服务器返回的数据

在使用Vue的AntDesign组件时,发现a-card的loading属性为true会导致a-upload上传文件时无法获取服务器返回数据。去掉loading属性后,上传功能恢复正常。

原因: <a-card> 中的 loading属性值为true时, 导致<a-upload> 上传时只有Uploading状态,无法获取服务器返回的数据,需要去掉 loading属性即可。

<template>
  <div>
  <!-- <a-card :bordered="false" title="上传升级包" class="mb20" :loading="loading"  hoverable> -->
    <a-card :bordered="false" title="上传升级包" class="mb20"  hoverable>
      <a-form @submit="handleSubmit">
        <a-form-item
          label="文件"
          :labelCol="{ lg: { span: 7 }, sm: { span: 7 } }"
          :wrapperCol="{ lg: { span: 10 }, sm: { span: 17 } }"
        >
          <a-upload
            name="file"
            :showUploadList="false"
            :multiple="false"
            :headers="tokenHeader"
            :action="uploadAction"
            accept=".apk"
            @change="handleUpload"
          >
            <a-button type="primary"><a-icon type="upload" class="fs-14"></a-icon>选择文件</a-button>
            <span class="tips line-2em"> 选择XXX.apk,进行升级操作 </span>
          </a-upload>
        </a-form-item>
      </a-form>
    </a-card>
  </div>
</template>
<template> <a-modal :visible="visible" title="添加新流程" @cancel="closeModal" :footer="null" :width="800" :destroyOnClose="true" :maskClosable="false" > <a-form ref="processForm" :form="form" @submit="submitForm" > <a-row :gutter="24"> <a-col :span="12"> <a-form-item label="流程名称"> <a-input v-decorator="[ 'processName', { rules: [ { required: true, message: '请输入流程名称' }, { validator: validateTrimmed } ] } ]" placeholder="请输入流程名称" /> </a-form-item> </a-col> <a-col :span="12"> <a-form-item label="产品编号"> <a-input v-decorator="[ 'productNumber', { rules: [ { required: true, message: '请输入产品编号' }, { validator: validateTrimmed } ] } ]" placeholder="请输入产品编号" /> </a-form-item> </a-col> </a-row> <a-row :gutter="24"> <a-col :span="12"> <a-form-item label="执行主机"> <a-input v-decorator="[ 'executeHost', { rules: [ { required: true, message: '请输入执行主机' }, { validator: validateTrimmed } ] } ]" placeholder="请输入执行主机" /> </a-form-item> </a-col> <a-col :span="12"> <a-form-item label="部门"> <a-input v-decorator="[ 'department', { rules: [ { required: true, message: '请输入部门' }, { validator: validateTrimmed } ] } ]" placeholder="请输入部门" /> </a-form-item> </a-col> </a-row> <a-row :gutter="24"> <a-col :span="12"> <a-form-item label="版本号"> <a-input v-decorator="[ 'version', { rules: [ { required: true, message: '请输入版本号' }, { validator: validateTrimmed } ] } ]" placeholder="请输入版本号" /> </a-form-item> </a-col> <a-col :span="12"> <a-form-item label="类型"> <a-input v-decorator="[ 'type', { rules: [{ required: true, message: '请输入类型' }] } ]" placeholder="请输入类型" /> </a-form-item> </a-col> </a-row> <a-row> <a-col :span="24"> <a-form-item label="流程文件"> <a-upload-dragger name="file" :multiple="false" :beforeUpload="beforeUpload" action="/win/gateway/uploadFile" :data="getUploadParams" @change="handleFileChange" :showUploadList="{ showRemoveIcon: true }" :disabled="uploading" :fileList="fileList" > <p class="ant-upload-drag-icon"> <a-icon type="inbox"/> </p> <p class="ant-upload-text">仅支持ZIP或者7Z文件</p> <p class="ant-upload-hint">点击或拖拽文件到此处上传</p> </a-upload-dragger> </a-form-item> </a-col> </a-row> <div class="action-buttons"> <a-button @click="closeModal" style="margin-right: 12px;">取消</a-button> <a-button type="primary" html-type="submit" :loading="uploading" :disabled="!fileUploadSuccess" > 提交 </a-button> </div> </a-form> </a-modal> </template> <script> import axios from 'axios'; export default { name: "AddProcessModal", data() { return { visible: false, form: this.$form.createForm(this), fileList: [], uploading: false, fileUploadSuccess: false, fileId: null, // 用于存储上传成功后返回的文件ID envName: '', executeHost: '' }; }, methods: { openModal(envName, executeHost) { console.log('add',envName, executeHost) this.visible = true; this.envName = envName; this.executeHost = executeHost; }, getUploadParams() { return { fileRequest: JSON.stringify({ envName: this.envName, executeHost: this.executeHost }) }; }, closeModal() { this.visible = false; this.form.resetFields(); this.fileList = []; this.fileUploadSuccess = false; this.fileId = null; }, validateTrimmed(rule, value, callback) { if (value && value.trim() === '') { callback('内容不能全为空格'); } else { callback(); } }, beforeUpload(file) { const isValidType = file.type === 'application/zip' || file.type === 'application/x-7z-compressed' || file.name.endsWith('.zip') || file.name.endsWith('.7z'); if (!isValidType) { this.$message.error({message: '只能上传ZIP或7Z格式的文件!'}); return false; } return true; // 允许上传 }, handleFileChange(info) { if (info.file.status === 'removed') { this.fileList = []; this.fileUploadSuccess = false; this.fileId = null; return; } // 只保留最新上传的文件 this.fileList = info.fileList.slice(-1); // 上传成功 if (info.file.status === 'done') { const response = info.file.response; console.log(response) // 检查后端返回的业务状态(根据你的接口调整) if (response && response.includes("上传并解压成功!")) { this.$message.success({message: response}); this.fileUploadSuccess = true; this.fileId = response?.fileId; // 确保路径正确 } else { // 业务逻辑错误,标记为失败 this.$message.error({message: response || '文件上传失败'}); this.fileUploadSuccess = false; this.fileList = []; info.file.status = 'error'; // 手动标记为错误状态 } } // 上传失败 if (info.file.status === 'error') { this.$message.error({message: '文件上传失败'}); this.fileUploadSuccess = false; this.fileList = []; } }, async uploadFile() { if (this.fileList.length === 0) return; this.uploading = true; this.fileUploadSuccess = false; try { const formData = new FormData(); formData.append('file', this.fileList[0].originFileObj || this.fileList[0]); const response = await axios.post('/win/gateway/uploadFile', formData, { headers: { 'Content-Type': 'multipart/form-data' } }); if (response.success) { this.$message.success({message: '文件上传成功'}); this.fileUploadSuccess = true; this.fileId = response.fileId; // 更新fileList中的文件状态 this.fileList = [{ ...this.fileList[0], status: 'done', response: response // 可以存储响应数据供后续使用 }]; } else { throw new Error(response.message || '文件上传失败'); } } catch (error) { this.$message.error({message: '文件上传失败: ' + (error.response?.data?.message || error.message)}); this.fileList = []; this.fileUploadSuccess = false; } finally { this.uploading = false; } }, async submitForm(e) { e.preventDefault(); if (!this.fileUploadSuccess || !this.fileId) { this.$message.warning({message: '请先上传文件'}); return; } try { const values = await this.form.validateFields(); const processRequest = { ...values, processName: values.processName.trim(), productNumber: values.productNumber.trim(), executeHost: values.executeHost.trim(), department: values.department.trim(), version: values.version.trim(), fileId: this.fileId, // 使用上传成功后返回的文件ID }; const response = await this.$axios.post('/startProcess', processRequest); this.$message.success({message: '流程添加成功'}); this.$emit('submit-success', response); this.closeModal(); } catch (error) { this.$message.error({message: '提交失败: ' + (error.response?.data?.message || error.message)}); } }, } }; </script> <style scoped lang="less"> .action-buttons { text-align: right; margin-top: 24px; } .ant-upload-dragger { height: 180px; } </style> 我想要用户表单均填写了,都非空, 才能上传文件,文件上传成功会返回boolean值,如果值为true,才允许用户点击提交按钮。给我最完整代码
最新发布
07-05
<template> <a-spin :spinning="confirmLoading"> <JFormContainer :disabled="disabled"> <template #detail> <a-form ref="formRef" class="antd-modal-form" :labelCol="labelCol" :wrapperCol="wrapperCol" name="MachineWeightForm"> <a-row> <a-col :span="24"> <a-form-item label="设备编号" v-bind="validateInfos.deviceSn" id="MachineWeightForm-deviceSn" name="deviceSn"> <a-input v-model:value="formData.deviceSn" placeholder="设备编号" allow-clear /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="车次编码" v-bind="validateInfos.carNumber" id="MachineWeightForm-carNumber" name="carNumber"> <a-input v-model:value="formData.carNumber" placeholder="车次编码" allow-clear /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="车牌号" v-bind="validateInfos.licensePlate" id="MachineWeightForm-licensePlate" name="licensePlate"> <a-input v-model:value="formData.licensePlate" placeholder="车牌号" allow-clear /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="毛重(kg)" v-bind="validateInfos.grossWeight" id="MachineWeightForm-grossWeight" name="grossWeight"> <a-input-number v-model:value="formData.grossWeight" placeholder="毛重(kg)" style="width: 100%" /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="皮重(kg)" v-bind="validateInfos.tareWeight" id="MachineWeightForm-tareWeight" name="tareWeight"> <a-input-number v-model:value="formData.tareWeight" placeholder="皮重(kg)" style="width: 100%" /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="净重(kg)" v-bind="validateInfos.netWeight" id="MachineWeightForm-netWeight" name="netWeight"> <a-input-number v-model:value="formData.netWeight" placeholder="净重(kg)" style="width: 100%" /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="单位名称" v-bind="validateInfos.companyName" id="MachineWeightForm-companyName" name="companyName"> <a-input v-model:value="formData.companyName" placeholder="车牌号" allow-clear /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="规格型号" v-bind="validateInfos.goodsModel" id="MachineWeightForm-goodsModel" name="goodsModel"> <a-input v-model:value="formData.goodsModel" placeholder="规格型号" allow-clear /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="商品名称" v-bind="validateInfos.goodsName" id="MachineWeightForm-goodsName" name="goodsName"> <a-input v-model:value="formData.goodsName" placeholder="商品名称" allow-clear /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="司机姓名" v-bind="validateInfos.driverName" id="MachineWeightForm-driverName" name="driverName"> <a-input v-model:value="formData.driverName" placeholder="司机姓名" allow-clear /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="司机电话" v-bind="validateInfos.driverMobile" id="MachineWeightForm-driverMobile" name="driverMobile"> <a-input v-model:value="formData.driverMobile" placeholder="司机电话" allow-clear /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第一次过磅间" v-bind="validateInfos.enterTime" id="MachineWeightForm-enterTime" name="enterTime" > <a-date-picker placeholder="第一次过磅间" v-model:value="formData.enterTime" showTime value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" allow-clear /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第一次过磅车牌图片" v-bind="validateInfos.enterPlateName" id="MachineWeightForm-enterPlateName" name="enterPlateName"> <Base64ImagePreview v-model:value="formData.enterPlateName"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第一次过磅车图片" v-bind="validateInfos.enterLicenseName" id="MachineWeightForm-enterLicenseName" name="enterLicenseName"> <Base64ImagePreview v-model:value="formData.enterLicenseName"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第一次过磅车头相机抓拍图片" v-bind="validateInfos.enterCaptureHead" id="MachineWeightForm-enterCaptureHead" name="enterCaptureHead"> <Base64ImagePreview v-model:value="formData.enterCaptureHead"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第一次过磅车尾相机抓拍图片" v-bind="validateInfos.enterCaptureTrail" id="MachineWeightForm-enterCaptureTrail" name="enterCaptureTrail"> <Base64ImagePreview v-model:value="formData.enterCaptureTrail"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第一次过磅车仓相机抓拍图片" v-bind="validateInfos.enterCaptureWare" id="MachineWeightForm-enterCaptureWare" name="enterCaptureWare"> <Base64ImagePreview v-model:value="formData.enterCaptureWare"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第一次过磅车身相机抓拍图片" v-bind="validateInfos.enterCaptureBody" id="MachineWeightForm-enterCaptureBody" name="enterCaptureBody"> <Base64ImagePreview v-model:value="formData.enterCaptureBody"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第一次过磅磅房相机抓拍图片" v-bind="validateInfos.enterCapturePoundRoom" id="MachineWeightForm-enterCapturePoundRoom" name="enterCapturePoundRoom"> <Base64ImagePreview v-model:value="formData.enterCapturePoundRoom"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第二次过磅间" v-bind="validateInfos.outTime" id="MachineWeightForm-outTime" name="outTime" > <a-date-picker placeholder="第二次过磅间" v-model:value="formData.outTime" showTime value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%" allow-clear /> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第二次过磅车牌图片" v-bind="validateInfos.outPlateName" id="MachineWeightForm-outPlateName" name="outPlateName"> <Base64ImagePreview v-model:value="formData.outPlateName"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第二次过磅车图片" v-bind="validateInfos.outLicenseName" id="MachineWeightForm-outLicenseName" name="outLicenseName"> <Base64ImagePreview v-model:value="formData.outLicenseName"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第二次过磅车头相机抓拍图片" v-bind="validateInfos.outCaptureHead" id="MachineWeightForm-outCaptureHead" name="outCaptureHead"> <Base64ImagePreview v-model:value="formData.outCaptureHead"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第二次过磅车尾相机抓拍图片" v-bind="validateInfos.outCaptureTrail" id="MachineWeightForm-outCaptureTrail" name="outCaptureTrail"> <Base64ImagePreview v-model:value="formData.outCaptureTrail"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第二次过磅车仓相机抓拍图片" v-bind="validateInfos.outCaptureWare" id="MachineWeightForm-outCaptureWare" name="outCaptureWare"> <Base64ImagePreview v-model:value="formData.outCaptureWare"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第二次过磅车身相机抓拍图片" v-bind="validateInfos.outCaptureBody" id="MachineWeightForm-outCaptureBody" name="outCaptureBody"> <Base64ImagePreview v-model:value="formData.outCaptureBody"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="第二次过磅磅房相机抓拍图片" v-bind="validateInfos.outCapturePoundRoom" id="MachineWeightForm-outCapturePoundRoom" name="outCapturePoundRoom"> <Base64ImagePreview v-model:value="formData.outCapturePoundRoom"/> </a-form-item> </a-col> <a-col :span="24"> <a-form-item label="备注" v-bind="validateInfos.remark" id="MachineWeightForm-remark" name="remark"> <a-textarea v-model:value="formData.remark" :rows="4" placeholder="备注" /> </a-form-item> </a-col> </a-row> </a-form> </template> </JFormContainer> </a-spin> </template> <script lang="ts" setup> import { ref, reactive, defineExpose, nextTick, defineProps, computed, onMounted } from 'vue'; import { defHttp } from '/@/utils/http/axios'; import { useMessage } from '/@/hooks/web/useMessage'; import { getValueType } from '/@/utils'; import {queryById, saveOrUpdate} from '@/api/machine/MachineWeight.api'; import { Form } from 'ant-design-vue'; import JFormContainer from '/@/components/Form/src/container/JFormContainer.vue'; import JDictSelectTag from '../../../../components/Form/src/jeecg/components/JDictSelectTag.vue'; import Base64ImagePreview from '@/components/Form/src/jeecg/components/Base64ImagePreview.vue'; const props = defineProps({ formDisabled: { type: Boolean, default: false }, formData: { type: Object, default: () => ({}) }, formBpm: { type: Boolean, default: true }, }); const formRef = ref(); const useForm = Form.useForm; const emit = defineEmits(['register', 'ok']); const formData = reactive<Record<string, any>>({ id: '', deviceSn: '', carNumber: '', licensePlate: '', plateColor: '', grossWeight: undefined, tareWeight: undefined, netWeight: undefined, companyName: '', goodsModel: '', goodsName: '', driverName: '', driverMobile: '', enterTime: '', enterPlateName: '', enterLicenseName: '', enterCaptureHead: '', enterCaptureTrail: '', enterCaptureWare: '', enterCaptureBody: '', enterCapturePoundRoom: '', outTime: '', outPlateName: '', outLicenseName: '', outCaptureHead: '', outCaptureTrail: '', outCaptureWare: '', outCaptureBody: '', outCapturePoundRoom: '', remark: '', }); const { createMessage } = useMessage(); const labelCol = ref<any>({ xs: { span: 24 }, sm: { span: 8 } }); const wrapperCol = ref<any>({ xs: { span: 24 }, sm: { span: 14 } }); const confirmLoading = ref<boolean>(false); //表单验证 const validatorRules = reactive({}); const { resetFields, validate, validateInfos } = useForm(formData, validatorRules, { immediate: false }); // 表单禁用 const disabled = computed(() => { if (props.formBpm === true) { if (props.formData.disabled === false) { return false; } else { return true; } } return props.formDisabled; }); /** * 新增 */ function add() { edit({}); } /** * 编辑 */ function edit(record) { nextTick(() => { resetFields(); let id = record.id if (id){ queryById({id}).then(res => { const tmpData = {}; Object.keys(formData).forEach((key) => { if (res.hasOwnProperty(key)) { tmpData[key] = res[key]; } }); //赋值 Object.assign(formData, tmpData); }) } }); } /** * 提交数据 */ async function submitForm() { try { // 触发表单验证 await validate(); } catch ({ errorFields }) { if (errorFields) { const firstField = errorFields[0]; if (firstField) { formRef.value.scrollToField(firstField.name, { behavior: 'smooth', block: 'center' }); } } return Promise.reject(errorFields); } confirmLoading.value = true; const isUpdate = ref<boolean>(false); //间格式化 let model = formData; if (model.id) { isUpdate.value = true; } //循环数据 for (let data in model) { //如果该数据是数组并且是字符串类型 if (model[data] instanceof Array) { let valueType = getValueType(formRef.value.getProps, data); //如果是字符串类型的需要变成以逗号分割的字符串 if (valueType === 'string') { model[data] = model[data].join(','); } } } await saveOrUpdate(model, isUpdate.value) .then((res) => { if (res.success) { createMessage.success(res.message); emit('ok'); } else { createMessage.warning(res.message); } }) .finally(() => { confirmLoading.value = false; }); } defineExpose({ add, edit, submitForm, }); </script> <style lang="less" scoped> .antd-modal-form { padding: 14px; } </style> <template> <div class="clearfix"> <a-upload :listType="listType" accept="image/*" :fileList="uploadFileList" :disabled="disabled" @preview="handlePreview" > </a-upload> <a-modal :open="previewVisible" :footer="null" @cancel="handleCancel()"> <img alt="example" style="width: 100%" :src="previewImage" /> </a-modal> </div> </template> <script lang="ts"> import { defineComponent, PropType, ref, reactive, watchEffect, computed, unref, watch, onMounted, nextTick } from 'vue'; import { LoadingOutlined, UploadOutlined } from '@ant-design/icons-vue'; import { useRuleFormItem } from '/@/hooks/component/useFormItem'; import { propTypes } from '/@/utils/propTypes'; import { useAttrs } from '/@/hooks/core/useAttrs'; import { useMessage } from '/@/hooks/web/useMessage'; import { getFileAccessHttpUrl, getHeaders, getRandom } from '/@/utils/common/compUtils'; const { createMessage, createErrorModal } = useMessage(); export default defineComponent({ name: 'Base64ImageUpload', components: { LoadingOutlined, UploadOutlined }, inheritAttrs: false, props: { //绑定值 value: propTypes.oneOfType([propTypes.string, propTypes.array]), //按钮文本 listType: { type: String, required: false, default: 'picture-card', }, //是否禁用 disabled: { type: Boolean, required: false, default: false, }, }, emits: ['options-change', 'change', 'update:value'], setup(props, { emit, refs }) { const emitData = ref<any[]>([]); const attrs = useAttrs(); const [state] = useRuleFormItem(props, 'value', 'change', emitData); //是否是初始化加载 const initTag = ref<boolean>(true); //文件列表 let uploadFileList = ref<any[]>([]); //预览图 const previewImage = ref<string | undefined>(''); //预览框状态 const previewVisible = ref<boolean>(false); /** * 监听value变化 */ watch( () => props.value, (val) => { if (initTag.value == true) { initFileList(val); } }, { immediate: true } ); /** * 初始化文件列表 */ function initFileList(paths) { if (!paths || paths.length == 0) { uploadFileList.value = []; return; } let files = []; const isBase64 = paths.startsWith('data:image'); let fileObj = { uid: getRandom(10), name: 'base64-image.png', status: 'done', url: isBase64 ? paths : `data:image/png;base64,${paths}`, response: { status: 'history', message: paths , }, } files.push(fileObj); uploadFileList.value = files; } /** * 预览图片 */ function handlePreview(file) { previewImage.value = file.url; previewVisible.value = true; } function handleCancel() { previewVisible.value = false; } return { state, attrs, previewImage, previewVisible, uploadFileList, handlePreview, handleCancel, }; }, }); </script> <style scoped> .ant-upload-select-picture-card i { font-size: 32px; color: #999; } .ant-upload-select-picture-card .ant-upload-text { margin-top: 8px; color: #666; } </style> 需求,图片等待接口响应,显示转圈圈加载,等响应后再显示图片
05-14
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

唐诺

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值