From 020a05d8d31565fc6fe2b0f76645cdabe8374e1c Mon Sep 17 00:00:00 2001
From: 云 <2163098428@qq.com>
Date: 星期一, 03 八月 2026 17:56:21 +0800
Subject: [PATCH] refactor(srm): 优化招标项目功能实现

---
 src/api/srm/tender/index.ts                           |   10 ++
 src/views/dashboard/analytics/modules/ai-insights.vue |   43 ++++------
 src/views/wls/salesnotice/modules/form.vue            |   26 ++++++
 src/views/hrm/employee/modules/form.vue               |   10 --
 src/views/srm/tender/data.ts                          |   44 +++++++++++
 src/views/srm/tender/index.vue                        |   10 ++
 src/views/srm/tender/detail.vue                       |   57 ++++++++-----
 src/views/srm/tender/modules/form.vue                 |   25 +++++-
 8 files changed, 164 insertions(+), 61 deletions(-)

diff --git a/src/api/srm/tender/index.ts b/src/api/srm/tender/index.ts
index 5cf30e8..5ac7ad8 100644
--- a/src/api/srm/tender/index.ts
+++ b/src/api/srm/tender/index.ts
@@ -99,6 +99,12 @@
     purchaseOrderId?: number;
     remark?: string;
   }
+
+  export interface TenderProjectPartialUpdateVO {
+    id: number;
+    tenderDesc?: string;
+    qualificationRequirements?: string;
+  }
 }
 
 // ========== 鎷涙爣椤圭洰 ==========
@@ -119,6 +125,10 @@
   return requestClient.put('/srm/tender-project/update', data);
 }
 
+export function updateTenderProjectPartial(data: SrmTenderApi.TenderProjectPartialUpdateVO) {
+  return requestClient.put('/srm/tender-project/update-info', data);
+}
+
 export function deleteTenderProject(id: number) {
   return requestClient.delete(`/srm/tender-project/delete?id=${id}`);
 }
diff --git a/src/views/dashboard/analytics/modules/ai-insights.vue b/src/views/dashboard/analytics/modules/ai-insights.vue
index 93b9ada..5e1a550 100644
--- a/src/views/dashboard/analytics/modules/ai-insights.vue
+++ b/src/views/dashboard/analytics/modules/ai-insights.vue
@@ -1,10 +1,5 @@
 <script lang="ts" setup>
-import { computed } from 'vue';
-
-import { CountTo } from '@vben/common-ui';
 import { IconifyIcon } from '@vben/icons';
-
-import { Col, Row } from 'ant-design-vue';
 
 defineOptions({ name: 'DashboardAiInsights' });
 
@@ -71,28 +66,28 @@
         {{ activeOrderCount }} 涓伐鍗曞彲鍒嗘瀽
       </span>
     </div>
-    <Row :gutter="16">
-      <Col v-for="cap in capabilities" :key="cap.key" :lg="6" :md="12" :sm="12" :xl="6" :xs="12" class="mb-3">
+    <div class="grid grid-cols-2 gap-3 lg:grid-cols-4">
+      <div
+        v-for="cap in capabilities"
+        :key="cap.key"
+        class="group flex cursor-pointer items-center gap-3 rounded-xl p-3 transition-all duration-300 hover:-translate-y-0.5 hover:shadow-lg"
+        @click="emit('navigate', cap.route)"
+      >
         <div
-          class="group flex cursor-pointer items-center gap-3 rounded-xl p-3 transition-all duration-300 hover:-translate-y-0.5 hover:shadow-lg"
-          @click="emit('navigate', cap.route)"
+          class="flex size-10 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br shadow-md transition-transform duration-300 group-hover:scale-110"
+          :class="cap.gradient"
         >
-          <div
-            class="flex size-10 flex-shrink-0 items-center justify-center rounded-lg bg-gradient-to-br shadow-md transition-transform duration-300 group-hover:scale-110"
-            :class="cap.gradient"
-          >
-            <IconifyIcon :icon="cap.icon" class="size-5 text-white" />
-          </div>
-          <div class="min-w-0 flex-1">
-            <div class="text-sm font-medium text-gray-800 dark:text-gray-100">
-              {{ cap.title }}
-            </div>
-            <div class="text-xs text-gray-400">{{ cap.desc }}</div>
-          </div>
-          <IconifyIcon icon="lucide:chevron-right" class="size-4 text-gray-300 transition-transform duration-200 group-hover:translate-x-0.5" />
+          <IconifyIcon :icon="cap.icon" class="size-5 text-white" />
         </div>
