From 474c2a6516139ccbafecd5fc3d139ee9104ecfd5 Mon Sep 17 00:00:00 2001
From: gaoluyang <2820782392@qq.com>
Date: 星期二, 31 三月 2026 17:28:21 +0800
Subject: [PATCH] 军泰伟业app 1.销售台账、采购台账选择产品逻辑修改 2.新增生产订单时将产品那边的图纸带过来 3.生产订单增加退料功能

---
 src/views/productionManagement/productionOrder/index.vue |  379 +++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 344 insertions(+), 35 deletions(-)

diff --git a/src/views/productionManagement/productionOrder/index.vue b/src/views/productionManagement/productionOrder/index.vue
index 40f3ba1..7984915 100644
--- a/src/views/productionManagement/productionOrder/index.vue
+++ b/src/views/productionManagement/productionOrder/index.vue
@@ -39,6 +39,19 @@
             :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''"
           />
         </template>
+        <template #productWorkOrders="{ row }">
+          <div class="work-order-circles">
+            <div
+              v-for="(workOrder, index) in row.productWorkOrders"
+              :key="index"
+              class="work-order-circle"
+              :class="getWorkOrderColorClass(workOrder.color)"
+              :title="workOrder.processName || workOrder.workOrderNo"
+            >
+              {{ workOrder.processName ? workOrder.processName.substring(0, 2) : index + 1 }}
+            </div>
+          </div>
+        </template>
       </PIMTable>
     </div>
     <el-dialog v-model="bindRouteDialogVisible"
@@ -72,19 +85,46 @@
 
     <!-- 鏌ョ湅鎶曞叆寮规 -->
     <el-dialog v-model="inputDialogVisible"
-               title="鎶曞叆"
-               width="1000px">
-      <PIMTable
-        rowKey="id"
-        :column="inputTableColumn"
-        :tableData="inputTableData"
-        :page="inputPage"
-        :tableLoading="inputTableLoading"
-        @pagination="handleInputPagination"
-      />
+               title="鎶曞叆涓庨��鏂�"
+               width="1200px"
+               :close-on-click-modal="false">
+      <el-table
+        :data="inputTableData"
+        border
+        size="small"
+        @selection-change="handleInputSelectionChange"
+        :header-cell-style="{ background: '#f5f7fa' }"
+        show-summary
+        :summary-method="summarizeInputTable"
+      >
+        <el-table-column type="selection" width="50" align="center" />
+        <el-table-column label="鎶曞叆浜у搧鍚嶇О" prop="productName" min-width="120" />
+        <el-table-column label="鍥剧焊缂栧彿" prop="model" min-width="100" />
+        <el-table-column label="鎶曞叆鏁伴噺" prop="quantity" min-width="100" align="center" />
+        <el-table-column label="宸查��鏂欐暟閲�" prop="returnQuantity" min-width="100" align="center" />
+        <el-table-column label="鍗曚綅" prop="unit" min-width="80" align="center" />
+        <el-table-column label="鏈閫�鏂欐暟閲�" min-width="180" align="center" prop="currentReturnQuantity">
+          <template #default="{ row }">
+            <el-input-number
+              v-model="row.currentReturnQuantity"
+              :min="0"
+              :max="row.quantity - (row.returnQuantity || 0)"
+              :precision="0"
+              size="small"
+              style="width: 160px"
+              @change="(val) => handleInputReturnQuantityChange(val, row)"
+            />
+          </template>
+        </el-table-column>
+      </el-table>
+      <div class="picking-footer-info">
+        <span>宸查�� {{ inputSelectedRows.length }} 鏉�</span>
+        <span>{{ inputTableData.length }} 鏉¤褰�</span>
+      </div>
       <template #footer>
         <div class="dialog-footer">
-          <el-button type="primary" @click="inputDialogVisible = false">鍏抽棴</el-button>
+          <el-button type="warning" @click="handleReturnSubmit">纭閫�鏂�</el-button>
+          <el-button @click="inputDialogVisible = false">鍏抽棴</el-button>
         </div>
       </template>
     </el-dialog>
