Преглед на файлове

Merge remote-tracking branch 'refs/remotes/origin/main'

Talegorithm преди 3 седмици
родител
ревизия
30602ba858

+ 0 - 0
.browser_use_files/browseruse_agent_data/todo.md


+ 226 - 189
frontend/react-template/src/components/FlowChart/FlowChart.tsx

@@ -9,8 +9,8 @@
  * 5. 线条粗细根据嵌套层级递减
  */
 
-import { useCallback, useEffect, useMemo, useRef, useState } from "react";
-import type { FC } from "react";
+import { useCallback, useEffect, useMemo, useRef, useState, forwardRef, useImperativeHandle } from "react";
+import type { ForwardRefRenderFunction } from "react";
 import type { Goal } from "../../types/goal";
 import type { Edge as EdgeType, Message } from "../../types/message";
 import { ArrowMarkers } from "./components/ArrowMarkers";
@@ -27,6 +27,14 @@ interface FlowChartProps {
   onSubTraceClick?: (parentGoal: Goal, entry: SubTraceEntry) => void; // 子追踪点击回调
 }
 
+/**
+ * FlowChart 组件对外暴露的引用接口
+ */
+export interface FlowChartRef {
+  expandAll: () => void;
+  collapseAll: () => void;
+}
+
 /**
  * 子追踪条目类型
  */
@@ -61,7 +69,10 @@ interface LayoutEdge {
   children?: LayoutNode[]; // 折叠时隐藏的子节点列表
 }
 
