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:
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user