gaoluyang
2026-08-03 0d576fce89c6d9b987c77277b063e37e2f0a6db4
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 {
@@ -63,6 +63,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);
@@ -150,6 +169,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 {
@@ -685,7 +725,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>