From 1ebab759b1cab2dcc2c29d04b8e4e7999f019623 Mon Sep 17 00:00:00 2001 From: zhaoying Date: Tue, 13 Jan 2026 14:04:28 +0800 Subject: [PATCH] feat(web): add graph detail page --- .../components/EmotionLine.tsx | 3 +- .../components/InteractionBar.tsx | 2 +- .../components/RelationshipNetwork.tsx | 22 ++- .../{components => pages}/GraphDetail.tsx | 127 ++++++++++-------- .../views/UserMemoryDetail/pages/index.tsx | 7 +- 5 files changed, 86 insertions(+), 75 deletions(-) rename web/src/views/UserMemoryDetail/{components => pages}/GraphDetail.tsx (50%) diff --git a/web/src/views/UserMemoryDetail/components/EmotionLine.tsx b/web/src/views/UserMemoryDetail/components/EmotionLine.tsx index c62fbfb9..68664d39 100644 --- a/web/src/views/UserMemoryDetail/components/EmotionLine.tsx +++ b/web/src/views/UserMemoryDetail/components/EmotionLine.tsx @@ -3,8 +3,7 @@ import { useTranslation } from 'react-i18next' import ReactEcharts from 'echarts-for-react'; import Empty from '@/components/Empty' import Loading from '@/components/Empty/Loading' -import type { Emotion } from './GraphDetail' -import { format } from 'echarts'; +import type { Emotion } from '../pages/GraphDetail' interface EmotionLineProps { chartData: Emotion[]; diff --git a/web/src/views/UserMemoryDetail/components/InteractionBar.tsx b/web/src/views/UserMemoryDetail/components/InteractionBar.tsx index 0db33b6f..60c977fd 100644 --- a/web/src/views/UserMemoryDetail/components/InteractionBar.tsx +++ b/web/src/views/UserMemoryDetail/components/InteractionBar.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import ReactEcharts from 'echarts-for-react' import Empty from '@/components/Empty' import Loading from '@/components/Empty/Loading' -import type { Interaction } from './GraphDetail' +import type { Interaction } from '../pages/GraphDetail' interface InteractionBarProps { chartData: Interaction[]; diff --git a/web/src/views/UserMemoryDetail/components/RelationshipNetwork.tsx b/web/src/views/UserMemoryDetail/components/RelationshipNetwork.tsx index 07095fe4..d12c3e57 100644 --- a/web/src/views/UserMemoryDetail/components/RelationshipNetwork.tsx +++ b/web/src/views/UserMemoryDetail/components/RelationshipNetwork.tsx @@ -1,19 +1,18 @@ import React, { type FC, useEffect, useState, useRef, useCallback } from 'react' import { useTranslation } from 'react-i18next' -import { useParams } from 'react-router-dom' +import { useParams, useNavigate } from 'react-router-dom' import { Col, Row, Space, Button } from 'antd' import dayjs from 'dayjs' import RbCard from '@/components/RbCard/Card' import ReactEcharts from 'echarts-for-react' import detailEmpty from '@/assets/images/userMemory/detail_empty.png' -import type { Node, Edge, GraphData, StatementNodeProperties, ExtractedEntityNodeProperties, GraphDetailRef } from '../types' +import type { Node, Edge, GraphData, StatementNodeProperties, ExtractedEntityNodeProperties } from '../types' import { getMemorySearchEdges, } from '@/api/memory' import Empty from '@/components/Empty' import Tag from '@/components/Tag' -import GraphDetail from '../components/GraphDetail' const colors = ['#155EEF', '#369F21', '#4DA8FF', '#FF5D34', '#9C6FFF', '#FF8A4C', '#8BAEF7', '#FFB048'] const RelationshipNetwork:FC = () => { @@ -26,7 +25,7 @@ const RelationshipNetwork:FC = () => { const [categories, setCategories] = useState<{ name: string }[]>([]) const [selectedNode, setSelectedNode] = useState(null) // const [fullScreen, setFullScreen] = useState(false) - const graphDetailRef = useRef(null) + const navigate = useNavigate() console.log('categories', categories) // 关系网络 @@ -133,15 +132,14 @@ const RelationshipNetwork:FC = () => { } }, [nodes]) - // const handleFullScreen = () => { - // setFullScreen(prev => !prev) - // } - - console.log('selectedNode', selectedNode) - const handleViewAll = () => { if (!selectedNode) return - graphDetailRef.current?.handleOpen(selectedNode) + const params = new URLSearchParams({ + nodeId: selectedNode.id, + nodeLabel: selectedNode.label, + nodeName: selectedNode.name || '' + }) + navigate(`/user-memory/detail/${id}/GRAPH?${params.toString()}`) } return ( @@ -336,8 +334,6 @@ const RelationshipNetwork:FC = () => { - - ) } diff --git a/web/src/views/UserMemoryDetail/components/GraphDetail.tsx b/web/src/views/UserMemoryDetail/pages/GraphDetail.tsx similarity index 50% rename from web/src/views/UserMemoryDetail/components/GraphDetail.tsx rename to web/src/views/UserMemoryDetail/pages/GraphDetail.tsx index aed795f5..72a7b13d 100644 --- a/web/src/views/UserMemoryDetail/components/GraphDetail.tsx +++ b/web/src/views/UserMemoryDetail/pages/GraphDetail.tsx @@ -1,16 +1,17 @@ -import { useState, forwardRef, useImperativeHandle, useMemo } from 'react' +import { useState, forwardRef, useImperativeHandle, useMemo, useEffect } from 'react' import { useTranslation } from 'react-i18next' +import { useSearchParams } from 'react-router-dom' import { Row, Col, Tabs, Space, Skeleton } from 'antd' import { getRelationshipEvolution, getTimelineMemories } from '@/api/memory' import type { Node, GraphDetailRef } from '../types' -import RbDrawer from '@/components/RbDrawer' import RbCard from '@/components/RbCard/Card' -import EmotionLine from './EmotionLine' +import EmotionLine from '../components/EmotionLine' import { formatDateTime } from '@/utils/format' import Tag from '@/components/Tag' -import InteractionBar from './InteractionBar' +import InteractionBar from '../components/InteractionBar' import Empty from '@/components/Empty' +import PageHeader from '../components/PageHeader' export interface Emotion { emotion_intensity: number; @@ -35,7 +36,7 @@ interface Timeline { const GraphDetail = forwardRef((_props, ref) => { const { t } = useTranslation() - const [open, setOpen] = useState(false); + const [searchParams] = useSearchParams() const [vo, setVo] = useState(null) const [loading, setLoading] = useState(false) const [emotionData, setEmotionData] = useState([]) @@ -43,14 +44,23 @@ const GraphDetail = forwardRef((_props, ref) => { const [activeTab, setActiveTab] = useState('timelines_memory') const [timelineLoading, setTimelineLoading] = useState(false) const [timelineMemories, setTimelineMemories] = useState({ timelines_memory: [], MemorySummary: [], Statement: [], ExtractedEntity: []}) + useEffect(() => { + const nodeId = searchParams.get('nodeId') + const nodeLabel = searchParams.get('nodeLabel') + const nodeName = searchParams.get('nodeName') + + if (nodeId && nodeLabel) { + const nodeFromUrl = { + id: nodeId, + label: nodeLabel, + name: nodeName || nodeLabel + } + handleOpen(nodeFromUrl as Node) + } + }, [searchParams]) - const handleCancel = () => { - setVo(null) - setOpen(false) - } const handleOpen = (vo: Node) => { setActiveTab('timelines_memory') - setOpen(true) setVo(vo) getRelationshipEvolutionData(vo) getTimelineMemoriesData(vo) @@ -85,56 +95,57 @@ const GraphDetail = forwardRef((_props, ref) => { }, [activeTab, timelineMemories]) return ( - -
{t('userMemory.relationshipEvolution')}
- - - - - - - - - - + <> + +
+
{t('userMemory.relationshipEvolution')}
+ + + + + + + + + + -
{t('userMemory.timelineMemories')}
- - ({ - label: t(`userMemory.${key}`), - key - }))} - onChange={(key: string) => setActiveTab(key)} - /> - {timelineLoading - ? - : !activeContent || activeContent.length === 0 - ? - : - {activeContent.map((vo, index) => ( - -
{formatDateTime(vo.created_at)}
- {vo.type} -
- ))} -
- } +
{t('userMemory.timelineMemories')}
+ + ({ + label: t(`userMemory.${key}`), + key + }))} + onChange={(key: string) => setActiveTab(key)} + /> + {timelineLoading + ? + : !activeContent || activeContent.length === 0 + ? + : + {activeContent.map((vo, index) => ( + +
{formatDateTime(vo.created_at)}
+ {vo.type} +
+ ))} +
+ } - -
- + +
+
+ ) }) export default GraphDetail \ No newline at end of file diff --git a/web/src/views/UserMemoryDetail/pages/index.tsx b/web/src/views/UserMemoryDetail/pages/index.tsx index 8f5ee146..f5b1a937 100644 --- a/web/src/views/UserMemoryDetail/pages/index.tsx +++ b/web/src/views/UserMemoryDetail/pages/index.tsx @@ -1,7 +1,7 @@ import { type FC, useEffect, useState, useMemo, useRef } from 'react' import { useParams, useNavigate } from 'react-router-dom' import { useTranslation } from 'react-i18next' -import { Dropdown, Space, Button } from 'antd' +import { Dropdown, Button } from 'antd' import PageHeader from '../components/PageHeader' import StatementDetail from './StatementDetail' @@ -16,6 +16,7 @@ import { getEndUserProfile, } from '@/api/memory' import refreshIcon from '@/assets/images/refresh_hover.svg' +import GraphDetail from './GraphDetail' const Detail: FC = () => { const { t } = useTranslation() @@ -47,6 +48,10 @@ const Detail: FC = () => { forgetDetailRef.current?.handleRefresh() } + if (type === 'GRAPH') { + return + } + return (