From 0ede6469f1507b81dd827efc36590cfd9773ebde Mon Sep 17 00:00:00 2001
From: zhangwencui <1064582902@qq.com>
Date: 星期四, 21 五月 2026 09:36:41 +0800
Subject: [PATCH] 生产订单增加附件的查看

---
 src/views/productionManagement/processRoute/processRouteItem/index.vue |  304 +++++++++++++++++++++++++++++++++++++++++++++----
 1 files changed, 275 insertions(+), 29 deletions(-)

diff --git a/src/views/productionManagement/processRoute/processRouteItem/index.vue b/src/views/productionManagement/processRoute/processRouteItem/index.vue
index fd1a6a8..3c52410 100644
--- a/src/views/productionManagement/processRoute/processRouteItem/index.vue
+++ b/src/views/productionManagement/processRoute/processRouteItem/index.vue
@@ -47,16 +47,45 @@
             <span class="info-value">{{ routeInfo.quantity || '-' }}</span>
           </div>
         </div>
-        <div class="info-item full-width"
-             v-if="routeInfo.description">
+        <div class="info-item">
           <div class="info-label-wrapper">
-            <span class="info-label">鎻忚堪</span>
+            <span class="info-label">澶囨敞</span>
           </div>
           <div class="info-value-wrapper">
             <span class="info-value">{{ routeInfo.description }}</span>
           </div>
         </div>
       </div>
+    </el-card>
+    <!-- 闄勪欢妯″潡 -->
+    <div v-if="pageType === 'order'"
+         class="section-header">
+      <div class="section-title">闄勪欢</div>
+    </div>
+    <el-card v-if="pageType === 'order'"
+             class="attachment-card"
+             shadow="hover"
+             style="margin-top: 10px; margin-bottom: 20px;">
+      <el-table :data="attachmentTableData"
+                border
+                class="attachment-table">
+        <el-table-column label="闄勪欢鍚嶇О"
+                         prop="originalFilename"
+                         show-overflow-tooltip />
+        <el-table-column fixed="right"
+                         label="鎿嶄綔"
+                         width="200"
+                         align="center">
+          <template #default="scope">
+            <el-button link
+                       type="primary"
+                       size="small"
+                       @click="downloadAttachmentFile(scope.row.downloadURL)">
+              涓嬭浇
+            </el-button>
+          </template>
+        </el-table-column>
+      </el-table>
     </el-card>
     <!-- 琛ㄦ牸瑙嗗浘 -->
     <div v-if="viewMode === 'table'"
@@ -68,7 +97,8 @@
                    style="margin-right: 10px;">
           鍗$墖瑙嗗浘
         </el-button>
-        <el-button type="primary"
+        <el-button v-if="editable"
+                   type="primary"
                    @click="handleAdd">鏂板</el-button>
       </div>
     </div>
@@ -110,6 +140,13 @@
       <el-table-column label="鍗曚綅"
                        prop="unit"
                        width="100" />
+      <el-table-column label="璁¤垂绫诲瀷"
+                       prop="type"
+                       width="100">
+        <template #default="scope">
+          {{scope.row.type==0 ? "璁℃椂" : "璁′欢"}}
+        </template>
+      </el-table-column>
       <el-table-column label="鏄惁璐ㄦ"
                        prop="isQuality"
                        width="100">
@@ -133,12 +170,12 @@
                      link
                      size="small"
                      @click="handleEdit(scope.row)"
-                     :disabled="scope.row.isComplete">缂栬緫</el-button>
+                     :disabled="scope.row.isComplete || !editable">缂栬緫</el-button>
           <el-button type="danger"
                      link
                      size="small"
                      @click="handleDelete(scope.row)"
-                     :disabled="scope.row.isComplete">鍒犻櫎</el-button>
+                     :disabled="scope.row.isComplete || !editable">鍒犻櫎</el-button>
         </template>
       </el-table-column>
     </el-table>
@@ -152,7 +189,8 @@
                      style="margin-right: 10px;">
             琛ㄦ牸瑙嗗浘
           </el-button>
-          <el-button type="primary"
+          <el-button v-if="editable"
+                     type="primary"
                      @click="handleAdd">鏂板</el-button>
         </div>
       </div>
@@ -179,12 +217,16 @@
                   {{ item.model }}
                   <!-- <span v-if="item.unit" class="product-unit">{{ item.unit }}</span> -->
                 </div>