@@ -138,13 +178,47 @@
       </template>
     </el-dialog>
 
+    <!-- 鏌ョ湅鍥剧焊寮圭獥 -->
+    <el-dialog
+      v-model="drawingDialogVisible"
+      title="鏌ョ湅鍥剧焊"
+      width="800px"
+      :close-on-click-modal="false"
+    >
+      <div class="drawing-container">
+        <div v-if="currentDrawingFiles.length === 0" class="empty-drawing">
+          <el-empty description="鏆傛棤鍥剧焊" />
+        </div>
+        <div v-else class="drawing-list">
+          <div v-for="(file, index) in currentDrawingFiles" :key="index" class="drawing-item">
+            <el-image
+              v-if="isImage(file.name)"
+              :src="getFileUrl(file)"
+              :preview-src-list="imagePreviewList"
+              fit="cover"
+              style="width: 150px; height: 150px; border-radius: 4px; cursor: pointer;"
+            />
+            <div v-else class="file-item" @click="downloadFile(file)">
+              <el-icon :size="50"><Document /></el-icon>
+              <span class="file-name">{{ file.name }}</span>
+            </div>
+          </div>
+        </div>
+      </div>
+      <template #footer>
+        <div class="dialog-footer">
+          <el-button @click="drawingDialogVisible = false">鍏抽棴</el-button>
+        </div>
+      </template>
+    </el-dialog>
+
   </div>
 </template>
 
 <script setup>
   import { onMounted, ref, computed } from "vue";
   import { ElMessageBox } from "element-plus";
-  import { Setting } from '@element-plus/icons-vue';
+  import { Setting, Document } from '@element-plus/icons-vue';
   import dayjs from "dayjs";
   import { useRouter } from "vue-router";
   import {
@@ -154,7 +228,7 @@
 } from "@/api/productionManagement/productionOrder.js";
 import { listPage as getProcessRouteList } from "@/api/productionManagement/processRoute.js";
   import { listMain as getOrderProcessRouteMain } from "@/api/productionManagement/productProcessRoute.js";
-  import { productionProductInputListPage } from "@/api/productionManagement/productionProductInput.js";
+  import { productionProductInputListPage, returnMaterial } from "@/api/productionManagement/productionProductInput.js";
   import { listPage as listProductStructureRecord, pick as pickMaterial } from "@/api/productionManagement/productStructureRecord.js";
 
   import {fileDel} from "@/api/financialManagement/revenueManagement.js";
@@ -165,6 +239,66 @@
 
   const router = useRouter();
   const isShowNewModal = ref(false);
