From f9e6eb6fc2c2e0c2a2b17238b817fcbfcb6bab56 Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 10 八月 2026 16:38:45 +0800
Subject: [PATCH] feat(erp): 添加采购来票管理和付款关联功能

---
 src/views/srm/tender/detail.vue |  255 ++++++++++++++++++++++++++++++++++++++++++--------
 1 files changed, 213 insertions(+), 42 deletions(-)

diff --git a/src/views/srm/tender/detail.vue b/src/views/srm/tender/detail.vue
index 772d2eb..7b2f1de 100644
--- a/src/views/srm/tender/detail.vue
+++ b/src/views/srm/tender/detail.vue
@@ -6,7 +6,7 @@
 
 import { Page } from '@vben/common-ui';
 
-import { message } from 'ant-design-vue';
+import { message, Modal } from 'ant-design-vue';
 import { IconifyIcon } from '@vben/icons';
 
 import {
@@ -17,6 +17,7 @@
   createBidOpen, getBidOpen,
   getEvaluationList, createBidEvaluation, updateBidEvaluation, calculateRanking,
   createAward, deleteAward, getAward, getAwardByProject, approveAward, generatePurchaseOrder,
+  confirmTenderProject,
 } from '#/api/srm/tender';
 import { getSupplierSimpleList } from '#/api/srm/supplier';
 import { getItemSimpleList } from '#/api/mdm/item';
@@ -31,6 +32,7 @@
   AWARD_STATUS,
   AWARD_STATUS_MAP,
   getLabel,
+  BIDDING_MODE,
 } from '../enums';
 
 defineOptions({ name: 'SrmTenderDetail' });
@@ -63,6 +65,25 @@
 const materialLoading = ref(false);
 const materialFormVisible = ref(false);
 const materialForm = ref<SrmTenderApi.TenderMaterialVO>({ tenderProjectId: projectId });
