落地销售调价:按自然语言改价并单独记销售价,拉群和后台不再把调后价当成 TMS 原价。

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-09-20 14:40:20 +08:00
co-authored by Cursor
parent e4743541f7
commit 26dd5d72a1
36 changed files with 2795 additions and 176 deletions
@@ -17,7 +17,7 @@ import ProtoIcon from "/@/views/inquiry/components/ProtoIcon.vue";
import AdminModal from "/@/views/inquiry/components/AdminModal.vue";
import AdminPagination from "/@/views/inquiry/components/AdminPagination.vue";
type TicketQuote = { source: string; price: string; highlight?: boolean };
type TicketQuote = { source: string; price: string; time?: string; highlight?: boolean };
type TicketAttach = { name: string; uploadedAt: string; uploader: string };
type TicketTimeline = { status: string; remark: string; time: string };
@@ -93,6 +93,21 @@ function dash(v: any) {
return s ? s : "-";
}
/**
* 后台报价来源:TMS标准报价 / 手工报价 / 销售调价。
* 禁止把产品报价或销售调价再显示成 TMS。
*/
function quoteSourceLabel(q: any) {
const src = String(q?.source ?? "").trim().toUpperCase();
if (src === "SALES_ADJUST") return "销售调价";
if (src === "PRODUCT") return "手工报价";
if (src === "TMS") return "TMS标准报价";
const label = String(q?.sourceLabel || q?.source || "").replace(/\s/g, "");
if (label.includes("销售调价")) return "销售调价";
if (label.includes("TMS")) return "TMS标准报价";
return label && label !== "报价" ? "手工报价" : "报价";
}
/**
* 主账 inq_ticket_status_log.action 是内部码,禁止当备注展示。
* transition 与「已报价」等 toStatus 重复,无用户备注时不再跟一段英文。
@@ -101,6 +116,8 @@ const STATUS_LOG_ACTION_LABEL: Record<string, string> = {
transition: "",
retry: "系统重试",
abnormal_handle: "异常已处理",
quote_adjust: "销售调价",
manual_quote: "手工报价",
};
function isInternalActionCode(v: string) {
@@ -239,8 +256,9 @@ function mapTicket(t: any, quotes: any[] = [], attachments: any[] = [], timeline
demandTitle: reqLines[0] || "—",
demandDetail: reqLines.slice(1).join("\n"),
quotes: (quotes || []).map((q) => ({
source: q.sourceLabel || q.source || "报价",
source: quoteSourceLabel(q),
price: q.priceText || "-",
time: fmtTime(q.createTime),
highlight: q.highlighted === 1 || q.highlighted === true,
})),
attachments: attachRows.map((a) => a.name),
@@ -306,7 +324,7 @@ async function loadList() {
const page = res?.records ? res : res?.result || {};
const records = page.records || [];
total.value = Number(page.total || records.length || 0);
rows.value = (records as any[]).map((t) => mapTicket(t));
rows.value = (records as any[]).map((t) => mapTicket(t, t.quotes || []));
// 删减后当前页可能越界,回退到最后一页
const maxPage = Math.max(1, Math.ceil(total.value / pageSize.value) || 1);
if (pageNo.value > maxPage) {
@@ -661,8 +679,8 @@ function refresh() {
<div class="ticket-detail-item"><label>利润点</label><span>{{ current.profit }}</span></div>
</div>
<div v-if="current.quotes.length" class="ticket-detail-demand">
<div v-for="q in current.quotes" :key="q.source + q.price" class="quote-row" style="margin-bottom: 6px">
<div class="quote-source">{{ q.source }}</div>
<div v-for="q in current.quotes" :key="q.source + q.price + (q.time || '')" class="quote-row" style="margin-bottom: 6px">
<div class="quote-source">{{ q.source }}<span v-if="q.time && q.time !== '-'"> · {{ q.time }}</span></div>
<div class="quote-price" :style="q.highlight ? { color: '#1d4ed8' } : undefined">{{ q.price }}</div>
</div>
</div>