From 913e7cd145459ca10e80392819aa052454927103 Mon Sep 17 00:00:00 2001
From: ZN <zhang_12370@163.com>
Date: 星期三, 18 三月 2026 17:56:34 +0800
Subject: [PATCH] feat: 新增销售合同导出功能并增强多个模块交互

---
 src/api/salesManagement/salesLedger.js                                |   12 ++
 src/views/basicData/customerFile/index.vue                            |  110 ++++++++++++++++++++-
 src/views/productionManagement/workOrder/index.vue                    |  103 ++++++++++++++++++++
 src/views/salesManagement/salesLedger/index.vue                       |   42 +++++++
 src/views/reportAnalysis/productionAnalysis/components/center-top.vue |   16 +++
 5 files changed, 272 insertions(+), 11 deletions(-)

diff --git a/src/api/salesManagement/salesLedger.js b/src/api/salesManagement/salesLedger.js
index 6548927..22b07f6 100644
--- a/src/api/salesManagement/salesLedger.js
+++ b/src/api/salesManagement/salesLedger.js
@@ -116,4 +116,16 @@
         method: "get",
         params: query,
     });
+}
+
+// 瀵煎嚭閿�鍞悎鍚�
+// /sales/ledger/exportProcessContract/id
+
+export function exportSalesContract(query) {
+  console.log(query);
+    return request({
+        url: "/sales/ledger/exportProcessContract/" + query.id,
+        method: "get",
+        responseType: "blob",
+      });
 }
\ No newline at end of file
diff --git a/src/views/basicData/customerFile/index.vue b/src/views/basicData/customerFile/index.vue
index ad1c5bb..4e9f87a 100644
--- a/src/views/basicData/customerFile/index.vue
+++ b/src/views/basicData/customerFile/index.vue
@@ -112,6 +112,14 @@
         </el-row>
         <el-row :gutter="30">
           <el-col :span="12">
+            <el-form-item label="寮�鎴烽摱琛岋細"
+                          prop="bankName">
+              <el-input v-model="form.bankName"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
             <el-form-item label="寮�鎴疯鍙凤細"
                           prop="bankCode">
               <el-input v-model="form.bankCode"
@@ -119,6 +127,8 @@
                         clearable />
             </el-form-item>
           </el-col>
+        </el-row>
+        <el-row :gutter="30">
           <el-col :span="12">
             <el-form-item label="瀹㈡埛鍒嗙被锛�"
                           prop="customerType">
@@ -130,6 +140,47 @@
                 <el-option label="杩涢攢鍟嗗鎴�"
                            value="杩涢攢鍟嗗鎴�" />
               </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="娉曚汉"
+                          prop="corporation">
+              <el-input v-model="form.corporation"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
+            </el-form-item>
+          </el-col>
+          
+        </el-row>
+        <el-row :gutter="30">
+          <el-col :span="12">
+            <el-form-item label="浠g悊浜�"
+                          prop="agent">
+              <el-select v-model="form.agent"
+                         placeholder="璇烽�夋嫨浠g悊浜�"
+                         clearable
+                         filterable
+                         :disabled="agentOptions.length === 0"
+                         :no-data-text="agentNoDataText">
+                <el-option v-for="(contact, index) in agentOptions"
+                           :key="getAgentOptionKey(contact, index)"
+                           :label="getAgentLabel(contact)"
+                           :value="contact.contactPhone"
+                           :disabled="!contact.contactPhone">
+                  <span>{{ contact.contactPerson || "-" }}</span>
+                  <span style="float: right; color: var(--el-text-color-secondary); font-size: 12px;">
+                    {{ contact.contactPhone || "" }}
+                  </span>
+                </el-option>
+              </el-select>
+            </el-form-item>
+          </el-col>
+          <el-col :span="12">
+            <el-form-item label="浼犵湡"
+                          prop="fax">
+              <el-input v-model="form.fax"
+                        placeholder="璇疯緭鍏�"
+                        clearable />
             </el-form-item>
           </el-col>
         </el-row>
@@ -418,12 +469,7 @@
             </el-col>
           </el-row>
           <el-row :gutter="20">
-            <el-col :span="12">
-              <div class="info-item">
-                <span class="info-label">閾惰璐﹀彿锛�</span>
-                <span class="info-value">{{ detailForm.bankAccount }}</span>
-              </div>
-            </el-col>
+            
             <el-col :span="12">
               <div class="info-item">
                 <span class="info-label">寮�鎴疯鍙凤細</span>
@@ -616,7 +662,7 @@
 </template>
 
 <script setup>
-  import { onMounted, ref, reactive, getCurrentInstance, toRefs } from "vue";
+  import { onMounted, ref, reactive, getCurrentInstance, toRefs, computed, watch } from "vue";
   import { Search, Paperclip, Upload } from "@element-plus/icons-vue";
   import {
     addCustomer,
@@ -690,6 +736,8 @@
     companyPhone: "",
     companyAddress: "",
     basicBankAccount: "",
+    corporation: "",
+    fax: "",
     bankAccount: "",
     bankCode: "",
     contactPerson: "",
@@ -768,6 +816,16 @@
     {
       label: "寮�鎴疯鍙�",
       prop: "bankCode",
+      width: 220,
+    },
+    {
+      label: "娉曚汉浠h〃",
+      prop: "corporation",
+      width: 220,
+    },
+    {
+      label: "浼犵湡",
+      prop: "fax",
       width: 220,
     },
     {
@@ -856,6 +914,8 @@
       maintenanceTime: "",
       basicBankAccount: "",
       bankAccount: "",
+      fax: "",
+      corporation: "",
       bankCode: "",
       customerType: "",
     },
@@ -874,6 +934,9 @@
       ],
       basicBankAccount: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       bankAccount: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
+      corporation: [{ required: true, message: "璇疯緭鍏ユ硶浜轰唬琛�", trigger: "blur" }],
+      agent: [{ required: true, message: "璇烽�夋嫨浠g悊浜�", trigger: "change" }],
+      bankName: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       bankCode: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
       customerType: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
     },
@@ -934,6 +997,39 @@
     },
   });
   const { searchForm, form, rules } = toRefs(data);
+
+  const agentOptions = computed(() => {
+    const list = formYYs.value?.contactList || [];
+    return list.filter(item => item && (item.contactPerson || item.contactPhone));
+  });
+
+  const agentNoDataText = computed(() => {
+    if (agentOptions.value.length === 0) return "璇峰厛鏂板鑱旂郴浜�";
+    return "鏃犲尮閰嶈仈绯讳汉";
+  });
+
+  const getAgentLabel = contact => {
+    const person = (contact?.contactPerson || "").trim();
+    const phone = (contact?.contactPhone || "").trim();
+    if (person && phone) return `${person}锛�${phone}锛塦;
+    return person || phone || "-";
+  };
+
+  const getAgentOptionKey = (contact, index) => {
+    return contact?.contactPhone || contact?.contactPerson || index;
+  };
+
+  watch(
+    () => agentOptions.value.map(item => item.contactPhone),
+    phones => {
+      const val = form.value?.agent;
+      if (!val) return;
+      if (!phones.includes(val)) {
+        form.value.agent = "";
+      }
+    }
+  );
+
   const addNewContact = () => {
     formYYs.value.contactList.push({
       contactPerson: "",
diff --git a/src/views/productionManagement/workOrder/index.vue b/src/views/productionManagement/workOrder/index.vue
index 162dd91..902ca25 100644
--- a/src/views/productionManagement/workOrder/index.vue
+++ b/src/views/productionManagement/workOrder/index.vue
@@ -218,13 +218,46 @@
         </span>
       </template>
     </el-dialog>
+    <el-dialog
+      v-model="auditDialogVisible"
+      title="瀹℃牳"
+      width="1000px"
+      :close-on-click-modal="false"
+    >
+      <el-table :data="auditTableData" border style="width: 100%" v-loading="auditLoading">
+        <el-table-column label="浜у搧鍚嶇О" prop="productName" min-width="140" show-overflow-tooltip />
+        <el-table-column label="瑙勬牸" prop="model" min-width="120" show-overflow-tooltip />
+        <el-table-column label="鍗曚綅" prop="unit" width="80" />
+        <el-table-column label="宸ュ簭鍚嶇О" prop="processName" min-width="120" show-overflow-tooltip />
+        <el-table-column label="闇�姹傛暟閲�" prop="planQuantity" width="110" />
+        <el-table-column label="瀹屾垚鏁伴噺" prop="completeQuantity" width="110" />
+        <el-table-column label="瀹屾垚杩涘害" prop="completionStatus" width="140">
+          <template #default="{ row }">
+            <el-progress
+              :percentage="toProgressPercentage(row?.completionStatus)"
+              :color="progressColor(toProgressPercentage(row?.completionStatus))"
+              :status="toProgressPercentage(row?.completionStatus) >= 100 ? 'success' : ''"
+            />
+          </template>
+        </el-table-column>
+        <el-table-column label="璁″垝寮�濮嬫椂闂�" prop="planStartTime" width="140" />
+        <el-table-column label="璁″垝缁撴潫鏃堕棿" prop="planEndTime" width="140" />
+      </el-table>
+      <template #footer>
+        <span class="dialog-footer">
+          <el-button type="primary" :loading="auditLoading" @click="submitAudit(1)">閫氳繃</el-button>
+          <el-button type="danger" :loading="auditLoading" @click="submitAudit(2)">涓嶉�氳繃</el-button>
+          <el-button :disabled="auditLoading" @click="auditDialogVisible = false">鍙栨秷</el-button>
+        </span>
+      </template>
+    </el-dialog>
     <FilesDia ref="workOrderFilesRef" />
   </div>
 </template>
 
 <script setup>
   import { onMounted, ref, nextTick } from "vue";
-  import { ElMessageBox } from "element-plus";
+  import { ElMessageBox, ElMessage } from "element-plus";
   import dayjs from "dayjs";
   import {
     productWorkOrderPage,
@@ -340,6 +373,14 @@
           },
           disabled: row => row.planQuantity <= 0,
         },
+        {
+          name:"瀹℃牳",
+          color: "#f56c6c",
+          clickFun: row => {
+            handleAudit(row);
+          },
+          disabled: row => Number(row?.auditStatus) === 1,
+        }
       ],
     },
   ]);
@@ -353,6 +394,10 @@
   const transferCardQrUrl = ref("");
   const transferCardRowData = ref(null);
   const reportDialogVisible = ref(false);
+  const auditDialogVisible = ref(false);
+  const auditRowData = ref(null);
+  const auditTableData = ref([]);
+  const auditLoading = ref(false);
   const workOrderFilesRef = ref(null);
   const reportFormRef = ref(null);
   const userOptions = ref([]);
@@ -398,6 +443,62 @@
     callback();
   };
 
+  // 瀹℃牳
+  const handleAudit = (row) => {
+    if (Number(row?.auditStatus) === 1) {
+      ElMessage.warning("璇ュ伐鍗曞凡瀹℃牳");
+      return;
+    }
+    auditRowData.value = row;
+    const workOrderNo = row?.workOrderNo;
+    const related = workOrderNo
+      ? tableData.value.filter(r => r?.workOrderNo === workOrderNo)
+      : [];
+    auditTableData.value = related.length > 0 ? related : [row];
+    auditDialogVisible.value = true;
+  };
+
+  const submitAudit = async (result) => {
+    const current = auditRowData.value;
+    if (!current) return;
+    if (auditLoading.value) return;
+
+    const confirmText = result === 1 ? "纭畾瀹℃牳閫氳繃鍚楋紵" : "纭畾瀹℃牳涓嶉�氳繃鍚楋紵";
+    try {
+      await ElMessageBox.confirm(confirmText, "鎻愮ず", {
+        confirmButtonText: "纭畾",
+        cancelButtonText: "鍙栨秷",
+        type: "warning",
+      });
+    } catch {
+      return;
+    }
+
+    auditLoading.value = true;
+    try {
+      const updates = auditTableData.value.map(item => {
+        const id = item?.id;
+        if (!id) return Promise.resolve();
+        return updateProductWorkOrder({ id, auditStatus: result });
+      });
+      await Promise.all(updates);
+      ElMessage.success("瀹℃牳鎴愬姛");
+      auditDialogVisible.value = false;
+      getList();
+    } finally {
+      auditLoading.value = false;
+    }
+  };
+
+  // 鏌ョ湅璇︽儏
+  const handleView = (row) => {
+    const { workOrderId } = row;
+    router.push({
+      path: "/productionManagement/workOrderDetail",
+      query: { workOrderId },
+    });
+  }
+
   // 楠岃瘉瑙勫垯
   const reportFormRules = {
     quantity: [{ required: true, validator: validateQuantity, trigger: "blur" }],
diff --git a/src/views/reportAnalysis/productionAnalysis/components/center-top.vue b/src/views/reportAnalysis/productionAnalysis/components/center-top.vue
index 7201828..e56f715 100644
--- a/src/views/reportAnalysis/productionAnalysis/components/center-top.vue
+++ b/src/views/reportAnalysis/productionAnalysis/components/center-top.vue
@@ -6,6 +6,7 @@
         v-for="item in statItems"
         :key="item.name"
         class="stat-card"
+        @click="handleClick(item)"
       >
         <img src="@/assets/BI/icon@2x.png" alt="鍥炬爣" class="card-icon" />
         <div class="card-content">
@@ -25,7 +26,11 @@
 
 <script setup>
 import { ref, onMounted } from 'vue'
+import { useRouter } from 'vue-router'
 import { orderCount } from '@/api/viewIndex.js'
+
+const router = useRouter()
+
 
 const statItems = ref([])
 
@@ -51,6 +56,16 @@
       console.error('鑾峰彇璁㈠崟鏁伴噺缁熻澶辫触:', err)
     })
 }
+const handleClick = (item) => {
+  // 鐐瑰嚮璺宠浆椤甸潰
+  console.log('鐐瑰嚮浜�', item)
+  router.push({
+    path: '/productionManagement/productionOrder',
+    query: {
+      name: item.name,
+    }
+  })
+}
 
 onMounted(() => {
   fetchData()
@@ -64,6 +79,7 @@
 }
 
 .stat-card {
+  cursor: pointer;
   flex: 1;
   display: flex;
   align-items: center;
diff --git a/src/views/salesManagement/salesLedger/index.vue b/src/views/salesManagement/salesLedger/index.vue
index 1833bc1..b5bc804 100644
--- a/src/views/salesManagement/salesLedger/index.vue
+++ b/src/views/salesManagement/salesLedger/index.vue
@@ -118,10 +118,11 @@
         <el-table-column label="褰曞叆鏃ユ湡" prop="entryDate" width="120" show-overflow-tooltip />
         <el-table-column label="绛捐鏃ユ湡" prop="executionDate" width="120" show-overflow-tooltip />
         <el-table-column label="浜や粯鏃ユ湡" prop="deliveryDate" width="120" show-overflow-tooltip />
-        <el-table-column label="澶囨敞" prop="remarks" width="200" show-overflow-tooltip />
-        <el-table-column fixed="right" label="鎿嶄綔" min-width="100" align="center">
+        <el-table-column label="鍏跺畠璇存槑浜嬮」" prop="remarks" width="200" show-overflow-tooltip />
+        <el-table-column fixed="right" label="鎿嶄綔" min-width="200" align="center">
           <template #default="scope">
             <el-button link type="primary" size="small" @click="openForm('edit', scope.row)" :disabled="!scope.row.isEdit">缂栬緫</el-button>
+			<el-button link type="primary" size="small" @click="exportSalesContracts(scope.row)">瀵煎嚭閿�鍞悎鍚�</el-button>
 <!--            <el-button link type="primary" size="small" @click="openForm('view', scope.row)">璇︽儏</el-button>-->
             <el-button link type="primary" size="small" @click="downLoadFile(scope.row)">闄勪欢</el-button>
 <!--            <el-button link type="primary" size="small" @click="openDeliveryForm(scope.row)">鍙戣揣</el-button>-->
@@ -213,6 +214,11 @@
                               type="date" placeholder="璇烽�夋嫨" clearable />
             </el-form-item>
           </el-col>
+		  <el-col :span="12">
+            <el-form-item label="绛捐鍦扮偣锛�" prop="placeOfSinging">
+				<el-input v-model="form.placeOfSinging" placeholder="璇疯緭鍏�" clearable :disabled="operationType === 'view'" />
+            </el-form-item>
+          </el-col>
         </el-row>
 				<el-row>
 					<el-form-item label="浜у搧淇℃伅锛�" prop="entryDate">
@@ -243,7 +249,7 @@
 				</el-table>
 				<el-row :gutter="30">
 					<el-col :span="24">
-						<el-form-item label="澶囨敞锛�" prop="remarks">
+						<el-form-item label="鍏跺畠璇存槑浜嬮」锛�" prop="remarks">
 							<el-input v-model="form.remarks" placeholder="璇疯緭鍏�" clearable type="textarea" :rows="2" :disabled="operationType === 'view'" />
 						</el-form-item>
 					</el-col>
@@ -675,6 +681,7 @@
 	addOrUpdateSalesLedgerProduct,
 	delProduct,
 	delLedgerFile, getProductInventory,
+	exportSalesContract
 } from "@/api/salesManagement/salesLedger.js";
 import { modelList, productTreeList } from "@/api/basicData/product.js";
 import useFormData from "@/hooks/useFormData.js";
@@ -728,6 +735,7 @@
 		entryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
     deliveryDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
 		executionDate: [{ required: true, message: "璇烽�夋嫨", trigger: "change" }],
+		placeOfSinging: [{ required: true, message: "璇疯緭鍏�", trigger: "blur" }],
 	},
 });
 const { form, rules } = toRefs(data);
@@ -2092,6 +2100,34 @@
 };
 
 /**
+ * 瀵煎嚭閿�鍞悎鍚�
+ *
+ * @param row 瀵煎嚭閿�鍞悎鍚岀殑鐩稿叧淇℃伅瀵硅薄
+ */
+const exportSalesContracts = (row) => {
+	exportSalesContract({ id: row.id }).then((res) => {
+		if (res) {
+			const downloadUrl = window.URL.createObjectURL(res);
+      const link = document.createElement('a');
+      link.href = downloadUrl;
+	  console.log(row.executionDate)
+      link.download = row.projectName+row.executionDate + "閿�鍞悎鍚�.docx"; // 璁剧疆涓嬭浇鏂囦欢鍚�
+      link.style.display = 'none'; // 闅愯棌a鏍囩
+      document.body.appendChild(link);
+      link.click(); // 瑙﹀彂鐐瑰嚮涓嬭浇
+      // 4. 娓呯悊璧勬簮锛堥伩鍏嶅唴瀛樻硠婕忥級
+      document.body.removeChild(link);
+      window.URL.revokeObjectURL(downloadUrl);
+
+      // 5. 鎻愮ず瀵煎嚭鎴愬姛
+      proxy.$modal.msgSuccess("瀵煎嚭閿�鍞悎鍚屾垚鍔�");
+		} else {
+			proxy.$modal.msgError(res.msg || "瀵煎嚭閿�鍞悎鍚屽け璐�");
+		}
+	});
+}
+
+/**
  * 涓嬭浇鏂囦欢
  *
  * @param row 涓嬭浇鏂囦欢鐨勭浉鍏充俊鎭璞�

--
Gitblit v1.9.3