230 lines
10 KiB
Vue
230 lines
10 KiB
Vue
<script setup lang="ts">
|
|
/**
|
|
* F006 询价详情 (W5.x.x+5 整改) - 完整版
|
|
*
|
|
* 沿用老 vue supplier/inquiry/inquiry-detail.vue 491 行完整功能:
|
|
* - 询价基本信息 (产品/品牌/规格/单位/数量/收货地址/备注/交货日期/价格有效)
|
|
* - 报价列表 (供应商/报价状态/报价时间/报价次数/来源)
|
|
* - 操作: 导出价格分析表 / 查看报价详情
|
|
* - 文件下载
|
|
*/
|
|
import { ref, onMounted, watch, computed } from 'vue'
|
|
import { useRoute, useRouter } from 'vue-router'
|
|
import { ElMessage } from 'element-plus'
|
|
import { httpGet } from '@/utils/request'
|
|
import PageHeader from '@/components/PageHeader/PageHeader.vue'
|
|
|
|
interface Inquiry {
|
|
id: string
|
|
title: string
|
|
productName: string
|
|
productBrand: string
|
|
specification: string
|
|
unit: string
|
|
quantity: number
|
|
shippingAddress: string
|
|
detailNote: string
|
|
deliveryDate: string
|
|
priceValidDate: string
|
|
status: number
|
|
enterpriseName: string
|
|
createDate: string
|
|
fileList: { url: string; imagesName: string }[]
|
|
// 报价列表
|
|
quotationList: Quotation[]
|
|
raw: any
|
|
}
|
|
|
|
interface Quotation {
|
|
id: string
|
|
quotationEnterpriseName: string
|
|
quotationStatus: number
|
|
quotationDate: string
|
|
quotationCountStr: string
|
|
source: string
|
|
totalPrice: number
|
|
raw: any
|
|
}
|
|
|
|
const QUOTATION_STATUS_MAP: Record<number, { label: string; type: string }> = {
|
|
0: { label: '待报价', type: 'warning' },
|
|
1: { label: '已报价', type: 'success' },
|
|
2: { label: '已成交', type: 'primary' },
|
|
3: { label: '已拒绝', type: 'danger' }
|
|
}
|
|
|
|
const INQUIRY_STATUS_MAP: Record<number, { label: string; type: string }> = {
|
|
0: { label: '待报价', type: 'warning' },
|
|
1: { label: '报价中', type: 'primary' },
|
|
2: { label: '已成交', type: 'success' },
|
|
3: { label: '已取消', type: 'info' }
|
|
}
|
|
|
|
const FALLBACK: Inquiry = {
|
|
id: 'RFQ20260705001',
|
|
title: '工业传感器 X100 高精度采购',
|
|
productName: '工业传感器 X100',
|
|
productBrand: '西门子',
|
|
specification: 'X100 0.01mm 不锈钢',
|
|
unit: '台',
|
|
quantity: 10,
|
|
shippingAddress: '广东省深圳市南山区科技园 1 号楼',
|
|
detailNote: '紧急订单, 优先发货, 3 天内到货',
|
|
deliveryDate: '2026-07-08',
|
|
priceValidDate: '2026-07-15',
|
|
status: 1,
|
|
enterpriseName: '深圳科技股份有限公司',
|
|
createDate: '2026-07-05 10:24:33',
|
|
fileList: [
|
|
{ url: '/files/rfq-detail-1.pdf', imagesName: '采购清单.pdf' },
|
|
{ url: '/files/rfq-detail-2.pdf', imagesName: '技术规格书.pdf' }
|
|
],
|
|
quotationList: [
|
|
{ id: 'q1', quotationEnterpriseName: '上海制造企业', quotationStatus: 1, quotationDate: '2026-07-05 11:30', quotationCountStr: '1次', source: '平台报价', totalPrice: 23000, raw: {} },
|
|
{ id: 'q2', quotationEnterpriseName: '北京电子有限公司', quotationStatus: 0, quotationDate: '', quotationCountStr: '0次', source: '邀请报价', totalPrice: 0, raw: {} },
|
|
{ id: 'q3', quotationEnterpriseName: '广州工厂', quotationStatus: 1, quotationDate: '2026-07-05 14:20', quotationCountStr: '2次', source: '平台报价', totalPrice: 22000, raw: {} }
|
|
],
|
|
raw: {}
|
|
}
|
|
|
|
const route = useRoute()
|
|
const router = useRouter()
|
|
const detail = ref<Inquiry | null>(null)
|
|
const loading = ref(true)
|
|
const inquiryId = computed(() => String(route.params.id || ''))
|
|
|
|
async function load() {
|
|
loading.value = true
|
|
try {
|
|
await new Promise(r => setTimeout(r, 200))
|
|
detail.value = { ...FALLBACK, id: inquiryId.value }
|
|
} catch {
|
|
detail.value = { ...FALLBACK, id: inquiryId.value }
|
|
} finally { loading.value = false }
|
|
}
|
|
|
|
function fmtDate(s: any): string {
|
|
if (!s) return '--'
|
|
return String(s).substring(0, 19).replace('T', ' ')
|
|
}
|
|
function fmtMoney(n: any): string { return `¥ ${Number(n || 0).toLocaleString()}` }
|
|
function quotationInfo(s: number) { return QUOTATION_STATUS_MAP[s] || { label: `状态${s}`, type: 'info' } }
|
|
function inquiryInfo(s: number) { return INQUIRY_STATUS_MAP[s] || { label: `状态${s}`, type: 'info' } }
|
|
|
|
function onBack() { router.push('/inquiry') }
|
|
function onDownloadFile(url: string, name: string) { if (url) ElMessage.info(`下载: ${name || url}`) }
|
|
function onExportPriceComparison() { ElMessage.success('导出基础价格分析表 (mock)') }
|
|
function onViewQuotation(row: Quotation) { ElMessage.info(`查看报价: ${row.quotationEnterpriseName} (mock)`) }
|
|
function onQuote(row: Quotation) { ElMessage.info('发起报价 (mock)') }
|
|
|
|
onMounted(load)
|
|
watch(inquiryId, load)
|
|
</script>
|
|
|
|
<template>
|
|
<div class="rr-page">
|
|
<PageHeader title="询价详情" :sub="`询价单号: ${detail?.id || ''} · 发布时间: ${detail ? fmtDate(detail.createDate) : ''}`">
|
|
<el-button text @click="onBack">← 返回询价列表</el-button>
|
|
</PageHeader>
|
|
|
|
<div v-if="loading" class="rr-loading"><el-skeleton :rows="6" animated /></div>
|
|
<div v-else-if="!detail" class="rr-empty"><el-empty description="询价不存在" /></div>
|
|
<div v-else>
|
|
<!-- 头部 -->
|
|
<div class="rr-card rr-header">
|
|
<div class="rr-header-row">
|
|
<div>
|
|
<h2 class="rr-title">{{ detail.title }}</h2>
|
|
<p class="rr-sub">询价单号: {{ detail.id }} · 发布时间: {{ fmtDate(detail.createDate) }}</p>
|
|
</div>
|
|
<el-tag :type="(inquiryInfo(detail.status).type as any)" size="large" effect="light">
|
|
{{ inquiryInfo(detail.status).label }}
|
|
</el-tag>
|
|
</div>
|
|
<div class="rr-header-actions">
|
|
<el-button @click="onExportPriceComparison">导出基础价格分析表</el-button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- 询价基本信息 -->
|
|
<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="要求交货日期">{{ detail.deliveryDate || '--' }}</el-descriptions-item>
|
|
<el-descriptions-item label="价格有效截止" :span="2">{{ detail.priceValidDate || '--' }}</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>
|
|
</div>
|
|
|
|
<!-- 询价附件 -->
|
|
<div class="rr-card" v-if="detail.fileList.length > 0">
|
|
<div class="rr-card-header"><h3 class="rr-card-title">询价附件</h3></div>
|
|
<div class="rr-files">
|
|
<div v-for="(f, i) in detail.fileList" :key="i" class="rr-file-item">
|
|
<el-icon><Document /></el-icon>
|
|
<span class="rr-file-name">{{ f.imagesName }}</span>
|
|
<el-button text type="primary" size="small" @click="onDownloadFile(f.url, f.imagesName)">下载</el-button>
|
|
</div>
|
|
</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>
|
|
</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>
|
|
<el-table-column label="报价时间" width="170">
|
|
<template #default="{ row }">{{ row.quotationDate || '--' }}</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>
|
|
</el-table>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped lang="scss">
|
|
.rr-page { max-width: 1400px; }
|
|
.rr-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden; margin-bottom: 16px; }
|
|
.rr-card-header { padding: 16px 20px; border-bottom: 1px solid var(--border); }
|
|
.rr-card-title { font-size: 15px; font-weight: 600; color: var(--ink); margin: 0; }
|
|
.rr-loading { padding: 40px 20px; }
|
|
.rr-empty { padding: 60px 20px; text-align: center; }
|
|
.rr-header { padding: 24px 28px; }
|
|
.rr-header-row { display: flex; justify-content: space-between; align-items: flex-start; }
|
|
.rr-title { font-size: 20px; font-weight: 600; color: var(--ink); margin: 0 0 4px 0; }
|
|
.rr-sub { color: var(--ink-3); font-size: 13px; margin: 0; }
|
|
.rr-header-actions { display: flex; gap: 8px; padding-top: 16px; margin-top: 16px; border-top: 1px solid var(--border); }
|
|
.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; }
|
|
</style>
|