gaoluyang
7 小时以前 801a3bc53415f6214800c561a562d17ea58b88f1
军泰伟业
1.bom导入和产品导入成功和失败逻辑修改
已修改3个文件
234 ■■■■ 文件已修改
src/components/Dialog/ImportDialog.vue 119 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/basicData/product/index.vue 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/views/productionManagement/productStructure/index.vue 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/components/Dialog/ImportDialog.vue
@@ -8,17 +8,16 @@
  >
    <el-upload
      ref="uploadRef"
      v-model:file-list="fileList"
      :limit="limit"
      :accept="accept"
      :headers="headers"
      :action="action"
      :disabled="disabled"
      :before-upload="beforeUpload"
      :on-progress="onProgress"
      :on-success="onSuccess"
      :on-error="onError"
      :on-change="onChange"
      :auto-upload="autoUpload"
      :http-request="handleHttpRequest"
      :on-change="onChange || (() => {})"
      :auto-upload="false"
      drag
    >
      <el-icon class="el-icon--upload"><UploadFilled /></el-icon>
@@ -39,7 +38,7 @@
    </el-upload>
    <template #footer>
      <div class="dialog-footer">
        <el-button type="primary" @click="handleConfirm">确 定</el-button>
        <el-button type="primary" @click="handleConfirm" :loading="uploading">确 定</el-button>
        <el-button @click="handleCancel">取 消</el-button>
      </div>
    </template>
