diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts
index f77955f5..8ab3765a 100644
--- a/web/src/i18n/en.ts
+++ b/web/src/i18n/en.ts
@@ -329,7 +329,8 @@ export const en = {
publicApiCannotRefreshToken: 'Public API cannot refresh token',
refreshTokenNotExist: 'Refresh token does not exist',
reset: 'Reset',
- refresh: 'Refresh'
+ refresh: 'Refresh',
+ return: 'Return',
},
model: {
searchPlaceholder: 'search model…',
@@ -1023,7 +1024,7 @@ export const en = {
drag: 'Drag and drop to move nodes',
zoom: 'Scroll zoom view',
memoryDetailEmpty: 'Please select a memory node',
- memoryDetailEmptyDesc: 'Click on any node in the above view to view detailed information',
+ memoryDetailEmptyDesc: 'Click on the node in the left graph to view the details of entity memory',
totalNumOfMemories: 'Total Number of Memories',
footprintCity: 'Footprint City',
@@ -1067,6 +1068,8 @@ export const en = {
hire_date: 'Hire Date',
memoryContent: 'Memory Content',
created_at: 'Created At',
+
+ memoryWindow: "{{name}}'s Window of Memory"
},
space: {
createSpace: 'Create Space',
diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts
index 5efcdfa6..f664fa14 100644
--- a/web/src/i18n/zh.ts
+++ b/web/src/i18n/zh.ts
@@ -805,7 +805,8 @@ export const zh = {
publicApiCannotRefreshToken: '公共接口不能刷新token',
refreshTokenNotExist: '刷新token不存在',
reset: '重置',
- refresh: '刷新'
+ refresh: '刷新',
+ return: '返回',
},
product: {
applicationManagement: '应用管理',
@@ -1105,7 +1106,7 @@ export const zh = {
drag: '拖放移动节点',
zoom: '滚动缩放视图',
memoryDetailEmpty: '请选择一个记忆节点',
- memoryDetailEmptyDesc: '点击上方视图中的任何节点以查看详细信息',
+ memoryDetailEmptyDesc: '点击左侧图表中的节点查看实体记忆详情',
totalNumOfMemories: '记忆总数',
footprintCity: '足迹城市',
@@ -1151,6 +1152,8 @@ export const zh = {
hire_date: '入职时间',
memoryContent: '记忆内容',
created_at: '创建时间',
+ updated_at: '最后更新时间',
+ fullScreen: '全屏'
},
space: {
createSpace: '创建空间',
@@ -1476,7 +1479,7 @@ export const zh = {
auth: '认证',
requestHeader: '请求头',
config: '配置',
- auth_Type: '认证方式',
+ auth_type: '认证方式',
none: '无需认证',
api_key: 'API Key',
basic_auth: 'Basic Auth',
diff --git a/web/src/routes/index.tsx b/web/src/routes/index.tsx
index a4c20163..9a2ea17d 100644
--- a/web/src/routes/index.tsx
+++ b/web/src/routes/index.tsx
@@ -38,6 +38,7 @@ const componentMap: Record
>> =
Home: lazy(() => import('@/views/Home')),
UserMemory: lazy(() => import('@/views/UserMemory')),
UserMemoryDetail: lazy(() => import('@/views/UserMemoryDetail')),
+ Neo4jUserMemoryDetail: lazy(() => import('@/views/UserMemoryDetail/Neo4j')),
MemberManagement: lazy(() => import('@/views/MemberManagement')),
MemoryManagement: lazy(() => import('@/views/MemoryManagement')),
ForgettingEngine: lazy(() => import('@/views/ForgettingEngine')),
diff --git a/web/src/routes/routes.json b/web/src/routes/routes.json
index a414185d..9d3e9cb8 100644
--- a/web/src/routes/routes.json
+++ b/web/src/routes/routes.json
@@ -41,7 +41,8 @@
"element": "BasicLayout",
"children": [
{ "path": "/application/config/:id", "element": "ApplicationConfig" },
- { "path": "/conversation/:token", "element": "Conversation" }
+ { "path": "/conversation/:token", "element": "Conversation" },
+ { "path": "/user-memory/neo4j/:id", "element": "Neo4jUserMemoryDetail" }
]
},
{
diff --git a/web/src/store/menu.json b/web/src/store/menu.json
index 1c4cec9d..8311e27d 100644
--- a/web/src/store/menu.json
+++ b/web/src/store/menu.json
@@ -284,7 +284,7 @@
"code": "userMemoryDetail",
"label": "记忆详情",
"i18nKey": "menu.userMemoryDetail",
- "path": "/user-memory/:id",
+ "path": "/user-memory/neo4j/:id",
"enable": true,
"display": false,
"level": 2,
@@ -304,6 +304,18 @@
"subs": null
}
]
+ },
+ {
+ "id": 81,
+ "parent": 8,
+ "code": "userMemoryDetail",
+ "label": "记忆详情",
+ "i18nKey": "menu.userMemoryDetail",
+ "path": "/user-memory/:id",
+ "enable": true,
+ "display": false,
+ "level": 2,
+ "sort": 0
}
]
},
diff --git a/web/src/views/UserMemory/index.tsx b/web/src/views/UserMemory/index.tsx
index b325c52d..af7db5e1 100644
--- a/web/src/views/UserMemory/index.tsx
+++ b/web/src/views/UserMemory/index.tsx
@@ -13,6 +13,7 @@ import onlineNum from '@/assets/images/memory/onlineNum.svg'
import Table from '@/components/Table'
import { getTotalEndUsers, userMemoryListUrl, getUserMemoryList } from '@/api/memory';
import ConfigModal from './components/ConfigModal';
+import { useUser } from '@/store/user'
const bgList = [
'linear-gradient( 180deg, #F1F6FE 0%, #FBFDFF 100%)',
@@ -31,6 +32,7 @@ const IconList: Record = {
export default function UserMemory() {
const { t } = useTranslation();
const navigate = useNavigate()
+ const { storageType } = useUser()
const configModalRef = useRef(null)
const [loading, setLoading] = useState(false);
const [data, setData] = useState([]);
@@ -58,8 +60,15 @@ export default function UserMemory() {
setLoading(false)
})
}
+ console.log('storageType', storageType)
const handleViewDetail = (id: string | number) => {
- navigate(`/user-memory/${id}`)
+ switch (storageType) {
+ case 'neo4j':
+ navigate(`/user-memory/neo4j/${id}`)
+ break;
+ default:
+ navigate(`/user-memory/${id}`)
+ }
}
const handleChangeLayout = (e: RadioChangeEvent) => {
const type = e.target.value
diff --git a/web/src/views/UserMemoryDetail/Neo4j.tsx b/web/src/views/UserMemoryDetail/Neo4j.tsx
index 784f962e..75cde3f9 100644
--- a/web/src/views/UserMemoryDetail/Neo4j.tsx
+++ b/web/src/views/UserMemoryDetail/Neo4j.tsx
@@ -1,168 +1,87 @@
-import { type FC, useEffect, useState, useRef } from 'react'
-import { useTranslation } from 'react-i18next'
-import clsx from 'clsx'
-import { Row, Col, Skeleton, Flex, Button } from 'antd'
+import { type FC, useEffect, useRef, useState } from 'react'
import { useParams } from 'react-router-dom'
-import aboutUs from '@/assets/images/userMemory/aboutUs.svg'
-import down from '@/assets/images/userMemory/down.svg'
-import interestDistribution from '@/assets/images/userMemory/interestDistribution.svg'
-import PieCard from './components/PieCard'
-import RbCard from '@/components/RbCard/Card'
-import {
- getUserSummary,
- analyticsRefresh
-} from '@/api/memory'
-import type { MemoryInsightRef } from './types'
+import { Row, Col, Space, Button } from 'antd'
+import { useTranslation } from 'react-i18next';
+
+import PageHeader from './components/PageHeader'
+import EndUserProfile from './components/EndUserProfile'
+import AboutMe from './components/AboutMe'
+import InterestDistribution from './components/InterestDistribution'
+import NodeStatistics from './components/NodeStatistics'
import RelationshipNetwork from './components/RelationshipNetwork'
import MemoryInsight from './components/MemoryInsight'
-import Empty from '@/components/Empty'
-
-import NodeStatistics from './components/NodeStatistics'
-import EndUserProfile from './components/EndUserProfile'
-
-interface TitleProps {
- type: string;
- title: string
- icon: string
- t: (key: string) => string;
- expanded: boolean;
- onClick: (type: string) => void;
-}
-const Title: FC = ({ type, title, icon, t, expanded, onClick }) => (
-
-
-
- {title}
-
-
-
onClick(type)}>
- {t(`userMemory.${expanded ? 'foldUp' : 'expanded'}`)}
-
-
-
-)
+import type { EndUserProfileRef, MemoryInsightRef, AboutMeRef } from './types'
+import {
+ analyticsRefresh,
+} from '@/api/memory'
const Neo4j: FC = () => {
- const { t } = useTranslation()
+ const { t } = useTranslation();
const { id } = useParams()
+ const [loading, setLoading] = useState(false)
+ const [name, setName] = useState('')
+ const ref = useRef(null)
const memoryInsightRef = useRef(null)
- const [expanded, setExpanded] = useState(['aboutUs', 'interestDistribution', 'importantRelationships', 'importantMomentsInLife'])
- const [summary, setSummary] = useState(null)
- const [loading, setLoading] = useState>({
- summary: false,
- refresh: false
- })
+ const aboutMeRef = useRef(null)
- useEffect(() => {
- if (!id) return
- getSummary()
- }, [id])
+ const handleNameUpdate = (data: { other_name?: string; id: string }) => {
+ setName(data.other_name ?? data.id)
+ }
- const handleTitleClick = (key: string) => {
- setExpanded(expanded.includes(key) ? expanded.filter((item) => item !== key) : [...expanded, key])
- }
- // 用户摘要
- const getSummary = () => {
- if (!id) return
- setLoading(prev => ({ ...prev, summary: true }))
- getUserSummary(id).then((res) => {
- setSummary((res as { summary?: string }).summary || null)
- })
- .finally(() => {
- setLoading(prev => ({ ...prev, summary: false }))
- })
- }
const handleRefresh = () => {
- setLoading(prev => ({ ...prev, refresh: true }))
+ setLoading(true)
analyticsRefresh(id as string)
.then(res => {
const response = res as { insight_success: boolean; summary_success: boolean; }
if (response.insight_success) {
- memoryInsightRef.current?.getInsightReport()
+ memoryInsightRef.current?.getData()
}
if (response.summary_success) {
- getSummary()
+ memoryInsightRef.current?.getData()
}
})
.finally(() => {
- setLoading(prev => ({ ...prev, refresh: false }))
+ setLoading(false)
})
}
+
+
return (
-
-
-
-
-
-
-
-
-
-
-
-
- {/* 关于我 */}
- <>
-
- {expanded.includes('aboutUs') && (
- <>
- {loading.summary
- ?
- : summary
- ?
- {summary || '-'}
-
- :
- }
- >
- )}
- >
-
- {/* 兴趣分布 */}
- <>
-
-
- {expanded.includes('interestDistribution') && (
-
- )}
- >
-
-
-
-
-
-
-
+
+
+ {!loading && }
+ {t('common.refresh')}
+
+ )}
+ />
+
+
+
+
+
+
+
+
+
+
+
-
- {/* 记忆洞察 */}
-
+
-
- {/* 关系网络 + 记忆详情 */}
-
-
-
+
+
+
)
}
diff --git a/web/src/views/UserMemoryDetail/components/AboutMe.tsx b/web/src/views/UserMemoryDetail/components/AboutMe.tsx
new file mode 100644
index 00000000..ba7e68fe
--- /dev/null
+++ b/web/src/views/UserMemoryDetail/components/AboutMe.tsx
@@ -0,0 +1,56 @@
+import { type FC, useEffect, useState, forwardRef, useImperativeHandle } from 'react'
+import { useTranslation } from 'react-i18next'
+import { useParams } from 'react-router-dom'
+import { Skeleton } from 'antd';
+
+import RbCard from '@/components/RbCard/Card'
+import Empty from '@/components/Empty';
+import {
+ getUserSummary,
+} from '@/api/memory'
+import type { AboutMeRef } from '../types'
+
+const AboutMe = forwardRef((_props, ref) => {
+ const { t } = useTranslation()
+ const { id } = useParams()
+ const [loading, setLoading] = useState(false)
+ const [data, setData] = useState(null)
+
+ useEffect(() => {
+ if (!id) return
+ getData()
+ }, [id])
+
+ // 记忆洞察
+ const getData = () => {
+ if (!id) return
+ setLoading(true)
+ getUserSummary(id)
+ .then((res) => {
+ setData((res as { summary?: string }).summary || null)
+ })
+ .finally(() => {
+ setLoading(false)
+ })
+ }
+ // 暴露给父组件的方法
+ useImperativeHandle(ref, () => ({
+ getData,
+ }));
+
+ return (
+
+ {loading
+ ?
+ : data
+ ?
+ {data || '-'}
+
+ :
+ }
+
+ )
+})
+export default AboutMe
\ No newline at end of file
diff --git a/web/src/views/UserMemoryDetail/components/EndUserProfile.tsx b/web/src/views/UserMemoryDetail/components/EndUserProfile.tsx
index 144beff7..ac98d9aa 100644
--- a/web/src/views/UserMemoryDetail/components/EndUserProfile.tsx
+++ b/web/src/views/UserMemoryDetail/components/EndUserProfile.tsx
@@ -1,17 +1,21 @@
-import { type FC, useEffect, useState, useRef, useCallback } from 'react'
+import { forwardRef, useImperativeHandle, useEffect, useState, useRef, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useParams } from 'react-router-dom'
-import { Skeleton, Descriptions, Button } from 'antd';
+import { Skeleton } from 'antd';
import dayjs from 'dayjs'
+
import RbCard from '@/components/RbCard/Card'
-import Empty from '@/components/Empty';
import {
getEndUserProfile,
} from '@/api/memory'
import EndUserProfileModal from './EndUserProfileModal'
-import type { EndUser, EndUserProfileModalRef } from '../types'
+import type { EndUser, EndUserProfileModalRef, EndUserProfileRef } from '../types'
-const EndUserProfile:FC = () => {
+interface EndUserProfileProps {
+ onDataLoaded?: (data: { other_name?: string; id: string }) => void
+}
+
+const EndUserProfile = forwardRef(({ onDataLoaded }, ref) => {
const { t } = useTranslation()
const { id } = useParams()
const endUserProfileModalRef = useRef(null)
@@ -28,7 +32,12 @@ const EndUserProfile:FC = () => {
if (!id) return
setLoading(true)
getEndUserProfile(id).then((res) => {
- setData(res as EndUser)
+ const userData = res as EndUser
+ setData(userData)
+ onDataLoaded?.({
+ other_name: userData.other_name,
+ id: userData.id
+ })
setLoading(false)
})
.finally(() => {
@@ -36,31 +45,45 @@ const EndUserProfile:FC = () => {
})
}
const formatItems = useCallback(() => {
- if (!data) return []
return ['other_name', 'position', 'department', 'contact', 'phone', 'hire_date'].map(key => ({
key,
label: t(`userMemory.${key}`),
- children: key === 'hire_date' && data[key] ? dayjs(data[key as keyof EndUser]).format('YYYY-MM-DD') : String(data[key as keyof EndUser] || ''),
+ children: key === 'hire_date' && data?.[key] ? dayjs(data[key as keyof EndUser]).format('YYYY-MM-DD') : String(data?.[key as keyof EndUser] || '-'),
}))
}, [data])
const handleEdit = () => {
if (!data) return
endUserProfileModalRef.current?.handleOpen(data)
}
+
+ useImperativeHandle(ref, () => ({
+ data
+ }));
+
return (
+ }
>
{loading
?
- : data
- ?
-
-
+ :
+ {formatItems().map(vo => (
+
+
{vo.label}
+
{vo.children}
+
+ ))}
+
+
+ {t('userMemory.updated_at')}: {data?.updatetime_profile ? dayjs(data?.updatetime_profile).format('YYYY/MM/DD HH:mm:ss') : ''}
+
- :
}
{
/>
)
-}
+})
export default EndUserProfile
\ No newline at end of file
diff --git a/web/src/views/UserMemoryDetail/components/PieCard.tsx b/web/src/views/UserMemoryDetail/components/InterestDistribution.tsx
similarity index 70%
rename from web/src/views/UserMemoryDetail/components/PieCard.tsx
rename to web/src/views/UserMemoryDetail/components/InterestDistribution.tsx
index 982a8be6..8a1edccb 100644
--- a/web/src/views/UserMemoryDetail/components/PieCard.tsx
+++ b/web/src/views/UserMemoryDetail/components/InterestDistribution.tsx
@@ -1,18 +1,24 @@
import { type FC, useRef, useEffect, useState } from 'react'
+import { useTranslation } from 'react-i18next'
import { useParams } from 'react-router-dom'
import ReactEcharts from 'echarts-for-react';
+import { Space } from 'antd'
+
import { getHotMemoryTagsByUser } from '@/api/memory';
import Empty from '@/components/Empty';
import Loading from '@/components/Empty/Loading';
+import RbCard from '@/components/RbCard/Card';
const Colors = ['#155EEF', '#4DA8FF', '#03BDFF', '#31E8FF', '#AD88FF', '#FFB048']
-const PieCard: FC = () => {
+const InterestDistribution: FC = () => {
+ const { t } = useTranslation()
const { id } = useParams()
const chartRef = useRef(null);
const resizeScheduledRef = useRef(false)
const [loading, setLoading] = useState(false)
const [data, setData] = useState>>([])
+ const totalValue = data.reduce((sum, item) => sum + Number(item.value), 0)
useEffect(() => {
getData()
@@ -55,12 +61,14 @@ const PieCard: FC = () => {
}, [data])
return (
- <>
+
{loading
?
: !data || data.length === 0
?
- : data && data.length > 0 &&
+ : data && data.length > 0 && <>
{
extraCssText: 'width: 36px; height: 36px; box-shadow: 0px 2px 4px 0px rgba(33,35,50,0.12);border-radius: 36px;'
},
legend: {
- type: data.length > 8 ? 'scroll' : 'plain',
- bottom: 0,
- left: 16,
- padding: 0,
- itemWidth: 12,
- itemHeight: 12,
- borderRadius: 2,
- // orient: 'horizontal',
- textStyle: {
- color: '#5B6167',
- fontFamily: 'PingFangSC, PingFang SC',
- lineHeight: 16,
- }
+ show: false
},
series: [
{
@@ -102,9 +98,9 @@ const PieCard: FC = () => {
avoidLabelOverlap: false,
percentPrecision: 0,
padAngle: 0,
- width: 220,
- height: 220,
- top: 32,
+ width: 200,
+ height: 200,
+ top: 18,
left: 'center',
itemStyle: {
borderRadius: 0
@@ -129,13 +125,27 @@ const PieCard: FC = () => {
}
]
}}
- style={{ height: '340px', width: '100%' }}
+ style={{ height: '250px', width: '100%' }}
notMerge={true}
lazyUpdate={true}
/>
- }
- >
+
+ {data.map((item, index) => (
+
+
+
+ {item.name}
+
+
{totalValue > 0 ? Math.round((Number(item.value) / totalValue) * 100) : 0}%
+
+ ))}
+
+ >}
+
)
}
-export default PieCard
+export default InterestDistribution
diff --git a/web/src/views/UserMemoryDetail/components/MemoryInsight.tsx b/web/src/views/UserMemoryDetail/components/MemoryInsight.tsx
index 983e8a41..0c0751f3 100644
--- a/web/src/views/UserMemoryDetail/components/MemoryInsight.tsx
+++ b/web/src/views/UserMemoryDetail/components/MemoryInsight.tsx
@@ -17,11 +17,11 @@ const MemoryInsight = forwardRef((_props, ref) => {
useEffect(() => {
if (!id) return
- getInsightReport()
+ getData()
}, [id])
// 记忆洞察
- const getInsightReport = () => {
+ const getData = () => {
if (!id) return
setLoading(true)
getMemoryInsightReport(id).then((res) => {
@@ -34,23 +34,18 @@ const MemoryInsight = forwardRef((_props, ref) => {
}
// 暴露给父组件的方法
useImperativeHandle(ref, () => ({
- getInsightReport,
+ getData,
}));
return (
{loading
?
: report
- ?
-
- {report|| '-'}
-
+ ?
+ {report || '-'}
:
}
diff --git a/web/src/views/UserMemoryDetail/components/NodeStatistics copy.tsx b/web/src/views/UserMemoryDetail/components/NodeStatistics copy.tsx
new file mode 100644
index 00000000..f5f7df34
--- /dev/null
+++ b/web/src/views/UserMemoryDetail/components/NodeStatistics copy.tsx
@@ -0,0 +1,88 @@
+import { type FC, useEffect, useState } from 'react'
+import clsx from 'clsx'
+import { useTranslation } from 'react-i18next'
+import { useParams, useNavigate } from 'react-router-dom'
+import { Skeleton } from 'antd';
+import RbCard from '@/components/RbCard/Card'
+import Empty from '@/components/Empty';
+import {
+ getNodeStatistics,
+} from '@/api/memory'
+import type { NodeStatisticsItem } from '../types'
+
+
+const NodeStatistics: FC = () => {
+ const navigate = useNavigate();
+ const { t } = useTranslation()
+ const { id } = useParams()
+ const [loading, setLoading] = useState
(false)
+ const [total, setTotal] = useState(0)
+ const [data, setData] = useState([])
+
+ useEffect(() => {
+ if (!id) return
+ getData()
+ }, [id])
+
+ // 记忆洞察
+ const getData = () => {
+ if (!id) return
+ setLoading(true)
+ getNodeStatistics(id).then((res) => {
+ const response = res as NodeStatisticsItem[]
+ setData(response)
+ // 计算count总计
+ const totalCount = response.reduce((sum, item) => sum + (item.count || 0), 0)
+ setTotal(totalCount)
+ setLoading(false)
+ })
+ .finally(() => {
+ setLoading(false)
+ })
+ }
+ const handleViewDetail = (type: string) => {
+ switch (type) {
+ case 'EMOTIONAL_MEMORY':
+ navigate(`/statement/${id}`)
+ break
+ }
+ }
+ return (
+ {t('userMemory.nodeStatistics')}{t('userMemory.total')}: {total}
>}
+ headerType="borderless"
+ >
+ {loading
+ ?
+ : data && data.length > 0
+ ?
+ {data.map(vo => (
+
handleViewDetail(vo.type)}
+ >
+
+
{t(`userMemory.${vo.type}`)}
+ {vo.type === 'EMOTIONAL_MEMORY' &&
}
+
+
+
+ {vo.count ?? 0}
+
+ {vo.percentage ?? 0}%
+
+
+
+ ))}
+
+ :
+ }
+
+ )
+}
+export default NodeStatistics
\ No newline at end of file
diff --git a/web/src/views/UserMemoryDetail/components/NodeStatistics.tsx b/web/src/views/UserMemoryDetail/components/NodeStatistics.tsx
index 7bfd19f3..288e11c0 100644
--- a/web/src/views/UserMemoryDetail/components/NodeStatistics.tsx
+++ b/web/src/views/UserMemoryDetail/components/NodeStatistics.tsx
@@ -11,6 +11,17 @@ import {
import type { NodeStatisticsItem } from '../types'
+const BG_LIST = [
+ 'rb:bg-[linear-gradient(316deg,rgba(21,94,239,0.06)_0%,rgba(251,253,255,0)_100%)]',
+ 'rb:bg-[linear-gradient(316deg,rgba(54,159,33,0.06)_0%,rgba(251,253,255,0)_100%)]',
+ 'rb:bg-[linear-gradient(314deg,rgba(156,111,255,0.06)_0%,rgba(251,253,255,0)_100%)]',
+ 'rb:bg-[linear-gradient(314deg,rgba(255,93,52,0.06)_0%,rgba(251,253,255,0)_100%)]',
+ 'rb:bg-[linear-gradient(180deg,rgba(156,111,255,0.06)_0%,rgba(251,253,255,0)_100%)]',
+ 'rb:bg-[linear-gradient(180deg,rgba(21,94,239,0.06)_0%,rgba(251,253,255,0)_100%)]',
+ 'rb:bg-[linear-gradient(180deg,rgba(54,159,33,0.06)_0%,rgba(251,253,255,0)_100%)]',
+ 'rb:bg-[]',
+]
+
const NodeStatistics: FC = () => {
const navigate = useNavigate();
const { t } = useTranslation()
@@ -49,40 +60,32 @@ const NodeStatistics: FC = () => {
}
return (
{t('userMemory.total')}: {total} }
+ title={<>{t('userMemory.nodeStatistics')} ({t('userMemory.total')}: {total})>}
headerType="borderless"
- headerClassName="rb:text-[18px]! rb:leading-[24px]"
- bgColor="linear-gradient(180deg,#F1F9FE 0%, #FBFCFF 100%)"
- height="100%"
>
{loading
?
: data && data.length > 0
- ?
- {data.map(vo => (
+ ?
+ {data.map((vo, index) => (
handleViewDetail(vo.type)}
>
-
-
{t(`userMemory.${vo.type}`)}
+
+
+ {t(`userMemory.${vo.type}`)}
+
{vo.type === 'EMOTIONAL_MEMORY' &&
}
-
-
- {vo.count ?? 0}
-
- {vo.percentage ?? 0}%
-
-
+
{vo.count ?? 0}
- ))}
+ ))}
:
}
diff --git a/web/src/views/UserMemoryDetail/components/PageHeader.tsx b/web/src/views/UserMemoryDetail/components/PageHeader.tsx
new file mode 100644
index 00000000..a1a0a2ea
--- /dev/null
+++ b/web/src/views/UserMemoryDetail/components/PageHeader.tsx
@@ -0,0 +1,38 @@
+import { type FC, type ReactNode } from 'react';
+import { useNavigate } from 'react-router-dom';
+import { Layout } from 'antd';
+import { useTranslation } from 'react-i18next';
+import logoutIcon from '@/assets/images/logout.svg'
+
+const { Header } = Layout;
+
+interface ConfigHeaderProps {
+ name?: string;
+ operation: ReactNode
+}
+const PageHeader: FC
= ({
+ name,
+ operation
+}) => {
+ const { t } = useTranslation();
+ const navigate = useNavigate();
+
+ const goBack = () => {
+ navigate('/user-memory', { replace: true })
+ }
+ return (
+
+ );
+};
+
+export default PageHeader;
\ No newline at end of file
diff --git a/web/src/views/UserMemoryDetail/components/RelationshipNetwork.tsx b/web/src/views/UserMemoryDetail/components/RelationshipNetwork.tsx
index c2e588ef..db7c9e57 100644
--- a/web/src/views/UserMemoryDetail/components/RelationshipNetwork.tsx
+++ b/web/src/views/UserMemoryDetail/components/RelationshipNetwork.tsx
@@ -1,25 +1,18 @@
import React, { type FC, useEffect, useState, useRef, useCallback } from 'react'
import { useTranslation } from 'react-i18next'
import { useParams } from 'react-router-dom'
-import { Col } from 'antd'
+import { Col, Row } from 'antd'
+import dayjs from 'dayjs'
+
import RbCard from '@/components/RbCard/Card'
import ReactEcharts from 'echarts-for-react'
-import zoom from '@/assets/images/userMemory/zoom.svg'
-import drag from '@/assets/images/userMemory/drag.svg'
-import pointer from '@/assets/images/userMemory/pointer.svg'
-import empty from '@/assets/images/userMemory/empty.svg'
+import detailEmpty from '@/assets/images/userMemory/detail_empty.png'
import type { Node, Edge, GraphData } from '../types'
import {
getMemorySearchEdges,
} from '@/api/memory'
import Empty from '@/components/Empty'
-import dayjs from 'dayjs'
-const operations = [
- { name: 'click', icon: pointer },
- { name: 'drag', icon: drag },
- { name: 'zoom', icon: zoom },
-]
const colors = ['#155EEF', '#369F21', '#4DA8FF', '#FF5D34', '#9C6FFF', '#FF8A4C', '#8BAEF7', '#FFB048']
const RelationshipNetwork:FC = () => {
const { t } = useTranslation()
@@ -30,6 +23,7 @@ const RelationshipNetwork:FC = () => {
const [links, setLinks] = useState([])
const [categories, setCategories] = useState<{ name: string }[]>([])
const [selectedNode, setSelectedNode] = useState(null)
+ // const [fullScreen, setFullScreen] = useState(false)
console.log('categories', categories)
// 关系网络
@@ -136,16 +130,30 @@ const RelationshipNetwork:FC = () => {
}
}, [nodes])
+ // const handleFullScreen = () => {
+ // setFullScreen(prev => !prev)
+ // }
+
+ console.log('selectedNode', selectedNode)
+
return (
- <>
+
{/* 关系网络 */}
-
+
+ //
+ // {t('userMemory.fullScreen')}
+ //
+ // }
>
-
+
{nodes.length === 0 ? (
) : (
@@ -157,7 +165,7 @@ const RelationshipNetwork:FC = () => {
},
legend: {
show: true,
- bottom: 20,
+ bottom: 12,
},
series: [
{
@@ -207,12 +215,12 @@ const RelationshipNetwork:FC = () => {
}
]
}}
- style={{ height: '496px', width: '100%' }}
+ style={{ height: '518px', width: '100%' }}
notMerge={false}
lazyUpdate={true}
onEvents={{
// 节点点击事件处理
- click: (params: { dataType: string; data: Node }) => {
+ click: (params: { dataType: string; data: Node; name: string }) => {
if (params.dataType === 'node') {
// 处理节点点击事件
console.log('Node clicked:', params.data);
@@ -224,57 +232,52 @@ const RelationshipNetwork:FC = () => {
/>
)}
-
- {operations.map((item) => (
-
-

- {t(`userMemory.${item.name}`)}
-
- ))}
-
{/* 记忆详情 */}
-
+
- {!selectedNode
- ?
- : <>
-
-
- {t('userMemory.memoryContent')}
-
- {['Chunk', 'Dialogue', 'MemorySummary'].includes(selectedNode.label) && 'content' in selectedNode.properties
- ? selectedNode.properties.content
- : selectedNode.label === 'ExtractedEntity' && 'description' in selectedNode.properties
- ? selectedNode.properties.description
- : selectedNode.label === 'Statement' && 'statement' in selectedNode.properties
- ? selectedNode.properties.statement
- : ''
- }
+
+ {!selectedNode
+ ?
+ : <>
+
{selectedNode.name}
+
+ <>
+
{t('userMemory.memoryContent')}
+
+ {['Chunk', 'Dialogue', 'MemorySummary'].includes(selectedNode.label) && 'content' in selectedNode.properties
+ ? selectedNode.properties.content
+ : selectedNode.label === 'ExtractedEntity' && 'description' in selectedNode.properties
+ ? selectedNode.properties.description
+ : selectedNode.label === 'Statement' && 'statement' in selectedNode.properties
+ ? selectedNode.properties.statement
+ : ''
+ }
+
+ >
+
+
{t('userMemory.created_at')}
+
+ {dayjs(selectedNode?.properties.created_at).format('YYYY-MM-DD HH:mm:ss')}
+
+
-
-
- {t('userMemory.created_at')}
-
- {dayjs(selectedNode?.properties.created_at).format('YYYY/MM/DD HH:mm:ss')}
-
-
- >
- }
+ >
+ }
+
- >
+
)
}
// 使用React.memo包装组件,避免不必要的渲染
diff --git a/web/src/views/UserMemoryDetail/types.ts b/web/src/views/UserMemoryDetail/types.ts
index 2c698b38..8fd050a9 100644
--- a/web/src/views/UserMemoryDetail/types.ts
+++ b/web/src/views/UserMemoryDetail/types.ts
@@ -121,16 +121,23 @@ export interface NodeStatisticsItem {
export interface EndUser {
end_user_id: string;
id: string;
- name: string;
+ other_name: string;
position: string;
department: string;
contact: string;
phone: string;
hire_date: string | number | Dayjs | null;
+ updatetime_profile?: number;
}
export interface EndUserProfileModalRef {
handleOpen: (vo: EndUser) => void;
}
export interface MemoryInsightRef {
- getInsightReport: () => void
+ getData: () => void
+}
+export interface AboutMeRef {
+ getData: () => void
+}
+export interface EndUserProfileRef {
+ data: EndUser | null
}
\ No newline at end of file