+                <el-tag class="product-tag"
+                        :type="item.type == 1 ? 'primary' : 'success'"
+                        style="margin-left: 8px;">{{ item.type==0?'璁℃椂':'璁′欢' }}</el-tag>
                 <el-tag type="primary"
                         class="product-tag"
+                        style="margin-left: 8px;"
                         v-if="item.isQuality">璐ㄦ</el-tag>
                 <el-tag type="primary"
                         class="product-tag"
-                        :style="item.isQuality?'margin-left:8px':''"
+                        style="margin-left: 8px;"
                         v-if="item.isProduction">鐢熶骇</el-tag>
               </div>
               <div v-else
@@ -196,7 +238,7 @@
                          link
                          size="small"
                          @click="handleEdit(item)"
-                         :disabled="item.isComplete">缂栬緫</el-button>
+                         :disabled="item.isComplete || !editable">缂栬緫</el-button>
               <el-button type="info"
                          link
                          size="small"
@@ -205,7 +247,7 @@
                          link
                          size="small"
                          @click="handleDelete(item)"
-                         :disabled="item.isComplete">鍒犻櫎</el-button>
+                         :disabled="item.isComplete || !editable">鍒犻櫎</el-button>
             </div>
           </div>
         </div>
@@ -216,7 +258,7 @@
          style="margin-top: 20px;">
       <div class="section-title">BOM 缁撴瀯</div>
       <div class="section-actions"
-           v-if="pageType === 'order'">
+           v-if="pageType === 'order' && editable">
         <el-button v-if="!bomDataValue.isEdit"
                    type="primary"
                    @click="bomDataValue.isEdit = true">
@@ -302,7 +344,7 @@
                                      :step="1"
                                      controls-position="right"
                                      style="width: 100%"
-                                     @change="handleUnitQuantityChange(row)"
+                                     @change="handleUnitQuantityChange"
                                      :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
                   </el-form-item>
                 </template>
@@ -320,7 +362,7 @@
                                      :step="1"
                                      controls-position="right"
                                      style="width: 100%"
-                                     :disabled="!bomDataValue.isEdit || bomDataValue.dataList.some(item => (item).tempId === row.tempId)" />
+                                     :disabled="true" />
                   </el-form-item>
                 </template>
               </el-table-column>
@@ -369,6 +411,18 @@
                          v-model="bomDataValue.showProductDialog"
                          :single="true"
                          @confirm="handleBomProduct" />
+    <!-- 涓婁紶缁勪欢寮圭獥 -->
+    <el-dialog v-model="uploadDialogVisible"
+               title="涓婁紶闄勪欢"
+               width="50%"
+               @close="closeAttachmentUpload">
+      <AttachmentUpload v-model:file-list="newFileList" />
+      <template #footer>
+        <el-button @click="saveAttachmentUpload"
+                   type="primary">淇濆瓨</el-button>
+        <el-button @click="closeAttachmentUpload">鍏抽棴</el-button>
+      </template>
+    </el-dialog>
     <!-- 鏂板/缂栬緫寮圭獥 -->
     <el-dialog v-model="dialogVisible"
                :title="operationType === 'add' ? '鏂板宸ヨ壓璺嚎椤圭洰' : '缂栬緫宸ヨ壓璺嚎椤圭洰'"
@@ -422,6 +476,13 @@
                       v-else>
           <span>{{ form.unit }}</span>
         </el-form-item>
+        <el-form-item label="璁¤垂绫诲瀷"
+                      prop="type">
+          <el-radio-group v-model="form.type">
+            <el-radio :label="0">璁℃椂</el-radio>
+            <el-radio :label="1">璁′欢</el-radio>
+          </el-radio-group>
+        </el-form-item>
         <el-form-item label="鏄惁璐ㄦ"
                       prop="isQuality">
           <el-switch v-model="form.isQuality"
@@ -447,7 +508,6 @@
                          @confirm="handleProductSelect"
                          single />
     <!-- 鍙傛暟鍒楄〃瀵硅瘽妗� -->
-    <!-- :editable="!routeInfo.status" -->
     <ProcessParamListDialog v-model="showParamListDialog"
                             :title="`${currentProcess ? (currentProcess.processName || currentProcess.technologyOperationName || currentProcess.operationName) : ''} - 鍙傛暟鍒楄〃`"
                             :route-id="routeId"