+
+// 宸叉坊鍔犵殑鐗╂枡浜у搧ID闆嗗悎锛岄伩鍏嶉噸澶嶆坊鍔�
+const addedProductIds = computed(() => {
+  const ids = new Set(
+    materialList.value
+      .filter((m) => m.productId !== undefined)
+      .map((m) => m.productId!),
+  );
+  // 缂栬緫鏃跺厑璁镐繚鐣欏綋鍓嶇墿鏂欑殑 productId
+  if (materialForm.value.id && materialForm.value.productId) {
+    ids.delete(materialForm.value.productId);
+  }
+  return ids;
+});
+
+// 鐗╂枡涓嬫媺鍙�夊垪琛紙鎺掗櫎宸叉坊鍔犵殑锛�
+const availableMdmItems = computed(() =>
+  mdmItemList.value.filter((item) => !addedProductIds.value.has(item.id!)),
+);
 
 async function loadProject() {
   project.value = await getTenderProject(projectId);
@@ -107,7 +128,7 @@
 const bidList = ref<SrmTenderApi.TenderBidVO[]>([]);
 const bidLoading = ref(false);
 const bidFormVisible = ref(false);
-const bidForm = ref<SrmTenderApi.TenderBidVO>({ tenderProjectId: projectId, supplierId: 0, bidNo: '' });
+const bidForm = ref<SrmTenderApi.TenderBidVO>({ tenderProjectId: projectId, supplierId: undefined!, bidNo: '' });
 
 async function loadBids() {
   bidLoading.value = true;
@@ -127,7 +148,7 @@
 const quoteList = ref<SrmTenderApi.QuoteVO[]>([]);
 const quoteLoading = ref(false);
 const quoteFormVisible = ref(false);
-const quoteForm = ref<SrmTenderApi.QuoteVO>({ bidId: 0, tenderMaterialId: 0 });
+const quoteForm = ref<SrmTenderApi.QuoteVO>({ bidId: undefined!, tenderMaterialId: undefined! });
 const selectedBidId = ref<number>();
 
 const availableMaterials = computed(() => {
@@ -150,6 +171,27 @@
   try { quoteList.value = await getQuoteList(bidId); } finally { quoteLoading.value = false; }
 }
 async function handleSaveQuote() {
+  // 鏍¢獙锛氭姤浠烽噾棰濅笌鎶曟爣閲戦涓�鑷存��
+  const bid = bidList.value.find((b) => b.id === quoteForm.value.bidId);
+  if (bid?.bidTotalAmount != null) {
+    const otherQuotesTotal = quoteList.value
+      .filter((q) => q.id !== quoteForm.value.id)
+      .reduce((sum, q) => sum + (q.quotePrice ?? 0), 0);
+    const newTotal = otherQuotesTotal + (quoteForm.value.quotePrice ?? 0);
+    if (newTotal !== bid.bidTotalAmount) {
+      await new Promise<void>((resolve, reject) => {
+        Modal.confirm({
+          title: '閲戦涓嶄竴鑷存彁绀�',
+          content: `鎶ヤ环鎬婚噾棰濓紙${newTotal.toLocaleString()}锛変笌鎶曟爣閲戦锛�${bid.bidTotalAmount!.toLocaleString()}锛変笉涓�鑷达紝鏄惁缁х画鎻愪氦锛焋,
+          okText: '缁х画鎻愪氦',
+          cancelText: '鍙栨秷',
+          onOk: () => { resolve(); },
+          onCancel: () => { reject(new Error('cancel')); },
+        });
+      });
+    }
+  }
+
   if (quoteForm.value.id) {
     await updateQuote(quoteForm.value);
   } else {
@@ -159,7 +201,7 @@
   loadQuotes(selectedBidId.value);
 }
 function handleAddQuote() {
-  quoteForm.value = { bidId: selectedBidId.value!, tenderMaterialId: 0 };
+  quoteForm.value = { bidId: selectedBidId.value!, tenderMaterialId: undefined! };
   quoteFormVisible.value = true;
 }
 function handleEditQuote(row: SrmTenderApi.QuoteVO) {
@@ -186,7 +228,7 @@
 const evaluationList = ref<SrmTenderApi.BidEvaluationVO[]>([]);
 const evalLoading = ref(false);
 const evalFormVisible = ref(false);
-const evalForm = ref<SrmTenderApi.BidEvaluationVO>({ tenderProjectId: projectId, supplierId: 0, bidId: 0 });
+const evalForm = ref<SrmTenderApi.BidEvaluationVO>({ tenderProjectId: projectId, supplierId: undefined!, bidId: undefined! });
 
 async function loadEvaluations() {
   evalLoading.value = true;
@@ -211,7 +253,41 @@
 // ========== 瀹氭爣 ==========
 const award = ref<SrmTenderApi.BidAwardVO>();
 const awardFormVisible = ref(false);
-const awardForm = ref<SrmTenderApi.BidAwardVO>({ tenderProjectId: projectId, supplierId: 0, bidId: 0, awardNo: '' });
+const awardForm = ref<SrmTenderApi.BidAwardVO>({ tenderProjectId: projectId, supplierId: undefined!, bidId: undefined!, awardNo: '' });
+
+/** 宸叉姇鏍囦笖鏈挙鏍囩殑渚涘簲鍟嗗垪琛紙浠庢姇鏍囪褰曚腑鎻愬彇锛� */
+const bidSuppliers = computed(() => {
+  const activeBids = bidList.value.filter((b) => b.bidStatus !== BID_STATUS.WITHDRAWN);
+  const seen = new Map<number, { id: number; name: string }>();
+  activeBids.forEach((b) => {
+    if (b.supplierId && !seen.has(b.supplierId)) {
+      seen.set(b.supplierId, { id: b.supplierId, name: b.supplierName || '' });
+    }
+  });
+  return [...seen.values()];
+});
+
+/** 鍙�夋姇鏍囪褰曪細鎺掗櫎鎾ゆ爣锛屼笖濡傛灉閫変簡渚涘簲鍟嗗垯鍙樉绀鸿渚涘簲鍟嗙殑鎶曟爣 */
+const activeBids = computed(() => {
+  return bidList.value.filter((b) => {
+    if (b.bidStatus === BID_STATUS.WITHDRAWN) return false;
+    if (awardForm.value.supplierId && b.supplierId !== awardForm.value.supplierId) return false;
+    return true;
+  });
+});
+
+/** 閫夋嫨渚涘簲鍟嗘椂娓呯┖宸查�夌殑鎶曟爣 */
+function onAwardSupplierChange() {
+  awardForm.value.bidId = undefined!;
+}
+
+/** 閫夋嫨鎶曟爣鏃惰嚜鍔ㄥ甫鍏ヨ鎶曟爣鐨勬姤浠烽噾棰� */
+function onAwardBidChange(bidId: number) {
+  const bid = bidList.value.find((b) => b.id === bidId);
+  if (bid?.bidTotalAmount != null) {
+    awardForm.value.awardAmount = bid.bidTotalAmount;
+  }
+}
 
 async function loadAward() {
   try { award.value = await getAwardByProject(projectId); } catch { award.value = undefined; }
@@ -244,6 +320,36 @@
   loadProject();
 }
 
+// ========== 绠�鏄撴ā寮忥細纭涓爣 ==========
+const confirmBidId = ref<number>();
+const confirmModalVisible = ref(false);
+
+async function handleConfirmBidAsWinner(bidId: number) {
+  await confirmTenderProject({ id: projectId, bidId });
+  message.success('纭涓爣鎴愬姛锛岄噰璐鍗曞凡鑷姩鐢熸垚');
+  loadProject();
+  loadBids();
+  loadAward();
+}
+
+function openSimpleConfirmModal() {
+  confirmBidId.value = undefined;
+  confirmModalVisible.value = true;
+}
+
+async function handleSimpleConfirm() {
+  if (!confirmBidId.value) {
+    message.warning('璇烽�夋嫨涓爣鐨勬姇鏍囪褰�');
+    return;
+  }
+  await confirmTenderProject({ id: projectId, bidId: confirmBidId.value });
+  message.success('纭涓爣鎴愬姛锛岄噰璐鍗曞凡鑷姩鐢熸垚');
+  confirmModalVisible.value = false;
+  loadProject();
+  loadBids();
+  loadAward();
+}
+
 // ========== 鍒濆鍖� ==========
 loadProject();
 loadMaterials();
@@ -255,25 +361,52 @@
 const statusLabel = computed(() => getLabel(TENDER_STATUS_CELLTAG.labels, project.value?.tenderStatus));
 const statusColor = computed(() => TENDER_STATUS_COLORS[project.value?.tenderStatus!] || 'default');
 const typeLabel = computed(() => getLabel(TENDER_TYPE_CELLTAG.labels, project.value?.tenderType));
-const isAwarded = computed(() => project.value?.tenderStatus === TENDER_STATUS.AWARDED);
+const isDraft = computed(() => project.value?.tenderStatus === TENDER_STATUS.DRAFT);
+const isPublished = computed(() => project.value?.tenderStatus === TENDER_STATUS.PUBLISHED);
+const isPublishedOrBidding = computed(() => {
+  const s = project.value?.tenderStatus;
+  return s === TENDER_STATUS.PUBLISHED || s === TENDER_STATUS.BIDDING;
+});
+const isBiddingOrEvaluating = computed(() => {
+  const s = project.value?.tenderStatus;
+  return s === TENDER_STATUS.BIDDING || s === TENDER_STATUS.EVALUATING;
+});
+const isEvaluating = computed(() => project.value?.tenderStatus === TENDER_STATUS.EVALUATING);
+const canDeleteAward = computed(() => award.value?.awardStatus === AWARD_STATUS.PENDING_APPROVAL);
+const canApproveAward = computed(() => award.value?.awardStatus === AWARD_STATUS.PENDING_APPROVAL);
+const canGeneratePO = computed(() => award.value?.awardStatus === AWARD_STATUS.AWARDED);
 
-const stepItems = [
-  { title: '鑽夌', status: TENDER_STATUS.DRAFT },
-  { title: '鍙戝竷', status: TENDER_STATUS.PUBLISHED },
-  { title: '鎶曟爣涓�', status: TENDER_STATUS.BIDDING },
-  { title: '璇勬爣涓�', status: TENDER_STATUS.EVALUATING },
-  { title: '宸插畾鏍�', status: TENDER_STATUS.AWARDED },
-];
+// 绠�鏄撴ā寮�
+const isSimpleMode = computed(() => project.value?.biddingMode === BIDDING_MODE.SIMPLE);
+const isPriceComparing = computed(() => project.value?.tenderStatus === TENDER_STATUS.PRICE_COMPARING);
+
+const stepItems = computed(() => {
+  if (isSimpleMode.value) {
+    return [
+      { title: '鑽夌', status: TENDER_STATUS.DRAFT },
+      { title: '姣斾环涓�', status: TENDER_STATUS.PRICE_COMPARING },
+      { title: '宸茬‘璁�', status: TENDER_STATUS.CONFIRMED },
+    ];
+  }
+  return [
+    { title: '鑽夌', status: TENDER_STATUS.DRAFT },
+    { title: '鍙戝竷', status: TENDER_STATUS.PUBLISHED },
+    { title: '鎶曟爣涓�', status: TENDER_STATUS.BIDDING },
+    { title: '璇勬爣涓�', status: TENDER_STATUS.EVALUATING },
+    { title: '宸插畾鏍�', status: TENDER_STATUS.AWARDED },
+  ];
+});
 const currentStep = computed(() => {
   const s = project.value?.tenderStatus ?? 0;
-  if (s === TENDER_STATUS.CLOSED) return 4;
-  const idx = stepItems.findIndex((item) => item.status >= s);
+  const items = stepItems.value;
+  if (s === TENDER_STATUS.CLOSED) return items.length - 1;
+  const idx = items.findIndex((item) => item.status >= s);
   return idx === -1 ? 0 : idx;
 });
 const stepStatus = computed(() => {
   const s = project.value?.tenderStatus;
   if (s === TENDER_STATUS.CLOSED) return 'error';
-  if (s === TENDER_STATUS.AWARDED) return 'finish';
+  if (s === TENDER_STATUS.AWARDED || s === TENDER_STATUS.CONFIRMED) return 'finish';
   return 'process';
 });
 
@@ -308,7 +441,7 @@
   { title: '鎶曟爣閲戦', dataIndex: 'bidTotalAmount', width: 120 },
   { title: '鎶曟爣鏃堕棿', dataIndex: 'bidTime', width: 160 },
   { title: '鐘舵��', dataIndex: 'bidStatus', width: 100, customRender: ({ text }: any) => BID_STATUS_MAP[text] || text },
-  { title: '鎿嶄綔', key: 'actions', width: 180 },
+  { title: '鎿嶄綔', key: 'actions', width: 240 },
 ];
 
 const quoteColumns = [
@@ -351,7 +484,16 @@
         <span class="text-gray-400 text-xs">{{ project.tenderNo }}</span>
         <a-tag :color="statusColor">{{ statusLabel }}</a-tag>
         <a-tag color="blue">{{ typeLabel }}</a-tag>
+        <a-tag v-if="isSimpleMode" color="purple">绠�鏄撶増</a-tag>
         <div class="flex-1" />
+        <a-button
+          v-if="isPriceComparing"
+          type="primary"
+          size="small"
+          @click="openSimpleConfirmModal"
+        >
+          纭涓爣
+        </a-button>
         <a-button size="small" @click="loadProject(); loadMaterials(); loadBids(); loadEvaluations(); loadAward();">
           <template #icon><IconifyIcon icon="ant-design:reload-outlined" /></template>
           鍒锋柊
@@ -422,7 +564,7 @@
                 <a-space>
                   <span class="text-xs text-gray-400">鍏� <b class="text-gray-600">{{ materialList.length }}</b> 椤癸紝棰勪及鎬婚噾棰� <b class="text-blue-500">{{ materialTotalAmount.toLocaleString() }}</b> 鍏�</span>
                 </a-space>
-                <a-button v-if="!isAwarded" type="primary" size="small" @click="handleAddMaterial">
+                <a-button v-if="isDraft" type="primary" size="small" @click="handleAddMaterial">
                   <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
                   娣诲姞鐗╂枡
                 </a-button>
@@ -433,7 +575,7 @@
                     {{ record.estimatedPrice ? record.estimatedPrice.toLocaleString() : '-' }}
                   </template>
                   <template v-if="column.key === 'actions'">
-                    <a-space v-if="!isAwarded">
+                    <a-space v-if="isDraft">
                       <a-button size="small" type="link" @click="handleEditMaterial(record)"><IconifyIcon icon="ant-design:edit-outlined" />缂栬緫</a-button>
                       <a-popconfirm title="纭鍒犻櫎璇ョ墿鏂欙紵" @confirm="handleDeleteMaterial(record.id)">
                         <a-button size="small" type="link" danger><IconifyIcon icon="ant-design:delete-outlined" />鍒犻櫎</a-button>
@@ -452,7 +594,7 @@
               </template>
               <div class="flex items-center justify-between mb-3">
                 <span class="text-xs text-gray-400">鍏� <b class="text-gray-600">{{ bidCount }}</b> 涓姇鏍�</span>
-                <a-button v-if="!isAwarded" type="primary" size="small" @click="bidForm = { tenderProjectId: projectId, supplierId: 0, bidNo: '' }; bidFormVisible = true">
+                <a-button v-if="isPublishedOrBidding || isDraft || isPriceComparing" type="primary" size="small" @click="bidForm = { tenderProjectId: projectId, supplierId: undefined!, bidNo: '' }; bidFormVisible = true">
                   <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
                   鏂板鎶曟爣
                 </a-button>
@@ -470,7 +612,10 @@
                   <template v-if="column.key === 'actions'">
                     <a-space>
                       <a-button size="small" type="link" @click="loadQuotes(record.id); activeTab = 'quote'"><IconifyIcon icon="ant-design:dollar-outlined" />鎶ヤ环</a-button>
-                      <a-popconfirm v-if="!isAwarded && record.bidStatus === BID_STATUS.REGISTERED" title="纭鎾ゆ爣锛�" @confirm="handleWithdrawBid(record.id)">
+                      <a-popconfirm v-if="isPriceComparing && record.bidStatus !== BID_STATUS.WITHDRAWN" title="纭閫夋嫨璇ユ姇鏍囦负涓爣锛熷皢鑷姩鐢熸垚閲囪喘璁㈠崟銆�" ok-text="纭涓爣" cancel-text="鍙栨秷" @confirm="handleConfirmBidAsWinner(record.id)">
+                        <a-button size="small" type="link" style="color: #52c41a;"><IconifyIcon icon="ant-design:check-circle-outlined" />纭涓爣</a-button>
+                      </a-popconfirm>
+                      <a-popconfirm v-if="(isPublishedOrBidding || isDraft || isPriceComparing) && record.bidStatus === BID_STATUS.REGISTERED" title="纭鎾ゆ爣锛�" @confirm="handleWithdrawBid(record.id)">
                         <a-button size="small" type="link" danger><IconifyIcon icon="ant-design:close-circle-outlined" />鎾ゆ爣</a-button>
                       </a-popconfirm>
                     </a-space>
@@ -493,7 +638,7 @@
                   <a-button size="small" type="link" class="ml-auto" @click="activeTab = 'bid'">鍒囨崲鎶曟爣</a-button>
                 </div>
                 <div class="text-right mb-3">
-                  <a-button v-if="!isAwarded && selectedBid.bidStatus !== BID_STATUS.WITHDRAWN" type="primary" size="small" @click="handleAddQuote">
+                  <a-button v-if="(isPublishedOrBidding || isDraft || isPriceComparing) && selectedBid?.bidStatus !== BID_STATUS.WITHDRAWN" type="primary" size="small" @click="handleAddQuote">
                     <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
                     鏂板鎶ヤ环
                   </a-button>
@@ -513,7 +658,7 @@
                       {{ record.warrantyPeriod != null ? `${record.warrantyPeriod} 涓湀` : '-' }}
                     </template>
                     <template v-if="column.key === 'actions'">
-                      <a-button v-if="!isAwarded && selectedBid?.bidStatus !== BID_STATUS.WITHDRAWN" size="small" type="link" @click="handleEditQuote(record)"><IconifyIcon icon="ant-design:edit-outlined" />缂栬緫</a-button>
+                      <a-button v-if="(isPublishedOrBidding || isDraft || isPriceComparing) && selectedBid?.bidStatus !== BID_STATUS.WITHDRAWN" size="small" type="link" @click="handleEditQuote(record)"><IconifyIcon icon="ant-design:edit-outlined" />缂栬緫</a-button>
                     </template>
                   </template>
                 </a-table>
@@ -525,7 +670,7 @@
             </a-tab-pane>
 
             <!-- Tab4: 寮�鏍� -->
-            <a-tab-pane key="bidOpen">
+            <a-tab-pane v-if="!isSimpleMode" key="bidOpen">
               <template #tab>
                 <a-space :size="4"><IconifyIcon icon="ant-design:unlock-outlined" />寮�鏍�</a-space>
               </template>
@@ -543,7 +688,7 @@
                 <div class="text-center py-8">
                   <a-result status="info" title="灏氭湭寮�鏍�" sub-title="鎵ц寮�鏍囧悗椤圭洰杩涘叆鎶曟爣闃舵锛屼緵搴斿晢鍙寮忔姇鏍�" />
                   <br />
-                  <a-button v-if="!isAwarded" type="primary" @click="handleCreateBidOpen">
+                  <a-button v-if="isPublished" type="primary" @click="handleCreateBidOpen">
                     <template #icon><IconifyIcon icon="ant-design:thunderbolt-outlined" /></template>
                     鎵ц寮�鏍�
                   </a-button>
@@ -552,18 +697,18 @@
             </a-tab-pane>
 
             <!-- Tab5: 璇勬爣 -->
-            <a-tab-pane key="evaluation">
+            <a-tab-pane v-if="!isSimpleMode" key="evaluation">
               <template #tab>
                 <a-space :size="4"><IconifyIcon icon="ant-design:trophy-outlined" />璇勬爣</a-space>
               </template>
               <div class="flex items-center justify-between mb-3">
                 <span class="text-xs text-gray-400">鍏� <b class="text-gray-600">{{ evalCount }}</b> 鏉★紝宸叉帓鍚� <b class="text-green-500">{{ rankedCount }}</b> 鏉�</span>
-                <a-space v-if="!isAwarded">
+                <a-space v-if="isBiddingOrEvaluating">
                   <a-button size="small" @click="handleCalculateRanking">
                     <template #icon><IconifyIcon icon="ant-design:bar-chart-outlined" /></template>
                     璁$畻鎺掑悕
                   </a-button>
-                  <a-button type="primary" size="small" @click="evalForm = { tenderProjectId: projectId, supplierId: 0, bidId: 0 }; evalFormVisible = true">
+                  <a-button type="primary" size="small" @click="evalForm = { tenderProjectId: projectId, supplierId: undefined!, bidId: undefined! }; evalFormVisible = true">
                     <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
                     鏂板璇勬爣
                   </a-button>
@@ -590,7 +735,7 @@
                     <span v-else class="text-gray-300">-</span>
                   </template>
                   <template v-if="column.key === 'actions'">
-                    <a-button v-if="!isAwarded" size="small" type="link" @click="evalForm = { ...record }; evalFormVisible = true"><IconifyIcon icon="ant-design:edit-outlined" />缂栬緫</a-button>
+                    <a-button v-if="isBiddingOrEvaluating" size="small" type="link" @click="evalForm = { ...record }; evalFormVisible = true"><IconifyIcon icon="ant-design:edit-outlined" />缂栬緫</a-button>
                   </template>
                 </template>
               </a-table>
@@ -598,7 +743,7 @@
             </a-tab-pane>
 
             <!-- Tab6: 瀹氭爣 -->
-            <a-tab-pane key="award">
+            <a-tab-pane v-if="!isSimpleMode" key="award">
               <template #tab>
                 <a-space :size="4"><IconifyIcon icon="ant-design:check-circle-outlined" />瀹氭爣</a-space>
               </template>
@@ -631,7 +776,7 @@
                             <span class="text-sm">閲囪喘璁㈠崟 ID: {{ award.purchaseOrderId }}</span>
                           </template>
                         </a-result>
-                        <a-popconfirm v-if="!isAwarded" title="纭畾鍒犻櫎璇ュ畾鏍囪褰曪紵" ok-text="纭畾" cancel-text="鍙栨秷" @confirm="handleDeleteAward">
+                        <a-popconfirm v-if="canDeleteAward" title="纭畾鍒犻櫎璇ュ畾鏍囪褰曪紵" ok-text="纭畾" cancel-text="鍙栨秷" @confirm="handleDeleteAward">
                           <a-button type="link" danger size="small" class="mt-2">鍒犻櫎瀹氭爣</a-button>
                         </a-popconfirm>
                       </div>
@@ -639,11 +784,11 @@
                     <template v-else-if="award.awardStatus === AWARD_STATUS.AWARDED">
                       <div class="text-center">
                         <a-result status="success" title="瀹氭爣瀹屾垚" class="p-0 mb-3" />
-                        <a-button v-if="!isAwarded" type="primary" block @click="handleGeneratePurchaseOrder">
+                        <a-button v-if="canGeneratePO" type="primary" block @click="handleGeneratePurchaseOrder">
                           <template #icon><IconifyIcon icon="ant-design:shopping-cart-outlined" /></template>
                           鐢熸垚閲囪喘璁㈠崟
                         </a-button>
-                        <a-popconfirm v-if="!isAwarded" title="纭畾鍒犻櫎璇ュ畾鏍囪褰曪紵" ok-text="纭畾" cancel-text="鍙栨秷" @confirm="handleDeleteAward">
+                        <a-popconfirm v-if="canDeleteAward" title="纭畾鍒犻櫎璇ュ畾鏍囪褰曪紵" ok-text="纭畾" cancel-text="鍙栨秷" @confirm="handleDeleteAward">
                           <a-button type="link" danger size="small" class="mt-2">鍒犻櫎瀹氭爣</a-button>
                         </a-popconfirm>
                       </div>
@@ -651,11 +796,11 @@
                     <template v-else>
                       <div class="text-center">
                         <div class="text-gray-500 text-sm mb-3">瀹氭爣淇℃伅宸插垱寤猴紝璇峰鎵�</div>
-                        <a-button v-if="!isAwarded" type="primary" block @click="handleApproveAward" style="background: #52c41a; border-color: #52c41a;">
+                        <a-button v-if="canApproveAward" type="primary" block @click="handleApproveAward" style="background: #52c41a; border-color: #52c41a;">
                           <template #icon><IconifyIcon icon="ant-design:check-outlined" /></template>
                           瀹℃壒閫氳繃
                         </a-button>
-                        <a-popconfirm v-if="!isAwarded" title="纭畾鍒犻櫎璇ュ畾鏍囪褰曪紵" ok-text="纭畾" cancel-text="鍙栨秷" @confirm="handleDeleteAward">
+                        <a-popconfirm v-if="canDeleteAward" title="纭畾鍒犻櫎璇ュ畾鏍囪褰曪紵" ok-text="纭畾" cancel-text="鍙栨秷" @confirm="handleDeleteAward">
                           <a-button type="link" danger size="small" class="mt-2">鍒犻櫎瀹氭爣</a-button>
                         </a-popconfirm>
                       </div>
@@ -667,7 +812,7 @@
                 <div class="text-center py-8">
                   <a-result status="info" title="灏氭湭瀹氭爣" sub-title="鍩轰簬璇勬爣鎺掑悕閫夋嫨涓爣渚涘簲鍟嗗苟鍒涘缓瀹氭爣" />
                   <br />
-                  <a-button v-if="!isAwarded" type="primary" @click="awardForm = { tenderProjectId: projectId, supplierId: 0, bidId: 0, awardNo: '' }; awardFormVisible = true">
+                  <a-button v-if="isEvaluating" type="primary" @click="awardForm = { tenderProjectId: projectId, supplierId: undefined!, bidId: undefined!, awardNo: '' }; awardFormVisible = true">
                     <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
                     鍒涘缓瀹氭爣
                   </a-button>
@@ -685,7 +830,7 @@
         <a-form layout="vertical" class="mt-4">
           <a-form-item label="MDM鐗╂枡" required>
             <a-select v-model:value="materialForm.productId" show-search placeholder="鎼滅储骞堕�夋嫨MDM鐗╂枡" :filter-option="filterOption" option-label-prop="label" @change="onMaterialSelect">
-              <a-select-option v-for="item in mdmItemList" :key="item.id" :value="item.id" :label="`${item.code} ${item.name}`">
+              <a-select-option v-for="item in availableMdmItems" :key="item.id" :value="item.id" :label="`${item.code} ${item.name}`">
                 <div class="flex flex-col">
                   <span class="font-medium">{{ item.code }}</span>
                   <span class="text-xs text-gray-400">{{ item.name }}{{ item.specification ? ` 路 ${item.specification}` : '' }}</span>
@@ -799,6 +944,32 @@
         </a-form>
       </a-modal>
 
+      <!-- 绠�鏄撴ā寮忥細纭涓爣寮圭獥 -->
+      <a-modal v-model:open="confirmModalVisible" title="纭涓爣" :width="460" @ok="handleSimpleConfirm">
+        <a-form layout="vertical" class="mt-4">
+          <a-form-item label="涓爣鎶曟爣璁板綍" required>
+            <a-select
+              v-model:value="confirmBidId"
+              show-search
+              placeholder="閫夋嫨涓爣鐨勬姇鏍囪褰�"
+              :filter-option="filterOption"
+            >
+              <a-select-option
+                v-for="b in bidList.filter((i) => i.bidStatus !== BID_STATUS.WITHDRAWN)"
+                :key="b.id"
+                :value="b.id"
+                :label="`${b.supplierName} - ${b.bidNo}`"
+              >
+                {{ b.supplierName }} - {{ b.bidNo }} (楼{{ b.bidTotalAmount?.toLocaleString?.() ?? b.bidTotalAmount }})
+              </a-select-option>
+            </a-select>
+          </a-form-item>
+          <div class="text-gray-400 text-xs">
+            纭鍚庡皢鑷姩鐢熸垚閲囪喘璁㈠崟锛岃鎿嶄綔涓嶅彲鎾ら攢銆�
+          </div>
+        </a-form>
+      </a-modal>
+
       <!-- 瀹氭爣琛ㄥ崟 -->
       <a-modal v-model:open="awardFormVisible" title="鍒涘缓瀹氭爣" :width="500" @ok="handleCreateAward">
         <a-form layout="vertical" class="mt-4">
@@ -806,15 +977,15 @@
           <a-row :gutter="12">
             <a-col :span="12">
               <a-form-item label="渚涘簲鍟�" required>
-                <a-select v-model:value="awardForm.supplierId" show-search placeholder="閫夋嫨渚涘簲鍟�" :filter-option="filterOption">
-                  <a-select-option v-for="s in supplierList" :key="s.id" :value="s.id" :label="s.name">{{ s.name }}</a-select-option>
+                <a-select v-model:value="awardForm.supplierId" show-search placeholder="閫夋嫨渚涘簲鍟�" :filter-option="filterOption" @change="onAwardSupplierChange">
+                  <a-select-option v-for="s in bidSuppliers" :key="s.id" :value="s.id" :label="s.name">{{ s.name }}</a-select-option>
                 </a-select>
               </a-form-item>
             </a-col>
             <a-col :span="12">
               <a-form-item label="鎶曟爣" required>
-                <a-select v-model:value="awardForm.bidId" show-search placeholder="閫夋嫨鎶曟爣" :filter-option="filterOption">
-                  <a-select-option v-for="b in bidList" :key="b.id" :value="b.id" :label="`${b.bidNo} - ${b.supplierName}`">{{ b.bidNo }} - {{ b.supplierName }}</a-select-option>
+                <a-select v-model:value="awardForm.bidId" show-search placeholder="閫夋嫨鎶曟爣" :filter-option="filterOption" :disabled="!awardForm.supplierId" @change="onAwardBidChange">
+                  <a-select-option v-for="b in activeBids" :key="b.id" :value="b.id" :label="`${b.bidNo} - ${b.supplierName}`">{{ b.bidNo }} - {{ b.supplierName }}</a-select-option>
                 </a-select>
               </a-form-item>
             </a-col>

--
Gitblit v1.9.3