-      </Col>
-    </Row>
+        <div class="min-w-0 flex-1">
+          <div class="text-sm font-medium text-gray-800 dark:text-gray-100">
+            {{ cap.title }}
+          </div>
+          <div class="text-xs text-gray-400">{{ cap.desc }}</div>
+        </div>
+        <IconifyIcon icon="lucide:chevron-right" class="size-4 text-gray-300 transition-transform duration-200 group-hover:translate-x-0.5" />
+      </div>
+    </div>
   </div>
 </template>
 
diff --git a/src/views/hrm/employee/modules/form.vue b/src/views/hrm/employee/modules/form.vue
index 0cd7c0d..e24e6ce 100644
--- a/src/views/hrm/employee/modules/form.vue
+++ b/src/views/hrm/employee/modules/form.vue
@@ -16,7 +16,6 @@
 import EducationList from './education-list.vue';
 import WorkHistoryList from './work-history-list.vue';
 import EmergencyContactList from './emergency-contact-list.vue';
-import SocialSecurityList from './social-security-list.vue';
 import EmployeeDetail from './detail.vue';
 
 const emit = defineEmits(['success']);
@@ -30,7 +29,6 @@
 const educationListRef = ref<InstanceType<typeof EducationList>>();
 const workHistoryListRef = ref<InstanceType<typeof WorkHistoryList>>();
 const emergencyContactListRef = ref<InstanceType<typeof EmergencyContactList>>();
-const socialSecurityListRef = ref<InstanceType<typeof SocialSecurityList>>();
 
 const isDetail = computed(() => formType.value === 'detail');
 const getTitle = computed(() => {
@@ -153,14 +151,6 @@
             ref="emergencyContactListRef"
             :form-type="formType"
             :employee-id="employeeId"
-          />
-        </Tabs.TabPane>
-        <Tabs.TabPane key="socialSecurity" tab="绀句繚鍏Н閲戞。妗�" v-if="employeeId">
-          <SocialSecurityList
-            ref="socialSecurityListRef"
-            :form-type="formType"
-            :employee-id="employeeId"
-            :user-id="employeeData?.userId"
           />
         </Tabs.TabPane>
       </Tabs>
diff --git a/src/views/srm/tender/data.ts b/src/views/srm/tender/data.ts
index d0bb157..c75f017 100644
--- a/src/views/srm/tender/data.ts
+++ b/src/views/srm/tender/data.ts
@@ -20,11 +20,23 @@
       },
     },
     {
+      component: 'Input',
+      fieldName: '_partial',
+      dependencies: {
+        triggerFields: [''],
+        show: () => false,
+      },
+    },
+    {
       fieldName: 'tenderNo',
       label: '鎷涙爣缂栧彿',
       component: 'Input',
       rules: 'required',
       componentProps: { placeholder: '璇疯緭鍏ユ嫑鏍囩紪鍙�' },
+      dependencies: {
+        triggerFields: ['_partial'],
+        show: (partial: boolean) => !partial,
+      },
     },
     {
       fieldName: 'tenderName',
@@ -32,6 +44,10 @@
       component: 'Input',
       rules: 'required',
       componentProps: { placeholder: '璇疯緭鍏ユ嫑鏍囬」鐩悕绉�' },
+      dependencies: {
+        triggerFields: ['_partial'],
+        show: (partial: boolean) => !partial,
+      },
     },
     {
       fieldName: 'tenderType',
@@ -42,18 +58,30 @@
         placeholder: '璇烽�夋嫨鎷涙爣绫诲瀷',
         options: TENDER_TYPE_OPTIONS,
       },
+      dependencies: {
+        triggerFields: ['_partial'],
+        show: (partial: boolean) => !partial,
+      },
     },
     {
       fieldName: 'purchaseOrg',
       label: '閲囪喘缁勭粐',
       component: 'Input',
       componentProps: { placeholder: '璇疯緭鍏ラ噰璐粍缁�' },
+      dependencies: {
+        triggerFields: ['_partial'],
+        show: (partial: boolean) => !partial,
+      },
     },
     {
       fieldName: 'projectLeaderName',
       label: '椤圭洰璐熻矗浜�',
       component: 'Input',
       componentProps: { placeholder: '璇疯緭鍏ラ」鐩礋璐d汉' },
+      dependencies: {
+        triggerFields: ['_partial'],
+        show: (partial: boolean) => !partial,
+      },
     },
     {
       fieldName: 'budgetAmount',
@@ -63,6 +91,10 @@
         class: '!w-full',
         min: 0,
         placeholder: '璇疯緭鍏ラ绠楅噾棰�',
+      },
+      dependencies: {
+        triggerFields: ['_partial'],
+        show: (partial: boolean) => !partial,
       },
     },
     {
@@ -76,6 +108,10 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
+      dependencies: {
+        triggerFields: ['_partial'],
+        show: (partial: boolean) => !partial,
+      },
     },
     {
       fieldName: 'endTime',
@@ -88,6 +124,10 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
+      dependencies: {
+        triggerFields: ['_partial'],
+        show: (partial: boolean) => !partial,
+      },
     },
     {
       fieldName: 'bidOpenTime',
@@ -100,6 +140,10 @@
         format: 'YYYY-MM-DD HH:mm:ss',
         valueFormat: 'YYYY-MM-DD HH:mm:ss',
       },