@@ -455,6 +515,7 @@
                             :process="currentProcess"
                             :page-type="pageType"
                             :param-list="paramList"
+                            :editable="editable"
                             @getsyncProcessParamItem="getsyncProcessParamItem"
                             @refresh="refreshParamList" />
   </div>
@@ -498,6 +559,12 @@
     queryList2,
     add2,
   } from "@/api/productionManagement/productStructure.js";
+  import AttachmentUpload from "@/components/AttachmentUpload/file/index.vue";
+  import {
+    attachmentList,
+    deleteAttachment,
+    createAttachment,
+  } from "@/api/basicData/storageAttachment.js";
 
   import { useRoute } from "vue-router";
   import { ElMessageBox, ElMessage } from "element-plus";
@@ -509,6 +576,8 @@
   const routeId = computed(() => route.query.id);
   const orderId = computed(() => route.query.orderId);
   const pageType = computed(() => route.query.type);
+  const editable = computed(() => route.query.editable !== "false");
+  const technologyRoutingId = computed(() => route.query.technologyRoutingId);
 
   const tableLoading = ref(false);
   const tableData = ref([]);
@@ -527,7 +596,66 @@
     bomNo: "",
     description: "",
     quantity: 0,
+    technologyRoutingId: "",
   });
+
+  // 闄勪欢鐩稿叧
+  const attachmentTableData = ref([]);
+  const uploadDialogVisible = ref(false);
+  const newFileList = ref([]);
+
+  const getAttachmentList = () => {
+    if (!technologyRoutingId.value) return;
+    attachmentList({
+      recordType: "technology_routing",
+      recordId: technologyRoutingId.value,
+    }).then(res => {
+      attachmentTableData.value = (res && res.data) || [];
+    });
+  };
+
+  const handleUploadAttachment = () => {
+    uploadDialogVisible.value = true;
+  };
+
+  const saveAttachmentUpload = async () => {
+    if (newFileList.value.length > 0) {
+      createAttachment({
+        application: "file",
+        recordType: "technology_routing",
+        recordId: technologyRoutingId.value,
+        storageBlobDTOs: [...newFileList.value, ...attachmentTableData.value],
+      })
+        .then(res => {
+          if (res && res.code === 200) {
+            proxy?.$modal?.msgSuccess("涓婁紶鎴愬姛");
+            newFileList.value = [];
+            getAttachmentList();
+          }
+        })
+        .finally(() => {
+          uploadDialogVisible.value = false;
+        });
+    }
+  };
+
+  const closeAttachmentUpload = () => {
+    newFileList.value = [];
+    uploadDialogVisible.value = false;
+  };
+
+  const handleDeleteAttachment = async row => {
+    deleteAttachment([row.storageAttachmentId]).then(res => {
+      if (res && res.code === 200) {
+        proxy?.$modal?.msgSuccess("鍒犻櫎鎴愬姛");
+        getAttachmentList();
+      }
+    });
+  };
+
+  const downloadAttachmentFile = url => {
+    window.open(url, "_blank");
+  };
 
   const processOptions = ref([]);
   const showProductSelectDialog = ref(false);
@@ -555,6 +683,7 @@
     model: "",
     unit: "",
     isQuality: false,
+    type: 0,
     isProduction: false,
   });
 
@@ -658,6 +787,7 @@
       bomId: route.query.bomId || "",
       description: route.query.description || "",
       quantity: route.query.quantity || 0,
+      technologyRoutingId: route.query.technologyRoutingId || "",
       status: !(route.query.status == 1 || route.query.status === "false"),
     };
     bomTableData.value[0].productName = routeInfo.value.productName;
@@ -684,6 +814,7 @@
       model: row.model || "",
       unit: row.unit || "",
       isQuality: row.isQuality,
+      type: row.type || 0,
       isProduction: row.isProduction,
     };
     dialogVisible.value = true;
@@ -755,6 +886,7 @@
                 operationName: getProcessName(form.value.technologyOperationId),
                 productModelId: form.value.productModelId,
                 isQuality: form.value.isQuality,
+                type: form.value.type,
                 isProduction: form.value.isProduction,
                 dragSort,
               })