+
+  // 鏌ョ湅鍥剧焊鐩稿叧
+  const drawingDialogVisible = ref(false);
+  const currentDrawingFiles = ref([]);
+
+  // 鍒ゆ柇鏄惁涓哄浘鐗�
+  const isImage = (fileName) => {
+    if (!fileName) return false;
+    const ext = fileName.toLowerCase().split('.').pop();
+    return ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'webp'].includes(ext);
+  };
+
+  // 鑾峰彇鏂囦欢URL
+  const getFileUrl = (file) => {
+    if (file.url) {
+      if (file.url.startsWith('http')) {
+        return file.url;
+      }
+      return import.meta.env.VITE_APP_BASE_API + file.url;
+    }
+    return '';
+  };
+
+  // 鍥剧墖棰勮鍒楄〃
+  const imagePreviewList = computed(() => {
+    return currentDrawingFiles.value
+      .filter(file => isImage(file.name))
+      .map(file => getFileUrl(file));
+  });
+
+  // 涓嬭浇鏂囦欢
+  const downloadFile = (file) => {
+    const url = getFileUrl(file);
+    if (url) {
+      const link = document.createElement('a');
+      link.href = url;
+      link.download = file.name;
+      document.body.appendChild(link);
+      link.click();
+      document.body.removeChild(link);
+    }
+  };
+
+  // 鏄剧ず鏌ョ湅鍥剧焊寮圭獥
+  const showDrawingDialog = (row) => {
+    currentDrawingFiles.value = row.salesLedgerFiles || [];
+    drawingDialogVisible.value = true;
+  };
+
+  // 鑾峰彇宸ュ崟鍦嗗湀棰滆壊绫诲悕
+  // color: 1-鐏拌壊 2-榛勮壊 3-缁胯壊 4-绾㈣壊
+  const getWorkOrderColorClass = (color) => {
+    const colorMap = {
+      1: 'gray',
+      2: 'yellow',
+      3: 'green',
+      4: 'red'
+    };
+    return colorMap[color] || 'gray';
+  };
 
   const tableColumn = ref([
     {
@@ -201,6 +335,13 @@
     },
     {
       dataType: "slot",
+      label: "宸ュ簭",
+      prop: "productWorkOrders",
+      slot: "productWorkOrders",
+      width: 200,
+    },
+    {
+      dataType: "slot",
       label: "瀹屾垚杩涘害",
       prop: "completionStatus",
       slot: "completionStatus",
@@ -229,7 +370,7 @@
       label: "鎿嶄綔",
       align: "center",
       fixed: "right",
-      width: 360,
+      width: 420,
       operation: [
         {
           name: "寮�濮�",
@@ -277,10 +418,18 @@
           },
         },
         {
-          name: "鏌ョ湅鎶曞叆",
+          name: "鎶曞叆/閫�鏂�",
           type: "text",
           clickFun: row => {
             showInputDialog(row);
+          },
+        },
+        {
+          name: "鏌ョ湅鍥剧焊",
+          type: "text",
+          showHide: row => row.salesLedgerFiles && row.salesLedgerFiles.length > 0,
+          clickFun: row => {
+            showDrawingDialog(row);
           },
         },
       ],
@@ -300,29 +449,12 @@
   const inputTableData = ref([]);
   const inputTableLoading = ref(false);
   const inputCurrentRow = ref(null);
+  const inputSelectedRows = ref([]);
   const inputPage = reactive({
     current: 1,
     size: 100,
     total: 0,
   });
-  const inputTableColumn = ref([
-    {
-      label: '鎶曞叆浜у搧鍚嶇О',
-      prop: 'productName',
-    },
-    {
-      label: '鍥剧焊缂栧彿',
-      prop: 'model'
-    },
-    {
-      label: '鎶曞叆鏁伴噺',
-      prop: 'quantity',
-    },
-    {
-      label: '鍗曚綅',
-      prop: 'unit',
-    },
-  ]);
 
   // 棰嗘枡鐩稿叧
   const pickingDialogVisible = ref(false);
@@ -331,6 +463,8 @@
   const pickingForm = reactive({
     orderId: null,
   });
+
+
 
   const data = reactive({
     searchForm: {
@@ -577,11 +711,12 @@
   const handleConfirmRoute = () => {};
 
   // 鏄剧ず鏌ョ湅鎶曞叆寮规
-  const showInputDialog = (row) => {
+  const showInputDialog = async (row) => {
     inputCurrentRow.value = row;
     inputDialogVisible.value = true;
     inputPage.current = 1;
     inputPage.total = 0;
+    inputSelectedRows.value = [];
     fetchInputData();
   };
 
@@ -599,7 +734,10 @@
     productionProductInputListPage(params)
       .then(res => {
         inputTableLoading.value = false;
-        inputTableData.value = res.data.records;
+        inputTableData.value = res.data.records.map(item => ({
+          ...item,
+          currentReturnQuantity: 0,
+        }));
         inputPage.total = res.data.total;
       })
       .catch(err => {
@@ -687,6 +825,58 @@
     ]);
   };
 
+  // 鎶曞叆琛ㄦ牸閫夋嫨鍙樺寲
+  const handleInputSelectionChange = (selection) => {
+    inputSelectedRows.value = selection;
+  };
+
+  // 鎶曞叆閫�鏂欐暟閲忓彉鍖栧鐞�
+  const handleInputReturnQuantityChange = (val, row) => {
+    const maxReturn = row.quantity - (row.returnQuantity || 0);
+    if (val > maxReturn) {
+      proxy.$modal.msgWarning("鏈閫�鏂欐暟閲忎笉鑳借秴杩囧墿浣欏彲閫�鏁伴噺");
+      row.currentReturnQuantity = maxReturn;
+    }
+  };
+
+  // 纭閫�鏂�
+  const handleReturnSubmit = async () => {
+    if (inputSelectedRows.value.length === 0) {
+      proxy.$modal.msgWarning("璇烽�夋嫨瑕侀��鏂欑殑鐗╂枡");
+      return;
+    }
+    // 鏍¢獙閫�鏂欐暟閲�
+    const invalidRows = inputSelectedRows.value.filter(row => !row.currentReturnQuantity || row.currentReturnQuantity <= 0);
+    if (invalidRows.length > 0) {
+      proxy.$modal.msgWarning("璇峰~鍐欐湰娆¢��鏂欐暟閲�");
+      return;
+    }
+
+    const returnData = inputSelectedRows.value.map(row => ({
+      ...row,
+      returnQuantity: row.currentReturnQuantity,
+    }));
+
+    try {
+      await returnMaterial(returnData);
+      proxy.$modal.msgSuccess("閫�鏂欐垚鍔�");
+      inputDialogVisible.value = false;
+      getList();
+    } catch (e) {
+      console.error("閫�鏂欏け璐ワ細", e);
+      proxy.$modal.msgError("閫�鏂欏け璐�");
+    }
+  };
+
+  // 鎶曞叆琛ㄦ牸鍚堣鏂规硶
+  const summarizeInputTable = (param) => {
+    return proxy.summarizeTable(param, [
+      "quantity",
+      "returnQuantity",
+      "currentReturnQuantity",
+    ]);
+  };
+
   onMounted(() => {
     getList();
   });
@@ -721,4 +911,123 @@
   color: #606266;
 }
 
+.drawing-container {
+  min-height: 200px;
+}
+
+.empty-drawing {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  min-height: 200px;
+}
+
+.drawing-list {
+  display: flex;
+  flex-wrap: wrap;
+  gap: 16px;
+  padding: 10px;
+}
+
+.drawing-item {
+  border: 1px solid #dcdfe6;
+  border-radius: 4px;
+  overflow: hidden;
+  transition: all 0.3s;
+  
+  &:hover {
+    box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+  }
+}
+
+.file-item {
+  width: 150px;
+  height: 150px;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  background: #f5f7fa;
+  cursor: pointer;
+  padding: 10px;
+  
+  .file-name {
+    font-size: 12px;
+    color: #606266;
+    margin-top: 10px;
+    text-align: center;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    max-width: 130px;
+  }
+  
+  &:hover {
+    background: #e4e7ed;
+  }
+}
+
+// 宸ュ崟鍦嗗湀鏍峰紡
+.work-order-circles {
+  display: flex;
+  gap: 8px;
+  flex-wrap: wrap;
+  align-items: center;
+}
+
+.work-order-circle {
+  width: 32px;
+  height: 32px;
+  border-radius: 6px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 12px;
+  font-weight: 500;
+  color: #fff;
+  cursor: pointer;
+  transition: all 0.2s ease;
+  
+  // 鐏拌壊 - 寰呭紑濮�
+  &.gray {
+    background-color: #909399;
+    
+    &:hover {
+      background-color: #a6a9ad;
+    }
+  }
+  
+  // 榛勮壊 - 杩涜涓�
+  &.yellow {
+    background-color: #e6a23c;
+    
+    &:hover {
+      background-color: #ebb563;
+    }
+  }
+  
+  // 缁胯壊 - 宸插畬鎴�
+  &.green {
+    background-color: #67c23a;
+    
+    &:hover {
+      background-color: #85ce61;
+    }
+  }
+  
+  // 绾㈣壊 - 寮傚父/鏆傚仠
+  &.red {
+    background-color: #f56c6c;
+    
+    &:hover {
+      background-color: #f78989;
+    }
+  }
+  
+  &:hover {
+    transform: translateY(-2px);
+    box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
+  }
+}
+
 </style>

--
Gitblit v1.9.3