+      dependencies: {
+        triggerFields: ['_partial'],
+        show: (partial: boolean) => !partial,
+      },
     },
     {
       fieldName: 'tenderDesc',
diff --git a/src/views/srm/tender/detail.vue b/src/views/srm/tender/detail.vue
index 7de94be..839df72 100644
--- a/src/views/srm/tender/detail.vue
+++ b/src/views/srm/tender/detail.vue
@@ -126,7 +126,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;
@@ -146,7 +146,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(() => {
@@ -199,7 +199,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) {
@@ -226,7 +226,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;
@@ -251,7 +251,7 @@
 // ========== 瀹氭爣 ==========
 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: '' });
 
 async function loadAward() {
   try { award.value = await getAwardByProject(projectId); } catch { award.value = undefined; }
@@ -295,7 +295,20 @@
 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 },
@@ -462,7 +475,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>
@@ -473,7 +486,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>
@@ -492,7 +505,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" type="primary" size="small" @click="bidForm = { tenderProjectId: projectId, supplierId: undefined!, bidNo: '' }; bidFormVisible = true">
                   <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
                   鏂板鎶曟爣
                 </a-button>
@@ -510,7 +523,7 @@
                   <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="isPublishedOrBidding && 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>
@@ -533,7 +546,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 && selectedBid.bidStatus !== BID_STATUS.WITHDRAWN" type="primary" size="small" @click="handleAddQuote">
                     <template #icon><IconifyIcon icon="ant-design:plus-outlined" /></template>
                     鏂板鎶ヤ环
                   </a-button>
@@ -553,7 +566,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 && selectedBid?.bidStatus !== BID_STATUS.WITHDRAWN" size="small" type="link" @click="handleEditQuote(record)"><IconifyIcon icon="ant-design:edit-outlined" />缂栬緫</a-button>
                     </template>
                   </template>
                 </a-table>
@@ -583,7 +596,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>
@@ -598,12 +611,12 @@
               </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>
@@ -630,7 +643,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>
@@ -671,7 +684,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>
@@ -679,11 +692,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>
@@ -691,11 +704,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>
@@ -707,7 +720,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>
diff --git a/src/views/srm/tender/index.vue b/src/views/srm/tender/index.vue
index f966d8f..7e09d1a 100644
--- a/src/views/srm/tender/index.vue
+++ b/src/views/srm/tender/index.vue
@@ -163,6 +163,14 @@
               onClick: handleEdit.bind(null, row),
             },
             {
+              label: '缂栬緫淇℃伅',
+              type: 'link',
+              icon: ACTION_ICON.EDIT,
+              auth: ['srm:tender-project:update'],
+              ifShow: row.tenderStatus === TENDER_STATUS.PUBLISHED || row.tenderStatus === TENDER_STATUS.BIDDING,
+              onClick: handleEdit.bind(null, row),
+            },
+            {
               label: $t('common.delete'),
               type: 'link',
               danger: true,
@@ -189,7 +197,7 @@
               type: 'link',
               danger: true,
               auth: ['srm:tender-project:update'],
-              ifShow: row.tenderStatus === TENDER_STATUS.PUBLISHED || row.tenderStatus === TENDER_STATUS.BIDDING,
+              ifShow: row.tenderStatus === TENDER_STATUS.PUBLISHED || row.tenderStatus === TENDER_STATUS.BIDDING || row.tenderStatus === TENDER_STATUS.EVALUATING,
               popConfirm: {
                 title: '纭瑕佸叧闂椤圭洰鍚楋紵',
                 confirm: handleClose.bind(null, row),
diff --git a/src/views/srm/tender/modules/form.vue b/src/views/srm/tender/modules/form.vue
index 49520dd..5812d05 100644
--- a/src/views/srm/tender/modules/form.vue
+++ b/src/views/srm/tender/modules/form.vue
@@ -12,14 +12,21 @@
   createTenderProject,
   getTenderProject,
   updateTenderProject,
+  updateTenderProjectPartial,
 } from '#/api/srm/tender';
 import { $t } from '#/locales';
 
+import { TENDER_STATUS } from '../../enums';
 import { useFormSchema } from '../data';
 
 const emit = defineEmits(['success']);
 const formData = ref<SrmTenderApi.TenderProjectVO>();
+const isPartial = computed(() => {
+  const data = formData.value;
+  return data?.id && data.tenderStatus !== undefined && data.tenderStatus !== TENDER_STATUS.DRAFT;
+});
 const getTitle = computed(() => {
+  if (isPartial.value) return '缂栬緫鎷涙爣淇℃伅';
   return formData.value?.id
     ? $t('ui.actionTitle.edit', ['鎷涙爣椤圭洰'])
     : $t('ui.actionTitle.create', ['鎷涙爣椤圭洰']);
@@ -45,9 +52,17 @@
     modalApi.lock();
     const data = (await formApi.getValues()) as SrmTenderApi.TenderProjectVO;
     try {
-      await (formData.value?.id
-        ? updateTenderProject(data)
-        : createTenderProject(data));
+      if (isPartial.value) {
+        await updateTenderProjectPartial({
+          id: data.id!,
+          tenderDesc: data.tenderDesc,
+          qualificationRequirements: data.qualificationRequirements,
+        });
+      } else {
+        await (formData.value?.id
+          ? updateTenderProject(data)
+          : createTenderProject(data));
+      }
       await modalApi.close();
       emit('success');
       message.success($t('ui.actionMessage.operationSuccess'));
@@ -67,7 +82,9 @@
     modalApi.lock();
     try {
       formData.value = await getTenderProject(data.id);
-      await formApi.setValues(formData.value);
+      const partial = formData.value.tenderStatus !== undefined
+        && formData.value.tenderStatus !== TENDER_STATUS.DRAFT;
+      await formApi.setValues({ ...formData.value, _partial: partial || undefined });
     } finally {
       modalApi.unlock();
     }
diff --git a/src/views/wls/salesnotice/modules/form.vue b/src/views/wls/salesnotice/modules/form.vue
index 0f8266b..044cb38 100644
--- a/src/views/wls/salesnotice/modules/form.vue
+++ b/src/views/wls/salesnotice/modules/form.vue
@@ -1,6 +1,7 @@
 <script lang="ts" setup>
 import type { FormType } from '../data';
 
+import type { ErpSaleOrderApi } from '#/api/erp/sale/order';
 import type { MesWmSalesNoticeApi } from '#/api/mes/wm/salesnotice';
 
 import { computed, ref } from 'vue';
@@ -17,6 +18,8 @@
   submitSalesNotice,
   updateSalesNotice,
 } from '#/api/mes/wm/salesnotice';
+import { createSalesNoticeLine } from '#/api/mes/wm/salesnotice/line';
+import { getSaleOrderItemListByOrderId } from '#/api/erp/sale/order';
 import { $t } from '#/locales';
 
 import { useFormSchema } from '../data';
@@ -107,6 +110,29 @@
       }
       // 鏂板锛氫繚鎸佸脊绐楁墦寮�锛屽垏鎹㈠埌缂栬緫妯″紡
       const id = await createSalesNotice(data);
+      // 閫夋嫨浜嗛攢鍞鍗曟椂锛岃嚜鍔ㄥ甫鍏ヨ鍗曚骇鍝佹槑缁�
+      if (data.saleOrderId) {
+        try {
+          const items = await getSaleOrderItemListByOrderId(data.saleOrderId);
+          if (items?.length) {
+            await Promise.all(items.map((item: ErpSaleOrderApi.SaleOrderItem) =>
+              createSalesNoticeLine({
+                noticeId: id,
+                saleOrderItemId: item.id,
+                itemId: item.productId,
+                itemCode: item.productBarCode,
+                itemName: item.productName,
+                unitMeasureName: item.productUnitName,
+                quantity: item.count,
+                batchCode: item.batchCode,
+                oqcCheckFlag: false,
+              }),
+            ));
+          }
+        } catch {
+          // 鑾峰彇璁㈠崟鏄庣粏澶辫触涓嶅奖鍝嶄富娴佺▼
+        }
+      }
       formData.value = {
         ...data,
         id,

--
Gitblit v1.9.3