gaoluyang
2026-08-07 a3057955f855b8a266c017df00645845e7471410
src/views/srm/tender/detail.vue
@@ -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' });
@@ -318,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();
@@ -344,23 +376,37 @@
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';
});
@@ -395,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 = [
@@ -438,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>
          刷新
@@ -539,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="isPublishedOrBidding" type="primary" size="small" @click="bidForm = { tenderProjectId: projectId, supplierId: undefined!, 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>
@@ -557,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="isPublishedOrBidding && 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>
@@ -580,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="isPublishedOrBidding && 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>
@@ -600,7 +658,7 @@
                      {{ record.warrantyPeriod != null ? `${record.warrantyPeriod} 个月` : '-' }}
                    </template>
                    <template v-if="column.key === 'actions'">
                      <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>
                      <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>
@@ -612,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>
@@ -639,7 +697,7 @@
            </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>
@@ -685,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>
@@ -886,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">