diff --git a/.gitignore b/.gitignore index c2ef52c6..23425d9b 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,6 @@ time.log celerybeat-schedule.db search_results.json *.txt -*.json migrations/versions tmp files diff --git a/api/README.md b/api/README.md index 7f0846b9..ea014bf4 100644 --- a/api/README.md +++ b/api/README.md @@ -60,6 +60,13 @@ LOG_LEVEL=INFO # 如需自动迁移数据库:设置 DB_AUTO_UPGRADE=true 或手动执行 alembic upgrade head +# 激活虚拟环境 +api\.venv\Scripts\activate + +# 目录切换到api下 +cd api + + # 启动开发服务 uvicorn app.main:app --reload --port 8000 diff --git a/api/app/core/memory/agent/utils/multimodal.py b/api/app/core/memory/agent/utils/multimodal.py index 5beaf892..439c46b7 100644 --- a/api/app/core/memory/agent/utils/multimodal.py +++ b/api/app/core/memory/agent/utils/multimodal.py @@ -7,8 +7,8 @@ This module provides utilities for detecting and processing multimodal inputs import logging from typing import List - -from app.core.memory.agent.multimodal.speech_model import Vico_recognition +# TODO 后续更新 +# from app.core.memory.agent.multimodal.speech_model import Vico_recognition from app.core.memory.agent.utils.llm_tools import picture_model_requests logger = logging.getLogger(__name__) diff --git a/api/app/core/memory/main.py b/api/app/core/memory/main.py index c4fd043a..08f91939 100644 --- a/api/app/core/memory/main.py +++ b/api/app/core/memory/main.py @@ -1,8 +1,5 @@ """ -MemSci 记忆系统主入口 - 重构版本 - -该模块是重构后的记忆系统主入口,使用新的模块化架构。 -旧版本入口(app/core/memory/src/main.py)已删除。 +MemSci 记忆系统主入口 主要功能: 1. 协调整个知识提取流水线 diff --git a/api/app/services/memory_agent_service.py b/api/app/services/memory_agent_service.py index ab9b8195..d4336703 100644 --- a/api/app/services/memory_agent_service.py +++ b/api/app/services/memory_agent_service.py @@ -29,7 +29,8 @@ from app.core.memory.agent.utils.mcp_tools import get_mcp_server_config from app.core.memory.agent.utils.type_classifier import status_typle from app.db import get_db from app.repositories.neo4j.neo4j_connector import Neo4jConnector -from app.core.memory.analytics.hot_memory_tags import get_hot_memory_tags +# TODO 后续更新 +# from app.core.memory.analytics.hot_memory_tags import get_hot_memory_tags from app.core.memory.utils.llm.llm_utils import get_llm_client from app.schemas.memory_storage_schema import ApiResponse, ok, fail from app.models.knowledge_model import Knowledge, KnowledgeType diff --git a/api/app/services/memory_storage_service.py b/api/app/services/memory_storage_service.py index daf041e7..7ba14f3d 100644 --- a/api/app/services/memory_storage_service.py +++ b/api/app/services/memory_storage_service.py @@ -23,10 +23,11 @@ from app.schemas.memory_storage_schema import ( ) from app.repositories.data_config_repository import DataConfigRepository from app.repositories.neo4j.neo4j_connector import Neo4jConnector -from app.core.memory.analytics.hot_memory_tags import get_hot_memory_tags -from app.core.memory.analytics.memory_insight import MemoryInsight -from app.core.memory.analytics.recent_activity_stats import get_recent_activity_stats -from app.core.memory.analytics.user_summary import generate_user_summary +# TODO 后续更新 +# from app.core.memory.analytics.hot_memory_tags import get_hot_memory_tags +# from app.core.memory.analytics.memory_insight import MemoryInsight +# from app.core.memory.analytics.recent_activity_stats import get_recent_activity_stats +# from app.core.memory.analytics.user_summary import generate_user_summary from app.repositories.data_config_repository import DataConfigRepository logger = get_logger(__name__) diff --git a/web/.gitignore b/web/.gitignore new file mode 100644 index 00000000..0de8ef71 --- /dev/null +++ b/web/.gitignore @@ -0,0 +1,32 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +package-lock.json + +# 文档和截图(不上传到仓库) +操作说明.md +记忆熊系统功能使用说明.md +截图清单.md +images/ \ No newline at end of file diff --git a/web/i18n-comparison-report.md b/web/i18n-comparison-report.md deleted file mode 100644 index 07e59b22..00000000 --- a/web/i18n-comparison-report.md +++ /dev/null @@ -1,205 +0,0 @@ -# i18n 中英文对比报告 - -## 📊 统计概览 - -- **中文键总数**: 1136 -- **英文键总数**: 1052 -- **中文缺失**: 27 个键 -- **英文缺失**: 111 个键 - ---- - -## ❌ 英文缺失的翻译(111个) - -### 1. Application 模块 (3个) -- `application.cluster` - 集群 -- `application.clusterDesc` - 创建Agent集群 -- `application.fullAmount` - 全量 - -### 2. Role 角色管理模块 (15个) -- `role.roleManagement` - 角色管理 -- `role.roleId` - 角色ID -- `role.roleName` - 角色名称 -- `role.roleCode` - 角色编码 -- `role.description` - 角色描述 -- `role.status` - 状态 -- `role.enabled` - 已启用 -- `role.disabled` - 已停用 -- `role.createTime` - 创建时间 -- `role.createRole` - 新建角色 -- `role.editRole` - 编辑角色 -- `role.roleTemplate` - 角色模板 -- `role.emptyTemplate` - 空模板 -- `role.adminTemplate` - 管理员模板 -- `role.userTemplate` - 用户模板 -- `role.confirmDelete` - 确定要删除这个角色吗? -- `role.createSuccess` - 角色创建成功 -- `role.updateSuccess` - 角色更新成功 -- `role.deleteSuccess` - 角色删除成功 -- `role.createFailed` - 角色创建失败 -- `role.updateFailed` - 角色更新失败 -- `role.deleteFailed` - 角色删除失败 - -### 3. Tenant 租户管理模块 (20个) -- `tenant.tenantId` - 租户ID -- `tenant.tenantName` - 租户名称 -- `tenant.contactPerson` - 联系人 -- `tenant.contactInfo` - 联系方式 -- `tenant.status` - 状态 -- `tenant.enabled` - 启用 -- `tenant.disabled` - 禁用 -- `tenant.expiryDate` - 到期时间 -- `tenant.createTenant` - 新增租户 -- `tenant.editTenant` - 编辑租户 -- `tenant.searchPlaceholder` - 搜索租户ID、名称、联系人或联系方式 -- `tenant.confirmDelete` - 确定要删除该租户吗? -- `tenant.confirmBatchDelete` - 确定要批量删除选中的租户吗? -- `tenant.fetchFailed` - 获取租户数据失败 -- `tenant.batchEnableSuccess` - 批量启用成功 -- `tenant.batchEnableFailed` - 批量启用失败 -- `tenant.batchDisableSuccess` - 批量停用成功 -- `tenant.batchDisableFailed` - 批量停用失败 -- `tenant.exportSuccess` - 导出成功 -- `tenant.batchDeleteSuccess` - 批量删除成功 -- `tenant.batchDeleteFailed` - 批量删除失败 -- `tenant.saveFailed` - 保存失败 -- `tenant.batchImport` - 批量导入 - -### 4. User 用户管理模块 (13个) -- `user.tenantName` - 所属租户 -- `user.password` - 密码 -- `user.expiryDate` - 有效期 -- `user.expiryDateDue` - 有效期至 -- `user.batchImport` - 批量导入 -- `user.batchImportUser` - 批量导入用户 -- `user.downloadTemplate` - 下载导入模板 -- `user.templateDownloadSuccess` - 模板下载成功 -- `user.startImport` - 开始导入 -- `user.batchImportSuccess` - 批量导入成功 -- `user.importFailed` - 导入失败,请检查文件格式 -- `user.noFileSelected` - 请选择要导入的文件 -- `user.onlyXlsxOrCsv` - 只能上传 .xlsx 或 .csv 格式的文件 -- `user.reselect` - 重新选择 -- `user.noFileSelectedTip` - 未选择任何文件 -- `user.downloadTemplateTip` - 请下载模板,填写用户信息后上传。 - -### 5. Product 产品管理模块 (13个) -- `product.applicationManagement` - 应用管理 -- `product.createApplication` - 创建应用 -- `product.applicationName` - 应用名称 -- `product.applicationIcon` - 应用图标 -- `product.applicationNameRequired` - 请输入应用名称 -- `product.associationStatus` - 关联状态 -- `product.associated` - 已关联 -- `product.notAssociated` - 未关联 -- `product.unassociate` - 解除关联 -- `product.unassociateSuccess` - 解除关联成功 -- `product.unassociateFailed` - 解除关联失败 -- `product.viewKey` - 查看KEY -- `product.viewStats` - 查看统计 -- `product.disableSuccess` - 停用成功 -- `product.enableSuccess` - 启用成功 -- `product.operationFailed` - 操作失败 - -### 6. 其他模块 (47个) -- `count` - 计数: {{count}} -- `increment` - 增加 -- `decrement` - 减少 -- `reset` - 重置 -- `switchLanguage` - 切换语言 -- `home.title` - 首页 -- `home.welcome` - 欢迎使用我们的带单页路由的 React 应用! -- `home.counterCard` - 计数器演示 -- `home.aboutCard` - 关于我们 -- `home.workflowCard` - 工作流编辑器 -- `home.websocketDemoCard` - WebSocket 演示 -- `home.sseDemoCard` - SSE演示 -- `workflow.title` - 工作流编辑器 -- `workflow.description` - 拖拽节点创建连接,构建您的工作流程。点击节点可进行配置。 -- `workflow.addNode` - 添加节点 -- `workflow.deleteNode` - 删除选中 -- `workflow.saveWorkflow` - 保存工作流 -- `workflow.startNode` - 触发节点 -- `workflow.conditionNode` - 条件判断 -- `workflow.actionNode` - 执行动作 -- `workflow.endNode` - 结束节点 -- `workflow.newNode` - 新节点 -- `workflow.node` - 节点 -- `workflow.nodesCreated` - 已创建节点 -- `workflow.loadingNodes` - 正在加载节点 {{progress}}% -- `workflow.loadingFailed` - 加载节点失败 -- `workflow.create5kNodes` - 创建5000节点 -- `workflow.create10kNodes` - 创建10000节点 -- `notFound.title` - 页面未找到 -- `notFound.description` - 请求的页面不存在。 -- `notFound.backToHome` - 返回首页 - ---- - -## ✅ 中文缺失的翻译(27个) - -### 1. Common 通用模块 (1个) -- `common.operateSuccess` - Operation successful - -### 2. KnowledgeBase 知识库模块 (3个) -- `knowledgeBase.models` - Model -- `knowledgeBase.owner` - Owner -- `knowledgeBase.operation` - Operation - -### 3. Application 应用模块 (15个) -- `application.multi_agent` - Cluster -- `application.multi_agentDesc` - Create an Agent Cluster -- `application.current` - Current -- `application.versionName` - Version Name -- `application.versionNameTip` - Version number format: v[major version number].[next version number].[revision number] (e.g. v1.3.0) -- `application.agentName` - Agent Name -- `application.roleType` - Role Type -- `application.coordinator` - Coordinator -- `application.analyzer` - Analyzer -- `application.executor` - Executor -- `application.reviewer` - Reviewer -- `application.updateSubAgent` - Update Sub Agent -- `application.subAgentMaxLength` - Sub Agent maximum {{maxLength}} -- `application.capabilities` - Capabilities - -### 4. Space 空间模块 (5个) -- `space.storageType` - Storage Type -- `space.rag` - RAG storage -- `space.ragDesc` - Based on vector retrieval, suitable for document Q&A and semantic search -- `space.neo4j` - Graph storage -- `space.neo4jDesc` - Based on knowledge graph, suitable for relational reasoning and path query - -### 5. MemoryExtractionEngine 记忆提取引擎模块 (4个) -- `memoryExtractionEngine.coreEntitiesAfterDedup` - Core entities after deduplication -- `memoryExtractionEngine.extractRelationalTriples` - Extracted relational triples (partial) -- `memoryExtractionEngine.extractRelationalTriplesDesc` - There are a total of {{count}} segments with clear semantic boundaries -- `memoryExtractionEngine.theEffectOfEntityDisambiguationLLMDriven` - The effect of entity disambiguation (LLM driven) - ---- - -## 🎯 建议 - -### 优先级 1 - 核心功能模块(需要立即补充) -1. **Role 角色管理** - 完整模块缺失(15个键) -2. **Tenant 租户管理** - 完整模块缺失(20个键) -3. **Product 产品管理** - 完整模块缺失(13个键) -4. **User 用户管理扩展** - 批量导入功能缺失(13个键) - -### 优先级 2 - 功能增强(建议补充) -1. **Application 应用模块** - 多代理相关功能(15个键) -2. **Space 空间模块** - 存储类型配置(5个键) -3. **MemoryExtractionEngine** - 实体去重相关(4个键) - -### 优先级 3 - 演示/测试功能(可选) -1. **Home/Workflow/NotFound** - 演示页面(30个键) -2. **通用计数器功能** - 测试功能(5个键) - ---- - -## 📝 下一步行动 - -1. **补充英文翻译**: 优先补充 Role、Tenant、Product、User 模块的英文翻译 -2. **补充中文翻译**: 补充 Application、Space、MemoryExtractionEngine 模块的中文翻译 -3. **清理无用翻译**: 如果 Home/Workflow 等演示功能不再使用,可以考虑从中文文件中移除 -4. **建立翻译规范**: 建议建立翻译键的命名规范和审查流程,避免未来出现遗漏 - diff --git a/web/package.json b/web/package.json new file mode 100644 index 00000000..d2c254ec --- /dev/null +++ b/web/package.json @@ -0,0 +1,72 @@ +{ + "name": "memory-bear-font-end", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@dnd-kit/core": "^6.3.1", + "@dnd-kit/modifiers": "^9.0.0", + "@dnd-kit/sortable": "^10.0.0", + "@dnd-kit/utilities": "^3.2.2", + "antd": "^5.27.4", + "axios": "^1.12.2", + "clsx": "^2.1.1", + "copy-to-clipboard": "^3.3.3", + "crypto-js": "^4.2.0", + "dayjs": "^1.11.18", + "echarts": "^5.6.0", + "echarts-for-react": "^3.0.2", + "i18next": "^25.6.0", + "mermaid": "^11.12.1", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "react-i18next": "^15.0.0", + "react-infinite-scroll-component": "^6.1.0", + "react-markdown": "^10.1.0", + "react-router-dom": "^6.22.0", + "react-syntax-highlighter": "^16.1.0", + "reactflow": "^11.11.4", + "rehype-katex": "^7.0.1", + "rehype-raw": "^7.0.0", + "remark-breaks": "^4.0.0", + "remark-gfm": "^4.0.1", + "remark-math": "^6.0.0", + "tailwindcss": "^4.1.14", + "zustand": "^5.0.8" + }, + "devDependencies": { + "@eslint/js": "^9.36.0", + "@tailwindcss/postcss": "^4.1.14", + "@tailwindcss/typography": "^0.5.19", + "@tailwindcss/vite": "^4.1.14", + "@types/crypto-js": "^4.2.2", + "@types/node": "^24.6.0", + "@types/react": "^18.2.0", + "@types/react-dom": "^18.2.0", + "@types/react-i18next": "^7.8.3", + "@types/react-router-dom": "^5.3.3", + "@types/react-syntax-highlighter": "^15.5.13", + "@vitejs/plugin-react": "^5.0.4", + "autoprefixer": "^10.4.21", + "eslint": "^9.36.0", + "eslint-plugin-react-hooks": "^5.2.0", + "eslint-plugin-react-refresh": "^0.4.22", + "globals": "^16.4.0", + "less": "^4.4.2", + "terser": "^5.44.0", + "typescript": "~5.9.3", + "typescript-eslint": "^8.45.0", + "unplugin-auto-import": "^20.2.0", + "unplugin-vue-components": "^29.1.0", + "vite": "npm:rolldown-vite@7.1.14" + }, + "overrides": { + "vite": "npm:rolldown-vite@7.1.14" + } +} diff --git a/web/public/auto-imports.d.ts b/web/public/auto-imports.d.ts index 62f88140..61cb2733 100644 --- a/web/public/auto-imports.d.ts +++ b/web/public/auto-imports.d.ts @@ -6,22 +6,31 @@ // biome-ignore lint: disable export {} declare global { + const Activity: typeof import('react').Activity + const Fragment: typeof import('react').Fragment const Link: typeof import('react-router-dom').Link const NavLink: typeof import('react-router-dom').NavLink const Navigate: typeof import('react-router-dom').Navigate const Outlet: typeof import('react-router-dom').Outlet const Route: typeof import('react-router-dom').Route const Routes: typeof import('react-router-dom').Routes + const Suspense: typeof import('react').Suspense + const cache: typeof import('react').cache + const cacheSignal: typeof import('react').cacheSignal + const createContext: typeof import('react').createContext const createRef: typeof import('react').createRef const forwardRef: typeof import('react').forwardRef const lazy: typeof import('react').lazy const memo: typeof import('react').memo const startTransition: typeof import('react').startTransition + const use: typeof import('react').use + const useActionState: typeof import('react').useActionState const useCallback: typeof import('react').useCallback const useContext: typeof import('react').useContext const useDebugValue: typeof import('react').useDebugValue const useDeferredValue: typeof import('react').useDeferredValue const useEffect: typeof import('react').useEffect + const useEffectEvent: typeof import('react').useEffectEvent const useHref: typeof import('react-router-dom').useHref const useId: typeof import('react').useId const useImperativeHandle: typeof import('react').useImperativeHandle @@ -33,6 +42,7 @@ declare global { const useMemo: typeof import('react').useMemo const useNavigate: typeof import('react-router-dom').useNavigate const useNavigationType: typeof import('react-router-dom').useNavigationType + const useOptimistic: typeof import('react').useOptimistic const useOutlet: typeof import('react-router-dom').useOutlet const useOutletContext: typeof import('react-router-dom').useOutletContext const useParams: typeof import('react-router-dom').useParams diff --git a/web/src/components/Table/index.tsx b/web/src/components/Table/index.tsx index 6d5cd7f1..6524d1a8 100644 --- a/web/src/components/Table/index.tsx +++ b/web/src/components/Table/index.tsx @@ -140,7 +140,8 @@ const TableComponent = forwardRef(({ const config: { x?: number | string | true; y?: number | string } = {}; - if (scrollX !== undefined) { + // 只有在有数据时才应用横向滚动 + if (scrollX !== undefined && data.length > 0) { config.x = scrollX; } else if (isScroll) { config.x = 'max-content'; diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index f93bd8ed..184c34cd 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -403,6 +403,7 @@ export const en = { apiKeyName: 'API Key Name', }, knowledgeBase: { + selectSpace: 'Please select a workspace.', preview:'Preview', pleaseUploadFileFirst: 'Please upload file first', shareSuccess: 'Share successfully', diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index ff9bd3cc..8dc17275 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -35,6 +35,7 @@ export const zh = { userMemoryDetail: '用户记忆详情', }, knowledgeBase: { + selectSpace: '请选择空间', preview:'预览', pleaseUploadFileFirst: '请先上传文件', shareSuccess: '分享成功', diff --git a/web/src/routes/routes.json b/web/src/routes/routes.json new file mode 100644 index 00000000..6c485a12 --- /dev/null +++ b/web/src/routes/routes.json @@ -0,0 +1,46 @@ +[ + { + "element": "AuthLayout", + "children": [ + { "path": "/user-management", "element": "UserManagement" }, + { "path": "/model", "element": "ModelManagement" }, + { "path": "/space", "element": "SpaceManagement" }, + { "path": "/no-permission", "element": "NoPermission" } + ] + }, + { + "element": "AuthSpaceLayout", + "children": [ + { "path": "/", "element": "Home" }, + { "path": "/user-memory", "element": "UserMemory" }, + { "path": "/user-memory/:id", "element": "UserMemoryDetail" }, + { "path": "/member", "element": "MemberManagement" }, + { "path": "/memory", "element": "MemoryManagement" }, + { "path": "/forgetting-engine/:id", "element": "ForgettingEngine" }, + { "path": "/memory-extraction-engine/:id", "element": "MemoryExtractionEngine" }, + { "path": "/application", "element": "ApplicationManagement" }, + { "path": "/memory-conversation", "element": "MemoryConversation" }, + { "path": "/knowledge-base", "element": "KnowledgeBase" }, + { "path": "/knowledge-base/:knowledgeBaseId/private", "element": "Private" }, + { "path": "/knowledge-base/:knowledgeBaseId/share", "element": "Share" }, + { "path": "/knowledge-base/:knowledgeBaseId/create-dataset", "element": "CreateDataset" }, + { "path": "/knowledge-base/:knowledgeBaseId/DocumentDetails", "element": "DocumentDetails" }, + { "path": "/no-permission", "element": "NoPermission" }, + { "path": "/*", "element": "NotFound" } + ] + }, + { + "element": "BasicLayout", + "children": [ + { "path": "/application/config/:id", "element": "ApplicationConfig" }, + { "path": "/conversation/:token", "element": "Conversation" } + ] + }, + { + "element": "LoginLayout", + "children": [ + { "path": "/login", "element": "Login" }, + { "path": "/invite-register/:token", "element": "InviteRegister" } + ] + } +] \ No newline at end of file diff --git a/web/src/store/menu.json b/web/src/store/menu.json new file mode 100644 index 00000000..448400b0 --- /dev/null +++ b/web/src/store/menu.json @@ -0,0 +1,248 @@ +{ + "manage": [ + { + "id": 1, + "parent": 0, + "code": "model", + "label": "模型管理", + "i18nKey": "menu.modelManagement", + "path": "/model", + "enable": true, + "display": true, + "level": 1, + "sort": 0, + "subs": [] + }, + { + "id": 2, + "parent": 0, + "code": "space", + "label": "空间管理", + "i18nKey": "menu.spaceManagement", + "path": "/space", + "enable": true, + "display": true, + "level": 1, + "sort": 0, + "subs": [] + }, + { + "id": 3, + "parent": 0, + "code": "user", + "label": "用户管理", + "i18nKey": "menu.userManagement", + "path": "/user-management", + "enable": true, + "display": true, + "level": 1, + "sort": 2, + "menuDesc": "管理系统用户信息", + "subs": null + } + ], + "space": [ + { + "id": 4, + "parent": 0, + "code": "dashboard", + "label": "记忆看板", + "i18nKey": "menu.home", + "path": "/", + "enable": true, + "display": true, + "level": 1, + "sort": 0, + "subs": null + }, + { + "id": 5, + "parent": 0, + "code": "application", + "label": "应用管理", + "i18nKey": "menu.applicationManagement", + "path": "/application", + "enable": true, + "display": true, + "level": 1, + "sort": 0, + "icon": null, + "iconActive": null, + "subs": null + }, + { + "id": 6, + "parent": 0, + "code": "knowledge", + "label": "知识库", + "i18nKey": "menu.knowledgeManagement", + "path": "/knowledge-base", + "enable": true, + "display": true, + "level": 1, + "sort": 0, + "icon": null, + "iconActive": null, + "subs": [ + { + "id": 61, + "parent": 6, + "code": "knowledgePrivate", + "label": "Private", + "i18nKey": "menu.knowledgePrivate", + "path": "/knowledge-base/:knowledgeBaseId/private", + "enable": true, + "display": false, + "level": 1, + "sort": 0, + "icon": null, + "iconActive": null, + "subs": null + }, + { + "id": 62, + "parent": 6, + "code": "knowledgeShare", + "label": "Share", + "i18nKey": "menu.knowledgeShare", + "path": "/knowledge-base/:knowledgeBaseId/share", + "enable": true, + "display": false, + "level": 1, + "sort": 0, + "icon": null, + "iconActive": null, + "subs": null + }, + { + "id": 63, + "parent": 6, + "code": "knowledgeCreateDataset", + "label": "CreateDataset", + "i18nKey": "menu.knowledgeCreateDataset", + "path": "/knowledge-base/:knowledgeBaseId/create-dataset", + "enable": true, + "display": false, + "level": 1, + "sort": 0, + "icon": null, + "iconActive": null, + "subs": null + }, + { + "id": 64, + "parent": 6, + "code": "knowledgeDocumentDetails", + "label": "DocumentDetails", + "i18nKey": "menu.knowledgeDocumentDetails", + "path": "/knowledge-base/:knowledgeBaseId/DocumentDetails", + "enable": true, + "display": false, + "level": 1, + "sort": 0, + "icon": null, + "iconActive": null, + "subs": null + } + ] + }, + { + "id": 7, + "parent": 0, + "code": "memory", + "label": "记忆管理", + "i18nKey": "menu.memoryManagement", + "path": "/memory", + "enable": true, + "display": true, + "level": 1, + "sort": 0, + "subs": [ + { + "id": 71, + "parent": 7, + "code": "forgettingEngine", + "label": "遗忘引擎", + "i18nKey": "menu.forgettingEngine", + "path": "/forgetting-engine/:id", + "enable": true, + "display": false, + "level": 1, + "sort": 0, + "subs": null + }, + { + "id": 72, + "parent": 7, + "code": "memoryExtractionEngine", + "label": "记忆萃取引擎", + "i18nKey": "menu.memoryExtractionEngine", + "path": "/memory-extraction-engine/:id", + "enable": true, + "display": false, + "level": 1, + "sort": 0, + "subs": null + } + ] + }, + { + "id": 8, + "parent": 0, + "code": "userMemory", + "label": "", + "i18nKey": "menu.userMemory", + "path": "/user-memory", + "enable": true, + "display": true, + "level": 1, + "sort": 1, + "menuDesc": "管理用户记忆", + "subs": [ + { + "id": 81, + "parent": 8, + "code": "userMemoryDetail", + "label": "记忆详情", + "i18nKey": "menu.userMemoryDetail", + "path": "/user-memory/:id", + "enable": true, + "display": false, + "level": 2, + "sort": 0, + "subs": null + } + ] + }, + { + "id": 19, + "parent": 0, + "code": "member", + "label": "成员管理", + "i18nKey": "menu.memberManagement", + "path": "/member", + "enable": true, + "display": true, + "level": 1, + "sort": 0, + "icon": null, + "iconActive": null, + "subs": null + }, + { + "id": 10, + "parent": 0, + "code": "memoryConversation", + "label": "记忆验证", + "i18nKey": "menu.memoryConversation", + "path": "/memory-conversation", + "enable": true, + "display": true, + "level": 1, + "sort": 0, + "icon": null, + "iconActive": null, + "subs": null + } + ] +} \ No newline at end of file diff --git a/web/src/store/menu.ts b/web/src/store/menu.ts index 0acbe73a..0d35a932 100644 --- a/web/src/store/menu.ts +++ b/web/src/store/menu.ts @@ -12,17 +12,17 @@ export interface MenuItem { display: boolean; level: number; sort: number; - icon: string | null; - iconActive: string | null; - menuDesc: string | null; - deleted: string | null; - updateTime: number; - new_: string | null; - keepAlive: boolean; - master: string | null; - disposable: boolean; - appSystem: string | null; - subs: MenuItem[]; + icon?: string | null; + iconActive?: string | null; + menuDesc?: string | null; + deleted?: string | null; + updateTime?: number; + new_?: string | null; + keepAlive?: boolean; + master?: string | null; + disposable?: boolean; + appSystem?: string | null; + subs: MenuItem[] | null; } interface MenuState { collapsed: boolean; @@ -31,6 +31,7 @@ interface MenuState { allBreadcrumbs: Record<'space' | 'manage' | string, MenuItem[]>; loadMenus: (source: 'space' | 'manage') => void; updateBreadcrumbs: (keyPath: string[], source: 'space' | 'manage') => void; + setCustomBreadcrumbs: (breadcrumbs: MenuItem[], source: 'space' | 'manage') => void; } const initBreadcrumbs = localStorage.getItem('breadcrumbs') || '[]' @@ -73,4 +74,9 @@ export const useMenu = create((set, get) => ({ set({ allBreadcrumbs }) localStorage.setItem('breadcrumbs', JSON.stringify(allBreadcrumbs)) }, + setCustomBreadcrumbs: (breadcrumbs, source) => { + const allBreadcrumbs = { ...get().allBreadcrumbs, [source]: breadcrumbs } + set({ allBreadcrumbs }) + localStorage.setItem('breadcrumbs', JSON.stringify(allBreadcrumbs)) + }, })) \ No newline at end of file diff --git a/web/src/views/KnowledgeBase/[knowledgeBaseId]/CreateDataset.tsx b/web/src/views/KnowledgeBase/[knowledgeBaseId]/CreateDataset.tsx index 1d46c639..44bd1d2f 100644 --- a/web/src/views/KnowledgeBase/[knowledgeBaseId]/CreateDataset.tsx +++ b/web/src/views/KnowledgeBase/[knowledgeBaseId]/CreateDataset.tsx @@ -145,6 +145,7 @@ const CreateDataset = () => { }); return; } + debugger // 显示确认弹框 confirm({ @@ -195,11 +196,12 @@ const CreateDataset = () => { title: t('common.deleteWarning'), content: t('common.deleteWarningContent', { content: record.name }), onOk: async () => { - // TODO: 实现删除逻辑 - const response = await deleteDocument(record.id); + await deleteDocument(record.id); - // 删除成功,刷新列表 - // messageApi.success(t('common.deleteSuccess')); + // 删除成功,从 rechunkFileIds 中移除该 id + setRechunkFileIds((prev) => prev.filter((id) => id !== record.id)); + + // 刷新列表 messageApi.success(t('common.deleteSuccess')); tableRef.current?.loadData(); @@ -560,15 +562,24 @@ const CreateDataset = () => { {current === 2 && (
- + {rechunkFileIds.length > 0 ? ( +
+ ) : ( +
+ )} )} diff --git a/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx b/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx index 075ba015..571f8ed0 100644 --- a/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx +++ b/web/src/views/KnowledgeBase/[knowledgeBaseId]/Private.tsx @@ -11,10 +11,6 @@ import type { AnyObject } from 'antd/es/_util/type'; import { MoreOutlined } from '@ant-design/icons'; import folderIcon from '@/assets/images/knowledgeBase/folder.png'; import textIcon from '@/assets/images/knowledgeBase/text.png'; -import imageIcon from '@/assets/images/knowledgeBase/image.png'; -import blankIcon from '@/assets/images/knowledgeBase/blankDocument.png'; -import templateIcon from '@/assets/images/knowledgeBase/template.png'; -import backupIcon from '@/assets/images/knowledgeBase/backup.png'; import editIcon from '@/assets/images/knowledgeBase/edit.png'; import { getKnowledgeBaseDetail, deleteDocument, downloadFile, updateKnowledgeBase } from '../service'; import type { @@ -35,6 +31,7 @@ import CreateDatasetModal from '../components/CreateDatasetModal'; import CreateImageDataset from '../components/CreateImageDataset'; import FolderTree, { type TreeNodeData } from '../components/FolderTree'; import { formatDateTime } from '@/utils/format'; +import { useMenu } from '@/store/menu'; import './Private.css' const { confirm } = Modal // 树节点数据类型 @@ -57,7 +54,6 @@ const Private: FC = () => { kb_id:knowledgeBaseId ?? '', parent_id:parentId ?? '' }); - const [keywords, setKeywords] = useState(''); const [query, setQuery] = useState>({ orderby: 'created_at', desc: true, @@ -66,6 +62,8 @@ const Private: FC = () => { const shareModalRef = useRef(null); const datasetModalRef = useRef(null); const [folderTreeRefreshKey, setFolderTreeRefreshKey] = useState(0); + const { allBreadcrumbs, setCustomBreadcrumbs } = useMenu(); + const [folderPath, setFolderPath] = useState>([]); useEffect(() => { if (knowledgeBaseId) { let url = `/documents/${knowledgeBaseId}/${parentId}/documents`; @@ -74,6 +72,13 @@ const Private: FC = () => { } }, [knowledgeBaseId]); + // 更新面包屑 + useEffect(() => { + if (knowledgeBase) { + updateBreadcrumbs(); + } + }, [knowledgeBase, folderPath]); + // 监听 tableApi 变化,自动刷新表格数据 useEffect(() => { if (tableApi) { @@ -95,17 +100,82 @@ const Private: FC = () => { setLoading(true); try { const res = await getKnowledgeBaseDetail(id); - setKnowledgeBase(res.data || res); + // 将 KnowledgeBase 转换为 KnowledgeBaseListItem + const listItem = res as unknown as KnowledgeBaseListItem; + setKnowledgeBase(listItem); } finally { setLoading(false); } }; + // 更新面包屑,包含知识库名称和文件夹路径 + const updateBreadcrumbs = () => { + if (!knowledgeBase) return; + + const baseBreadcrumbs = allBreadcrumbs['space'] || []; + // 只保留知识库菜单项之前的面包屑 + const knowledgeBaseMenuIndex = baseBreadcrumbs.findIndex(item => item.path === '/knowledge-base'); + const filteredBaseBreadcrumbs = knowledgeBaseMenuIndex >= 0 + ? baseBreadcrumbs.slice(0, knowledgeBaseMenuIndex + 1) + : baseBreadcrumbs; + + const customBreadcrumbs = [ + ...filteredBaseBreadcrumbs, + { + id: 0, + parent: 0, + code: null, + label: knowledgeBase.name, + i18nKey: null, + path: null, + enable: true, + display: true, + level: 0, + sort: 0, + icon: null, + iconActive: null, + menuDesc: null, + deleted: null, + updateTime: 0, + new_: null, + keepAlive: false, + master: null, + disposable: false, + appSystem: null, + subs: [], + }, + ...folderPath.map((folder) => ({ + id: 0, + parent: 0, + code: null, + label: folder.name, + i18nKey: null, + path: null, + enable: true, + display: true, + level: 0, + sort: 0, + icon: null, + iconActive: null, + menuDesc: null, + deleted: null, + updateTime: 0, + new_: null, + keepAlive: false, + master: null, + disposable: false, + appSystem: null, + subs: [], + })), + ]; + + setCustomBreadcrumbs(customBreadcrumbs, 'space'); + }; + // 处理树节点选择 - const onSelect = (selectedKeys: React.Key[], info: any) => { + const onSelect = (selectedKeys: React.Key[]) => { if (!selectedKeys.length) return; if (!folder) return; - const node = info.node as TreeNodeData; const f = { ...folder, parent_id: String(selectedKeys[0]), @@ -114,17 +184,16 @@ const Private: FC = () => { setTableApi(url); setParentId(String(selectedKeys[0])) setFolder(f) - // 根据节点类型执行不同操作 - if (node.type === 'folder') { - - // 文件夹:展开/收起 - } else if (node.type === 'text' || node.type === 'image' || node.type === 'dataset') { - // 文件:打开详情 - } + }; + + // 处理文件夹路径变化 + const handleFolderPathChange = (path: Array<{ id: string; name: string }>) => { + setFolderPath(path); }; // 处理树节点展开 - const onExpand = (expandedKeys: React.Key[], info: any) => { + const onExpand = (_expandedKeys: React.Key[], _info: any) => { + // 展开节点时不需要特殊处理 }; // create / import list const createItems: MenuProps['items'] = [ @@ -190,10 +259,7 @@ const Private: FC = () => { // }, ]; - // - const handleCreate = (type: string) => { - console.log('create', type); - } + // 处理开关 const onChange = (checked: boolean) => { updateKnowledgeBase(knowledgeBaseId || '', { @@ -348,8 +414,7 @@ const Private: FC = () => { title: t('knowledgeBase.status'), dataIndex: 'progress', key: 'progress', - render: (value: string | number, record: AnyObject) => { - + render: (value: string | number) => { return ( { knowledgeBaseId={knowledgeBaseId ?? ''} refreshKey={folderTreeRefreshKey} onRootLoad={handleRootTreeLoad} + onFolderPathChange={handleFolderPathChange} /> )} diff --git a/web/src/views/KnowledgeBase/components/FolderTree.tsx b/web/src/views/KnowledgeBase/components/FolderTree.tsx index aa029621..dad92411 100644 --- a/web/src/views/KnowledgeBase/components/FolderTree.tsx +++ b/web/src/views/KnowledgeBase/components/FolderTree.tsx @@ -58,6 +58,7 @@ interface FolderTreeProps { style?: CSSProperties; refreshKey?: number; onRootLoad?: (nodes: TreeNodeData[] | null) => void; + onFolderPathChange?: (path: Array<{ id: string; name: string }>) => void; } const renderIcon = (icon?: string) => { @@ -271,6 +272,7 @@ const FolderTree: FC = ({ style, refreshKey = 0, onRootLoad, + onFolderPathChange, }) => { const [treeData, setTreeData] = useState([]); @@ -347,6 +349,42 @@ const FolderTree: FC = ({ } }; + // 查找节点路径的辅助函数 + const findNodePath = (nodes: TreeNodeData[], targetKey: Key, currentPath: Array<{ id: string; name: string }> = []): Array<{ id: string; name: string }> | null => { + for (const node of nodes) { + const newPath = [...currentPath, { id: String(node.key), name: String(node.title) }]; + + if (node.key === targetKey) { + return newPath; + } + + if (node.children) { + const found = findNodePath(node.children, targetKey, newPath); + if (found) { + return found; + } + } + } + return null; + }; + + // 处理选择事件,计算并传递路径 + const handleSelect: TreeProps['onSelect'] = (selectedKeys, info) => { + if (selectedKeys.length > 0) { + const path = findNodePath(treeData, selectedKeys[0]); + if (path && onFolderPathChange) { + onFolderPathChange(path); + } + } else if (onFolderPathChange) { + onFolderPathChange([]); + } + + // 调用原始的 onSelect 回调 + if (onSelect) { + onSelect(selectedKeys, info); + } + }; + const treeNodes = useMemo(() => transformTreeData(treeData), [treeData]); return ( @@ -354,7 +392,7 @@ const FolderTree: FC = ({ multiple={multiple} className={className} style={style} - onSelect={onSelect} + onSelect={handleSelect} onExpand={onExpand} loadData={onLoadData} treeData={treeNodes} diff --git a/web/src/views/KnowledgeBase/components/ShareSpaceModal.tsx b/web/src/views/KnowledgeBase/components/ShareSpaceModal.tsx index 6e70daac..899f6ec4 100644 --- a/web/src/views/KnowledgeBase/components/ShareSpaceModal.tsx +++ b/web/src/views/KnowledgeBase/components/ShareSpaceModal.tsx @@ -4,7 +4,7 @@ * @Author: yujiangping * @Date: 2025-11-10 18:52:55 * @LastEditors: yujiangping - * @LastEditTime: 2025-11-25 17:46:36 + * @LastEditTime: 2025-12-03 18:44:58 */ import { forwardRef, useImperativeHandle, useState } from 'react'; import { Switch } from 'antd'; @@ -24,7 +24,7 @@ const ShareModal = forwardRef(({ handleShare: const [messageApi, contextHolder] = message.useMessage(); const [visible, setVisible] = useState(false); const [loading, setLoading] = useState(false) - const [curIndex, setCurIndex] = useState(9999); + const [curIndex, setCurIndex] = useState(-1); const [kbId, setKbId] = useState(''); const [spaceIds, setSpaceIds] = useState(''); const [knowledgeBase, setKnowledgeBase] = useState(null); @@ -32,7 +32,7 @@ const ShareModal = forwardRef(({ handleShare: // 封装取消方法,添加关闭弹窗逻辑 const handleClose = () => { - setCurIndex(9999); + setCurIndex(-1); setLoading(false) setVisible(false); }; @@ -53,8 +53,13 @@ const ShareModal = forwardRef(({ handleShare: // 获取所有 checked 为 true 的数据 const checkedItems = spaceList.filter(item => item.is_active); + debugger // 获取当前选中的项(curIndex 对应的数据) - const selectedItem = curIndex !== 9999 ? spaceList[curIndex] : null; + const selectedItem = curIndex !== -1 ? spaceList[curIndex] : null; + if(!selectedItem){ + messageApi.error(t('knowledgeBase.selectSpace')); + return; + } const payload = { source_kb_id: kbId ?? '', target_workspace_id: selectedItem?.id ?? '', diff --git a/web/tsconfig.app.json b/web/tsconfig.app.json new file mode 100644 index 00000000..b91f0eef --- /dev/null +++ b/web/tsconfig.app.json @@ -0,0 +1,33 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client"], + "skipLibCheck": true, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + }, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"], + "exclude": ["**/*copy*"] +} diff --git a/web/tsconfig.json b/web/tsconfig.json new file mode 100644 index 00000000..fe70f411 --- /dev/null +++ b/web/tsconfig.json @@ -0,0 +1,10 @@ +{ + "files": [], + "compilerOptions": { + "ignoreDeprecations": "5.0" + }, + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/web/tsconfig.node.json b/web/tsconfig.node.json new file mode 100644 index 00000000..8a67f62f --- /dev/null +++ b/web/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +}