补齐超时响应唯一 timeoutCode 与智能体按码查询,并增强报价模板预览与解析落库。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* 报价模板预览:Excel 样式网格(上传原文),辅以映射摘要。
|
||||
* 主数据来自 /previewSheet;mappingJson 仅用于未映射必填提示。
|
||||
* 主数据来自 /previewSheet;mappingJson 用于已映射/未映射清单。
|
||||
*/
|
||||
|
||||
/** 系统字段 code → 中文标签 */
|
||||
@@ -27,8 +27,131 @@ export const FIELD_LABELS: Record<string, string> = {
|
||||
weight_kg: "重量",
|
||||
volume_cbm: "体积",
|
||||
total_amount: "报价合计",
|
||||
chargeable_weight_kg: "计费重",
|
||||
delivery_address: "送货地址",
|
||||
pickup_address: "提货地址",
|
||||
transit_time: "运输时效",
|
||||
cargo_value: "货值",
|
||||
hs_code: "HS编码",
|
||||
oil_elec_mag: "油电磁",
|
||||
has_battery: "是否带电",
|
||||
on_board_date: "开船日",
|
||||
special_remarks: "特殊备注",
|
||||
eta_request: "要求到港",
|
||||
sailing_schedule: "船期",
|
||||
shipping_line: "船公司",
|
||||
flight_no: "航班号",
|
||||
transfer_airport: "中转机场",
|
||||
effective_date: "生效日期",
|
||||
fuel_surcharge_rate: "燃油附加费率",
|
||||
tax_rate: "税率",
|
||||
has_oil: "是否含油",
|
||||
has_magnet: "是否含磁",
|
||||
vehicle_qty: "车型/数量",
|
||||
};
|
||||
|
||||
/** 解析结果摘要:供预览弹窗展示「已解析 / 未解析」 */
|
||||
export type MappingFieldItem = {
|
||||
code: string;
|
||||
label: string;
|
||||
cell: string;
|
||||
};
|
||||
|
||||
export type MappingRegionItem = {
|
||||
kind: "fee" | "route";
|
||||
id: string;
|
||||
title: string;
|
||||
};
|
||||
|
||||
export type MappingParseSummary = {
|
||||
fields: MappingFieldItem[];
|
||||
regions: MappingRegionItem[];
|
||||
gaps: string[];
|
||||
unmappedRequired: string[];
|
||||
unmappedLabels: string[];
|
||||
pointCount: number;
|
||||
};
|
||||
|
||||
function asMappingObject(mappingJson: unknown): Record<string, any> | null {
|
||||
if (!mappingJson) return null;
|
||||
if (typeof mappingJson === "object") return mappingJson as Record<string, any>;
|
||||
if (typeof mappingJson === "string") {
|
||||
try {
|
||||
const obj = JSON.parse(mappingJson);
|
||||
return obj && typeof obj === "object" ? obj : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 从 mappingJson 拆出运营可读的已映射 / 未映射清单。
|
||||
* 不打接口;mappingGaps 为空时(旧版解析)仍可看 fields 与 unmapped*。
|
||||
*/
|
||||
export function summarizeMappingParse(mappingJson: unknown): MappingParseSummary {
|
||||
const obj = asMappingObject(mappingJson);
|
||||
const empty: MappingParseSummary = {
|
||||
fields: [],
|
||||
regions: [],
|
||||
gaps: [],
|
||||
unmappedRequired: [],
|
||||
unmappedLabels: [],
|
||||
pointCount: 0,
|
||||
};
|
||||
if (!obj) return empty;
|
||||
|
||||
const fieldsObj = obj.fields && typeof obj.fields === "object" ? obj.fields : {};
|
||||
const fields: MappingFieldItem[] = Object.keys(fieldsObj)
|
||||
.filter((k) => fieldsObj[k])
|
||||
.map((code) => ({
|
||||
code,
|
||||
label: FIELD_LABELS[code] || code,
|
||||
cell: String(fieldsObj[code] || ""),
|
||||
}))
|
||||
.sort((a, b) => a.label.localeCompare(b.label, "zh"));
|
||||
|
||||
const regions: MappingRegionItem[] = [];
|
||||
const feeRegions = Array.isArray(obj.feeRegions) ? obj.feeRegions : [];
|
||||
for (const r of feeRegions) {
|
||||
if (!r || typeof r !== "object") continue;
|
||||
regions.push({
|
||||
kind: "fee",
|
||||
id: String(r.id || ""),
|
||||
title: String(r.title || r.id || "费用区"),
|
||||
});
|
||||
}
|
||||
const routeRegions = Array.isArray(obj.routeRegions) ? obj.routeRegions : [];
|
||||
for (const r of routeRegions) {
|
||||
if (!r || typeof r !== "object") continue;
|
||||
regions.push({
|
||||
kind: "route",
|
||||
id: String(r.id || ""),
|
||||
title: String(r.title || r.id || "线路区"),
|
||||
});
|
||||
}
|
||||
|
||||
const gaps = Array.isArray(obj.mappingGaps)
|
||||
? obj.mappingGaps.map((x: any) => String(x)).filter(Boolean)
|
||||
: [];
|
||||
const unmappedRequired = Array.isArray(obj.unmappedRequired)
|
||||
? obj.unmappedRequired.map((x: any) => String(x)).filter(Boolean)
|
||||
: [];
|
||||
const unmappedLabels = Array.isArray(obj.unmappedLabels)
|
||||
? obj.unmappedLabels.map((x: any) => String(x)).filter(Boolean)
|
||||
: [];
|
||||
|
||||
return {
|
||||
fields,
|
||||
regions,
|
||||
gaps,
|
||||
unmappedRequired,
|
||||
unmappedLabels,
|
||||
pointCount: fields.length + regions.length,
|
||||
};
|
||||
}
|
||||
|
||||
export type SheetPreviewCell = {
|
||||
addr: string;
|
||||
text: string;
|
||||
|
||||
Reference in New Issue
Block a user