|
@@ -277,38 +277,36 @@ function sourceKindClass(kind: string): string {
|
|
|
return "unknown";
|
|
return "unknown";
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-// 首轮取词三源:需求池直出(seed_term)/ 票圈帖子的点(query_seed_point)/ 分类叶子(category_leaf_element)。
|
|
|
|
|
-const FIRST_ROUND_SOURCE_TYPES = ["seed_term", "query_seed_point", "category_leaf_element"];
|
|
|
|
|
-
|
|
|
|
|
-function sourceTypeHasMaterial(type: string, demand?: DemandSummary): boolean {
|
|
|
|
|
- if (!demand) return false;
|
|
|
|
|
- if (type === "seed_term") return demand.seedTerms.length > 0;
|
|
|
|
|
- if (type === "category_leaf_element") return demand.categoryPaths.length > 0;
|
|
|
|
|
- if (type === "query_seed_point") return demand.extractedPoints.length > 0;
|
|
|
|
|
- return false;
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
-// CFA 取词时同一个词只搜一次,后命中的来源被去重丢弃(不入库),前端看不到。
|
|
|
|
|
-// 这里据需求素材补出"被去重的来源"占位行:那行留空 + 标注与前面哪些来源重复。
|
|
|
|
|
|
|
+// CFA 取词时同一个词只搜一次:分类叶子来源的某个词若与前面某来源的词完全相同,就被去重丢弃、不入库,
|
|
|
|
|
+// 前端看不到。这里据 demand 的 category_leaf_terms 比对首轮 query,把"被去重的分类词"补成占位行,
|
|
|
|
|
+// 并讲清它和前面哪条来源的哪个搜索词一模一样。
|
|
|
function DeducedSourceRows({ rows, demand }: { rows: FlowLedgerRow[]; demand?: DemandSummary }) {
|
|
function DeducedSourceRows({ rows, demand }: { rows: FlowLedgerRow[]; demand?: DemandSummary }) {
|
|
|
- const present = new Set(rows.map((row) => row.source.sourceKind));
|
|
|
|
|
- const shownLabels = FIRST_ROUND_SOURCE_TYPES.filter((type) => present.has(type)).map((type) => sourceLabel(type));
|
|
|
|
|
- const deduped = FIRST_ROUND_SOURCE_TYPES.filter(
|
|
|
|
|
- (type) => !present.has(type) && sourceTypeHasMaterial(type, demand)
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ const categoryTerms = demand?.categoryLeafTerms || [];
|
|
|
|
|
+ const deduped = categoryTerms
|
|
|
|
|
+ .map((term) => {
|
|
|
|
|
+ // 该分类词已真生成了分类来源 query → 正常显示,不补占位。
|
|
|
|
|
+ const asCategory = rows.find((row) => row.query.text === term && row.source.sourceKind === "category_leaf_element");
|
|
|
|
|
+ if (asCategory) return null;
|
|
|
|
|
+ // 该词出现在前面某条非分类来源里 → 被去重,补占位并指明与谁重复。
|
|
|
|
|
+ const match = rows.find((row) => row.query.text === term && row.source.sourceKind !== "category_leaf_element");
|
|
|
|
|
+ return match ? { term, dupLabel: sourceLabel(match.source.sourceKind) } : null;
|
|
|
|
|
+ })
|
|
|
|
|
+ .filter((item): item is { term: string; dupLabel: string } => item !== null);
|
|
|
if (!deduped.length) return null;
|
|
if (!deduped.length) return null;
|
|
|
return (
|
|
return (
|
|
|
<>
|
|
<>
|
|
|
- {deduped.map((type) => (
|
|
|
|
|
- <tr className="ledger-row deduped-source-row" key={`deduped-${type}`}>
|
|
|
|
|
|
|
+ {deduped.map(({ term, dupLabel }) => (
|
|
|
|
|
+ <tr className="ledger-row deduped-source-row" key={`deduped-${term}`}>
|
|
|
<td className="source-cell sticky-source">
|
|
<td className="source-cell sticky-source">
|
|
|
<div className="cell-stack">
|
|
<div className="cell-stack">
|
|
|
- <span className={`source-kind-badge ${sourceKindClass(type)}`}>{sourceLabel(type)}</span>
|
|
|
|
|
|
|
+ <span className={`source-kind-badge ${sourceKindClass("category_leaf_element")}`}>{sourceLabel("category_leaf_element")}</span>
|
|
|
<span className="muted">(此处留空)</span>
|
|
<span className="muted">(此处留空)</span>
|
|
|
</div>
|
|
</div>
|
|
|
</td>
|
|
</td>
|
|
|
- <td className="muted deduped-source-note" colSpan={4}>
|
|
|
|
|
- 这是「{sourceLabel(type)}」来源,搜索词与前面的{shownLabels.join(" / ") || "已展示"}来源重复(同词只搜一次),所以此处不单独显示。
|
|
|
|
|
|
|
+ <td className="deduped-source-note" colSpan={4}>
|
|
|
|
|
+ <strong style={{ fontSize: "1.05rem", fontWeight: 700, lineHeight: 1.6 }}>
|
|
|
|
|
+ 「{sourceLabel("category_leaf_element")}」来源的搜索词「{term}」,与前面「{dupLabel}」来源的搜索词「{term}」完全相同(同词只搜一次),所以此处不单独显示。
|
|
|
|
|
+ </strong>
|
|
|
</td>
|
|
</td>
|
|
|
</tr>
|
|
</tr>
|
|
|
))}
|
|
))}
|