From 801a3bc53415f6214800c561a562d17ea58b88f1 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期三, 01 四月 2026 11:40:14 +0800
Subject: [PATCH] 军泰伟业 1.bom导入和产品导入成功和失败逻辑修改

---
 src/views/basicData/product/index.vue                     |   76 +++++++++++++++++-
 src/components/Dialog/ImportDialog.vue                    |  119 ++++++++++++++++++++++++-----
 src/views/productionManagement/productStructure/index.vue |   39 ++++++++-
 3 files changed, 201 insertions(+), 33 deletions(-)

diff --git a/src/components/Dialog/ImportDialog.vue b/src/components/Dialog/ImportDialog.vue
index 5b126dc..9388b20 100644
--- a/src/components/Dialog/ImportDialog.vue
+++ b/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 瑙f瀽涓� JSON锛堟棤璁� content-type 鏄粈涔堬級
+    try {
+      const text = await blob.text()
+      const jsonResponse = JSON.parse(text)
+      // 鎴愬姛瑙f瀽涓� 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>
-
diff --git a/src/views/basicData/product/index.vue b/src/views/basicData/product/index.vue
index b887d9d..6f1fed3 100644
--- a/src/views/basicData/product/index.vue
+++ b/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;
 }
diff --git a/src/views/productionManagement/productStructure/index.vue b/src/views/productionManagement/productStructure/index.vue
index 93f9112..fb9e7d8 100644
--- a/src/views/productionManagement/productStructure/index.vue
+++ b/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()
+  // 杩欓噷涓嶉渶瑕侀澶栨搷浣�
 };
 
 //  瀵煎嚭鎸夐挳鎿嶄綔

--
Gitblit v1.9.3