|
@@ -8,8 +8,6 @@ import { EmptyState, ErrorState, LoadingState } from "@/components/StateBlocks";
|
|
|
import {
|
|
import {
|
|
|
actionLabel,
|
|
actionLabel,
|
|
|
assetSummaryText,
|
|
assetSummaryText,
|
|
|
- clueText,
|
|
|
|
|
- methodLabel,
|
|
|
|
|
platformLabel,
|
|
platformLabel,
|
|
|
ruleHeadline,
|
|
ruleHeadline,
|
|
|
scoreItemRows,
|
|
scoreItemRows,
|
|
@@ -21,11 +19,10 @@ import {
|
|
|
import type { FlowLedgerRow, VideoRef } from "@/lib/flow-ledger/types";
|
|
import type { FlowLedgerRow, VideoRef } from "@/lib/flow-ledger/types";
|
|
|
import { useFlowLedger } from "./useFlowLedger";
|
|
import { useFlowLedger } from "./useFlowLedger";
|
|
|
|
|
|
|
|
-type GroupId = "source" | "query" | "videos" | "rules" | "walk" | "assets";
|
|
|
|
|
|
|
+type GroupId = "source" | "videos" | "rules" | "walk" | "assets";
|
|
|
|
|
|
|
|
const GROUPS: Array<{ id: GroupId; label: string; tone: string }> = [
|
|
const GROUPS: Array<{ id: GroupId; label: string; tone: string }> = [
|
|
|
{ id: "source", label: "搜索词来源", tone: "gray" },
|
|
{ id: "source", label: "搜索词来源", tone: "gray" },
|
|
|
- { id: "query", label: "搜索词", tone: "yellow" },
|
|
|
|
|
{ id: "videos", label: "首轮搜到的视频", tone: "green" },
|
|
{ id: "videos", label: "首轮搜到的视频", tone: "green" },
|
|
|
{ id: "rules", label: "内容判断", tone: "green" },
|
|
{ id: "rules", label: "内容判断", tone: "green" },
|
|
|
{ id: "walk", label: "继续扩展", tone: "blue" },
|
|
{ id: "walk", label: "继续扩展", tone: "blue" },
|
|
@@ -38,8 +35,7 @@ export function LedgerPage({ runId }: { runId: string }) {
|
|
|
|
|
|
|
|
const rows = ledger?.rows || [];
|
|
const rows = ledger?.rows || [];
|
|
|
const tableClass = [
|
|
const tableClass = [
|
|
|
- hidden.has("source") ? "source-hidden" : "",
|
|
|
|
|
- hidden.has("query") ? "query-hidden" : ""
|
|
|
|
|
|
|
+ hidden.has("source") ? "source-hidden" : ""
|
|
|
].filter(Boolean).join(" ");
|
|
].filter(Boolean).join(" ");
|
|
|
|
|
|
|
|
function toggle(group: GroupId) {
|
|
function toggle(group: GroupId) {
|
|
@@ -85,7 +81,7 @@ export function LedgerPage({ runId }: { runId: string }) {
|
|
|
</button>
|
|
</button>
|
|
|
</span>
|
|
</span>
|
|
|
))}
|
|
))}
|
|
|
- <span className="legend-hint">点击组名显示/隐藏 · 左侧两列固定 · 评分详情按 V4 分项展示</span>
|
|
|
|
|
|
|
+ <span className="legend-hint">点击组名显示/隐藏 · 左侧来源列固定 · 评分详情按 V4 分项展示</span>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
|
|
<section className="table-wrap">
|
|
<section className="table-wrap">
|
|
@@ -93,15 +89,13 @@ export function LedgerPage({ runId }: { runId: string }) {
|
|
|
<thead>
|
|
<thead>
|
|
|
<tr>
|
|
<tr>
|
|
|
{!hidden.has("source") ? <th className="group-source sticky-source">搜索词来源</th> : null}
|
|
{!hidden.has("source") ? <th className="group-source sticky-source">搜索词来源</th> : null}
|
|
|
- {!hidden.has("query") ? <th className="group-query sticky-query">搜索词</th> : null}
|
|
|
|
|
{!hidden.has("videos") ? <th className="group-videos">首轮搜到的视频</th> : null}
|
|
{!hidden.has("videos") ? <th className="group-videos">首轮搜到的视频</th> : null}
|
|
|
{!hidden.has("rules") ? <th className="group-rules" colSpan={2}>内容判断</th> : null}
|
|
{!hidden.has("rules") ? <th className="group-rules" colSpan={2}>内容判断</th> : null}
|
|
|
{!hidden.has("walk") ? <th className="group-walk">继续扩展</th> : null}
|
|
{!hidden.has("walk") ? <th className="group-walk">继续扩展</th> : null}
|
|
|
{!hidden.has("assets") ? <th className="group-assets">最终收获</th> : null}
|
|
{!hidden.has("assets") ? <th className="group-assets">最终收获</th> : null}
|
|
|
</tr>
|
|
</tr>
|
|
|
<tr>
|
|
<tr>
|
|
|
- {!hidden.has("source") ? <th className="sub-source sticky-source">从哪里来</th> : null}
|
|
|
|
|
- {!hidden.has("query") ? <th className="sub-query sticky-query">搜什么 / 怎么来</th> : null}
|
|
|
|
|
|
|
+ {!hidden.has("source") ? <th className="sub-source sticky-source">搜什么 / 从哪里来</th> : null}
|
|
|
{!hidden.has("videos") ? <th className="sub-videos">预览 / 统计 / 入口</th> : null}
|
|
{!hidden.has("videos") ? <th className="sub-videos">预览 / 统计 / 入口</th> : null}
|
|
|
{!hidden.has("rules") ? <th className="sub-rules">判断结果</th> : null}
|
|
{!hidden.has("rules") ? <th className="sub-rules">判断结果</th> : null}
|
|
|
{!hidden.has("rules") ? <th className="sub-rules">评分详情</th> : null}
|
|
{!hidden.has("rules") ? <th className="sub-rules">评分详情</th> : null}
|
|
@@ -110,10 +104,9 @@ export function LedgerPage({ runId }: { runId: string }) {
|
|
|
</tr>
|
|
</tr>
|
|
|
</thead>
|
|
</thead>
|
|
|
<tbody>
|
|
<tbody>
|
|
|
- {rows.map((row, index) => (
|
|
|
|
|
|
|
+ {rows.map((row) => (
|
|
|
<tr className="ledger-row" key={row.id}>
|
|
<tr className="ledger-row" key={row.id}>
|
|
|
- {!hidden.has("source") ? <SourceCell row={row} index={index} /> : null}
|
|
|
|
|
- {!hidden.has("query") ? <QueryCell row={row} /> : null}
|
|
|
|
|
|
|
+ {!hidden.has("source") ? <SourceCell row={row} /> : null}
|
|
|
{!hidden.has("videos") ? <VideoCell runId={runId} row={row} /> : null}
|
|
{!hidden.has("videos") ? <VideoCell runId={runId} row={row} /> : null}
|
|
|
{!hidden.has("rules") ? <RuleSummaryCell row={row} /> : null}
|
|
{!hidden.has("rules") ? <RuleSummaryCell row={row} /> : null}
|
|
|
{!hidden.has("rules") ? (
|
|
{!hidden.has("rules") ? (
|
|
@@ -134,14 +127,13 @@ export function LedgerPage({ runId }: { runId: string }) {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function SourceCell({ row, index }: { row: FlowLedgerRow; index: number }) {
|
|
|
|
|
|
|
+function SourceCell({ row }: { row: FlowLedgerRow }) {
|
|
|
const details = row.source.details.length ? row.source.details : [sourceEvidence(row)];
|
|
const details = row.source.details.length ? row.source.details : [sourceEvidence(row)];
|
|
|
return (
|
|
return (
|
|
|
<td className="source-cell sticky-source">
|
|
<td className="source-cell sticky-source">
|
|
|
- <div className="idx">第 {index + 1} 条</div>
|
|
|
|
|
<div className="cell-stack">
|
|
<div className="cell-stack">
|
|
|
- <span className="chip dark">{sourceLabel(row.source.sourceKind)}</span>
|
|
|
|
|
- <strong>{row.source.evidenceLabel}</strong>
|
|
|
|
|
|
|
+ <span className={`source-kind-badge ${sourceKindClass(row.source.sourceKind)}`}>{sourceLabel(row.source.sourceKind)}</span>
|
|
|
|
|
+ <strong className="source-query-text">{row.query.text}</strong>
|
|
|
<div className="source-detail-list">
|
|
<div className="source-detail-list">
|
|
|
{details.map((item) => (
|
|
{details.map((item) => (
|
|
|
<span className="muted" key={item}>{item}</span>
|
|
<span className="muted" key={item}>{item}</span>
|
|
@@ -152,21 +144,6 @@ function SourceCell({ row, index }: { row: FlowLedgerRow; index: number }) {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-function QueryCell({ row }: { row: FlowLedgerRow }) {
|
|
|
|
|
- const clue = clueText(row);
|
|
|
|
|
- return (
|
|
|
|
|
- <td className="query-cell sticky-query">
|
|
|
|
|
- <div className="cell-stack">
|
|
|
|
|
- <strong className="query-text">{row.query.text}</strong>
|
|
|
|
|
- <div className="chip-row">
|
|
|
|
|
- <span className="chip yellow">{methodLabel(row.query.method)}</span>
|
|
|
|
|
- </div>
|
|
|
|
|
- {clue ? <span className="muted">{clue}</span> : null}
|
|
|
|
|
- </div>
|
|
|
|
|
- </td>
|
|
|
|
|
- );
|
|
|
|
|
-}
|
|
|
|
|
-
|
|
|
|
|
function VideoCell({ runId, row }: { runId: string; row: FlowLedgerRow }) {
|
|
function VideoCell({ runId, row }: { runId: string; row: FlowLedgerRow }) {
|
|
|
const top = row.firstRoundVideos.slice(0, 3);
|
|
const top = row.firstRoundVideos.slice(0, 3);
|
|
|
return (
|
|
return (
|
|
@@ -192,6 +169,13 @@ function VideoCell({ runId, row }: { runId: string; row: FlowLedgerRow }) {
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+function sourceKindClass(kind: string): string {
|
|
|
|
|
+ if (kind === "seed_term") return "seed";
|
|
|
|
|
+ if (kind === "piaoquan_topic_point") return "piaoquan";
|
|
|
|
|
+ if (kind === "category_leaf_element") return "category";
|
|
|
|
|
+ return "unknown";
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
function VideoMini({ runId, video }: { runId: string; video: VideoRef }) {
|
|
function VideoMini({ runId, video }: { runId: string; video: VideoRef }) {
|
|
|
return (
|
|
return (
|
|
|
<Link className="video-mini" href={`/runs/${encodeURIComponent(runId)}/videos/${encodeURIComponent(video.id)}`}>
|
|
<Link className="video-mini" href={`/runs/${encodeURIComponent(runId)}/videos/${encodeURIComponent(video.id)}`}>
|