From 00ca5a7d2c93420dc8696b3b3574885ad0bf3ff8 Mon Sep 17 00:00:00 2001
From: yaowanxin <3588231647@qq.com>
Date: 星期一, 29 十二月 2025 11:35:38 +0800
Subject: [PATCH] 添加入库数量输入和合格状态选择功能,实现采购异常记录的添加和更新接口

---
 src/api/procurementManagement/transferManagement.js          |   34 ++++
 src/views/procurementManagement/transferManagement/index.vue |  434 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 468 insertions(+), 0 deletions(-)

diff --git a/src/api/procurementManagement/transferManagement.js b/src/api/procurementManagement/transferManagement.js
new file mode 100644
index 0000000..fa404d1
--- /dev/null
+++ b/src/api/procurementManagement/transferManagement.js
@@ -0,0 +1,34 @@
+import request from "@/utils/request";
+
+// 鍒嗛〉鏌ヨ
+export function getPurchaseOrders(query) {
+  return request({
+    url: "/purchase/ledger/listPage",
+    method: "get",
+    params: query,
+  });
+}
+//
+export function confirmReceipt(query) {
+    return request({
+      url: "",
+      method: "post",
+      data: query,
+    });
+}
+// 澧炴坊閲囪喘寮傚父璁板綍
+export function addPurchaseException(query) {
+    return request({
+      url: "/procurementExceptionRecord/add",
+      method: "post",
+      data: query,
+    });
+}
+// 淇敼閲囪喘寮傚父璁板綍
+export function updatePurchaseException(query) {
+    return request({
+        url: "/procurementExceptionRecord/update",
+        method: "post",
+        data: query,
+    });
+}
\ No newline at end of file
diff --git a/src/views/procurementManagement/transferManagement/index.vue b/src/views/procurementManagement/transferManagement/index.vue
new file mode 100644
index 0000000..10ef52f
--- /dev/null
+++ b/src/views/procurementManagement/transferManagement/index.vue
@@ -0,0 +1,434 @@
+<template>
+  <div class="app-container">
+    <!-- 鎼滅储杩囨护鍖� -->
+    <el-form :model="searchForm" :inline="true">
+      <el-form-item label="閲囪喘鍚堝悓鍙�">
+        <el-input v-model="searchForm.purchaseContractNumber" placeholder="璇疯緭鍏�" />
+      </el-form-item>
+      <el-form-item label="渚涘簲鍟�">
+        <el-input v-model="searchForm.supplierName" placeholder="璇疯緭鍏�" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="search">鎼滅储</el-button>
+        <el-button @click="resetSearch">閲嶇疆</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 琛ㄦ牸灞曠ず鍖� -->
+    <el-table :data="orderList" border v-loading="loading" height="calc(100vh - 12em)">
+      <!-- 娣诲姞搴忓彿鍒� -->
+      <el-table-column align="center" label="搴忓彿" type="index" width="60" />
+      <el-table-column prop="purchaseContractNumber" label="閲囪喘鍚堝悓鍙�" show-overflow-tooltip />
+      <el-table-column prop="supplierName" label="渚涘簲鍟�" show-overflow-tooltip />
+      <el-table-column label="浠樻鐘舵��">
+        <template #default="scope">
+          <el-tag
+            :type="getPaymentStatusType(scope.row.paymentStatus)"
+            size="small"
+          >
+            {{ getPaymentStatusText(scope.row.paymentStatus) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="鏀惰揣鐘舵��">
+        <template #default="scope">
+          <el-tag
+            :type="getReceiptStatusType(scope.row.receiptStatus)"
+            size="small"
+          >
+            {{ getReceiptStatusText(scope.row.receiptStatus) }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column prop="receivedQuantity" label="宸叉敹璐ф暟閲�"/>
+      <el-table-column prop="unreceivedQuantity" label="鏈敹璐ф暟閲�"/>
+      <el-table-column label="鎿嶄綔" width="200"  fixed="right" align="center">
+        <template #default="scope">
+          <el-button
+            type="primary"
+            size="small"
+            @click="confirmReceipter(scope.row)"
+          >
+            纭鏀惰揣
+          </el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 鍦ㄨ〃鏍间笅鏂规坊鍔犲垎椤� -->
+    <pagination
+        v-show="total > 0"
+        :total="total"
+        layout="total, sizes, prev, pager, next, jumper"
+        :page="page.current"
+        :limit="page.size"
+        @pagination="paginationChange"
+    />
+    <!-- 纭鏀惰揣瀵硅瘽妗� -->
+    <el-dialog v-model="receiptDialogVisible" title="纭鏀惰揣" width="70%">
+      <el-form :model="receiptForm" label-width="120px" ref="formRef">
+        <el-form-item label="閲囪喘鍚堝悓鍙�">
+          <el-input v-model="receiptForm.purchaseContractNumber" disabled />
+        </el-form-item>
+        <el-form-item label="寮傚父鍘熷洜">
+          <el-input
+            v-model="receiptForm.exceptionReason"
+            type="textarea"
+            placeholder="璇疯緭鍏ュ紓甯稿師鍥狅紙涓嶅悎鏍兼椂濉啓锛�"
+          />
+        </el-form-item>
+        <el-table
+            :data="productList"
+            border
+            v-loading="loadingProducts"
+            @selection-change="handleSelectionChange"
+        >
+          <el-table-column align="center" type="selection" width="55" />
+          <el-table-column
+              align="center"
+              label="搴忓彿"
+              type="index"
+              width="60"
+          />
+          <el-table-column label="浜у搧澶х被" prop="productCategory" />
+          <el-table-column label="瑙勬牸鍨嬪彿" prop="specificationModel" />
+          <el-table-column label="鍗曚綅" prop="unit" width="70" />
+          <el-table-column label="渚涘簲鍟�" prop="supplierName" width="100" />
+          <el-table-column label="閲囪喘鏁伴噺" prop="quantity" width="100" />
+          <el-table-column label="寰呭叆搴撴暟閲�" prop="quantity0" width="100" />
+          <el-table-column label="鏈鍏ュ簱鏁伴噺" prop="quantityStock" width="150">
+            <template #default="scope">
+              <el-input-number :step="0.01" :min="0" style="width: 100%" v-model="scope.row.quantityStock" />
+            </template>
+          </el-table-column>
+<!--          鍚堟牸鎴栦笉鍚堟牸-->
+          <el-table-column label="鏄惁鍚堟牸" width="100">
+            <template #default="scope">
+              <el-select v-model="scope.row.isQualified" placeholder="璇烽�夋嫨">
+                <el-option label="鍚堟牸" value="1" />
+                <el-option label="涓嶅悎鏍�" value="2" />
+              </el-select>
+            </template>
+          </el-table-column>
+          <el-table-column label="绋庣巼(%)" prop="taxRate" width="120" />
+          <el-table-column
+              label="鍚◣鍗曚环(鍏�)"
+              prop="taxInclusiveUnitPrice"
+              :formatter="formattedNumber"
+              width="150"
+          />
+          <el-table-column
+              label="鍚◣鎬讳环(鍏�)"
+              prop="taxInclusiveTotalPrice"
+              :formatter="formattedNumber"
+              width="150"
+          />
+          <el-table-column
+              label="涓嶅惈绋庢�讳环(鍏�)"
+              prop="taxExclusiveTotalPrice"
+              :formatter="formattedNumber"
+              width="150"
+          />
+        </el-table>
+      </el-form>
+      <template #footer>
+        <el-button @click="receiptDialogVisible = false">鍙栨秷</el-button>
+        <el-button type="primary" @click="submitReceipt">纭鏀惰揣</el-button>
+      </template>
+    </el-dialog>
+  </div>
+</template>
+
+<script setup>
+import {ref, onMounted, getCurrentInstance} from 'vue'
+import {
+  getPurchaseOrders,
+  confirmReceipt,
+  addPurchaseException
+} from '@/api/procurementManagement/transferManagement.js'
+import {selectProductRecordListByPuechaserId, addSutockIn, updateStockIn} from "@/api/inventoryManagement/stockIn.js";
+import useUserStore from "@/store/modules/user.js";
+
+
+const userStore = useUserStore()
+const { proxy } = getCurrentInstance()
+
+// 鏁版嵁瀹氫箟
+const orderList = ref([])
+const receiptDialogVisible = ref(false)
+const receiptForm = ref({
+  purchaseContractNumber: '',
+  exceptionReason: '',
+  purchaseLedgerId: '',
+})
+const operationType = ref('')// 鎿嶄綔绫诲瀷: 'add' 鎴� 'edit'
+const productList = ref([]);// 浜у搧鍒楄〃鏁版嵁
+const loadingProducts = ref(false);// 浜у搧鍔犺浇鐘舵��
+const selectedRows = ref([]);
+const loading = ref(false);
+const total = ref(0); // 鎬昏褰曟暟
+// 鎼滅储琛ㄥ崟
+const searchForm = ref({
+  purchaseContractNumber: '',
+  supplierName: '',
+})
+// 鍒嗛〉鏁版嵁
+const page = reactive({
+  current: 1,
+  size: 100, // 姣忛〉鏄剧ず鏁伴噺
+});
+
+// 鍒嗛〉鍙樺寲澶勭悊
+const paginationChange = (obj) => {
+  page.current = obj.page;
+  page.size = obj.limit;
+  getReceiptOrders(); // 閲嶆柊鑾峰彇鏁版嵁
+};
+// 鑾峰彇璁㈠崟鍒楄〃
+const getReceiptOrders = async () => {
+  loading.value = true;
+  try {
+    const response = await getPurchaseOrders({
+      ...searchForm.value,
+      current: page.current,
+      size: page.size
+    });
+    // 浣跨敤 Promise.all 澶勭悊鎵�鏈夊紓姝ヨ姹�
+    const processedOrders = await Promise.all(response.data.records.map(async (order) => {
+      // 绛夊緟寮傛鑾峰彇浜у搧璁板綍
+      const productRes = await selectProductRecordListByPuechaserId({
+        purchaseContractNumber: order.purchaseContractNumber
+      });
+
+      // 纭繚 productRes.data 瀛樺湪
+      if (productRes && productRes.data && Array.isArray(productRes.data)) {
+        // 璁$畻鎬绘暟閲�
+        order.totalQuantity = productRes.data.reduce((acc, cur) => acc + (cur.quantity || 0), 0);
+        // 璁$畻鏈敹璐ф暟閲�
+        order.unreceivedQuantity = productRes.data.reduce((acc, cur) => acc + (cur.quantity0 || 0), 0);
+        // 璁$畻宸叉敹璐ф暟閲�
+        order.receivedQuantity = order.totalQuantity - order.unreceivedQuantity;
+
+        // 淇鐘舵�佸垽鏂�昏緫锛堜娇鐢� === 杩涜姣旇緝锛�
+        if (order.unreceivedQuantity === 0) {
+          order.paymentStatus = 1;
+          order.receiptStatus = 1;
+        } else if (order.receivedQuantity === 0) {
+          order.paymentStatus = 3;
+          order.receiptStatus = 3;
+        } else {
+          order.paymentStatus = 2;
+          order.receiptStatus = 2;
+        }
+      } else {
+        // 濡傛灉娌℃湁浜у搧璁板綍锛岃缃粯璁ゅ��
+        order.totalQuantity = 0;
+        order.unreceivedQuantity = 0;
+        order.receivedQuantity = 0;
+        order.receiptStatus = 3; // 鏈叆搴�
+      }
+
+      return order;
+    }));
+
+    // 姝g‘璧嬪�肩粰 orderList
+    orderList.value = processedOrders;
+    total.value = response.data.total;
+  } catch (error) {
+    console.error('鑾峰彇璁㈠崟鍒楄〃澶辫触:', error);
+    proxy.$modal.msgError('鑾峰彇璁㈠崟鍒楄〃澶辫触');
+  } finally {
+    loading.value = false;
+  }
+}
+
+// 浠樻鐘舵�佹樉绀哄鐞�
+const getPaymentStatusText = (status) => {
+  const statusMap = { '1': '宸蹭粯娆�', '2': '閮ㄥ垎浠樻', '3': '鏈粯娆�' }
+  return statusMap[status] || '鏈煡'
+}
+
+const getPaymentStatusType = (status) => {
+  const typeMap = { '1': 'success', '2': 'warning', '3': 'danger' }
+  return typeMap[status] || 'info'
+}
+
+// 鏀惰揣鐘舵�佸鐞�
+const getReceiptStatusText = (status) => {
+  const statusMap = { '1': '鏀惰揣瀹屾垚', '2': '閮ㄥ垎鍏ュ簱', '3': '鏈叆搴�' }
+  return statusMap[status] || '鏈煡'
+}
+
+const getReceiptStatusType = (status) => {
+  const typeMap = { '1': 'success', '2': 'warning', '3': 'info' }
+  return typeMap[status] || 'info'
+}
+const exceedsAddLimit = (product) => {
+  const stock = Number(product?.quantityStock ?? 0);
+  const waiting = Number(product?.quantity0 ?? 0);
+  if (!Number.isFinite(stock) || !Number.isFinite(waiting)) {
+    return false;
+  }
+  return stock > waiting;
+};
+const exceedsEditLimit = (product) => {
+  const stock = Number(product?.quantityStock ?? 0);
+  const waiting = Number(product?.quantity0 ?? 0);
+  const original = Number(product?.originalQuantityStock ?? 0);
+  if (!Number.isFinite(stock) || !Number.isFinite(waiting) || !Number.isFinite(original)) {
+    return false;
+  }
+  return stock > waiting + original;
+};
+const updatePro = async () => {
+  const target = selectedRows.value[0];
+  const stock = Number(target?.quantityStock ?? 0);
+  if (!Number.isFinite(stock) || stock <= 0) {
+    proxy.$modal.msgWarning('璇峰~鍐欐湁鏁堢殑鍏ュ簱鏁伴噺');
+    return;
+  }
+  if (exceedsEditLimit(target)) {
+    proxy.$modal.msgError('鏈鍏ュ簱鏁伴噺涓嶈兘瓒呰繃鍘熷叆搴撴暟閲忎笌寰呭叆搴撴暟閲忎箣鍜�');
+    return;
+  }
+  const stockInData = {
+    id: selectedRows.value[0].recordId,
+    quantityStock: Number(selectedRows.value[0].quantityStock),// 浣跨敤鏂版牸寮忓寲鍑芥暟
+  };
+  await updateStockIn(stockInData)
+  proxy.$modal.msgSuccess('淇敼鍏ュ簱鎴愬姛')
+  closeDia()
+  getReceiptOrders() // 鍒锋柊鍒楄〃
+}
+// 琛ㄦ牸閫夋嫨鏁版嵁
+const handleSelectionChange = (selection) => {
+  // 杩囨护鎺夊瓙鏁版嵁
+  selectedRows.value = selection.filter(item => item.id);
+}
+// 鎵撳紑寮规-纭鏀惰揣
+const confirmReceipter = (row) => {
+  receiptForm.value = {
+    purchaseContractNumber: row.purchaseContractNumber,
+    purchaseLedgerId: row.id,
+    exceptionReason: ''
+  }
+  selectedRows.value = []
+  receiptDialogVisible.value = true
+  fetchProductsByContract()
+}
+
+const fetchProductsByContract = async () =>
+{
+  try {
+    loadingProducts.value = true
+    // 鏍规嵁鍚堝悓鏌ヨ浜у搧璁板綍
+    const productRes = await selectProductRecordListByPuechaserId({
+      purchaseContractNumber: receiptForm.value.purchaseContractNumber
+    });
+    console.log('productRes:', productRes)
+    operationType.value = 'add'
+    if (!productRes.data || productRes.data.length === 0) {
+      proxy.$modal.msgWarning('璇ュ悎鍚屼笅娌℃湁浜у搧璁板綍')
+      productList.value = [];
+      return
+    }
+    // 澶勭悊浜у搧鏁版嵁锛屾坊鍔犳湰娆″叆搴撴暟閲忓瓧娈�
+    productList.value = productRes.data.map(item => ({
+      ...item,
+      quantityStock: 0,
+      originalQuantityStock: Number(item.quantityStock ?? item.inboundQuantity ?? 0),
+    }))
+    selectedRows.value = productList.value
+  } catch (error) {
+    console.error('鏌ヨ浜у搧璁板綍澶辫触:', error)
+    proxy.$modal.msgError('鏌ヨ浜у搧璁板綍澶辫触')
+    productList.value = [];
+  } finally {
+    loadingProducts.value = false
+  }
+}
+
+
+// 鎻愪氦鏀惰揣纭
+const submitReceipt = async () => {
+  if(operationType.value !== 'add'){
+    await updatePro()
+    return
+  }
+  try {
+    await proxy.$refs.formRef.validate()
+    // 楠岃瘉鍏ュ簱鏁伴噺
+    const invalidProducts = selectedRows.value.filter((product) => {
+      const stock = Number(product?.quantityStock ?? 0);
+      if (!Number.isFinite(stock) || stock <= 0) {
+        return true;
+      }
+      return exceedsAddLimit(product);
+    })
+
+    if (invalidProducts.length > 0) {
+      proxy.$modal.msgError('鏈鍏ュ簱鏁伴噺闇�澶т簬0锛屼笖涓嶈兘瓒呰繃寰呭叆搴撴暟閲�')
+      return
+    }
+    loading.value = true
+    // 鍑嗗鎻愪氦鏁版嵁 - 淇敼涓哄悗绔渶瑕佺殑鏍煎紡
+    const stockInData = {
+      // 鍏ュ簱鍗曞熀鏈俊鎭�
+      ...receiptForm.value,
+      nickName: userStore.nickName,
+      details: selectedRows.value.map(product => ({
+        id: product.id,
+        inboundQuantity: Number(product.quantityStock)
+      })),
+    };
+    //濡傛灉浜у搧鍚堟牸
+    if(productList.value.every(product => product.isQualified === '1')){
+      await addSutockIn(stockInData)
+
+      proxy.$modal.msgSuccess('纭鏀惰揣,鍏ュ簱鎴愬姛')
+    }else{
+       stockInData.details.forEach(item => {
+        const ProcurementExceptionRecord = {
+          purchaseContractNumber: receiptForm.value.purchaseContractNumber,
+          purchaseLedgerId: receiptForm.value.purchaseLedgerId,
+          exceptionNum: item.inboundQuantity,
+          exceptionReason: receiptForm.value.exceptionReason
+        }
+        addPurchaseException(ProcurementExceptionRecord).then(response => {
+          proxy.$modal.msgSuccess('浜у搧涓嶅悎鏍硷紝閲囪喘寮傚父璁板綍鎴愬姛')
+        })
+      })
+    }
+    closeDia()
+    getReceiptOrders() // 鍒锋柊鍒楄〃
+
+  } catch (error) {
+    console.error('鎻愪氦澶辫触:', error)
+    if (!error.errors) {
+      proxy.$modal.msgError('鎿嶄綔澶辫触锛岃閲嶈瘯')
+    }
+  } finally {
+    loading.value = false
+  }
+}
+// 鍏抽棴寮规
+const closeDia = () => {
+  proxy.$refs.formRef.resetFields()
+  receiptDialogVisible.value = false
+}
+// 鎼滅储鍜岄噸缃�
+const search = () => {
+  getReceiptOrders()
+}
+
+const resetSearch = () => {
+  searchForm.value = {
+    purchaseContractNumber: '',
+    supplierName: '',
+  }
+  getReceiptOrders()
+}
+
+onMounted(() => {
+  getReceiptOrders()
+})
+</script>

--
Gitblit v1.9.3