Просмотр исходного кода

Link walk graph videos to detail pages

Sam Lee 2 недель назад
Родитель
Сommit
da4ac8a8b0
2 измененных файлов с 61 добавлено и 12 удалено
  1. 24 0
      web2/app/globals.css
  2. 37 12
      web2/features/walk/WalkTree.tsx

+ 24 - 0
web2/app/globals.css

@@ -3145,6 +3145,22 @@ a.walk-flow-node:hover {
   font: inherit;
 }
 
+.wt-summary-trigger {
+  flex: 0 0 auto;
+  display: inline-flex;
+  gap: 8px;
+  align-items: center;
+  padding: 2px;
+  border: none;
+  border-radius: 4px;
+  background: transparent;
+  cursor: pointer;
+}
+
+.wt-summary-trigger:hover {
+  background: #ECE5D6;
+}
+
 .wt-dot { flex: 0 0 auto; width: 8px; height: 8px; border-radius: 999px; background: #D9CFBB; }
 .wt-dot.good { background: #5E7E45; }
 .wt-dot.warn { background: #f59e0b; }
@@ -3167,6 +3183,14 @@ a.walk-flow-node:hover {
   overflow: hidden; white-space: nowrap; text-overflow: ellipsis;
   color: #211F1A; font-size: 14px; font-weight: 750;
 }
+.wt-title-link {
+  text-decoration: none;
+}
+.wt-title-link:hover {
+  color: #B4502F;
+  text-decoration: underline;
+  text-underline-offset: 3px;
+}
 .wt-title.route { flex: 0 0 auto; max-width: 320px; }
 .wt-newquery { flex: 0 0 auto; color: #B4502F; font-size: 13px; font-weight: 750; }
 .wt-author { flex: 0 0 auto; color: #8A8275; font-size: 13px; max-width: 130px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }

+ 37 - 12
web2/features/walk/WalkTree.tsx

@@ -190,16 +190,24 @@ function VideoRow({
     <div className="wt-branch" role="treeitem" aria-expanded={hasRoutes ? open : undefined}>
       <div className={`wt-row video ${tone}`}>
         <Toggle open={open} disabled={!hasRoutes} onClick={() => onToggle(node.key)} />
-        <button type="button" className="wt-row-main" onClick={() => onOpenDrawer(videoDrawer(node))}>
-          <span className={`wt-dot ${tone}`} />
-          {depth === 0 && hideRootIndex ? null : (
-            <span className="wt-kicker">{depth === 0 ? `起点 ${(index ?? 0) + 1}` : "游走到的"}</span>
-          )}
-          <span className="wt-title">{node.title}</span>
+        <div className="wt-row-main">
+          <button
+            type="button"
+            className="wt-summary-trigger"
+            onClick={() => onOpenDrawer(videoDrawer(node))}
+            aria-label={`查看摘要:${node.title}`}
+            title="查看摘要"
+          >
+            <span className={`wt-dot ${tone}`} />
+            {depth === 0 && hideRootIndex ? null : (
+              <span className="wt-kicker">{depth === 0 ? `起点 ${(index ?? 0) + 1}` : "游走到的"}</span>
+            )}
+          </button>
+          <VideoTitleLink runId={runId} video={node.video} title={node.title} />
           <span className="wt-author">{node.author}</span>
           {node.score ? <span className="wt-score">{node.score}</span> : null}
           <span className={`wt-status ${tone}`}>{node.decisionLabel}</span>
-        </button>
+        </div>
         <span className="wt-row-right">
           {hasRoutes ? (
             <span className={`wt-badge ${walked ? "go" : "stop"}`}>
@@ -304,15 +312,13 @@ function Toggle({ open, disabled, onClick }: { open: boolean; disabled: boolean;
 }
 
 function RowLinks({ video, runId }: { video: RawRecord; runId: string }) {
-  // platform content ids (视频号 finder ids) can contain / + = which break path routing;
-  // prefer the clean content_discovery_id for the detail route.
-  const detailId = maybeText(video.content_discovery_id) || text(video.id, "");
-  if (!detailId) return null;
+  const detailHref = videoDetailHref(runId, video);
+  if (!detailHref) return null;
   const platformUrl = text(video.platform_url, "");
   const platformLabel = text(video.platform_label, "原视频");
   return (
     <span className="wt-links">
-      <Link className="wt-link" href={`/runs/${encodeURIComponent(runId)}/videos/${encodeURIComponent(detailId)}`} onClick={(e) => e.stopPropagation()}>
+      <Link className="wt-link" href={detailHref} onClick={(e) => e.stopPropagation()}>
         详情
       </Link>
       {platformUrl ? (
@@ -325,6 +331,25 @@ function RowLinks({ video, runId }: { video: RawRecord; runId: string }) {
   );
 }
 
+function VideoTitleLink({ runId, video, title }: { runId: string; video: RawRecord; title: string }) {
+  const detailHref = videoDetailHref(runId, video);
+  if (!detailHref) return <span className="wt-title">{title}</span>;
+  return (
+    <Link className="wt-title wt-title-link" href={detailHref}>
+      {title}
+    </Link>
+  );
+}
+
+function videoDetailHref(runId: string, video: RawRecord): string {
+  // platform content ids (视频号 finder ids) can contain / + = which break path routing;
+  // prefer the clean content_discovery_id for the detail route.
+  const detailId = maybeText(video.content_discovery_id) || text(video.id, "");
+  return detailId
+    ? `/runs/${encodeURIComponent(runId)}/videos/${encodeURIComponent(detailId)}`
+    : "";
+}
+
 // ---------------------------------------------------------------------------
 // build
 // ---------------------------------------------------------------------------