| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181 |
- import { useRef, useState, useEffect } from "react";
- import type { FC } from "react";
- import { Select } from "@douyinfe/semi-ui";
- import { FlowChart } from "../FlowChart/FlowChart";
- import type { FlowChartRef } from "../FlowChart/FlowChart";
- import { useFlowChartData } from "../FlowChart/hooks/useFlowChartData";
- import { traceApi } from "../../api/traceApi";
- import type { Goal } from "../../types/goal";
- import type { Edge, Message } from "../../types/message";
- import type { TraceListItem } from "../../types/trace";
- import styles from "./MainContent.module.css";
- interface MainContentProps {
- traceId: string | null;
- onNodeClick?: (node: Goal | Message, edge?: Edge) => void;
- onTraceChange?: (traceId: string, title?: string) => void;
- refreshTrigger?: number;
- messageRefreshTrigger?: number;
- }
- export const MainContent: FC<MainContentProps> = ({
- traceId,
- onNodeClick,
- onTraceChange,
- refreshTrigger,
- messageRefreshTrigger,
- }) => {
- const flowChartRef = useRef<FlowChartRef>(null);
- const [isAllExpanded, setIsAllExpanded] = useState(true);
- const [traceList, setTraceList] = useState<TraceListItem[]>([]);
- const [cachedGoals, setCachedGoals] = useState<Goal[]>([]);
- const [cachedMsgGroups, setCachedMsgGroups] = useState<Record<string, Message[]>>({});
- const [cachedInvalidBranches, setCachedInvalidBranches] = useState<Message[][]>([]);
- const { goals, connected, msgGroups, reloading, invalidBranches } = useFlowChartData(traceId, messageRefreshTrigger);
- console.log("%c [ msgGroups ]-34", "font-size:13px; background:pink; color:#bf2c9f;", msgGroups);
- const displayGoals = goals.length > 0 ? goals : cachedGoals;
- const displayMsgGroups = Object.keys(msgGroups).length > 0 ? msgGroups : cachedMsgGroups;
- const displayInvalidBranches =
- invalidBranches && invalidBranches.length > 0 ? invalidBranches : cachedInvalidBranches;
- useEffect(() => {
- const fetchTraces = async () => {
- try {
- const data = await traceApi.fetchTraces({ limit: 100 });
- setTraceList(data.traces);
- } catch (error) {
- console.error("Failed to load traces:", error);
- }
- };
- fetchTraces();
- }, [refreshTrigger]);
- useEffect(() => {
- // 移除 reload 调用,因为 useFlowChartData 内部会监听 messageRefreshTrigger 并重新加载
- }, [messageRefreshTrigger]);
- useEffect(() => {
- if (goals.length > 0) {
- setCachedGoals(goals);
- }
- }, [goals]);
- useEffect(() => {
- if (Object.keys(msgGroups).length > 0) {
- setCachedMsgGroups(msgGroups);
- }
- }, [msgGroups]);
- useEffect(() => {
- if (invalidBranches && invalidBranches.length > 0) {
- setCachedInvalidBranches(invalidBranches);
- }
- }, [invalidBranches]);
- useEffect(() => {
- setCachedGoals([]);
- setCachedMsgGroups({});
- setCachedInvalidBranches([]);
- }, [traceId]);
- if (!traceId && !reloading) {
- return (
- <div className={styles.main}>
- <div className={styles.header}>
- <div className={styles.title}>暂无 Trace</div>
- <div className={styles.status}>未连接</div>
- </div>
- <div className={styles.content}>
- <div className={styles.empty}>暂无可展示的数据</div>
- </div>
- </div>
- );
- }
- return (
- <div className={styles.main}>
- <div className={styles.header}>
- <div className={styles.title}>{connected ? "WebSocket 已连接" : "WebSocket 未连接"}</div>
- <div className={styles.headerRight}>
- <Select
- value={traceId}
- onChange={(value: unknown) => {
- const trace = traceList.find((t) => t.trace_id === value);
- onTraceChange?.(value as string, trace?.task || trace?.trace_id);
- }}
- style={{ width: 200 }}
- placeholder="选择 Trace"
- optionList={traceList.map((t) => ({
- label: t.task?.length > 15 ? `${t.task.slice(0, 15)}...` : t.task || t.trace_id,
- value: t.trace_id,
- }))}
- />
- {/* <div className={styles.status}>{connected ? "WebSocket 已连接" : "WebSocket 未连接"}</div> */}
- {/* <div className={styles.legend}>
- <div className={styles.legendItem}>
- <span
- className={styles.legendDot}
- style={{ background: "#00c853" }}
- />
- 已完成
- </div>
- <div className={styles.legendItem}>
- <span
- className={styles.legendDot}
- style={{ background: "#f44336" }}
- />
- 失败
- </div>
- <div className={styles.legendItem}>
- <span
- className={styles.legendDot}
- style={{ background: "#ff9800" }}
- />
- 运行中
- </div>
- <div className={styles.legendItem}>
- <span
- className={styles.legendDot}
- style={{ background: "#4e79a7" }}
- />
- 默认
- </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>
- <div className={styles.content}>
- {reloading ? (
- <div className={styles.loading}>加载中...</div>
- ) : displayGoals.length === 0 ? (
- <div className={styles.empty}>暂无数据</div>
- ) : (
- <FlowChart
- ref={flowChartRef}
- goals={displayGoals}
- msgGroups={displayMsgGroups}
- invalidBranches={displayInvalidBranches}
- onNodeClick={onNodeClick}
- onSubTraceClick={(_parentGoal, entry) => {
- onTraceChange?.(entry.id, entry.mission || entry.id);
- }}
- />
- )}
- </div>
- </div>
- );
- };
|