diff --git a/web/src/api/common.ts b/web/src/api/common.ts new file mode 100644 index 00000000..7637ccd3 --- /dev/null +++ b/web/src/api/common.ts @@ -0,0 +1 @@ +import { request } from "@/utils/request"; diff --git a/web/src/api/knowledgeBase.ts b/web/src/api/knowledgeBase.ts index c8e388a0..a6979b92 100644 --- a/web/src/api/knowledgeBase.ts +++ b/web/src/api/knowledgeBase.ts @@ -285,3 +285,14 @@ export const getRetrievalModeType = async () => { const response = await request.get(`${apiPrefix}/chunks/retrieve_type`); return response as any; }; + +// 获取知识库图谱 +export const getKnowledgeGraph = async (kb_id: string) => { + const response = await request.get(`${apiPrefix}/knowledges/${kb_id}/knowledge_graph`); + return response; +}; +// 获取知识库图谱实体类型 +export const getKnowledgeGraphEntityTypes = async (query: any) => { + const response = await request.get(`${apiPrefix}/knowledges/knowledge_graph_entity_types`,query); + return response ; +}; \ No newline at end of file diff --git a/web/src/assets/images/index/apps.svg b/web/src/assets/images/index/apps.svg new file mode 100644 index 00000000..58907fd6 --- /dev/null +++ b/web/src/assets/images/index/apps.svg @@ -0,0 +1,18 @@ + + + 编组 34 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/arrow_down.svg b/web/src/assets/images/index/arrow_down.svg new file mode 100644 index 00000000..b77a3f8a --- /dev/null +++ b/web/src/assets/images/index/arrow_down.svg @@ -0,0 +1,17 @@ + + + 箭头_向上 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/arrow_down_d.svg b/web/src/assets/images/index/arrow_down_d.svg new file mode 100644 index 00000000..40e5d94b --- /dev/null +++ b/web/src/assets/images/index/arrow_down_d.svg @@ -0,0 +1,18 @@ + + + 编组 30 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/arrow_right.svg b/web/src/assets/images/index/arrow_right.svg new file mode 100644 index 00000000..b2742d11 --- /dev/null +++ b/web/src/assets/images/index/arrow_right.svg @@ -0,0 +1,16 @@ + + + 编组 5 + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/arrow_right_blue.svg b/web/src/assets/images/index/arrow_right_blue.svg new file mode 100644 index 00000000..e83701f1 --- /dev/null +++ b/web/src/assets/images/index/arrow_right_blue.svg @@ -0,0 +1,16 @@ + + + 编组 5 + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/arrow_up.svg b/web/src/assets/images/index/arrow_up.svg new file mode 100644 index 00000000..62aeee96 --- /dev/null +++ b/web/src/assets/images/index/arrow_up.svg @@ -0,0 +1,17 @@ + + + 箭头_向上 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/arrow_up_d.svg b/web/src/assets/images/index/arrow_up_d.svg new file mode 100644 index 00000000..3c19fef3 --- /dev/null +++ b/web/src/assets/images/index/arrow_up_d.svg @@ -0,0 +1,18 @@ + + + 编组 30 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/data_export.svg b/web/src/assets/images/index/data_export.svg new file mode 100644 index 00000000..ba9fee2e --- /dev/null +++ b/web/src/assets/images/index/data_export.svg @@ -0,0 +1,22 @@ + + + 编组 23 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/guide_bg@2x.png b/web/src/assets/images/index/guide_bg@2x.png new file mode 100644 index 00000000..3b7490fb Binary files /dev/null and b/web/src/assets/images/index/guide_bg@2x.png differ diff --git a/web/src/assets/images/index/help_center.svg b/web/src/assets/images/index/help_center.svg new file mode 100644 index 00000000..6d272121 --- /dev/null +++ b/web/src/assets/images/index/help_center.svg @@ -0,0 +1,21 @@ + + + 编组 17 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/index_bg@2x.png b/web/src/assets/images/index/index_bg@2x.png new file mode 100644 index 00000000..d20ee4d3 Binary files /dev/null and b/web/src/assets/images/index/index_bg@2x.png differ diff --git a/web/src/assets/images/index/log_mgt.svg b/web/src/assets/images/index/log_mgt.svg new file mode 100644 index 00000000..66712671 --- /dev/null +++ b/web/src/assets/images/index/log_mgt.svg @@ -0,0 +1,22 @@ + + + 编组 21 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/model_mgt.svg b/web/src/assets/images/index/model_mgt.svg new file mode 100644 index 00000000..89e13ec3 --- /dev/null +++ b/web/src/assets/images/index/model_mgt.svg @@ -0,0 +1,30 @@ + + + 编组 25 + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/models.svg b/web/src/assets/images/index/models.svg new file mode 100644 index 00000000..890f240a --- /dev/null +++ b/web/src/assets/images/index/models.svg @@ -0,0 +1,19 @@ + + + 编组 14 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/note_mgt.svg b/web/src/assets/images/index/note_mgt.svg new file mode 100644 index 00000000..5104a37a --- /dev/null +++ b/web/src/assets/images/index/note_mgt.svg @@ -0,0 +1,21 @@ + + + 编组 20 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/space_mgt.svg b/web/src/assets/images/index/space_mgt.svg new file mode 100644 index 00000000..af1db66c --- /dev/null +++ b/web/src/assets/images/index/space_mgt.svg @@ -0,0 +1,20 @@ + + + 编组 26 + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/spaces.svg b/web/src/assets/images/index/spaces.svg new file mode 100644 index 00000000..1c61bc6b --- /dev/null +++ b/web/src/assets/images/index/spaces.svg @@ -0,0 +1,26 @@ + + + 编组 32 + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/user_mgt.svg b/web/src/assets/images/index/user_mgt.svg new file mode 100644 index 00000000..d53a97b9 --- /dev/null +++ b/web/src/assets/images/index/user_mgt.svg @@ -0,0 +1,22 @@ + + + 编组 24 + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/users.svg b/web/src/assets/images/index/users.svg new file mode 100644 index 00000000..545d9636 --- /dev/null +++ b/web/src/assets/images/index/users.svg @@ -0,0 +1,18 @@ + + + 编组 33 + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/assets/images/index/workflow_mgt.svg b/web/src/assets/images/index/workflow_mgt.svg new file mode 100644 index 00000000..8d63dae4 --- /dev/null +++ b/web/src/assets/images/index/workflow_mgt.svg @@ -0,0 +1,21 @@ + + + 编组 29 + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index 971e37b1..7f83e352 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -3,6 +3,58 @@ export const en = { welcome: 'Welcome to React Font CLI', title: 'Memory Bear.AI ', memoryBear: 'Memory Bear.AI', + index:{ + viewGuide: 'View Guide', + watchVideo: 'Watch Video', + viewDetails: 'View Details', + changeLog: 'Change Log', + latestUpdate: 'Latest Update', + latestUpdateDesc: 'Version v0.2.0 release: Added visual workflow editor, model performance monitoring panel, and multi tenant permission management system.', + getStarted: 'Getting Started', + startedDesc: 'Understand the core functions of the platform and quickly get started through graphic guidance and video tutorials. Includes a full process demonstration from creating a space to publishing an application.', + spaceTitle:'Memory Bear Intelligent Space Management Platform', + spaceSubTitle: 'Making it easier to implement intelligent models - a one-stop platform for model management, knowledge building, workflow orchestration, and spatial operations', + }, + quickActions:{ + title: 'Quick Actions', + spaceManagement: 'Space Management', + modelManagement: 'Model Management', + workflowOrchestration: 'Workflow Orchestration', + userManagement: 'User Management', + dataExport: 'Data Export', + logQuery:'Log Query', + notificationReminder: 'Notification Reminder', + helpCenter: 'Help Center', + knowledgeBase: 'Knowledge Base', + knowledgeBaseDesc: 'Manage and maintain your knowledge base data', + modelManagementDesc: 'Configure and optimize AI model parameters', + userManagementDesc: 'Manage system users and permission settings', + systemSettings: 'System Settings', + systemSettingsDesc: 'Configure global system parameters and options', + memoryManagement: 'Memory Management', + memoryManagementDesc: 'Manage user memories and conversation history', + apiManagement: 'API Management', + apiManagementDesc: 'Manage API keys and interface configurations', + workflowEngine: 'Workflow Engine', + workflowEngineDesc: 'Design and manage automated workflows', + performanceMonitor: 'Performance Monitor', + performanceMonitorDesc: 'Monitor system performance and operational status', + }, + guide: { + quickStart: 'Quick Start', + createKnowledge: 'Create Knowledge Base', + createKnowledgeDesc: 'Build your exclusive knowledge base to help AI better understand your business', + manageModel: 'Manage Models', + manageModelDesc: 'Configure and manage your AI models to optimize performance', + createSpace: 'Create Space', + createSpaceDesc: 'Create new workspaces to organize your projects and teams', + systemConfig: 'System Configuration', + systemConfigDesc: 'Configure system parameters to personalize your experience', + startCreate: 'Start Creating', + goManage: 'Go Manage', + createNow: 'Create Now', + goConfig: 'Go Configure', + }, menu: { home: 'Home', tenantManagement: 'Tenant Management', @@ -40,12 +92,22 @@ export const en = { selfReflectionEngine: 'Self Reflection Engine', }, dashboard: { + total_models: 'Total number of available models', + total_spaces: 'Number of active spaces', + total_users: 'Total number of users', + total_apps_runs: 'Number of application runs', + desc_models: 'Contains {{ account }} LLMs and {{ nums }} Embeddings', + desc_spaces: 'more than yesterday', + desc_users: 'New additions this week', + desc_apps_runs: "Today's success rate", totalMemoryCapacity: 'Total Memory Capacity', userMemory: 'User Memory', knowledgeBaseCount: 'Knowledge Base Count', apiCallCount: 'API Call Count', comparedToYesterday: 'compared to yesterday', thisWeek: 'this week', + thisDay: 'day on day', + failureRate: 'Failure Rate', application: 'Application Count', total_num: 'Total Memory Capacity', @@ -625,6 +687,37 @@ export const en = { fileDurationExceeds: 'File duration exceeds the limit', fileDurationLimitError: 'The duration of the media file exceeds the limit. The maximum supported duration is 150 seconds. Current duration', unableReadFile:'Unable to read the information of the media file. Please check the file format.', + // Knowledge Graph related + knowledgeGraph: 'Knowledge Graph', + basicConfig: 'Basic Configuration', + enableKnowledgeGraph: 'Enable Knowledge Graph', + enableKnowledgeGraphTips: 'Once activated, it will automatically construct an entity relationship network.', + graphConfig: 'Graph Configuration', + sceneName: 'Scene Name', + sceneNamePlaceholder: 'Please enter scene name', + entityTypes: 'Entity Types', + entityTypesPlaceholder: 'Please enter entity types, separate multiple types with line breaks', + entityNormalization: 'Entity Normalization', + entityNormalizationTips: 'Merge similar entities when enabled', + entityMethod: 'Entity Method', + entityMethodGeneral: 'General', + entityMethodLight: 'Light', + communityReportGeneration: 'Community Report Generation', + communityReportGenerationTips: 'Generate community analysis reports when enabled', + generateEntityTypes: 'Generate Entity Types', + regenerateEntityTypes: 'Regenerate', + generateEntityTypesSuccess: 'Entity types generated successfully', + generateEntityTypesFailed: 'Failed to generate entity types', + unknownError: 'Unknown error', + pleaseSelectLLMModel: 'Please select a LLM model in basic configuration first', + enterScenarioName: 'Please enter scenario name', + entityDetails: 'Entity Details', + entityDetailEmpty: 'Click on a node in the graph to view details', + entityDetailEmptyDesc: 'Select an entity node to view its detailed information', + entityDescription: 'Entity Description', + graphTitle: 'Knowledge Graph: The Network of Entity, Relationship and Attribute Associations', + graphTips: 'Explore the entity nodes in the knowledge base and their relationship networks', + sourceDocuments: 'Source Documents', createForm:{ name: 'Name', embedding_id: 'Embedding', diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 0ecb011c..f261ba15 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -3,6 +3,58 @@ export const zh = { title: '记忆熊', memoryBear: '记忆熊', welcome: '欢迎使用 React Font CLI', + index:{ + viewGuide: '查看引导', + watchVideo: '观看视频', + viewDetails: '查看详情', + changeLog: '变更日志', + getStarted:'快速开始', + latestUpdate: '最新更新', + latestUpdateDesc: '版本 v0.2.0 发布:新增了可视化工作流编辑器、模型性能监控面板以及多租户权限管理系统。', + startedDesc: '了解该平台的核心功能,并通过图形指引和视频教程快速上手。包含从创建空间到发布应用程序的整个操作流程演示。', + spaceTitle:'记忆熊智能空间管理平台', + spaceSubTitle: '使智能模型的实施变得更加容易——一个集模型管理、知识构建、工作流程编排以及空间操作于一体的综合性平台', + }, + quickActions:{ + title: '快速操作', + spaceManagement: '空间管理', + modelManagement: '模型管理', + workflowOrchestration: '工作流编排', + userManagement: '用户管理', + dataExport: '数据导出', + logQuery:'日志查询', + notificationReminder: '通知提醒', + helpCenter: '帮助中心', + knowledgeBase: '知识库', + knowledgeBaseDesc: '管理和维护您的知识库数据', + modelManagementDesc: '配置和优化AI模型参数', + userManagementDesc: '管理系统用户和权限设置', + systemSettings: '系统设置', + systemSettingsDesc: '配置系统全局参数和选项', + memoryManagement: '记忆管理', + memoryManagementDesc: '管理用户记忆和对话历史', + apiManagement: 'API管理', + apiManagementDesc: '管理API密钥和接口配置', + workflowEngine: '工作流引擎', + workflowEngineDesc: '设计和管理自动化工作流程', + performanceMonitor: '性能监控', + performanceMonitorDesc: '监控系统性能和运行状态', + }, + guide: { + quickStart: '快速开始', + createKnowledge: '创建知识库', + createKnowledgeDesc: '构建您的专属知识库,让AI更好地理解您的业务', + manageModel: '管理模型', + manageModelDesc: '配置和管理您的AI模型,优化性能表现', + createSpace: '创建空间', + createSpaceDesc: '创建新的工作空间,组织您的项目和团队', + systemConfig: '系统配置', + systemConfigDesc: '配置系统参数,个性化您的使用体验', + startCreate: '开始创建', + goManage: '去管理', + createNow: '立即创建', + goConfig: '去配置', + }, menu: { home: '首页', tenantManagement: '租户管理', @@ -247,6 +299,37 @@ export const zh = { fileDurationExceeds:'文件时长超过限制', fileDurationLimitError: '媒体文件时长超过限制,最大支持150秒,当前时长', unableReadFile:'无法读取媒体文件信息,请检查文件格式', + // 知识图谱相关 + knowledgeGraph: '知识图谱', + basicConfig: '基础配置', + enableKnowledgeGraph: '启用知识图谱', + enableKnowledgeGraphTips: '开启后将自动构建实体关系网络', + graphConfig: '图谱配置', + sceneName: '场景名称', + sceneNamePlaceholder: '请输入场景名称', + entityTypes: '实体类型', + entityTypesPlaceholder: '请输入实体类型,多个类型用换行分隔', + entityNormalization: '实体归一化', + entityNormalizationTips: '开启后将对相似实体进行合并处理', + entityMethod: '实体方法', + entityMethodGeneral: '通用', + entityMethodLight: '轻量', + communityReportGeneration: '社区报告生成', + communityReportGenerationTips: '开启后将生成社区分析报告', + generateEntityTypes: '生成实体类型', + regenerateEntityTypes: '重新生成', + generateEntityTypesSuccess: '实体类型生成成功', + generateEntityTypesFailed: '生成实体类型失败', + unknownError: '未知错误', + pleaseSelectLLMModel: '请先在基础配置中选择大语言模型', + enterScenarioName: '请输入场景名称', + entityDetails: '实体详情', + entityDetailEmpty: '请点击图谱中的节点查看详情', + entityDetailEmptyDesc: '选择一个实体节点来查看其详细信息', + entityDescription: '实体描述', + sourceDocuments: '来源文档', + graphTitle: '知识图谱:实体、关系与属性的关联网络', + graphTips: '探索知识库中的实体节点及其关系脉络', createForm: { name: '名称', embedding_id: '嵌入模型', @@ -581,12 +664,22 @@ export const zh = { totalRecords: '共 {{total}} 条记录' }, dashboard: { + total_models: '可用模型总数', + total_spaces: '活跃空间数量', + total_users: '用户总数', + total_apps_runs: '应用运行次数', + desc_models: '包含 {{ account }} 个大语言模型和 {{ nums }} 个嵌入模型', + desc_spaces: '比昨天多', + desc_users: '本周新增', + desc_apps_runs: '今日成功率', totalMemoryCapacity: '总记忆容量', userMemory: '用户记忆', knowledgeBaseCount: '知识库数量', apiCallCount: 'API调用次数', comparedToYesterday: '与昨天相比', thisWeek: '本周', + thisDay: '本日', + failureRate: '故障率', application: '应用数量', total_num: '总记忆容量', diff --git a/web/src/routes/index.tsx b/web/src/routes/index.tsx index 06032d5b..7d480599 100644 --- a/web/src/routes/index.tsx +++ b/web/src/routes/index.tsx @@ -35,6 +35,7 @@ const componentMap: Record>> = BasicLayout: lazy(() => import('@/components/Layout/BasicLayout')), LoginLayout: lazy(() => import('@/components/Layout/LoginLayout')), // 视图组件 + Index: lazy(() => import('@/views/Index')), Home: lazy(() => import('@/views/Home')), UserMemory: lazy(() => import('@/views/UserMemory')), UserMemoryDetail: lazy(() => import('@/views/UserMemoryDetail')), diff --git a/web/src/routes/routes.json b/web/src/routes/routes.json index d4f6a12e..44df2b93 100644 --- a/web/src/routes/routes.json +++ b/web/src/routes/routes.json @@ -2,6 +2,7 @@ { "element": "AuthLayout", "children": [ + { "path": "/index", "element": "Index" }, { "path": "/user-management", "element": "UserManagement" }, { "path": "/model", "element": "ModelManagement" }, { "path": "/space", "element": "SpaceManagement" }, diff --git a/web/src/store/menu.json b/web/src/store/menu.json index ce6486ad..274ec6e1 100644 --- a/web/src/store/menu.json +++ b/web/src/store/menu.json @@ -1,5 +1,18 @@ { "manage": [ + { + "id": 999, + "parent": 0, + "code": "dashboard", + "label": "首页", + "i18nKey": "menu.home", + "path": "/index", + "enable": true, + "display": true, + "level": 1, + "sort": 0, + "subs": [] + }, { "id": 1, "parent": 0, diff --git a/web/src/views/Index/components/GuideCard.tsx b/web/src/views/Index/components/GuideCard.tsx new file mode 100644 index 00000000..3b61ae72 --- /dev/null +++ b/web/src/views/Index/components/GuideCard.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import guideBgImg from '@/assets/images/index/guide_bg@2x.png' +import { Button } from 'antd'; +import { ArrowRightOutlined } from '@ant-design/icons' +import arrowRight from '@/assets/images/index/arrow_right_blue.svg' +const GuideCard: React.FC = () => { + const { t } = useTranslation(); + + return ( +
+
+ { t('index.getStarted')} +
+
+ { t('index.startedDesc')} +
+
+ + +
+
+ ); +}; + +export default GuideCard; \ No newline at end of file diff --git a/web/src/views/Index/components/QuickActions.tsx b/web/src/views/Index/components/QuickActions.tsx new file mode 100644 index 00000000..7cdb90ec --- /dev/null +++ b/web/src/views/Index/components/QuickActions.tsx @@ -0,0 +1,101 @@ +import { type FC } from 'react'; +import { useTranslation } from 'react-i18next'; + +import modelIcon from '@/assets/images/index/model_mgt.svg' +import spaceIcon from '@/assets/images/index/space_mgt.svg' +import workflowIcon from '@/assets/images/index/workflow_mgt.svg' +import userIcon from '@/assets/images/index/user_mgt.svg' +import dataExportIcon from '@/assets/images/index/data_export.svg' +import logIcon from '@/assets/images/index/log_mgt.svg' +import noteIcon from '@/assets/images/index/note_mgt.svg' +import helpCenterIcon from '@/assets/images/index/help_center.svg' +interface QuickAction { + key: string; + icon: string; + title: string; + onClick?: () => void; +} + +interface QuickActionsProps { + className?: string; + onNavigate?: (path: string) => void; +} + +const QuickActions: FC = ({ onNavigate }) => { + const { t } = useTranslation(); + + const quickActions: QuickAction[] = [ + { + key: 'model-management', + icon: modelIcon, + title: t('quickActions.modelManagement'), + onClick: () => onNavigate?.('/model-management') + }, + { + key: 'space-management', + icon: spaceIcon, + title: t('quickActions.spaceManagement'), + onClick: () => onNavigate?.('/spce') + }, + { + key: 'workflow-orchestration', + icon: workflowIcon, + title: t('quickActions.workflowOrchestration'), + onClick: () => onNavigate?.('/workflow') + }, + { + key: 'user-management', + icon: userIcon, + title: t('quickActions.userManagement'), + onClick: () => onNavigate?.('/user-management') + }, + { + key: 'data-export', + icon: dataExportIcon, + title: t('quickActions.dataExport'), + onClick: () => onNavigate?.('/') + }, + { + key: 'log-query', + icon: logIcon, + title: t('quickActions.logQuery'), + onClick: () => onNavigate?.('/log') + }, + { + key: 'notification-reminder', + icon: noteIcon, + title: t('quickActions.notificationReminder'), + onClick: () => onNavigate?.('/notification-reminder') + }, + + { + key: 'help-center', + icon: helpCenterIcon, + title: t('quickActions.helpCenter'), + onClick: () => onNavigate?.('/help-center') + } + ]; + + return ( +
+
+ { t('quickActions.title') } +
+
+ + {quickActions.map((action) => ( +
+ +
+ {action.title} +
+
+ ))} +
+
); +}; + +export default QuickActions; diff --git a/web/src/views/Index/components/TopCardList/index.module.css b/web/src/views/Index/components/TopCardList/index.module.css new file mode 100644 index 00000000..46ed689c --- /dev/null +++ b/web/src/views/Index/components/TopCardList/index.module.css @@ -0,0 +1,99 @@ +.card { + border-radius: 12px; + border: 1px solid #DFE4ED; + padding: 16px; +} +.header { + line-height: 16px; + font-family: PingFangSC, PingFang SC; + font-weight: 400; + font-size: 12px; + color: #212332; + font-style: normal; + display: flex; + align-items: center; + justify-content: space-between; +} +.headerTitle { + /* Add your header title styles here */ +} +.avatar { + width: 32px; + height: 32px; + background: #FFFFFF; + box-shadow: 0px 2px 6px 0px rgba(33, 35, 50, 0.1); + border-radius: 12px; + display: flex; + align-items: center; + justify-content: center; + /* margin-right: 12px; */ +} +.avatar img { + width: 32px; + height: 32px; +} +.content { + padding: 24px 24px 8px 0; + display: flex; + justify-content: space-between; + align-items: center; + font-family: Gilroy, Gilroy; + font-weight: 800; + font-size: 28px; + color: #212332; + text-align: left; + font-style: normal; +} +.content-right { + text-align: right; + font-family: PingFangSC, PingFang SC; + font-weight: 400; + font-size: 12px; + color: #5F6266; + line-height: 16px; + font-style: normal; + row-gap: 4px; +} +.trend { + font-family: PingFangSC, PingFang SC; + font-weight: 500; + font-size: 14px; + line-height: 16px; + font-style: normal; + padding-left: 15px; + position: relative; + margin-bottom: 4px; + display: inline-block; +} +.trend::before { + width: 14px; + height: 14px; + content: ''; + position: absolute; + left: 0; + top: 1px; + background-repeat: no-repeat; + background-size: contain; +} +.trend.up { + color: #369F21; +} +.trend.up::before { + background-image: url('@/assets/images/home/arrow_up_success.svg'); +} +.trend.down { + color: #FF5D34; +} +.trend.down::before { + background-image: url('@/assets/images/home/arrow_down.png'); +} + +.trend-desc { + font-family: PingFangSC, PingFang SC; + font-weight: 500; + font-size: 14px; + color: #155EEF; + line-height: 16px; + text-align: left; + font-style: normal; +} \ No newline at end of file diff --git a/web/src/views/Index/components/TopCardList/index.tsx b/web/src/views/Index/components/TopCardList/index.tsx new file mode 100644 index 00000000..86973598 --- /dev/null +++ b/web/src/views/Index/components/TopCardList/index.tsx @@ -0,0 +1,110 @@ +import { type FC } from 'react' +import { useTranslation } from 'react-i18next' +import totalModels from '@/assets/images/index/models.svg'; +import totalSpaces from '@/assets/images/index/spaces.svg'; +import totalUsers from '@/assets/images/index/users.svg'; +import totalApps from '@/assets/images/index/apps.svg'; +import arrowUpDb from '@/assets/images/index/arrow_up_d.svg' +import arrowDownDb from '@/assets/images/index/arrow_down_d.svg' +import arrowUp from '@/assets/images/index/arrow_up.svg' +import arrowDown from '@/assets/images/index/arrow_down.svg' +import styles from './index.module.css' +import type { DashboardData } from '../../types' + +const list = [ + { + key: 'models', + icon: totalModels, + value: '24', + // trendValue: '12.5%', + trend: 'up', + // trendDesc: 'comparedToYesterday', + rate:"up", + rateValue: '12%', + background: 'linear-gradient( 136deg, rgba(21,94,239,0.06) 0%, rgba(251,253,255,0) 100%)' + }, + { + key: 'spaces', + icon: totalSpaces, + value: '156', + trendValue: '+8', + trend: 'down', + rate:"up", + rateValue: '5.4%', + // trendDesc: 'comparedToYesterday', + background: 'linear-gradient( 134deg, rgba(54,159,33,0.06) 0%, rgba(251,253,255,0) 100%)', + }, + { + key: 'users', + icon: totalUsers, + value: '1,248', + trendValue: '+42', + trend: 'up', + rate:"up", + rateValue: '12%', + // trendDesc: 'thisWeek', + background: 'linear-gradient( 136deg, rgba(77,168,255,0.06) 0%, rgba(251,253,255,0) 100%)', + }, + { + key: 'apps_runs', + icon: totalApps, + value: '12.8k', + trendValue: '98.7%', + trend: 'up', + rate:"down", + rateValue: '2.1%', + // trendDesc: 'comparedToYesterday', + background: 'linear-gradient( 136deg, rgba(156,111,255,0.06) 0%, rgba(251,253,255,0) 100%)', + }, +] +const TopCardList: FC<{data?: DashboardData}> = ({ data }) => { + const { t } = useTranslation() + return ( +
+ {list.map((item) => { + return ( +
+
+
{t(`dashboard.${'total_' + item.key}`)}
+
+
+ +
+ {data?.[`total_${item.key}` as keyof DashboardData] || item.value || 0} +
+
+ {item.key === 'models' ? ( +
+ {t(`dashboard.${'desc_' + item.key}`, { account: 18, nums: 6 })} +
+ ) : (<> +
+ + {item.trendValue} +
+
+ {t(`dashboard.${'desc_' + item.key}`)} +
+ )} +
+
+ + {item.rateValue} + {(item.key === 'models' || item.key === 'users') && ({t('dashboard.thisWeek')})} + {item.key === 'apps_runs' && ({t('dashboard.failureRate')})} + {item.key === 'spaces' && ({t('dashboard.thisDay')})} +
+
+ ) + })} +
+ ) +} + +export default TopCardList \ No newline at end of file diff --git a/web/src/views/Index/components/VersionCard.tsx b/web/src/views/Index/components/VersionCard.tsx new file mode 100644 index 00000000..3967dac4 --- /dev/null +++ b/web/src/views/Index/components/VersionCard.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; +import { Button } from 'antd'; +import arrowRight from '@/assets/images/index/arrow_right.svg' +const GuideCard: React.FC = () => { + const { t } = useTranslation(); + + return ( +
+
+ { t('index.latestUpdate')} +
+
+ { t('index.latestUpdateDesc')} +
+
+ + +
+
+ ); +}; + +export default GuideCard; \ No newline at end of file diff --git a/web/src/views/Index/index.tsx b/web/src/views/Index/index.tsx new file mode 100644 index 00000000..759bbed2 --- /dev/null +++ b/web/src/views/Index/index.tsx @@ -0,0 +1,121 @@ +import { useEffect, useState, useRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { Row, Col, Space, Button } from 'antd'; +import TopCardList from './components/TopCardList'; +import GuideCard from './components/GuideCard'; +import VersionCard from './components/VersionCard'; +import QuickActions from './components/QuickActions'; +import bgImg from '@/assets/images/index/index_bg@2x.png' +import type { DashboardData } from './types'; +import Table, { type TableRef } from '@/components/Table' +import type { ColumnsType } from 'antd/es/table'; +import { formatDateTime } from '@/utils/format'; +const Index = () => { + const { t } = useTranslation(); + const [dashboardData, setDashboardData] = useState({ + total_models: 24, + total_spaces: 156, + total_users: 1248, + total_apps_runs: '12.8k', + }); + const tableRef = useRef(null); + const tableApi = '/workspaces'; + const [loading, setLoading] = useState({ + knowledgeTypeDistribution: true, + }); + const [knowledgeTypeDistribution, setKnowledgeTypeDistribution] = useState>([]); + const [memoryIncrement, setMemoryIncrement] = useState>([]); + const [limit, setLimit] = useState(7); + const columns: ColumnsType = [ + { + title: t('space.spaceName'), + dataIndex: 'name', + key: 'name', + }, + // { + // title: t('space.associated') + ' ' + t('memorySummary.user'), + // dataIndex: 'name', + // key: 'name', + // }, + { + title: t('space.spaceIcon'), + dataIndex: 'icon', + key: 'icon', + render:(value:string) => { + return( + icon + ) + } + }, + { + title: t('apiKey.createdAt'), + dataIndex: 'created_at', + key: 'created_at', + render:(value:string) => { + return( + {formatDateTime(Number(value) * 1000 ,'YYYY-MM-DD HH:mm:ss')} + ) + } + }, + { + title: t('common.operation'), + key: 'action', + fixed: 'right', + width: 100, + render: (_, record) => ( + + + + ), + }, + ] + // 模拟API获取数据 + useEffect(() => { + tableRef.current?.loadData(); + }, [tableApi]); + + + + return ( +
+ + +
+
+ { t('index.spaceTitle' )} +
+
+ { t('index.spaceSubTitle' )} +
+
+ {/* 统计卡片 */} + +
+ + + + + {/* 引导 */} + +
+ +
+ {/* 快捷操作 */} +
+ +
+ + + + + + ); +} + +export default Index \ No newline at end of file diff --git a/web/src/views/Index/types.ts b/web/src/views/Index/types.ts new file mode 100644 index 00000000..437c54e3 --- /dev/null +++ b/web/src/views/Index/types.ts @@ -0,0 +1,13 @@ +export interface TopCardListProps { + title:string; + description:string; + icon: Element; + number: number; + label: string; +} +export interface DashboardData { + total_models:number; + total_spaces:number; + total_users:number; + total_apps_runs: string; +} \ No newline at end of file diff --git a/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx b/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx index 136dc615..65ca05e8 100644 --- a/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx +++ b/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx @@ -2,18 +2,18 @@ import { useEffect, useState, useRef, useCallback, type FC } from 'react'; import { useNavigate, useParams, useLocation } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; -import { Switch, Button, Dropdown, Space, Modal, message } from 'antd'; +import { Switch, Button, Dropdown, Space, Modal, message, Radio } from 'antd'; import type { MenuProps } from 'antd'; import SearchInput from '@/components/SearchInput' import Table, { type TableRef } from '@/components/Table' import type { ColumnsType } from 'antd/es/table'; import type { AnyObject } from 'antd/es/_util/type'; -import { MoreOutlined } from '@ant-design/icons'; +import { MoreOutlined, DeploymentUnitOutlined, BarsOutlined } from '@ant-design/icons'; import folderIcon from '@/assets/images/knowledgeBase/folder.png'; import textIcon from '@/assets/images/knowledgeBase/text.png'; import editIcon from '@/assets/images/knowledgeBase/edit.png'; -import blankIcon from '@/assets/images/knowledgeBase/blankDocument.png'; -import imageIcon from '@/assets/images/knowledgeBase/image.png' +// import blankIcon from '@/assets/images/knowledgeBase/blankDocument.png'; +// import imageIcon from '@/assets/images/knowledgeBase/image.png' import { getKnowledgeBaseDetail, deleteDocument, downloadFile, updateKnowledgeBase } from '@/api/knowledgeBase'; import { type CreateModalRef, @@ -22,8 +22,10 @@ import { type CreateFolderModalRef, type CreateSetModalRef, type ShareModalRef, - type CreateDatasetModalRef,type FolderFormData, - type KnowledgeBaseDocumentData, + type CreateDatasetModalRef, + type FolderFormData, + type KnowledgeBaseDocumentData, + type KnowledgeBaseFormData, } from '@/views/KnowledgeBase/types'; import RecallTestDrawer from '../components/RecallTestDrawer'; import CreateFolderModal from '../components/CreateFolderModal'; @@ -34,7 +36,7 @@ import CreateDatasetModal from '../components/CreateDatasetModal'; import CreateImageDataset from '../components/CreateImageDataset'; import FolderTree, { type TreeNodeData } from '../components/FolderTree'; import { formatDateTime } from '@/utils/format'; - +import KnowledgeGraphCard from '../components/KnowledgeGraphCard'; import { useBreadcrumbManager, type BreadcrumbItem } from '@/hooks/useBreadcrumbManager'; import './Private.css' const { confirm } = Modal @@ -68,7 +70,7 @@ const Private: FC = () => { const datasetModalRef = useRef(null); const [folderTreeRefreshKey, setFolderTreeRefreshKey] = useState(0); const [autoExpandPath, setAutoExpandPath] = useState>([]); - + const [isGraph, setIsGraph] = useState(false); const { updateBreadcrumbs } = useBreadcrumbManager({ breadcrumbType: 'detail', // 不提供 onKnowledgeBaseMenuClick,让它使用默认的导航行为(返回列表页面) @@ -376,9 +378,37 @@ const Private: FC = () => { // 处理开关 const onChange = (checked: boolean) => { - updateKnowledgeBase(knowledgeBaseId || '', { + if (!knowledgeBase) return; + + // 构造完整的更新数据,保留现有配置 + const updateData: KnowledgeBaseFormData = { + name: knowledgeBase.name, + description: knowledgeBase.description, + embedding_id: knowledgeBase.embedding_id, + llm_id: knowledgeBase.llm_id, + image2text_id: knowledgeBase.image2text_id, + reranker_id: knowledgeBase.reranker_id, + permission_id: knowledgeBase.permission_id, + type: knowledgeBase.type, status: checked ? 1 : 0, - }); + parser_config: knowledgeBase.parser_config || { + chunk_token_num: 512, + delimiter: '\n', + auto_keywords: 0, + auto_questions: 0, + html4excel: false, + graphrag: { + use_graphrag: false, + scene_name: '', + entity_types: '', + method: '', + resolution: false, + community: false + } + } + }; + + updateKnowledgeBase(knowledgeBaseId || '', updateData); console.log(`switch to ${checked}`); }; // 处理搜索 @@ -626,17 +656,15 @@ const Private: FC = () => { } const handleRefreshTable = () => { - debugger // 刷新表格数据 tableRef.current?.loadData(); } - return ( <> {contextHolder}
{folder && ( -
+
{
+ setIsGraph(e.target.value)}> + + + + + + + @@ -688,14 +724,18 @@ const Private: FC = () => {
-
} - columns={columns} - rowKey="id" - scrollX={1500} - /> + {isGraph ? ( + + ) : ( +
} + columns={columns} + rowKey="id" + scrollX={1500} + /> + )} (({ refreshTable }, ref) => { const { t } = useTranslation(); + const [messageApi, contextHolder] = message.useMessage(); const [visible, setVisible] = useState(false); const [modelTypeList, setModelTypeList] = useState([]); const [modelOptionsByType, setModelOptionsByType] = useState>({}); const [datasets, setDatasets] = useState(null); const [currentType, setCurrentType] = useState<'General' | 'Web' | 'Third-party' | 'Folder'>('General'); const [form] = Form.useForm(); - const [loading, setLoading] = useState(false) + const [loading, setLoading] = useState(false); + const [activeTab, setActiveTab] = useState('basic'); + const [generatingEntityTypes, setGeneratingEntityTypes] = useState(false); + + // 监听 parser_config.graphrag 相关字段的变化 + const parserConfig = Form.useWatch('parser_config', form); + const graphragConfig = parserConfig?.graphrag; + const enableKnowledgeGraph = graphragConfig?.use_graphrag || false; + const entityTypes = graphragConfig?.entity_types || ''; + const entityNormalization = graphragConfig?.resolution || false; + const communityReportGeneration = graphragConfig?.community || false; // 封装取消方法,添加关闭弹窗逻辑 const handleClose = () => { setDatasets(null); form.resetFields(); - setLoading(false) + setLoading(false); + setActiveTab('basic'); setVisible(false); }; + // 生成实体类型的函数 + const generateEntityTypes = async () => { + const sceneName = form.getFieldValue(['parser_config', 'graphrag', 'scene_name']); + if (!sceneName) { + // 可以添加提示用户输入场景名称 + messageApi.error(t('knowledgeBase.enterScenarioName')); + return; + } + + // 检查是否选择了 LLM 模型 + const llmId = form.getFieldValue('llm_id'); + if (!llmId) { + // 跳转到基础配置页 + setActiveTab('basic'); + messageApi.error(t('knowledgeBase.pleaseSelectLLMModel')); + return; + } + + setGeneratingEntityTypes(true); + try { + // 这里应该调用实际的API接口 + // const user = JSON.parse(localStorage.getItem('user') as any); + //datasets?.id || datasets?.parent_id || user?.current_workspace_id, + const params = { + scenario: sceneName, + llm_id: llmId + }; + const response = await getKnowledgeGraphEntityTypes(params); + // 模拟API调用 + // await new Promise(resolve => setTimeout(resolve, 1000)); + + // 处理API响应数据 + console.log('API Response:', response); // 调试日志 + + // 检查响应结构 - API直接返回字符串 + if (response && typeof response === 'string' && response.trim()) { + // 将逗号分隔的字符串转换为换行分隔的格式以便在TextArea中显示 + const entityTypesString = response.replace(/,\s*/g, '\n'); + console.log('Converted entity types:', entityTypesString); // 调试日志 + + const currentGraphrag = form.getFieldValue(['parser_config', 'graphrag']) || {}; + const updatedGraphrag = { + ...currentGraphrag, + entity_types: entityTypesString + }; + + console.log('Updating form with:', updatedGraphrag); // 调试日志 + + // 使用更直接的方式更新表单字段 + form.setFieldValue(['parser_config', 'graphrag', 'entity_types'], entityTypesString); + + // 强制触发表单重新渲染 + form.validateFields([['parser_config', 'graphrag', 'entity_types']]); + + // 额外的强制更新机制 + setTimeout(() => { + form.setFieldValue(['parser_config', 'graphrag', 'entity_types'], entityTypesString); + }, 100); + + messageApi.success(t('knowledgeBase.generateEntityTypesSuccess')); + } else { + messageApi.error(t('knowledgeBase.generateEntityTypesFailed') + ':' + t('knowledgeBase.unknownError')); + } + } catch (error) { + console.error(t('knowledgeBase.generateEntityTypesFailed') + ':', error); + } finally { + setGeneratingEntityTypes(false); + } + }; + const typeToFieldKey = (type: string): string => { switch ((type || '').toLowerCase()) { case 'embedding': @@ -89,6 +177,30 @@ const CreateModal = forwardRef(({ type: type || record.type || currentType, status: record.status, }; + + // 处理 parser_config 配置数据,如果没有则设置默认值 + baseValues.parser_config = record.parser_config || { + graphrag: { + use_graphrag: false, + scene_name: '', + entity_types: [] as any, + method: 'general', + resolution: false, + community: false, + } + }; + + // 如果存在 entity_types,转换为换行分隔格式用于 TextArea 显示 + if (baseValues.parser_config.graphrag.entity_types) { + if (Array.isArray(baseValues.parser_config.graphrag.entity_types)) { + // 如果是数组格式,转换为换行分隔字符串 + (baseValues.parser_config.graphrag as any).entity_types = baseValues.parser_config.graphrag.entity_types.join('\n'); + } else if (typeof baseValues.parser_config.graphrag.entity_types === 'string') { + // 如果是逗号分隔字符串格式,转换为换行分隔字符串(兼容旧数据) + (baseValues.parser_config.graphrag as any).entity_types = (baseValues.parser_config.graphrag.entity_types as string).replace(/,\s*/g, '\n'); + } + } + form.setFieldsValue(baseValues); }; @@ -142,12 +254,26 @@ const CreateModal = forwardRef(({ .then(() => { setLoading(true) const formValues = form.getFieldsValue(); + + // 处理 entity_types 格式转换:从换行分隔字符串转换为字符串数组 + if (formValues.parser_config && formValues.parser_config.graphrag && formValues.parser_config.graphrag.entity_types) { + const entityTypesString = formValues.parser_config.graphrag.entity_types as any as string; + const entityTypesArray = entityTypesString + .split('\n') + .map((item: string) => item.trim()) + .filter((item: string) => item.length > 0); + formValues.parser_config.graphrag.entity_types = entityTypesArray; + } + const payload: KnowledgeBaseFormData = { ...formValues, type: formValues.type || currentType, permission_id: formValues.permission_id || 'Private', parent_id: datasets?.parent_id || undefined, }; + + console.log('Saving payload:', payload); // 调试日志 + const submit = datasets?.id ? updateKnowledgeBase(datasets.id, payload) : createKnowledgeBase(payload); @@ -205,6 +331,192 @@ const CreateModal = forwardRef(({ const dynamicTypeList = useMemo(() => modelTypeList.filter((tp) => (modelOptionsByType[tp] || []).length), [modelTypeList, modelOptionsByType]); + // 基础配置表单内容 + const renderBasicConfig = () => ( + <> + {!datasets?.id && ( + + + + )} + +