Files
MemoryBear/web/src/views/UserMemoryDetail/components/GraphDetail.tsx
2026-01-12 14:42:02 +08:00

140 lines
4.6 KiB
TypeScript

import { useState, forwardRef, useImperativeHandle, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
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 { formatDateTime } from '@/utils/format'
import Tag from '@/components/Tag'
import InteractionBar from './InteractionBar'
import Empty from '@/components/Empty'
export interface Emotion {
emotion_intensity: number;
emotion_type: string;
created_at: string | number;
}
export interface Interaction {
created_at: string | number;
count: number;
}
interface TimelineMemory {
text: string;
type: string;
created_at: number | string;
}
interface Timeline {
MemorySummary: TimelineMemory[];
Statement: TimelineMemory[];
ExtractedEntity: TimelineMemory[];
timelines_memory: TimelineMemory[];
}
const GraphDetail = forwardRef<GraphDetailRef>((_props, ref) => {
const { t } = useTranslation()
const [open, setOpen] = useState(false);
const [vo, setVo] = useState<Node | null>(null)
const [loading, setLoading] = useState(false)
const [emotionData, setEmotionData] = useState<Emotion[]>([])
const [interactionData, setInteractionData] = useState<Interaction[]>([])
const [activeTab, setActiveTab] = useState('timelines_memory')
const [timelineLoading, setTimelineLoading] = useState(false)
const [timelineMemories, setTimelineMemories] = useState<Timeline>({ timelines_memory: [], MemorySummary: [], Statement: [], ExtractedEntity: []})
const handleCancel = () => {
setVo(null)
setOpen(false)
}
const handleOpen = (vo: Node) => {
setActiveTab('timelines_memory')
setOpen(true)
setVo(vo)
getRelationshipEvolutionData(vo)
getTimelineMemoriesData(vo)
}
const getRelationshipEvolutionData = (vo: Node) => {
if (!vo.id || !vo.label) return
setLoading(true)
getRelationshipEvolution({ id: vo.id as string, label: vo.label })
.then(res => {
const { emotion, interaction } = res as { emotion: Emotion[]; interaction: Interaction[] } || {}
setEmotionData(emotion)
setInteractionData(interaction)
})
.finally(() => setLoading(false))
}
const getTimelineMemoriesData = (vo: Node) => {
if (!vo.id || !vo.label) return
setTimelineLoading(true)
getTimelineMemories({ id: vo.id as string, label: vo.label })
.then(res => {
setTimelineMemories(res as Timeline)
})
.finally(() => setTimelineLoading(false))
}
useImperativeHandle(ref, () => ({
handleOpen,
}));
const activeContent = useMemo(() => {
return timelineMemories[activeTab as keyof Timeline] || []
}, [activeTab, timelineMemories])
return (
<RbDrawer
title={vo?.name}
open={open}
onClose={handleCancel}
width={1000}
>
<div className="rb:text-[16px] rb:font-medium rb:leading-5.5 rb:mb-3">{t('userMemory.relationshipEvolution')}</div>
<RbCard>
<Row gutter={16}>
<Col span={12}>
<EmotionLine chartData={emotionData} loading={loading} />
</Col>
<Col span={12}>
<InteractionBar chartData={interactionData} loading={loading} />
</Col>
</Row>
</RbCard>
<div className="rb:text-[16px] rb:font-medium rb:leading-5.5 rb:mb-3 rb:mt-6">{t('userMemory.timelineMemories')}</div>
<RbCard>
<Tabs
activeKey={activeTab}
items={['timelines_memory', 'ExtractedEntity', 'Statement', 'MemorySummary'].map(key => ({
label: t(`userMemory.${key}`),
key
}))}
onChange={(key: string) => setActiveTab(key)}
/>
{timelineLoading
? <Skeleton active />
: !activeContent || activeContent.length === 0
? <Empty size={120} className="rb:mt-12 rb:mb-20.25" />
: <Space size={16} direction="vertical" className="rb:w-full">
{activeContent.map((vo, index) => (
<RbCard
key={index}
headerType="borderL"
headerClassName="rb:before:bg-[#155EEF]!"
title={vo.text}
>
<div className="rb:text-[#A8A9AA] rb:text-[12px] rb:leading-4">{formatDateTime(vo.created_at)}</div>
<Tag className="rb:mt-2">{vo.type}</Tag>
</RbCard>
))}
</Space>
}
</RbCard>
</RbDrawer>
)
})
export default GraphDetail