-export const FlowChart: FC<FlowChartProps> = ({ goals, msgGroups = {}, onNodeClick, onSubTraceClick }) => {
+const FlowChartComponent: ForwardRefRenderFunction<FlowChartRef, FlowChartProps> = (
+  { goals, msgGroups = {}, onNodeClick, onSubTraceClick },
+  ref,
+) => {
   console.log("%c [ msgGroups ]-33", "font-size:13px; background:pink; color:#bf2c9f;", msgGroups);
   // 过滤掉有父节点的 goals,只保留主链节点
   goals = goals.filter((g) => !g.parent_id);
@@ -489,6 +500,26 @@ export const FlowChart: FC<FlowChartProps> = ({ goals, msgGroups = {}, onNodeCli
     return { nodes, edges };
   }, [displayGoals, dimensions, msgGroups, collapsedEdges]);
 
+  // 暴露给父组件的方法
+  useImperativeHandle(
+    ref,
+    () => ({
+      expandAll: () => {
+        setCollapsedEdges(new Set());
+      },
+      collapseAll: () => {
+        const allCollapsible = new Set<string>();
+        layoutData.edges.forEach((edge) => {
+          if (edge.collapsible) {
+            allCollapsible.add(edge.id);
+          }
+        });
+        setCollapsedEdges(allCollapsible);
+      },
+    }),
+    [layoutData],
+  );
+
   // 初始化折叠状态:只展开第一个主链节点(A->B)之间的内容
   useEffect(() => {
     if (initializedRef.current || !layoutData || layoutData.edges.length === 0) return;
@@ -724,203 +755,207 @@ export const FlowChart: FC<FlowChartProps> = ({ goals, msgGroups = {}, onNodeCli
     <div
       className={styles.container}
       ref={containerRef}
-      style={{
-        overflow: viewMode === "scroll" ? "auto" : "hidden",
-      }}
     >
-      <svg
-        ref={svgRef}
-        viewBox={
-          viewMode === "scroll"
-            ? `200 0 ${contentSize.width} ${contentSize.height}`
-            : `0 0 ${dimensions.width} ${dimensions.height}`
-        }
-        preserveAspectRatio="xMidYMid meet"
-        className={`${styles.svg} ${isPanning ? styles.panning : ""}`}
+      <div
+        className={styles.scrollContainer}
         style={{
-          cursor: viewMode === "scroll" ? "default" : isPanning ? "grabbing" : "grab",
-          width: viewMode === "scroll" ? "100%" : "100%",
-          height: viewMode === "scroll" ? contentSize.height : "100%",
-          minWidth: viewMode === "scroll" ? contentSize.width : undefined,
-          minHeight: viewMode === "scroll" ? contentSize.height : undefined,
-        }}
-        onWheel={(event) => {
-          if (viewMode === "scroll") return; // 滚动模式下使用原生滚动
-
-          // 鼠标滚轮缩放
-          event.preventDefault();
-          const rect = svgRef.current?.getBoundingClientRect();
-          if (!rect) return;
-          const cursorX = event.clientX - rect.left;
-          const cursorY = event.clientY - rect.top;
-          const nextZoom = clampZoom(zoom * (event.deltaY > 0 ? 0.92 : 1.08));
-          if (nextZoom === zoom) return;
-          const scale = nextZoom / zoom;
-          // 以鼠标位置为中心缩放
-          setPanOffset((prev) => ({
-            x: cursorX - (cursorX - prev.x) * scale,
-            y: cursorY - (cursorY - prev.y) * scale,
-          }));
-          setZoom(nextZoom);
-        }}
-        onDoubleClick={() => resetView()} // 双击重置视图
-        onMouseDown={(event) => {
-          if (viewMode === "scroll") return; // 滚动模式下禁用拖拽
-
-          // 开始平移
-          if (event.button !== 0) return; // 只响应左键
-          const target = event.target as Element;
-          // 如果点击的是节点或连接线,不触发平移
-          if (target.closest(`.${styles.nodes}`) || target.closest(`.${styles.links}`)) return;
-          panStartRef.current = {
-            x: event.clientX,
-            y: event.clientY,
-            originX: panOffset.x,
-            originY: panOffset.y,
-          };
-          setIsPanning(true);
+          overflow: viewMode === "scroll" ? "auto" : "hidden",
         }}
       >
-        <defs>
-          <ArrowMarkers /> {/* 箭头标记定义 */}
-        </defs>
-
-        {/* 应用平移和缩放变换(仅在 panzoom 模式下) */}
-        <g transform={viewMode === "scroll" ? undefined : `translate(${panOffset.x},${panOffset.y}) scale(${zoom})`}>
-          {/* 绘制连接线 */}
-          <g className={styles.links}>
-            {visibleData.edges.map((edge) => {
-              // 根据连接线类型选择路径
-              const path =
-                edge.type === "arc" && !edge.collapsed
-                  ? getArcPath(edge.source, edge.target, edge.level)
-                  : getLinePath(edge.source, edge.target);
-
-              const strokeWidth = getStrokeWidth(edge.level); // 根据层级计算线条粗细
-
-              // 根据节点类型决定颜色
-              let color = "#2196F3"; // 默认蓝色
-
-              // 判断连接线连接的节点类型
-              const sourceIsMessage = edge.source.type === "message";
-              const targetIsMessage = edge.target.type === "message";
-              const sourceIsMainGoal = edge.source.type === "goal" && edge.source.level === 0;
-              const targetIsMainGoal = edge.target.type === "goal" && edge.target.level === 0;
-
-              if (sourceIsMessage || targetIsMessage) {
-                // msgGroup 相关的连接线用灰色
-                color = "#9E9E9E";
-              } else if (sourceIsMainGoal && targetIsMainGoal) {
-                // 主节点之间的连接线用绿色
-                color = "#4CAF50";
-              } else {
-                // sub_goals 之间的连接线用蓝色
-                color = "#2196F3";
-              }
-
-              return (
-                <g key={edge.id}>
-                  <path
-                    d={path}
-                    fill="none"
-                    stroke={color}
-                    strokeWidth={strokeWidth}
-                    markerEnd="url(#arrow-default)" // 箭头
-                    style={{ cursor: edge.collapsible ? "pointer" : "default" }} // 可折叠的显示手型光标
-                    onClick={() => edge.collapsible && toggleCollapse(edge.id)} // 点击切换折叠状态
-                  />
-                  {/* 折叠状态提示徽章 */}
-                  {edge.collapsed && (
-                    <g
-                      transform={`translate(${(edge.source.x + edge.target.x) / 2},${
-                        (edge.source.y + edge.target.y) / 2
-                      })`}
-                      onClick={(e) => {
-                        e.stopPropagation();
-                        toggleCollapse(edge.id);
-                      }}
-                      style={{ cursor: "pointer" }}
-                    >
-                      <circle
-                        r={10}
-                        fill="#FFFFFF"
-                        stroke={color}
-                        strokeWidth={1}
-                      />
-                      <text
-                        x={0}
-                        y={4}
-                        fontSize={10}
-                        fill={color}
-                        textAnchor="middle"
-                        fontWeight="bold"
+        <svg
+          ref={svgRef}
+          viewBox={
+            viewMode === "scroll"
+              ? `${contentSize.width / 2} 0 ${contentSize.width} ${contentSize.height}`
+              : `0 0 ${dimensions.width} ${dimensions.height}`
+          }
+          preserveAspectRatio="xMidYMid meet"
+          className={`${styles.svg} ${isPanning ? styles.panning : ""}`}
+          style={{
+            cursor: viewMode === "scroll" ? "default" : isPanning ? "grabbing" : "grab",
+            width: viewMode === "scroll" ? "100%" : "100%",
+            height: viewMode === "scroll" ? contentSize.height : "100%",
+            minWidth: viewMode === "scroll" ? contentSize.width : undefined,
+            minHeight: viewMode === "scroll" ? contentSize.height : undefined,
+          }}
+          onWheel={(event) => {
+            if (viewMode === "scroll") return; // 滚动模式下使用原生滚动
+
+            // 鼠标滚轮缩放
+            event.preventDefault();
+            const rect = svgRef.current?.getBoundingClientRect();
+            if (!rect) return;
+            const cursorX = event.clientX - rect.left;
+            const cursorY = event.clientY - rect.top;
+            const nextZoom = clampZoom(zoom * (event.deltaY > 0 ? 0.92 : 1.08));
+            if (nextZoom === zoom) return;
+            const scale = nextZoom / zoom;
+            // 以鼠标位置为中心缩放
+            setPanOffset((prev) => ({
+              x: cursorX - (cursorX - prev.x) * scale,
+              y: cursorY - (cursorY - prev.y) * scale,
+            }));
+            setZoom(nextZoom);
+          }}
+          onDoubleClick={() => resetView()} // 双击重置视图
+          onMouseDown={(event) => {
+            if (viewMode === "scroll") return; // 滚动模式下禁用拖拽
+
+            // 开始平移
+            if (event.button !== 0) return; // 只响应左键
+            const target = event.target as Element;
+            // 如果点击的是节点或连接线,不触发平移
+            if (target.closest(`.${styles.nodes}`) || target.closest(`.${styles.links}`)) return;
+            panStartRef.current = {
+              x: event.clientX,
+              y: event.clientY,
+              originX: panOffset.x,
+              originY: panOffset.y,
+            };
+            setIsPanning(true);
+          }}
+        >
+          <defs>
+            <ArrowMarkers /> {/* 箭头标记定义 */}
+          </defs>
+
+          {/* 应用平移和缩放变换(仅在 panzoom 模式下) */}
+          <g transform={viewMode === "scroll" ? undefined : `translate(${panOffset.x},${panOffset.y}) scale(${zoom})`}>
+            {/* 绘制连接线 */}
+            <g className={styles.links}>
+              {visibleData.edges.map((edge) => {
+                // 根据连接线类型选择路径
+                const path =
+                  edge.type === "arc" && !edge.collapsed
+                    ? getArcPath(edge.source, edge.target, edge.level)
+                    : getLinePath(edge.source, edge.target);
+
+                const strokeWidth = getStrokeWidth(edge.level); // 根据层级计算线条粗细
+
+                // 根据节点类型决定颜色
+                let color = "#2196F3"; // 默认蓝色
+
+                // 判断连接线连接的节点类型
+                const sourceIsMessage = edge.source.type === "message";
+                const targetIsMessage = edge.target.type === "message";
+                const sourceIsMainGoal = edge.source.type === "goal" && edge.source.level === 0;
+                const targetIsMainGoal = edge.target.type === "goal" && edge.target.level === 0;
+
+                if (sourceIsMessage || targetIsMessage) {
+                  // msgGroup 相关的连接线用灰色
+                  color = "#9E9E9E";
+                } else if (sourceIsMainGoal && targetIsMainGoal) {
+                  // 主节点之间的连接线用绿色
+                  color = "#4CAF50";
+                } else {
+                  // sub_goals 之间的连接线用蓝色
+                  color = "#2196F3";
+                }
+
+                return (
+                  <g key={edge.id}>
+                    <path
+                      d={path}
+                      fill="none"
+                      stroke={color}
+                      strokeWidth={strokeWidth}
+                      markerEnd="url(#arrow-default)" // 箭头
+                      style={{ cursor: edge.collapsible ? "pointer" : "default" }} // 可折叠的显示手型光标
+                      onClick={() => edge.collapsible && toggleCollapse(edge.id)} // 点击切换折叠状态
+                    />
+                    {/* 折叠状态提示徽章 */}
+                    {edge.collapsed && (
+                      <g
+                        transform={`translate(${(edge.source.x + edge.target.x) / 2},${
+                          (edge.source.y + edge.target.y) / 2
+                        })`}
+                        onClick={(e) => {
+                          e.stopPropagation();
+                          toggleCollapse(edge.id);
+                        }}
+                        style={{ cursor: "pointer" }}
                       >
-                        {edge.children ? edge.children.length : "+"}
-                      </text>
-                    </g>
-                  )}
-                </g>
-              );
-            })}
-          </g>
-
-          {/* 绘制节点 */}
-          <g className={styles.nodes}>
-            {visibleData.nodes.map((node) => {
-              const isGoal = node.type === "goal";
-              const data = node.data as Goal;
-              const text = isGoal ? data.description : (node.data as Message).description || "";
-
-              let textColor = "#2196F3"; // 默认蓝色
-              if (node.type === "message") {
-                textColor = "#9E9E9E"; // 消息节点灰色
-              } else if (node.type === "goal" && node.level === 0) {
-                textColor = "#4CAF50"; // 主节点绿色
-              }
-
-              return (
-                <g
-                  key={node.id}
-                  transform={`translate(${node.x},${node.y})`}
-                  onClick={() => handleNodeClick(node)}
-                  style={{ cursor: "pointer" }}
-                >
-                  {/* 节点矩形 */}
-                  <rect
-                    x={-70}
-                    y={-25}
-                    width={150}
-                    height={50}
-                    rx={8}
-                    fill={isGoal ? "#E3F2FD" : "#F5F5F5"} // 目标节点浅蓝色,消息节点灰色
-                    stroke={selectedNodeId === node.id ? "#2196F3" : "#BDBDBD"} // 选中节点蓝色边框
-                    strokeWidth={selectedNodeId === node.id ? 2 : 1}
-                  />
-                  {/* 节点文本(带 Tooltip) */}
-                  <Tooltip content={text}>
-                    <foreignObject
+                        <circle
+                          r={10}
+                          fill="#FFFFFF"
+                          stroke={color}
+                          strokeWidth={1}
+                        />
+                        <text
+                          x={0}
+                          y={4}
+                          fontSize={10}
+                          fill={color}
+                          textAnchor="middle"
+                          fontWeight="bold"
+                        >
+                          {edge.children ? edge.children.length : "+"}
+                        </text>
+                      </g>
+                    )}
+                  </g>
+                );
+              })}
+            </g>
+
+            {/* 绘制节点 */}
+            <g className={styles.nodes}>
+              {visibleData.nodes.map((node) => {
+                const isGoal = node.type === "goal";
+                const data = node.data as Goal;
+                const text = isGoal ? data.description : (node.data as Message).description || "";
+
+                let textColor = "#2196F3"; // 默认蓝色
+                if (node.type === "message") {
+                  textColor = "#9E9E9E"; // 消息节点灰色
+                } else if (node.type === "goal" && node.level === 0) {
+                  textColor = "#4CAF50"; // 主节点绿色
+                }
+
+                return (
+                  <g
+                    key={node.id}
+                    transform={`translate(${node.x},${node.y})`}
+                    onClick={() => handleNodeClick(node)}
+                    style={{ cursor: "pointer" }}
+                  >
+                    {/* 节点矩形 */}
+                    <rect
                       x={-70}
                       y={-25}
                       width={150}
                       height={50}
-                    >
-                      <div
-                        className="w-full h-full flex items-center justify-center text-xs text-center leading-[1.2] px-1 box-border line-clamp-3"
-                        style={{
-                          color: textColor,
-                          WebkitBoxPack: "center", // 垂直居中
-                        }}
+                      rx={8}
+                      fill={isGoal ? "#E3F2FD" : "#F5F5F5"} // 目标节点浅蓝色,消息节点灰色
+                      stroke={selectedNodeId === node.id ? "#2196F3" : "#BDBDBD"} // 选中节点蓝色边框
+                      strokeWidth={selectedNodeId === node.id ? 2 : 1}
+                    />
+                    {/* 节点文本(带 Tooltip) */}
+                    <Tooltip content={text}>
+                      <foreignObject
+                        x={-70}
+                        y={-25}
+                        width={150}
+                        height={50}
                       >
-                        {text}
-                      </div>
-                    </foreignObject>
-                  </Tooltip>
-                </g>
-              );
-            })}
+                        <div
+                          className="w-full h-full flex items-center justify-center text-xs text-center leading-[1.2] px-1 box-border line-clamp-3"
+                          style={{
+                            color: textColor,
+                            WebkitBoxPack: "center", // 垂直居中
+                          }}
+                        >
+                          {text}
+                        </div>
+                      </foreignObject>
+                    </Tooltip>
+                  </g>
+                );
+              })}
+            </g>
           </g>
-        </g>
-      </svg>
+        </svg>
+      </div>
 
       {/* 控制按钮 */}
       <div className={styles.controls}>
@@ -960,3 +995,5 @@ export const FlowChart: FC<FlowChartProps> = ({ goals, msgGroups = {}, onNodeCli
     </div>
   );
 };
+
+export const FlowChart = forwardRef(FlowChartComponent);

+ 7 - 0
frontend/react-template/src/components/FlowChart/styles/FlowChart.module.css

@@ -4,6 +4,12 @@
   background: #fafafa;
   position: relative;
 }
+
+.scrollContainer {
+  width: 100%;
+  height: 100%;
+}
+
 .svg {
   width: 100%;
   height: 100%;
@@ -32,6 +38,7 @@
   border-radius: 10px;
   padding: 6px;
   box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
+  z-index: 10;
 }
 
 .controlButton {

+ 21 - 0
frontend/react-template/src/components/MainContent/MainContent.module.css

@@ -65,3 +65,24 @@
   font-size: 14px;
   margin: auto;
 }
+
+.buttons {
+  display: flex;
+  gap: 8px;
+}
+
+.btn {
+  padding: 4px 12px;
+  border: 1px solid #d9d9d9;
+  border-radius: 4px;
+  background: #fff;
+  cursor: pointer;
+  font-size: 12px;
+  color: #333;
+  transition: all 0.3s;
+}
+
+.btn:hover {
+  color: #1890ff;
+  border-color: #1890ff;
+}

+ 23 - 4
frontend/react-template/src/components/MainContent/MainContent.tsx

@@ -1,6 +1,7 @@
-import { useMemo } from "react";
+import { useMemo, useRef, useState } from "react";
 import type { FC } from "react";
 import { FlowChart } from "../FlowChart/FlowChart";
+import type { FlowChartRef } from "../FlowChart/FlowChart";
 import { useTrace } from "../../hooks/useTrace";
 import { useFlowChartData } from "../FlowChart/hooks/useFlowChartData";
 import type { Goal } from "../../types/goal";
@@ -13,6 +14,8 @@ interface MainContentProps {
 }
 
 export const MainContent: FC<MainContentProps> = ({ traceId, onNodeClick }) => {
+  const flowChartRef = useRef<FlowChartRef>(null);
+  const [isAllExpanded, setIsAllExpanded] = useState(true);
   const { trace, loading } = useTrace(traceId);
   const initialGoals = useMemo(() => trace?.goal_tree?.goals ?? [], [trace]);
   const { goals, connected, msgGroups } = useFlowChartData(traceId, initialGoals);
@@ -35,10 +38,10 @@ export const MainContent: FC<MainContentProps> = ({ traceId, onNodeClick }) => {
   return (
     <div className={styles.main}>
       <div className={styles.header}>
-        <div className={styles.title}></div>
+        <div className={styles.title}>{connected ? "WebSocket 已连接" : "WebSocket 未连接"}</div>
         <div className={styles.headerRight}>
-          <div className={styles.status}>{connected ? "WebSocket 已连接" : "WebSocket 未连接"}</div>
-          <div className={styles.legend}>
+          {/* <div className={styles.status}>{connected ? "WebSocket 已连接" : "WebSocket 未连接"}</div> */}
+          {/* <div className={styles.legend}>
             <div className={styles.legendItem}>
               <span
                 className={styles.legendDot}
@@ -67,6 +70,21 @@ export const MainContent: FC<MainContentProps> = ({ traceId, onNodeClick }) => {
               />
               默认
             </div>
+          </div> */}
+          <div className={styles.buttons}>
+            <button
+              className={styles.btn}
+              onClick={() => {
+                if (isAllExpanded) {
+                  flowChartRef.current?.collapseAll();
+                } else {
+                  flowChartRef.current?.expandAll();
+                }
+                setIsAllExpanded(!isAllExpanded);
+              }}
+            >
+              {isAllExpanded ? "全折叠" : "全展开"}
+            </button>
           </div>
         </div>
       </div>
@@ -77,6 +95,7 @@ export const MainContent: FC<MainContentProps> = ({ traceId, onNodeClick }) => {
           <div className={styles.empty}>暂无节点</div>
         ) : (
           <FlowChart
+            ref={flowChartRef}
             goals={goals}
             msgGroups={msgGroups}
             onNodeClick={onNodeClick}

+ 29 - 5
frontend/react-template/src/components/TopBar/TopBar.module.css

@@ -37,20 +37,44 @@
 
 .button {
   padding: 8px 16px;
-  border: 1px solid #0070f3;
+  border: 1px solid #d9d9d9;
   border-radius: 4px;
   font-size: 14px;
-  background: #0070f3;
-  color: white;
+  background: #fff;
+  color: #666;
   cursor: pointer;
-  transition: background 0.2s;
+  transition: all 0.2s;
 }
 
 .button:hover:not(:disabled) {
-  background: #0051cc;
+  color: #0070f3;
+  border-color: #0070f3;
 }
 
 .button:disabled {
   opacity: 0.6;
   cursor: not-allowed;
 }
+
+.buttonPrimary {
+  composes: button;
+  background: #0070f3;
+  color: white;
+  border-color: #0070f3;
+}
+
+.buttonPrimary:hover:not(:disabled) {
+  background: #0051cc;
+  color: white;
+}
+
+.buttonDanger {
+  composes: button;
+  color: #f44336;
+  border-color: #f44336;
+}
+
+.buttonDanger:hover:not(:disabled) {
+  background: #fff1f0;
+  color: #f44336;
+}

+ 28 - 27
frontend/react-template/src/components/TopBar/TopBar.tsx

@@ -1,5 +1,5 @@
 import { useCallback, useEffect, useState } from "react";
-import type { FC, ChangeEvent } from "react";
+import type { FC } from "react";
 import { traceApi } from "../../api/traceApi";
 import styles from "./TopBar.module.css";
 
@@ -8,13 +8,10 @@ interface TopBarProps {
 }
 
 export const TopBar: FC<TopBarProps> = ({ onTraceSelect }) => {
-  const [statusFilter, setStatusFilter] = useState<"running" | "completed" | "failed" | "">("");
   const [title, setTitle] = useState("流程图可视化系统");
-  const [loading, setLoading] = useState(false);
 
   const loadTraces = useCallback(
     async (status?: string) => {
-      setLoading(true);
       try {
         const data = await traceApi.fetchTraces({
           status: status || undefined,
@@ -32,19 +29,11 @@ export const TopBar: FC<TopBarProps> = ({ onTraceSelect }) => {
         }
       } catch (error) {
         console.error("Failed to load traces:", error);
-      } finally {
-        setLoading(false);
       }
     },
     [onTraceSelect],
   );
 
-  const handleStatusChange = (status: string) => {
-    const value = status as "running" | "completed" | "failed" | "";
-    setStatusFilter(value);
-    loadTraces(value);
-  };
-
   useEffect(() => {
     loadTraces();
   }, [loadTraces]);
@@ -55,24 +44,36 @@ export const TopBar: FC<TopBarProps> = ({ onTraceSelect }) => {
         <h1>{title}</h1>
       </div>
       <div className={styles.filters}>
-        {/* <select
-          value={statusFilter}
-          onChange={(e: ChangeEvent<HTMLSelectElement>) => handleStatusChange(e.target.value)}
-          className={styles.select}
-          disabled={loading}
+        <button
+          className={styles.button}
+          onClick={() => console.log("新任务")}
+        >
+          新任务
+        </button>
+        <button
+          className={styles.buttonPrimary}
+          onClick={() => console.log("运行")}
+        >
+          运行
+        </button>
+        <button
+          className={styles.buttonDanger}
+          onClick={() => console.log("停止")}
+        >
+          停止
+        </button>
+        <button
+          className={styles.button}
+          onClick={() => console.log("反思")}
         >
-          <option value="">全部状态</option>
-          <option value="running">运行中</option>
-          <option value="completed">已完成</option>
-          <option value="failed">失败</option>
-        </select> */}
-        {/* <button
-          onClick={() => loadTraces(statusFilter)}
+          反思
+        </button>
+        <button
           className={styles.button}
-          disabled={loading}
+          onClick={() => console.log("经验")}
         >
-          {loading ? "加载中..." : "刷新"}
-        </button> */}
+          经验
+        </button>
       </div>
     </header>
   );