fix(inquiry): 后台询价详情对齐新结构——询价主体=inquiryParty、采购类型/报价方式/报价要求/付款/发票/截止全字段、商品明细表、报价按企业分组(getQuotationById)

This commit is contained in:
zou 2026-09-04 15:56:04 +08:00
parent 5103eb269f
commit 241c269e37

View File

@ -32,6 +32,16 @@ interface Inquiry {
fileList: { url: string; imagesName: string }[]
// 报价列表
quotationList: Quotation[]
// 新结构(与发布询价信息一致)
procurementTypeName?: string
quotationTypeName?: string
quotationDemandName?: string
paymentMethodName?: string
invoiceTypeName?: string
inquiryValidDate?: string
publishDate?: string
note?: string
detailList?: any[]
raw: any
}
@ -93,29 +103,44 @@ const detail = ref<Inquiry | null>(null)
const loading = ref(true)
const inquiryId = computed(() => String(route.params.id || ''))
const PROC_TYPE: Record<number, string> = { 1: '定量采购', 2: '框架采购' }
const DEMAND: Record<number, string> = { 1: '不允许部分报价', 2: '允许部分报价' }
const INVOICE: Record<number, string> = { 1: '普通发票', 2: '增值税专用发票', 3: '无' }
const QUOTE_TYPE: Record<number, string> = { 1: '公开报价', 2: '邀请报价' }
const mapLabel = (m: Record<number, string>, v: any) => (v == null ? '--' : m[Number(v)] || String(v))
const mapLabel1 = (m: Record<number, string>, v: any) => (v == null ? '--' : m[Number(v)] || String(v))
async function load() {
loading.value = true
try {
const resp: any = await httpGet('/supplier/inquiry/getById', { id: inquiryId.value })
if (resp && resp.id) {
const first = resp.inquiryDetailList?.[0] || {}
detail.value = {
...FALLBACK,
...resp,
id: String(resp.id || inquiryId.value),
title: resp.title || FALLBACK.title,
productName: resp.productName || resp.product || FALLBACK.productName,
productBrand: resp.brand || resp.productBrand || FALLBACK.productBrand,
specification: resp.specification || resp.spec || FALLBACK.specification,
unit: resp.unit || FALLBACK.unit,
quantity: resp.purchaseQuantity || resp.quantity || FALLBACK.quantity,
shippingAddress: resp.shippingAddress || resp.address || FALLBACK.shippingAddress,
deliveryDate: resp.deliveryDate || FALLBACK.deliveryDate,
priceValidDate: resp.priceValidDate || FALLBACK.priceValidDate,
enterpriseName: resp.inquiryEnterpriseName || resp.enterpriseName || FALLBACK.enterpriseName,
fileList: resp.fileList || FALLBACK.fileList,
quotationList: resp.quotationList || FALLBACK.quotationList,
productName: first.productName || '--',
productBrand: first.productBrand || '--',
specification: first.specification || '--',
unit: first.unit || '--',
quantity: resp.purchaseQuantity ?? '--',
// 新结构字段:与发布询价信息一致
procurementTypeName: mapLabel(PROC_TYPE, resp.procurementType),
quotationTypeName: mapLabel(QUOTE_TYPE, resp.quotationType),
quotationDemandName: mapLabel(DEMAND, resp.quotationDemand),
paymentMethodName: resp.paymentMethodName || '--',
invoiceTypeName: mapLabel(INVOICE, resp.invoiceType),
inquiryValidDate: resp.inquiryValidDate || '--',
note: resp.note || '',
enterpriseName: resp.inquiryParty || resp.inquiryEnterpriseName || FALLBACK.enterpriseName,
detailList: resp.inquiryDetailList || [],
fileList: resetFiles(resp),
raw: resp,
}
// 报价回复:按企业分组(getQuotationById 返回逐商品报价行)
await loadQuotes(resp.orderId || resp.id)
} else {
detail.value = { ...FALLBACK, id: inquiryId.value }
}
@ -126,6 +151,34 @@ async function load() {
}
}
// 附件:发布时 images 为 URL 数组 + imageNames 名称数组
function resetFiles(resp: any): any[] {
const urls: string[] = resp.images || []
const names: string[] = resp.imageNames || []
return urls.map((u: string, i: number) => ({ url: u, imagesName: names[i] || ('附件' + (i + 1)) }))
}
const quoteGroups = ref<any[]>([])
const quoteLoading = ref(false)
async function loadQuotes(orderId: any) {
quoteLoading.value = true
try {
const res: any = await httpPost('/supplier/inquiry/getQuotationById', { orderId: String(orderId) })
const rows = (res && (Array.isArray(res) ? res : [])) || []
const groups: any[] = []
for (const q of rows) {
const name = q.quotationEnterpriseName || '未报价'
let g = groups.find(x => x.name === name)
if (!g) { g = { name, rows: [], count: 0, total: 0, date: '', list: [] }; groups.push(g) }
g.rows.push(q)
g.count++
g.total += Number(q.unitPriceTaxIncluded || q.unitPrice || 0) * Number(q.quantity || 0)
if (q.quotationDate && !g.date) g.date = q.quotationDate
}
quoteGroups.value = groups
} catch { quoteGroups.value = [] } finally { quoteLoading.value = false }
}
// === 审核操作: apprStatus 2=通过 3=驳回 ===
const auditLoading = ref(false)
async function onAudit(status: number) {
@ -203,19 +256,41 @@ watch(inquiryId, load)
<div class="rr-card">
<div class="rr-card-header"><h3 class="rr-card-title">询价信息</h3></div>
<el-descriptions :column="2" border>
<el-descriptions-item label="产品名称">{{ detail.productName }}</el-descriptions-item>
<el-descriptions-item label="品牌">{{ detail.productBrand || '--' }}</el-descriptions-item>
<el-descriptions-item label="规格型号">{{ detail.specification || '--' }}</el-descriptions-item>
<el-descriptions-item label="单位">{{ detail.unit }}</el-descriptions-item>
<el-descriptions-item label="数量">{{ detail.quantity }}</el-descriptions-item>
<el-descriptions-item label="询价主体(采购方)" :span="2">{{ detail.enterpriseName }}</el-descriptions-item>
<el-descriptions-item label="采购类型">{{ detail.procurementTypeName }}</el-descriptions-item>
<el-descriptions-item label="报价方式">{{ detail.quotationTypeName }}</el-descriptions-item>
<el-descriptions-item label="采购数量">{{ detail.quantity }}</el-descriptions-item>
<el-descriptions-item label="报价要求">{{ detail.quotationDemandName }}</el-descriptions-item>
<el-descriptions-item label="付款方式">{{ detail.paymentMethodName }}</el-descriptions-item>
<el-descriptions-item label="发票要求">{{ detail.invoiceTypeName }}</el-descriptions-item>
<el-descriptions-item label="询价截止">{{ detail.inquiryValidDate }}</el-descriptions-item>
<el-descriptions-item label="要求交货日期">{{ detail.deliveryDate || '--' }}</el-descriptions-item>
<el-descriptions-item label="价格有效截止" :span="2">{{ detail.priceValidDate || '--' }}</el-descriptions-item>
<el-descriptions-item label="发布时间" :span="2">{{ detail.createDate || detail.publishDate || '--' }}</el-descriptions-item>
<el-descriptions-item label="收货地址" :span="2">{{ detail.shippingAddress || '--' }}</el-descriptions-item>
<el-descriptions-item label="询价备注" :span="2">{{ detail.detailNote || '--' }}</el-descriptions-item>
<el-descriptions-item label="采购方" :span="2">{{ detail.enterpriseName }}</el-descriptions-item>
<el-descriptions-item label="询价备注" :span="2">{{ detail.note || detail.detailNote || '--' }}</el-descriptions-item>
</el-descriptions>
</div>
<!-- 商品明细 -->
<div class="rr-card" v-if="(detail.detailList || []).length">
<div class="rr-card-header"><h3 class="rr-card-title">商品明细 ({{ (detail.detailList || []).length }})</h3></div>
<el-table :data="(detail.detailList || [])" border size="small">
<el-table-column type="index" label="#" width="50" align="center" />
<el-table-column prop="productName" label="商品名称" min-width="160" show-overflow-tooltip />
<el-table-column prop="productBrand" label="品牌" min-width="120">
<template #default="{ row }">{{ row.productBrand || '--' }}</template>
</el-table-column>
<el-table-column prop="specification" label="规格" min-width="140">
<template #default="{ row }">{{ row.specification || '--' }}</template>
</el-table-column>
<el-table-column prop="quantity" label="数量" width="90" align="center" />
<el-table-column prop="unit" label="单位" width="80" align="center" />
<el-table-column prop="detailNote" label="备注" min-width="140">
<template #default="{ row }">{{ row.detailNote || '--' }}</template>
</el-table-column>
</el-table>
</div>
<!-- 询价附件 -->
<div class="rr-card" v-if="detail.fileList.length > 0">
<div class="rr-card-header"><h3 class="rr-card-title">询价附件</h3></div>
@ -228,39 +303,46 @@ watch(inquiryId, load)
</div>
</div>
<!-- 报价列表 -->
<!-- 报价列表(按企业分组) -->
<div class="rr-card">
<div class="rr-card-header"><h3 class="rr-card-title">报价列表 ({{ detail.quotationList.length }})</h3></div>
<el-table :data="detail.quotationList" border style="width: 100%">
<el-table-column type="index" label="#" width="50" align="center" />
<el-table-column prop="quotationEnterpriseName" label="供应商名称" min-width="200" show-overflow-tooltip />
<el-table-column label="报价状态" width="120" align="center">
<template #default="{ row }">
<el-tag :type="(quotationInfo(row.quotationStatus).type as any)" size="small" effect="light">
{{ quotationInfo(row.quotationStatus).label }}
</el-tag>
</template>
<div class="rr-card-header"><h3 class="rr-card-title">报价列表 ({{ quoteGroups.reduce((a, g) => a + g.count, 0) }})</h3></div>
<div v-if="quoteLoading" class="rr-empty">加载报价中...</div>
<div v-else-if="!quoteGroups.length" class="rr-empty">暂无报价回复</div>
<div v-else class="rr-quote-groups">
<div v-for="(g, gi) in quoteGroups" :key="gi" class="rr-quote-group">
<div class="rr-quote-group-head">
<b>{{ g.name }}</b>
<span v-if="g.name !== '未报价'" class="rr-tag rr-tag-green">已报价</span>
<span v-else class="rr-tag">待报价</span>
<span class="rr-quote-meta">{{ g.count }} 项{{ g.date ? ' · ' + g.date : '' }}</span>
</div>
<el-table :data="g.rows" border size="small">
<el-table-column type="index" label="#" width="46" align="center" />
<el-table-column prop="productName" label="商品" min-width="150" show-overflow-tooltip />
<el-table-column prop="specification" label="规格" min-width="120">
<template #default="{ row }">{{ row.specification || '--' }}</template>
</el-table-column>
<el-table-column label="报价金额" width="140" align="right">
<template #default="{ row }">
<span v-if="row.totalPrice > 0">{{ fmtMoney(row.totalPrice) }}</span>
<span v-else>--</span>
</template>
<el-table-column label="报价单价(含税)" width="130" align="right">
<template #default="{ row }">¥ {{ Number(row.unitPriceTaxIncluded || row.unitPrice || 0).toLocaleString() }}</template>
</el-table-column>
<el-table-column label="报价时间" width="170">
<template #default="{ row }">{{ row.quotationDate || '--' }}</template>
<el-table-column prop="quantity" label="数量" width="80" align="center" />
<el-table-column prop="unit" label="单位" width="70" align="center">
<template #default="{ row }">{{ row.unit || '件' }}</template>
</el-table-column>
<el-table-column prop="quotationCountStr" label="报价次数" width="100" align="center" />
<el-table-column prop="source" label="来源" width="120" align="center" />
<el-table-column label="操作" width="160" align="center" fixed="right">
<template #default="{ row }">
<el-button v-if="row.quotationStatus === 1" text type="primary" size="small" @click="onViewQuotation(row)">查看报价</el-button>
<el-button v-else text type="primary" size="small" @click="onQuote(row)">邀请报价</el-button>
</template>
<el-table-column label="生产周期" min-width="110">
<template #default="{ row }">{{ row.productionCycle || '--' }}</template>
</el-table-column>
<el-table-column prop="quotationNote" label="备注" min-width="130">
<template #default="{ row }">{{ row.quotationNote || '--' }}</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- 旧操作列说明 -->
<div class="rr-tip mt10">报价详情可在前台「我的询价-询价详情-企业报价回复」查看</div>
</div>
</div>
</div>
</template>
@ -279,4 +361,14 @@ watch(inquiryId, load)
.rr-files { padding: 16px 20px; display: flex; flex-direction: column; gap: 8px; }
.rr-file-item { display: flex; align-items: center; gap: 8px; padding: 8px 12px; background: var(--bg-soft); border-radius: var(--radius); }
.rr-file-name { flex: 1; color: var(--ink); font-size: 13px; }
.rr-quote-groups { display: flex; flex-direction: column; gap: 12px; }
.rr-quote-group { border: 1px solid var(--el-border-color); border-radius: 8px; overflow: hidden; }
.rr-quote-group-head { display: flex; align-items: center; gap: 8px; padding: 10px 14px; background: var(--el-fill-color-light); font-size: 13px; flex-wrap: wrap; }
.rr-quote-meta { margin-left: auto; color: var(--el-text-color-secondary); font-size: 12px; }
.rr-tag { font-size: 11px; padding: 1px 8px; border-radius: 10px; background: var(--el-color-info-light-9); color: var(--el-text-color-secondary); }
.rr-tag-green { background: #f0f9eb; color: #67c23a; }
.rr-empty { padding: 24px; text-align: center; color: var(--el-text-color-secondary); font-size: 13px; }
.rr-tip { font-size: 12px; color: var(--el-text-color-secondary); }
.mt10 { margin-top: 10px; }
</style>