@@ -763,6 +895,7 @@
                 technologyOperationId: form.value.technologyOperationId,
                 productModelId: form.value.productModelId,
                 isQuality: form.value.isQuality,
+                type: form.value.type,
                 isProduction: form.value.isProduction,
                 dragSort,
               });
@@ -790,6 +923,7 @@
                 operationName: getProcessName(form.value.technologyOperationId),
                 productModelId: form.value.productModelId,
                 isQuality: form.value.isQuality,
+                type: form.value.type,
                 isProduction: form.value.isProduction,
               })
             : addOrUpdateProcessRouteItem1({
@@ -798,6 +932,7 @@
                 productModelId: form.value.productModelId,
                 id: form.value.id,
                 isQuality: form.value.isQuality,
+                type: form.value.type,
                 isProduction: form.value.isProduction,
               });
 
@@ -829,6 +964,7 @@
       model: "",
       unit: "",
       isQuality: false,
+      type: 0,
       isProduction: false,
     };
     formRef.value?.resetFields();
@@ -878,6 +1014,7 @@
   // 鍒濆鍖栨嫋鎷芥帓搴�
   const initSortable = () => {
     destroySortable();
+    if (!editable.value) return;
 
     if (viewMode.value === "table") {
       // 琛ㄦ牸瑙嗗浘鐨勬嫋鎷芥帓搴�
@@ -1060,18 +1197,97 @@
       }
     });
   };
+
+  const toQuantityNumber = value => {
+    const numberValue = Number(value);
+    if (!Number.isFinite(numberValue)) {
+      return 0;
+    }
+    return Number(numberValue.toFixed(2));
+  };
+
+  const syncDemandedQuantityTree = (items, parentDemandedQuantity = null) => {
+    items.forEach(item => {
+      if (parentDemandedQuantity !== null) {
+        item.demandedQuantity = toQuantityNumber(
+          parentDemandedQuantity * toQuantityNumber(item.unitQuantity)
+        );
+      }
+
+      if (Array.isArray(item.children) && item.children.length > 0) {
+        syncDemandedQuantityTree(
+          item.children,
+          toQuantityNumber(item.demandedQuantity)
+        );
+      }
+    });
+  };
+
+  const recalculateDemandedQuantities = () => {
+    if (pageType.value !== "order") {
+      return;
+    }
+
+    const rootDemandedQuantity = routeInfo.value.quantity;
+    if (
+      rootDemandedQuantity === undefined ||
+      rootDemandedQuantity === null ||
+      rootDemandedQuantity === ""
+    ) {
+      syncDemandedQuantityTree(bomDataValue.value.dataList);
+      return;
+    }
+
+    syncDemandedQuantityTree(
+      bomDataValue.value.dataList,
+      toQuantityNumber(rootDemandedQuantity)
+    );
+  };
+
   const processChange = value => {
     processOptions.value.forEach(item => {
       if (item.id == value) {
         form.value.isQuality = item.isQuality;
+        form.value.type = item.type || 0;
         form.value.isProduction = item.isProduction;
       }
     });
   };
 
+  const findSiblings = (items, tempId) => {
+    if (!items || items.length === 0) return null;
+    // 妫�鏌ュ綋鍓嶅眰绾�
+    if (items.some(item => item.tempId === tempId)) {
+      return items;
+    }
+    // 閫掑綊鏌ユ壘瀛愮骇
+    for (const item of items) {
+      if (item.children && item.children.length > 0) {
+        const result = findSiblings(item.children, tempId);
+        if (result) return result;
+      }
+    }
+    return null;
+  };
+
   const handleBomProcessChange = (row, value) => {
     row.processId = value || "";
     syncProcessOperationFields(row);
+
+    // 妫�鏌ュ悓涓�灞傜骇鏄惁宸茬粡鏈夊叾浠栦笉鍚岀殑宸ュ簭琚�変腑
+    const siblings = findSiblings(bomDataValue.value.dataList, row.tempId);
+    if (siblings && value) {
+      const hasDifferentProcess = siblings.some(sibling => {
+        return (
+          sibling.tempId !== row.tempId &&
+          sibling.processId &&
+          sibling.processId !== value
+        );
+      });
+      if (hasDifferentProcess) {
+        ElMessage.warning("鍚屼竴灞傜骇宸插瓨鍦ㄤ笉鍚岀殑宸ュ簭锛岃鍏堢粺涓�宸ュ簭鍚庡啀杩涜淇敼");
+      }
+    }
   };
 
   const openBomDialog = tempId => {
@@ -1087,6 +1303,7 @@
       );
       bomDataValue.value.dataList = data || [];
       normalizeTreeData(bomDataValue.value.dataList);
