feat(bid): 新增基于甲方报价快速创建RFQ功能

本次提交完成以下核心变更:
1. 新增RFQ编号自动生成逻辑,添加selectNextRfqNo方法获取月度递增的RFQ编号
2. 在biz_rfq表新增client_quote_id关联字段,添加索引并完善实体类映射
3. 实现基于甲方报价复制物料快速创建RFQ的业务逻辑,包括事务处理和明细复制
4. 新增RFQ列表页关联甲方报价展示,支持点击跳转查看甲方报价详情
5. 在RFQ编辑页新增甲方报价选择器,选中后自动填充对应物料和标题
6. 优化甲方报价单页面,新增生成RFQ按钮和已生成RFQ列表展示
7. 调整RFQ详情页,新增编辑模式支持草稿状态修改
8. 修复路由跳转路径,统一RFQ相关页面路由到/bid/rfq路径组
This commit is contained in:
2026-06-02 18:44:44 +08:00
parent a75589018f
commit 9db84336bc
12 changed files with 514 additions and 70 deletions

View File

@@ -78,12 +78,6 @@
</template>
</el-table-column>
<el-table-column label="客户名称" prop="clientName" min-width="140" show-overflow-tooltip />
<el-table-column label="关联询价" min-width="150">
<template slot-scope="s">
<div style="font-weight:600;color:#303133">{{ s.row.rfqNo || '-' }}</div>
<div style="font-size:12px;color:#909399;margin-top:2px" v-if="s.row.rfqTitle">{{ s.row.rfqTitle }}</div>
</template>
</el-table-column>
<el-table-column label="总金额" width="130" align="right">
<template slot-scope="s">
<strong style="color:#409EFF;font-size:15px">¥{{ s.row.totalAmount | money }}</strong>
@@ -103,11 +97,12 @@
</el-table-column>
<el-table-column label="创建人" prop="createBy" width="100" align="center" />
<el-table-column label="创建时间" prop="createTime" width="160" align="center" />
<el-table-column label="操作" align="center" width="210" fixed="right">
<el-table-column label="操作" align="center" width="280" fixed="right">
<template slot-scope="s">
<el-button size="mini" type="text" icon="el-icon-view" @click="handleView(s.row)">详情</el-button>
<el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(s.row)">编辑</el-button>
<el-button size="mini" type="text" icon="el-icon-document-copy" @click="handleQuickCreate(s.row)">快速新建</el-button>
<el-button size="mini" type="text" icon="el-icon-s-promotion" style="color:#67C23A" @click="handleCreateRfq(s.row)">生成RFQ</el-button>
<el-button size="mini" type="text" icon="el-icon-delete" style="color:#f56c6c"
@click="handleDelete(s.row)" v-if="s.row.status==='draft'">删除</el-button>
</template>
@@ -126,16 +121,8 @@
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="关联询价单">
<el-select v-model="form.rfqId" placeholder="选择关联RFQ(可选)" filterable clearable style="width:100%" @change="onRfqSelect">
<el-option v-for="r in rfqOptions" :key="r.rfqId"
:label="r.rfqNo + ' · ' + r.rfqTitle" :value="r.rfqId">
<div style="display:flex;justify-content:space-between">
<span style="font-weight:600">{{ r.rfqNo }}</span>
<span style="color:#909399;font-size:12px;margin-left:8px">{{ r.rfqTitle }}</span>
</div>
</el-option>
</el-select>
<el-form-item label=" ">
<span style="color:#909399;font-size:12px">RFQ 通过生成RFQ按钮创建自动关联此报价单</span>
</el-form-item>
</el-col>
</el-row>
@@ -300,7 +287,10 @@
</el-descriptions-item>
<el-descriptions-item label="币种">{{ detailData.currency || 'CNY' }}</el-descriptions-item>
<el-descriptions-item label="有效期">{{ detailData.validityDate | dateFmt }}</el-descriptions-item>
<el-descriptions-item label="关联询价">{{ detailData.rfqNo || '-' }}</el-descriptions-item>
<el-descriptions-item label="RFQ数量">
<span v-if="detailRfqList.length > 0" style="color:#409eff;font-weight:600">{{ detailRfqList.length }} </span>
<span v-else style="color:#c0c4cc">-</span>
</el-descriptions-item>
<el-descriptions-item label="总金额" :span="3">
<strong style="color:#409EFF;font-size:18px">¥{{ detailData.totalAmount | money }}</strong>
</el-descriptions-item>
@@ -326,6 +316,33 @@
</el-table-column>
<el-table-column label="交期(天)" prop="deliveryDays" width="80" align="center" />
</el-table>
<!-- 关联的RFQ列表 -->
<div style="margin-top:20px">
<div class="section-title" style="margin-bottom:12px">
已生成的采购计划RFQ
<el-button size="mini" type="success" icon="el-icon-s-promotion" style="margin-left:12px"
@click="handleCreateRfq(detailData)">生成RFQ</el-button>
</div>
<el-table :data="detailRfqList" v-loading="detailRfqLoading" border size="small">
<el-table-column label="RFQ编号" prop="rfqNo" width="150" />
<el-table-column label="标题" prop="rfqTitle" min-width="160" show-overflow-tooltip />
<el-table-column label="状态" width="90" align="center">
<template slot-scope="s">
<el-tag :type="rfqStatusType(s.row.status)" size="small">{{ rfqStatusLabel(s.row.status) }}</el-tag>
</template>
</el-table-column>
<el-table-column label="创建时间" prop="createTime" width="160" align="center" />
<el-table-column label="操作" width="80" align="center">
<template slot-scope="s">
<el-button type="text" size="small" @click="viewRfqDetail(s.row)">查看</el-button>
</template>
</el-table-column>
</el-table>
<div v-if="!detailRfqLoading && detailRfqList.length === 0" style="text-align:center;padding:16px;color:#c0c4cc;font-size:13px">
暂未生成采购计划点击上方生成RFQ按钮创建
</div>
</div>
</div>
<div slot="footer">
<el-button @click="detailOpen = false">关闭</el-button>
@@ -339,7 +356,7 @@
<script>
import { listClientQuote, getClientQuote, addClientQuote, updateClientQuote, delClientQuote,
getClientQuoteStatistics, quickCreateFromQuote } from "@/api/bid/clientquote";
import { listRfq } from "@/api/bid/rfq";
import { listRfq, createRfqFromQuote } from "@/api/bid/rfq";
import { listMaterial } from "@/api/bid/material";
export default {
@@ -365,7 +382,6 @@ export default {
dialogOpen: false,
dialogTitle: "",
saving: false,
rfqOptions: [],
materialCache: [],
form: { items: [], currency: "CNY", status: "draft" },
rules: {
@@ -373,7 +389,9 @@ export default {
},
// 详情
detailOpen: false,
detailData: null
detailData: null,
detailRfqList: [],
detailRfqLoading: false
};
},
computed: {
@@ -384,7 +402,6 @@ export default {
created() {
this.getList();
this.getStats();
listRfq({ pageSize: 200 }).then(r => { this.rfqOptions = r.rows || []; });
},
methods: {
// ===== 列表 =====
@@ -418,7 +435,7 @@ export default {
// ===== 新建 =====
handleAdd() {
this.form = { items: [], currency: "CNY", status: "draft", clientName: "", rfqId: null, validityDate: "", remark: "" };
this.form = { items: [], currency: "CNY", status: "draft", clientName: "", validityDate: "", remark: "" };
this.dialogTitle = "新建甲方报价单";
this.dialogOpen = true;
},
@@ -430,9 +447,6 @@ export default {
this.form = {
quoteId: data.quoteId,
clientName: data.clientName,
rfqId: data.rfqId,
rfqNo: data.rfqNo,
rfqTitle: data.rfqTitle,
status: data.status,
validityDate: data.validityDate,
currency: data.currency,
@@ -465,6 +479,7 @@ export default {
this.detailData = r.data || {};
if (!this.detailData.items) this.detailData.items = [];
this.detailOpen = true;
this.loadRfqForDetail(row.quoteId);
});
},
editFromDetail() {
@@ -491,9 +506,6 @@ export default {
this.form = {
quoteId: res.data.quoteId,
clientName: res.data.clientName,
rfqId: res.data.rfqId,
rfqNo: res.data.rfqNo,
rfqTitle: res.data.rfqTitle,
status: res.data.status,
validityDate: res.data.validityDate,
currency: res.data.currency,
@@ -506,17 +518,37 @@ export default {
}).catch(() => {});
},
// ===== 生成RFQ =====
handleCreateRfq(row) {
this.$modal.confirm("确认基于报价单【" + row.quoteNo + "】生成采购询价RFQ").then(() => {
return createRfqFromQuote(row.quoteId);
}).then(res => {
this.detailOpen = false;
this.$modal.msgSuccess("RFQ已创建");
this.$router.push({ path: '/bid/rfq', query: { rfqId: res.data.rfqId, rfqNo: res.data.rfqNo, action: 'edit' } });
}).catch(() => {});
},
// ===== 删除 =====
handleDelete(row) {
this.$modal.confirm("确认删除报价单【" + row.quoteNo + "】?").then(() => delClientQuote(row.quoteId))
.then(() => { this.$modal.msgSuccess("删除成功"); this.getList(); this.getStats(); });
},
// ===== RFQ选择 =====
onRfqSelect(rfqId) {
const rfq = this.rfqOptions.find(r => r.rfqId === rfqId);
if (rfq) { this.form.rfqNo = rfq.rfqNo; this.form.rfqTitle = rfq.rfqTitle; }
// ===== RFQ列表(详情弹窗中展示) =====
loadRfqForDetail(quoteId) {
this.detailRfqLoading = true;
listRfq({ clientQuoteId: quoteId, pageSize: 50 }).then(r => {
this.detailRfqList = r.rows || [];
this.detailRfqLoading = false;
}).catch(() => { this.detailRfqLoading = false; });
},
viewRfqDetail(rfq) {
this.detailOpen = false;
this.$router.push({ path: '/bid/rfq', query: { rfqId: rfq.rfqId, rfqNo: rfq.rfqNo, action: 'edit' } });
},
rfqStatusType(s) { return { draft:"info", published:"warning", closed:"", completed:"success" }[s] || ""; },
rfqStatusLabel(s) { return { draft:"草稿", published:"已发布", closed:"已关闭", completed:"已完成" }[s] || s; },
// ===== 物料搜索 =====
queryMaterialSearch(query, cb) {