From f5e56edeac0d9a386fd711819ec8e6850f58c8ca Mon Sep 17 00:00:00 2001
From: liu <2021943741@qq.com>
Date: 星期五, 04 九月 2026 11:32:16 +0800
Subject: [PATCH] feat(SRM): 报价页支持投标总金额自动带出人工维护,去掉金额强校验弹窗
---
src/api/srm/tender/index.ts | 5 +
src/views/srm/tender/detail.vue | 122 ++++++++++++++++++++++++++++++++--------
2 files changed, 102 insertions(+), 25 deletions(-)
diff --git a/src/api/srm/tender/index.ts b/src/api/srm/tender/index.ts
index cefefdb..8b6d54d 100644
--- a/src/api/srm/tender/index.ts
+++ b/src/api/srm/tender/index.ts
@@ -198,6 +198,11 @@
return requestClient.put(`/srm/tender-bid/withdraw?id=${id}`);
}
+export function updateBidTotal(id: number, bidTotalAmount?: number | null) {
+ const params = bidTotalAmount == null ? '' : `&bidTotalAmount=${bidTotalAmount}`;
+ return requestClient.put(`/srm/tender-bid/update-total?id=${id}${params}`);
+}
+
// ========== 鎶ヤ环绠$悊 ==========
export function getQuoteList(bidId: number) {
diff --git a/src/views/srm/tender/detail.vue b/src/views/srm/tender/detail.vue
index 3f3a7f2..fda47ec 100644
--- a/src/views/srm/tender/detail.vue
+++ b/src/views/srm/tender/detail.vue
@@ -6,13 +6,13 @@
import { Page } from '@vben/common-ui';
-import { message, Modal } from 'ant-design-vue';
+import { message } from 'ant-design-vue';
import { IconifyIcon } from '@vben/icons';
import {
getTenderProject,
getMaterialList, createMaterial, updateMaterial, deleteMaterial,
- getBidListByProject, createBid, withdrawBid,
+ getBidListByProject, createBid, withdrawBid, updateBidTotal,
getQuoteList, createQuote, updateQuote,
createBidOpen, getBidOpen,
getEvaluationList, createBidEvaluation, updateBidEvaluation, calculateRanking,
@@ -150,6 +150,8 @@
const quoteFormVisible = ref(false);
const quoteForm = ref<SrmTenderApi.QuoteVO>({ bidId: undefined!, tenderMaterialId: undefined! });
const selectedBidId = ref<number>();
+/** 鎶曟爣鎬婚噾棰濊崏绋匡細杩涘叆鎶ヤ环椤垫椂甯﹀嚭宸蹭繚瀛樻�讳环锛屽彲鑷姩甯﹀嚭鎶ヤ环鍚堣鍚庝汉宸ュ井璋� */
+const bidTotalDraft = ref<number>();
const availableMaterials = computed(() => {
const quotedIds = new Set(
@@ -169,29 +171,11 @@
selectedBidId.value = bidId;
quoteLoading.value = true;
try { quoteList.value = await getQuoteList(bidId); } finally { quoteLoading.value = false; }
+ // 甯﹀嚭璇ユ姇鏍囧凡淇濆瓨鐨勬姇鏍囨�婚噾棰濓紙涓虹┖鍒欒鐢ㄦ埛褰曞叆/鑷姩甯﹀嚭锛�
+ const bid = bidList.value.find((b) => b.id === bidId);
+ bidTotalDraft.value = bid?.bidTotalAmount ?? undefined;
}
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 {
@@ -208,6 +192,62 @@
quoteForm.value = { ...row };
quoteFormVisible.value = true;
}
+
+/** 鎷涙爣鐗╂枡 Map锛氭姤浠疯鎸� tenderMaterialId 鍙嶆煡鐗╂枡鏁伴噺 */
+const materialMap = computed(() => {
+ const map = new Map<number, SrmTenderApi.TenderMaterialVO>();
+ materialList.value.forEach((m) => {
+ if (m.id != null) map.set(m.id, m);
+ });
+ return map;
+});
+
+/** 鎶ヤ环鏄庣粏瀵瑰簲鐨勬嫑鏍囩墿鏂欐暟閲� */
+function materialQuantityOf(quote: SrmTenderApi.QuoteVO): number {
+ return materialMap.value.get(quote.tenderMaterialId!)?.quantity ?? 0;
+}
+
+/** 鎶ヤ环琛岄噾棰� = 鎶ヤ环鍗曚环 脳 鐗╂枡鏁伴噺 */
+function quoteAmountOf(quote: SrmTenderApi.QuoteVO): number {
+ return (quote.quotePrice ?? 0) * materialQuantityOf(quote);
+}
+
+/** 閫変腑鎶曟爣涓嬪凡褰曟姤浠风殑鍚堣閲戦 */
+const selectedQuoteTotalAmount = computed(() =>
+ quoteList.value.reduce((sum, q) => sum + quoteAmountOf(q), 0),
+);
+
+/** 缁存姢锛堟暣鍗曪級鎶曟爣鎬婚噾棰濓細鍙嚜鍔ㄥ甫鍑烘姤浠峰悎璁″悗浜哄伐寰皟锛堢敤浜庢暣鍗曡鍒╃瓑锛� */
+async function handleSaveBidTotal() {
+ if (!selectedBidId.value) return;
+ if (bidTotalDraft.value == null) {
+ message.warning('璇峰~鍐欐姇鏍囨�婚噾棰�');
+ return;
+ }
+ await updateBidTotal(selectedBidId.value, bidTotalDraft.value);
+ message.success('鎶曟爣鎬婚噾棰濆凡淇濆瓨');
+ loadBids();
+}
+
+/** 宸蹭繚瀛樼殑鎶曟爣鎬婚噾棰� 涓� 鎶ヤ环鍚堣 鐨勫樊寮傛彁绀猴紙鍙彁绀猴紝涓嶆嫤鎴級 */
+const bidTotalCompareHint = computed<{ text: string; tone: 'success' | 'warning' } | null>(() => {
+ const sum = selectedQuoteTotalAmount.value;
+ if (sum <= 0) {
+ return null; // 灏氭湭褰曟姤浠凤紝鏆備笉姣旇緝
+ }
+ const stored = selectedBid.value?.bidTotalAmount;
+ if (stored == null) {
+ return { text: '璇ユ姇鏍囧皻鏈淮鎶ゆ�婚噾棰濓細鍙偣銆岃嚜鍔ㄥ甫鍑烘姤浠峰悎璁°�嶅~鍏ュ悗淇濆瓨', tone: 'warning' };
+ }
+ const diff = Math.abs(stored - sum);
+ if (diff < 0.01) {
+ return { text: `鎶曟爣鎬婚噾棰� 楼${stored.toLocaleString()} 涓庢姤浠峰悎璁′竴鑷碻, tone: 'success' };
+ }
+ return {
+ text: `鎶曟爣鎬婚噾棰� 楼${stored.toLocaleString()} 涓庢姤浠峰悎璁� 楼${sum.toLocaleString()} 涓嶄竴鑷达紙宸 楼${diff.toLocaleString()}锛夈�傚闇�鏁村崟璁╁埄/璋冧环锛岀洿鎺ュ湪鎶曟爣鎬婚噾棰濆淇敼骞朵繚瀛樸�俙,
+ tone: 'warning',
+ };
+});
// ========== 寮�鏍� ==========
const bidOpen = ref<any>(null);
@@ -448,6 +488,8 @@
const quoteColumns = [
{ title: '鐗╂枡', dataIndex: 'materialName', width: 120 },
{ title: '鎶ヤ环鍗曚环', dataIndex: 'quotePrice', width: 100 },
+ { title: '鏁伴噺', dataIndex: 'quantity', width: 70 },
+ { title: '鎶ヤ环閲戦', dataIndex: 'quoteAmount', width: 110 },
{ title: '绋庣巼(%)', dataIndex: 'taxRate', width: 80 },
{ title: '浜ゆ湡(澶�)', dataIndex: 'deliveryCycle', width: 80 },
{ title: '浠樻鏉′欢', dataIndex: 'paymentTerms', width: 120 },
@@ -656,7 +698,29 @@
<span class="text-gray-400">渚涘簲鍟嗭細<b class="text-gray-700">{{ selectedBid.supplierName }}</b></span>
<a-button size="small" type="link" class="ml-auto" @click="activeTab = 'bid'">鍒囨崲鎶曟爣</a-button>
</div>
- <div class="text-right mb-3">
+ <!-- 鎶曟爣鎬婚噾棰濓紙鏁村崟鎬讳环锛夛細鍙笌鎶ヤ环鍚堣鑷姩甯﹀嚭骞朵汉宸ヨ皟鏁� -->
+ <div v-if="(isPublishedOrBidding || isDraft || isPriceComparing) && selectedBid?.bidStatus !== BID_STATUS.WITHDRAWN" class="flex flex-wrap items-center gap-x-3 gap-y-2 mb-2 px-3 py-2 bg-blue-50/70 rounded text-xs">
+ <span class="text-gray-600 font-medium">鎶曟爣鎬婚噾棰�</span>
+ <a-input-number v-model:value="bidTotalDraft" :min="0" :precision="2" class="!w-40" placeholder="鏁村崟鎬讳环">
+ <template #addonAfter>鍏�</template>
+ </a-input-number>
+ <a-button size="small" type="link" :disabled="selectedQuoteTotalAmount <= 0" @click="bidTotalDraft = selectedQuoteTotalAmount">
+ 鑷姩甯﹀嚭鎶ヤ环鍚堣 楼{{ selectedQuoteTotalAmount.toLocaleString() }}
+ </a-button>
+ <a-button size="small" type="primary" @click="handleSaveBidTotal">淇濆瓨鎬讳环</a-button>
+ </div>
+ <a-alert
+ v-if="bidTotalCompareHint"
+ :type="bidTotalCompareHint.tone"
+ :show-icon="true"
+ class="mb-3"
+ :message="bidTotalCompareHint.text"
+ />
+ <div class="flex items-center justify-between mb-3">
+ <span class="text-xs text-gray-400">
+ 宸叉姤浠� <b class="text-gray-600">{{ quoteList.length }}</b> / <b class="text-gray-600">{{ materialList.length }}</b> 椤圭墿鏂欙紝鎶ヤ环鍚堣
+ <b class="text-blue-500">{{ selectedQuoteTotalAmount.toLocaleString() }}</b> 鍏�
+ </span>
<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>
鏂板鎶ヤ环
@@ -666,6 +730,12 @@
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'quotePrice'">
<span class="font-medium text-blue-600">{{ record.quotePrice?.toLocaleString() }}</span>
+ </template>
+ <template v-if="column.dataIndex === 'quantity'">
+ {{ materialQuantityOf(record) || '-' }}
+ </template>
+ <template v-if="column.dataIndex === 'quoteAmount'">
+ <span class="font-medium text-blue-600">{{ quoteAmountOf(record).toLocaleString() }}</span>
</template>
<template v-if="column.dataIndex === 'taxRate'">
{{ record.taxRate != null ? `${record.taxRate}%` : '-' }}
@@ -892,7 +962,9 @@
</a-select>
</a-form-item>
<a-form-item label="鎶曟爣鎬婚噾棰�">
- <a-input-number v-model:value="bidForm.bidTotalAmount" :min="0" class="!w-full" placeholder="鎶曟爣鎬婚噾棰�"><template #addonAfter>鍏�</template></a-input-number>
+ <div class="text-xs text-gray-400 leading-5">
+ 鍙殏涓嶅~锛氭姤浠锋槑缁嗗綍瀹屽悗锛屽湪銆屾姤浠风鐞嗐�嶉〉鎸夋姤浠峰悎璁¤嚜鍔ㄥ甫鍑恒�佸啀浜哄伐寰皟淇濆瓨銆�
+ </div>
</a-form-item>
</a-form>
</a-modal>
--
Gitblit v1.9.3