+      recalculateDemandedQuantities();
     } catch (err) {
       console.error("鑾峰彇BOM鏁版嵁澶辫触锛�", err);
     }
@@ -1182,10 +1399,8 @@
     });
   };
 
-  const handleUnitQuantityChange = row => {
-    if (routeInfo.value.quantity && routeInfo.value.quantity !== 0) {
-      row.demandedQuantity = (row.unitQuantity || 0) * routeInfo.value.quantity;
-    }
+  const handleUnitQuantityChange = () => {
+    recalculateDemandedQuantities();
   };
 
   const addchildItem = (item, tempId) => {
@@ -1206,14 +1421,12 @@
           "",
         operationName: "",
         unitQuantity: 1,
-        demandedQuantity:
-          routeInfo.value.quantity && routeInfo.value.quantity !== 0
-            ? 1 * routeInfo.value.quantity
-            : 0,
+        demandedQuantity: 0,
         children: [],
         unit: "",
         tempId: new Date().getTime(),
       });
+      recalculateDemandedQuantities();
       return true;
     }
     if (item.children && item.children.length > 0) {
@@ -1245,14 +1458,12 @@
             "",
           operationName: "",
           unitQuantity: 1,
-          demandedQuantity:
-            routeInfo.value.quantity && routeInfo.value.quantity !== 0
-              ? 1 * routeInfo.value.quantity
-              : 0,
+          demandedQuantity: 0,
           unit: "",
           children: [],
           tempId: new Date().getTime(),
         });
+        recalculateDemandedQuantities();
         return;
       }
       addchildItem(item, tempId);
@@ -1292,9 +1503,36 @@
       }
     };
 
+    // 鏍¢獙鍚屼竴灞傜骇鐨勫伐搴忔槸鍚︿竴鑷�
+    const validateProcessConsistency = items => {
+      if (!items || items.length === 0) return;
+
+      // 妫�鏌ュ綋鍓嶅眰绾�
+      const processes = items
+        .filter(item => item.processId)
+        .map(item => item.processId);
+      if (processes.length > 1) {
+        const uniqueProcesses = [...new Set(processes)];
+        if (uniqueProcesses.length > 1) {
+          ElMessage.error("鍚屼竴灞傜骇鐨勫伐搴忓繀椤讳竴鑷�");
+          isValid = false;
+          return;
+        }
+      }
+
+      // 閫掑綊妫�鏌ュ瓙绾�
+      items.forEach(item => {
+        if (item.children && item.children.length > 0) {
+          validateProcessConsistency(item.children);
+        }
+      });
+    };
+
     bomDataValue.value.dataList.forEach(item => {
       validateItem(item, true);
     });
+
+    validateProcessConsistency(bomDataValue.value.dataList);
 
     return isValid;
   };
@@ -1320,6 +1558,7 @@
     console.log(bomDataValue.value.dataList, "bomDataValue.value.dataList");
 
     normalizeTreeData(bomDataValue.value.dataList);
+    recalculateDemandedQuantities();
 
     const valid = validateAllBom();
     if (valid) {
@@ -1331,7 +1570,7 @@
         .then(() => {
           ElMessage.success("BOM淇濆瓨鎴愬姛");
           bomDataValue.value.isEdit = false;
-          fetchBomData();
+          refreshCurrentPage();
         })
         .catch(() => {
           ElMessage.error("BOM淇濆瓨澶辫触");
@@ -1344,11 +1583,18 @@
     }
   };
 
-  onMounted(() => {
+  const refreshCurrentPage = () => {
     getRouteInfo();
     getList();
     getProcessList();
     fetchBomData();
+    if (pageType.value === "order") {
+      getAttachmentList();
+    }
+  };
+
+  onMounted(() => {
+    refreshCurrentPage();
   });
 
   onUnmounted(() => {
@@ -1615,4 +1861,4 @@
     line-height: 1.5;
     word-break: break-all;
   }
-</style>
+</style>
\ No newline at end of file

--
Gitblit v1.9.3