@@ -49,6 +48,9 @@
<script setup>
import { computed, ref } from 'vue'
import { UploadFilled } from '@element-plus/icons-vue'
import axios from 'axios'
import { getToken } from '@/utils/auth'
import { ElMessage } from 'element-plus'
const props = defineProps({
  modelValue: {
@@ -84,10 +86,6 @@
    required: true
  },
  disabled: {
    type: Boolean,
    default: false
  },
  autoUpload: {
    type: Boolean,
    default: false
  },
@@ -129,19 +127,96 @@
})
const uploadRef = ref(null)
const fileList = ref([])
const uploading = ref(false)
const handleClose = () => {
  emit('close')
}
const handleConfirm = () => {
  emit('confirm')
const handleHttpRequest = async (options) => {
  const { file, onProgress, onSuccess, onError } = options
  uploading.value = true
  const formData = new FormData()
  formData.append('file', file)
  try {
    const response = await axios({
      url: props.action,
      method: 'post',
      data: formData,
      headers: {
        'Authorization': 'Bearer ' + getToken(),
        ...props.headers,
        'Content-Type': 'multipart/form-data'
      },
      responseType: 'blob',
      onUploadProgress: (progressEvent) => {
        const percent = Math.round((progressEvent.loaded * 100) / progressEvent.total)
        if (onProgress) {
          onProgress({ percent })
        }
        if (props.onProgress) {
          props.onProgress({ percent }, file)
        }
      }
    })
    uploading.value = false
    const blob = response.data
    // 先尝试将 blob 解析为 JSON(无论 content-type 是什么)
    try {
      const text = await blob.text()
      const jsonResponse = JSON.parse(text)
      // 成功解析为 JSON
      if (jsonResponse.code === 200) {
        if (onSuccess) onSuccess(jsonResponse)
        if (props.onSuccess) props.onSuccess(jsonResponse, file)
      } else {
        const error = new Error(jsonResponse.msg || '导入失败')
        error.response = { data: jsonResponse }
        if (onError) onError(error)
        if (props.onError) props.onError(jsonResponse, file)
      }
    } catch (e) {
      // 不是 JSON,是文件流,下载错误文件
      downloadBlob(blob, '导入错误数据.xlsx')
      if (onError) {
        const error = new Error('导入失败,请查看下载的错误文件')
        error.response = { data: blob }
        onError(error)
      }
      if (props.onError) {
        props.onError(blob, file)
      }
    }
  } catch (error) {
    uploading.value = false
    if (options.onError) options.onError(error)
    if (props.onError) props.onError(error, file)
  }
}
const submit = () => {
  if (uploadRef.value) {
    uploadRef.value.submit()
const downloadBlob = (blob, filename) => {
  const downloadElement = document.createElement('a')
  const href = window.URL.createObjectURL(blob)
  downloadElement.href = href
  downloadElement.download = filename
  document.body.appendChild(downloadElement)
  downloadElement.click()
  document.body.removeChild(downloadElement)
  window.URL.revokeObjectURL(href)
}
const handleConfirm = () => {
  if (!fileList.value || fileList.value.length === 0) {
    ElMessage.warning('请选择文件')
    return
  }
  uploadRef.value.submit()
}
const handleCancel = () => {
@@ -153,14 +228,15 @@
  emit('download-template')
}
const clearFiles = () => {
  if (uploadRef.value) {
    uploadRef.value.clearFiles()
  }
}
defineExpose({
  uploadRef,
  submit,
  clearFiles: () => {
    if (uploadRef.value) {
      uploadRef.value.clearFiles()
    }
  }
  clearFiles
})
</script>
@@ -169,4 +245,3 @@
  text-align: center;
}
</style>
src/views/basicData/product/index.vue
@@ -172,6 +172,7 @@
        :limit="1"
        accept=".xlsx,.xls"
        :action="importUpload.url"
        :http-request="importUpload.httpRequest"
        :headers="importUpload.headers"
        :before-upload="importUpload.beforeUpload"
        :on-success="importUpload.onSuccess"
@@ -198,6 +199,7 @@
<script setup>
import { ref, reactive, onMounted } from "vue";
import axios from "axios";
import { ElMessageBox } from "element-plus";
import { Plus } from "@element-plus/icons-vue";
import { getToken } from "@/utils/auth.js";
@@ -314,12 +316,79 @@
});
const { modelForm, modelRules } = toRefs(data);
const downloadImportErrorFile = (blob, filename = "import-error.xlsx") => {
  const downloadElement = document.createElement("a");
  const href = window.URL.createObjectURL(blob);
  downloadElement.href = href;
  downloadElement.download = filename;
  document.body.appendChild(downloadElement);
  downloadElement.click();
  document.body.removeChild(downloadElement);
  window.URL.revokeObjectURL(href);
};
const tryParseJsonBlob = async (blob) => {
  try {
    const text = await blob.text();
    if (!text || !text.trim()) {
      return null;
    }
    return JSON.parse(text);
  } catch (_) {
    return null;
  }
};
const importUpload = reactive({
  title: "产品导入",
  open: false,
  url: import.meta.env.VITE_APP_BASE_API + "/basic/product/import",
  headers: { Authorization: "Bearer " + getToken() },
  isUploading: false,
  httpRequest: async (options) => {
    const { file, onProgress, onSuccess, onError } = options;
    importUpload.isUploading = true;
    const formData = new FormData();
    formData.append("file", file);
    try {
      const response = await axios({
        url: importUpload.url,
        method: "post",
        headers: {
          ...importUpload.headers,
          "Content-Type": "multipart/form-data",
        },
        data: formData,
        responseType: "blob",
        onUploadProgress: (progressEvent) => {
          const total = progressEvent.total || 1;
          const percent = Math.round((progressEvent.loaded * 100) / total);
          onProgress?.({ percent }, file);
        },
      });
      importUpload.isUploading = false;
      const blob = response.data;
      // Contract: success => empty response body; failure => binary error file.
      if (!blob || blob.size === 0) {
        onSuccess?.({ code: 200, msg: "import success" }, file);
        return;
      }
      const json = await tryParseJsonBlob(blob);
      if (json) {
        if (String(json.code) === "200" || json.success === true) {
          onSuccess?.(json, file);
        } else {
          onError?.(new Error(json.msg || json.message || "import failed"), file);
        }
        return;
      }
      downloadImportErrorFile(blob);
      onError?.(new Error("import failed, error file downloaded"), file);
    } catch (error) {
      importUpload.isUploading = false;
      onError?.(error, file);
    }
  },
  beforeUpload: (file) => {
    const isExcel = file.name.endsWith('.xlsx') || file.name.endsWith('.xls');
    const isLt10M = file.size / 1024 / 1024 < 10;
@@ -342,7 +411,7 @@
  onSuccess: (response, file, fileList) => {
    console.log('上传成功', response, file, fileList);
    importUpload.isUploading = false;
    if (response.code === 200) {
    if (String(response?.code) === "200" || response?.success === true) {
      proxy.$modal.msgSuccess("导入成功");
      importDia.value = false;
      if (importUploadRef.value) {
@@ -621,11 +690,6 @@
}
:deep(.el-upload--picture-card) {
  width: 148px;
  height: 148px;
}
:deep(.el-upload-list__item) {
  width: 148px;
  height: 148px;
}
src/views/productionManagement/productStructure/index.vue
@@ -45,7 +45,7 @@
    <!-- BOM导入对话框 -->
    <ImportDialog ref="uploadRef" v-model="upload.open" :title="upload.title" :action="upload.url"
      :headers="upload.headers" :disabled="upload.isUploading" :on-progress="handleFileUploadProgress"
      :on-success="handleFileSuccess" :show-download-template="true" @confirm="submitFileForm"
      :on-success="handleFileSuccess" :on-error="handleFileError" :show-download-template="true" @confirm="submitFileForm"
      @download-template="handleDownloadTemplate" @close="handleImportClose" />
  </div>
</template>
@@ -334,7 +334,7 @@
};
//  文件上传成功处理
const handleFileSuccess = (response, file, fileList) => {
const handleFileSuccess = (response, file) => {
  upload.open = false;
  upload.isUploading = false;
  proxy.$refs["uploadRef"].clearFiles();
@@ -342,13 +342,42 @@
    proxy.$modal.msgSuccess(response.msg || "导入成功");
    getList();
  } else {
    proxy.$alert("<div style='overflow: auto;overflow-x: hidden;max-height: 70vh;padding: 10px 20px 0;'>" + response.msg + "</div>", "导入结果", { dangerouslyUseHTMLString: true });
    proxy.$modal.msgError(response.msg || "导入失败");
  }
};
// 提交上传文件
// 文件上传失败处理 - 后端返回错误文件流
const handleFileError = (error, file) => {
  upload.open = false;
  upload.isUploading = false;
  proxy.$refs["uploadRef"].clearFiles();
  // error 可能是 Blob 对象(后端返回的错误文件)
  if (error instanceof Blob) {
    // 下载错误文件
    const blob = error;
    const downloadElement = document.createElement('a');
    const href = window.URL.createObjectURL(blob);
    downloadElement.href = href;
    downloadElement.download = "导入错误数据.xlsx";
    document.body.appendChild(downloadElement);
    downloadElement.click();
    document.body.removeChild(downloadElement);
    window.URL.revokeObjectURL(href);
    proxy.$modal.msgError("导入失败,请查看下载的错误文件");
  } else if (error && error.msg) {
    // 后端返回的错误信息
    proxy.$modal.msgError(error.msg);
  } else {
    // 普通错误
    proxy.$modal.msgError("导入失败");
  }
};
// 提交上传文件 - 现在由 ImportDialog 内部处理
const submitFileForm = () => {
  proxy.$refs["uploadRef"].submit();
  // ImportDialog 的 handleConfirm 会调用 uploadRef.value.submit()
  // 这里不需要额外操作
};
//  导出按钮操作