From fb76f765cc6995bf078b59c04d41a0de3c85a1f8 Mon Sep 17 00:00:00 2001 From: zhaoying Date: Tue, 3 Feb 2026 14:01:28 +0800 Subject: [PATCH 1/2] style(web): translate the comments in the web/src/api directory into English --- web/src/api/apiKey.ts | 18 +++++++++----- web/src/api/application.ts | 51 ++++++++++++++++++++++---------------- web/src/api/common.ts | 8 +++--- web/src/api/fileStorage.ts | 6 +++++ web/src/api/member.ts | 16 ++++++++---- web/src/api/memory.ts | 6 +++++ web/src/api/models.ts | 6 +++++ web/src/api/order.ts | 11 ++++++-- web/src/api/prompt.ts | 6 +++++ web/src/api/user.ts | 24 +++++++++++------- web/src/api/workspaces.ts | 22 ++++++++++------ 11 files changed, 118 insertions(+), 56 deletions(-) diff --git a/web/src/api/apiKey.ts b/web/src/api/apiKey.ts index 56ad79c4..92df70c9 100644 --- a/web/src/api/apiKey.ts +++ b/web/src/api/apiKey.ts @@ -1,33 +1,39 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 13:59:41 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 13:59:41 + */ import { request } from '@/utils/request' import type { ApiKey } from '@/views/ApiKeyManagement/types' -// API Key列表 +// API Key list export const getApiKeyListUrl = '/apikeys' export const getApiKeyList = (data: Record) => { return request.get(getApiKeyListUrl, data) } -// API Key详情 +// API Key details export const getApiKey = (id: string) => { return request.get(`/apikeys/${id}`) } -// 创建API Key +// Create API Key export const createApiKey = (values: ApiKey) => { return request.post('/apikeys', values) } -// 更新API Key +// Update API Key export const updateApiKey = (id: string, values: ApiKey) => { return request.put(`/apikeys/${id}`, values) } -// 删除 API Key +// Delete API Key export const deleteApiKey = (id: string) => { return request.delete(`/apikeys/${id}`) } -// 使用统计 +// Usage statistics export const getApiKeyStats = (app_key_id: string) => { return request.get(`/apikeys/${app_key_id}/stats`) } \ No newline at end of file diff --git a/web/src/api/application.ts b/web/src/api/application.ts index 1f20282e..244f3503 100644 --- a/web/src/api/application.ts +++ b/web/src/api/application.ts @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 13:59:45 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 13:59:45 + */ import { request } from '@/utils/request' import type { ApplicationModalData } from '@/views/ApplicationManagement/types' import type { Config } from '@/views/ApplicationConfig/types' @@ -5,71 +11,72 @@ import { handleSSE, type SSEMessage } from '@/utils/stream' import type { QueryParams } from '@/views/Conversation/types' import type { WorkflowConfig } from '@/views/Workflow/types' -// 应用列表 +// Application list export const getApplicationListUrl = '/apps' export const getApplicationList = (data: Record) => { return request.get(getApplicationListUrl, data) } -// 获取应用配置 +// Get application config export const getApplicationConfig = (id: string) => { return request.get(`/apps/${id}/config`) } -// 获取集群应用配置 +// Get multi-agent config export const getMultiAgentConfig = (id: string) => { return request.get(`/apps/${id}/multi-agent`) } -// 获取 workflow应用配置 +// Get workflow config export const getWorkflowConfig = (id: string) => { return request.get(`/apps/${id}/workflow`) } -// 应用详情 +// Application details export const getApplication = (id: string) => { return request.get(`/apps/${id}`) } -// 更新应用 +// Update application export const updateApplication = (id: string, values: ApplicationModalData) => { return request.put(`/apps/${id}`, values) } -// 创建应用 +// Create application export const addApplication = (values: ApplicationModalData) => { return request.post('/apps', values) } -// 保存Agent配置 +// Save agent config export const saveAgentConfig = (app_id: string, values: Config) => { return request.put(`/apps/${app_id}/config`, values) } -// 保存集群配置 +// Save multi-agent config export const saveMultiAgentConfig = (app_id: string, values: Config) => { return request.put(`/apps/${app_id}/multi-agent`, values) } -// 保存workflow配置 +// Save workflow config export const saveWorkflowConfig = (app_id: string, values: WorkflowConfig) => { return request.put(`/apps/${app_id}/workflow`, values) } -// 模型比对试运行 +// Model comparison test run export const runCompare = (app_id: string, values: Record, onMessage?: (data: SSEMessage[]) => void) => { return handleSSE(`/apps/${app_id}/draft/run/compare`, values, onMessage) } +// Test run export const draftRun = (app_id: string, values: Record, onMessage?: (data: SSEMessage[]) => void) => { return handleSSE(`/apps/${app_id}/draft/run`, values, onMessage) } -// 删除应用 +// Delete application export const deleteApplication = (app_id: string) => { return request.delete(`/apps/${app_id}`) } -// 发布版本列表 +// Release version list export const getReleaseList = (app_id: string) => { return request.get(`/apps/${app_id}/releases`) } -// 发布版本 +// Publish release export const publishRelease = (app_id: string, values: Record) => { return request.post(`/apps/${app_id}/publish`, values) } -// 回滚版本 +// Rollback release export const rollbackRelease = (app_id: string, version: string) => { return request.post(`/apps/${app_id}/rollback/${version}`) } -// 发布版本分享 +// Share release export const shareRelease = (app_id: string, release_id: string) => { return request.post(`/apps/${app_id}/releases/${release_id}/share`, { "is_enabled": true, @@ -77,7 +84,7 @@ export const shareRelease = (app_id: string, release_id: string) => { "allow_embed": true }) } -// 获取体验对话历史 +// Get conversation history export const getConversationHistory = (share_token: string, data: { page: number; pagesize: number }) => { return request.get(`/public/share/conversations`, data, { headers: { @@ -85,7 +92,7 @@ export const getConversationHistory = (share_token: string, data: { page: number } }) } -// 发送体验对话 +// Send conversation export const sendConversation = (values: QueryParams, onMessage: (data: SSEMessage[]) => void, shareToken: string) => { return handleSSE(`/public/share/chat`, values, onMessage, { headers: { @@ -93,7 +100,7 @@ export const sendConversation = (values: QueryParams, onMessage: (data: SSEMessa } }) } -// 获取体验会话详情 +// Get conversation details export const getConversationDetail = (share_token: string, conversation_id: string) => { return request.get(`/public/share/conversations/${conversation_id}`, {}, { headers: { @@ -101,15 +108,15 @@ export const getConversationDetail = (share_token: string, conversation_id: stri } }) } -// 获取体验对话token +// Get share token export const getShareToken = (share_token: string, user_id: string) => { return request.post(`/public/share/${share_token}/token`, { user_id }) } -// 复制应用 +// Copy application export const copyApplication = (app_id: string, new_name: string) => { return request.post(`/apps/${app_id}/copy?new_name=${new_name}`) } -// 数据统计 +// Data statistics export const getAppStatistics = (app_id: string, data: { start_date: number; end_date: number; }) => { return request.get(`/apps/${app_id}/statistics`, data) } diff --git a/web/src/api/common.ts b/web/src/api/common.ts index 2f6033d1..b53e4d5f 100644 --- a/web/src/api/common.ts +++ b/web/src/api/common.ts @@ -1,5 +1,5 @@ import { request } from "@/utils/request"; -// 列表查询参数 +// List query parameters export interface Query { page?: number; pagesize?: number; @@ -38,15 +38,15 @@ export interface versionResponse{ codeName: string; }; } -// 首页数据统计 +// Dashboard data statistics export const getDashboardData = `/home-page/workspaces` -// 首页数据看板统计 +// Dashboard statistics export const getDashboardStatistics = async () => { const response = await request.get(`/home-page/statistics`); return response as DataResponse; }; -// 获取版本号 +// Get version export const getVersion = async () => { const response = await request.get(`/home-page/version`); return response as versionResponse; diff --git a/web/src/api/fileStorage.ts b/web/src/api/fileStorage.ts index e7b476a3..86da129c 100644 --- a/web/src/api/fileStorage.ts +++ b/web/src/api/fileStorage.ts @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 13:59:56 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 13:59:56 + */ import { request, API_PREFIX } from '@/utils/request' // Upload file,file storage has expiration period diff --git a/web/src/api/member.ts b/web/src/api/member.ts index 8e456e24..f186fdc4 100644 --- a/web/src/api/member.ts +++ b/web/src/api/member.ts @@ -1,20 +1,26 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:00:01 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:00:01 + */ import { request } from '@/utils/request' -// 成员列表 +// Member list export const memberListUrl = '/workspaces/members' -// 邀请成员 +// Invite member export const inviteMember = (values: { email: string }) => { return request.post(`/workspaces/invites`, values) } -// 删除成员 +// Delete member export const deleteMember = (id: string) => { return request.delete(`/workspaces/members/${id}`) } -// 更新成员 +// Update member export const updateMember = (values: { id: string, role: string }) => { return request.put(`/workspaces/members`, [values]) } -// 验证邀请token +// Validate invite token export const validateInviteToken = (token: string) => { return request.get(`/workspaces/invites/validate/${token}`) } diff --git a/web/src/api/memory.ts b/web/src/api/memory.ts index ff8e0435..6f4e7f0e 100644 --- a/web/src/api/memory.ts +++ b/web/src/api/memory.ts @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:00:06 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:00:06 + */ import { request } from '@/utils/request' import type { MemoryFormData, diff --git a/web/src/api/models.ts b/web/src/api/models.ts index e5d0f339..eb18ce91 100644 --- a/web/src/api/models.ts +++ b/web/src/api/models.ts @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:00:09 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:00:09 + */ import { request } from '@/utils/request' import type { MultiKeyForm, Query, KeyConfigModalForm, CompositeModelForm, CustomModelForm } from '@/views/ModelManagement/types' diff --git a/web/src/api/order.ts b/web/src/api/order.ts index e5d9d916..9d83538f 100644 --- a/web/src/api/order.ts +++ b/web/src/api/order.ts @@ -1,16 +1,23 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:00:14 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:00:14 + */ import { request } from '@/utils/request' import type { VoucherForm } from '@/views/OrderPayment/types' export const getOrderListUrl = '/v1/orders/customer' -// 提交支付凭证API +// Submit payment voucher API export const submitPaymentVoucherAPI = (voucherData: VoucherForm) => { return request.post('/v1/orders/', voucherData) } -// 订单详情 +// Order details export const getOrderDetail = (order_no: string) => { return request.get(`/v1/orders/customer/${order_no}`) } +// Order status enum export const orderStatusUrl = '/v1/order-status/' export const getOrderStatus = () => { return request.get(orderStatusUrl) diff --git a/web/src/api/prompt.ts b/web/src/api/prompt.ts index 79ea374c..55398ca5 100644 --- a/web/src/api/prompt.ts +++ b/web/src/api/prompt.ts @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:00:17 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:00:17 + */ import { request } from '@/utils/request' import type { AiPromptForm } from '@/views/ApplicationConfig/types' import type { PromptReleaseData } from '@/views/Prompt/types' diff --git a/web/src/api/user.ts b/web/src/api/user.ts index 3ff03386..f37e685b 100644 --- a/web/src/api/user.ts +++ b/web/src/api/user.ts @@ -1,40 +1,46 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:00:23 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:00:23 + */ import { request } from '@/utils/request' import type { CreateModalData } from '@/views/UserManagement/types' import { cookieUtils } from '@/utils/request' -// 用户信息 +// User info export const getUsers = () => { return request.get('/users') } -// 用户列表 +// User list export const getUserListUrl = '/users/superusers' -// 登录 +// Login export const loginUrl = '/token' export const login = (data: { email: string; password: string; invite?: string; username?: string }) => { return request.post(loginUrl, data) } -// 刷新token +// Refresh token export const refreshTokenUrl = '/refresh' export const refreshToken = () => { return request.post(refreshTokenUrl, { refresh_token: cookieUtils.get('refreshToken') }) } -// 重置密码 +// Reset password export const changePassword = (data: { user_id: string; new_password: string }) => { return request.put('/users/admin/change-password', data) } -// 禁用用户 +// Disable user export const deleteUser = (user_id: string) => { return request.delete(`/users/${user_id}`) } -// 启用用户 +// Enable user export const enableUser = (user_id: string) => { return request.post(`/users/${user_id}/activate`) } -// 创建用户 +// Create user export const addUser = (data: CreateModalData) => { return request.post('/users/superuser', data) } -// 注销 +// Logout export const logoutUrl = '/logout' export const logout = () => { return request.post(logoutUrl) diff --git a/web/src/api/workspaces.ts b/web/src/api/workspaces.ts index 4e78194b..01f3be72 100644 --- a/web/src/api/workspaces.ts +++ b/web/src/api/workspaces.ts @@ -1,28 +1,34 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:00:26 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:00:26 + */ import { request } from '@/utils/request' import type { SpaceModalData } from '@/views/SpaceManagement/types' -import type { ConfigModalData } from '@/views/UserMemory/types' +import type { SpaceConfigData } from '@/views/SpaceConfig/types' -// 空间列表 +// Workspace list export const getWorkspaces = () => { return request.get('/workspaces') } -// 创建空间 +// Create workspace export const createWorkspace = (values: SpaceModalData) => { return request.post('/workspaces', values) } -// 切换空间 +// Switch workspace export const switchWorkspace = (workspaceId: string) => { return request.put(`/workspaces/${workspaceId}/switch`) } -// 获取空间存储类型 +// Get workspace storage type export const getWorkspaceStorageType = () => { return request.get(`/workspaces/storage`) } -// 获取空间模型配置 +// Get workspace model config export const getWorkspaceModels = () => { return request.get(`/workspaces/workspace_models`) } -// 更新空间模型配置 -export const updateWorkspaceModels = (data: ConfigModalData) => { +// Update workspace model config +export const updateWorkspaceModels = (data: SpaceConfigData) => { return request.put(`/workspaces/workspace_models`, data) } From 5e1e5f68e142f20fc7a66fe492e67bd0c8a9e679 Mon Sep 17 00:00:00 2001 From: zhaoying Date: Tue, 3 Feb 2026 14:12:06 +0800 Subject: [PATCH 2/2] feat(web): Ontology support import & export; docs(web): add comments to the src/views/Ontology directory --- web/src/api/ontology.ts | 16 +- web/src/components/Upload/UploadFiles.tsx | 36 +++-- web/src/i18n/en.ts | 6 + web/src/i18n/zh.ts | 6 + web/src/utils/request.ts | 7 +- .../components/OntologyClassExtractModal.tsx | 50 +++++- .../components/OntologyClassModal.tsx | 36 ++++- .../components/OntologyExportModal.tsx | 144 ++++++++++++++++++ .../components/OntologyImportModal.tsx | 139 +++++++++++++++++ .../Ontology/components/OntologyModal.tsx | 37 ++++- .../views/Ontology/components/PageHeader.tsx | 21 +++ web/src/views/Ontology/index.tsx | 86 ++++++++++- web/src/views/Ontology/pages/Detail.tsx | 32 ++++ web/src/views/Ontology/types.ts | 137 ++++++++++++++++- 14 files changed, 714 insertions(+), 39 deletions(-) create mode 100644 web/src/views/Ontology/components/OntologyExportModal.tsx create mode 100644 web/src/views/Ontology/components/OntologyImportModal.tsx diff --git a/web/src/api/ontology.ts b/web/src/api/ontology.ts index 4213d362..becf899f 100644 --- a/web/src/api/ontology.ts +++ b/web/src/api/ontology.ts @@ -1,5 +1,11 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 13:59:12 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 13:59:12 + */ import { request } from '@/utils/request' -import type { Query, OntologyModalData, OntologyClassModalData, OntologyClassExtractModalData } from '@/views/Ontology/types' +import type { Query, OntologyModalData, OntologyClassModalData, OntologyClassExtractModalData, OntologyExportModalData } from '@/views/Ontology/types' // Scene list export const getOntologyScenesUrl = '/memory/ontology/scenes' @@ -37,3 +43,11 @@ export const createOntologyClass = (data: OntologyClassModalData) => { export const deleteOntologyClass = (class_id: string) => { return request.delete(`/memory/ontology/class/${class_id}`) } +// Import scenario +export const ontologyImport = (data: unknown) => { + return request.uploadFile('/memory/ontology/import', data) +} +// Export scenario +export const ontologyExport = (data: OntologyExportModalData, fileName: string, callback: () => void) => { + return request.downloadFile('/memory/ontology/export', fileName, data, callback) +} \ No newline at end of file diff --git a/web/src/components/Upload/UploadFiles.tsx b/web/src/components/Upload/UploadFiles.tsx index 74b6a452..91b844f5 100644 --- a/web/src/components/Upload/UploadFiles.tsx +++ b/web/src/components/Upload/UploadFiles.tsx @@ -57,6 +57,10 @@ const ALL_FILE_TYPE: { htm: 'text/html', html: 'text/html', json: 'application/json', + owl: 'application/rdf+xml', + ttl: 'text/turtle', + rdf: 'application/rdf+xml', + xml: 'application/rdf+xml', } export interface UploadFilesRef { fileList: UploadFile[]; @@ -122,7 +126,7 @@ const UploadFiles = forwardRef(({ if (fileSize) { const isLtMaxSize = (file.size / 1024 / 1024) < fileSize; if (!isLtMaxSize) { - message.error(`文件大小不能超过 ${fileSize}MB`); + message.error(t('common.fileSizeTip', { size: fileSize })); return Upload.LIST_IGNORE; } } @@ -139,7 +143,7 @@ const UploadFiles = forwardRef(({ const isValidMimeType = file.type && accept ? accept.includes(file.type) : true; if (!isValidExtension && !isValidMimeType) { - message.error(`不支持的文件类型: ${fileExtension || file.type}`); + message.error(`${t('common.fileAcceptTip')}${fileExtension || file.type}`); return Upload.LIST_IGNORE; } } @@ -236,12 +240,12 @@ const UploadFiles = forwardRef(({ fileList, beforeUpload, headers: { - authorization: cookieUtils.get('authToken') || '', + authorization: `Bearer ${cookieUtils.get('authToken')}`, }, onRemove: handleRemove, onChange: handleChange, accept, - disabled, + disabled: disabled || fileList.length >= maxCount, showUploadList: { showPreviewIcon: false, showRemoveIcon: true, @@ -249,12 +253,12 @@ const UploadFiles = forwardRef(({ }, itemRender: (_, file, __, actions) => { return ( -
-
+
+
{file.name} - actions?.remove()}>Cancel + actions?.remove()}>{t('common.cancel')}
- + {isAutoUpload && }
); }, @@ -267,20 +271,20 @@ const UploadFiles = forwardRef(({ clearFiles })); - const hasProgress = fileList.some((item) => item.percent !== 100); + const hasProgress = isAutoUpload && fileList.some((item) => item.percent !== 100); if (isCanDrag) { return ( -
+
- - {!hasProgress && (!fileList || !fileList.length) && + + {(!isAutoUpload || !hasProgress && (!fileList || !fileList.length)) && <> -
- {t('common.dragUploadTip')}{t('common.uploadClickTip')} +
+ {t('common.dragUploadTip')}{t('common.uploadClickTip')}
- {fileType &&
{t('common.supportedFileTypes', { types: fileType.join(',') })}
} + {fileType &&
{t('common.supportedFileTypes', { types: fileType.join(',') })}
} {(fileSize || fileType || maxCount > 1) && (
{t('common.uploadFileTipMax', { max: fileSize, maxCount: maxCount })} @@ -288,7 +292,7 @@ const UploadFiles = forwardRef(({ )} } - {hasProgress &&
{t('common.uploading')}
} + {hasProgress &&
{t('common.uploading')}
}
diff --git a/web/src/i18n/en.ts b/web/src/i18n/en.ts index 03d68016..d3c788df 100644 --- a/web/src/i18n/en.ts +++ b/web/src/i18n/en.ts @@ -426,6 +426,7 @@ export const en = { fileAcceptTip: 'Unsupported file type:', nextStep: 'Next Step', prevStep: 'Previous Step', + exportSuccess: 'Export successful', }, model: { searchPlaceholder: 'search model…', @@ -2471,6 +2472,11 @@ Memory Bear: After the rebellion, regional warlordism intensified for several re extract: 'Project Inference', source: 'Not Added', target: 'Added', + import: 'Import Scenario', + format: 'Export Format', + export: 'Export Scenario', + scene_id: 'Scenario', + file: 'Import File', }, prompt: { editor: 'Prompt Generator', diff --git a/web/src/i18n/zh.ts b/web/src/i18n/zh.ts index 30d9481b..4e1e52e4 100644 --- a/web/src/i18n/zh.ts +++ b/web/src/i18n/zh.ts @@ -980,6 +980,7 @@ export const zh = { fileAcceptTip: '不支持的文件类型:', nextStep: '下一步', prevStep: '上一步', + exportSuccess: '导出成功', }, product: { applicationManagement: '应用管理', @@ -2560,6 +2561,11 @@ export const zh = { extract: '工程推理', source: '未添加项', target: '已添加项', + import: '导入场景', + format: '导出格式', + export: '导出场景', + scene_id: '场景', + file: '导入文件', }, prompt: { editor: '提示词生成器', diff --git a/web/src/utils/request.ts b/web/src/utils/request.ts index e7112ded..fb37d82d 100644 --- a/web/src/utils/request.ts +++ b/web/src/utils/request.ts @@ -288,19 +288,20 @@ export const request = { ...config }); }, - downloadFile(url: string, fileName: string, data?: unknown) { + downloadFile(url: string, fileName: string, data?: unknown, callback?: () => void) { service.post(url, data, { responseType: "blob", }) .then(res =>{ const link = document.createElement("a"); - const blob = new Blob([res.data], { type: "application/vnd.ms-excel" }); + const blob = new Blob([res as unknown as BlobPart]); link.style.display = "none"; link.href = URL.createObjectURL(blob); - link.setAttribute("download", decodeURI(res.headers['filename'] || fileName)); + link.setAttribute("download", decodeURI(fileName || fileName)); document.body.appendChild(link); link.click(); document.body.removeChild(link); + callback?.() }); } }; diff --git a/web/src/views/Ontology/components/OntologyClassExtractModal.tsx b/web/src/views/Ontology/components/OntologyClassExtractModal.tsx index 1aba9c67..802202ef 100644 --- a/web/src/views/Ontology/components/OntologyClassExtractModal.tsx +++ b/web/src/views/Ontology/components/OntologyClassExtractModal.tsx @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:10:42 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:10:42 + */ import { forwardRef, useImperativeHandle, useState } from 'react'; import { Form, Input, App, Transfer, type TransferProps, Flex } from 'antd'; import { useTranslation } from 'react-i18next'; @@ -12,24 +18,37 @@ import Tag from '@/components/Tag'; const FormItem = Form.Item; +/** + * Props for OntologyClassExtractModal component + */ interface OntologyClassExtractModalProps { + /** Callback function to refresh parent list after extraction */ refresh: () => void; } +/** + * Modal component for extracting ontology classes using LLM + * Two-step process: 1) Extract classes from scenario 2) Select and confirm classes to add + */ const OntologyClassExtractModal = forwardRef(({ refresh }, ref) => { + // Hooks const { t } = useTranslation(); const { message } = App.useApp(); - const [visible, setVisible] = useState(false); const [form] = Form.useForm(); + + // State + const [visible, setVisible] = useState(false); const [loading, setLoading] = useState(false) const [data, setData] = useState(null) const [extractData, setExtractData] = useState(null) const [targetKeys, setTargetKeys] = useState([]); const [selectedKeys, setSelectedKeys] = useState([]); - // 封装取消方法,添加关闭弹窗逻辑 + /** + * Close modal and reset all state + */ const handleClose = () => { setVisible(false); form.resetFields(); @@ -38,12 +57,19 @@ const OntologyClassExtractModal = forwardRef { form.resetFields(); setVisible(true); setData(vo) }; - // 封装保存方法,添加提交逻辑 + + /** + * Execute LLM extraction to get class suggestions + */ const handleSave = () => { if (!data?.scene_id) return; form @@ -69,6 +95,10 @@ const OntologyClassExtractModal = forwardRef { if (!extractData) { handleSave() @@ -92,11 +122,19 @@ const OntologyClassExtractModal = forwardRef { setTargetKeys(nextTargetKeys.filter(Boolean)); }; + /** + * Handle transfer component selection change + * @param sourceSelectedKeys - Selected keys in source list + * @param targetSelectedKeys - Selected keys in target list + */ const onSelectChange: TransferProps['onSelectChange'] = ( sourceSelectedKeys, targetSelectedKeys, @@ -104,7 +142,9 @@ const OntologyClassExtractModal = forwardRef ({ handleOpen, })); diff --git a/web/src/views/Ontology/components/OntologyClassModal.tsx b/web/src/views/Ontology/components/OntologyClassModal.tsx index 73e45060..087e542c 100644 --- a/web/src/views/Ontology/components/OntologyClassModal.tsx +++ b/web/src/views/Ontology/components/OntologyClassModal.tsx @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:10:39 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:10:39 + */ import { forwardRef, useImperativeHandle, useState } from 'react'; import { Form, Input, App } from 'antd'; import { useTranslation } from 'react-i18next'; @@ -8,33 +14,53 @@ import { createOntologyClass } from '@/api/ontology' const FormItem = Form.Item; +/** + * Props for OntologyClassModal component + */ interface OntologyClassModalProps { + /** Callback function to refresh parent list after save */ refresh: () => void; } +/** + * Modal component for adding new ontology classes + * Provides form interface for class name and description + */ const OntologyClassModal = forwardRef(({ refresh }, ref) => { + // Hooks const { t } = useTranslation(); const { message } = App.useApp(); - const [visible, setVisible] = useState(false); const [form] = Form.useForm(); + + // State + const [visible, setVisible] = useState(false); const [loading, setLoading] = useState(false) const [scene_id, setSceneId] = useState(null) - // 封装取消方法,添加关闭弹窗逻辑 + /** + * Close modal and reset form state + */ const handleClose = () => { setVisible(false); form.resetFields(); setLoading(false) }; + /** + * Open modal for adding a new class + * @param scene_id - Target scene identifier + */ const handleOpen = (scene_id: string) => { form.resetFields(); setVisible(true); setSceneId(scene_id) }; - // 封装保存方法,添加提交逻辑 + + /** + * Validate and submit form data to create new class + */ const handleSave = () => { if (!scene_id) return; form @@ -56,7 +82,9 @@ const OntologyClassModal = forwardRef ({ handleOpen, })); diff --git a/web/src/views/Ontology/components/OntologyExportModal.tsx b/web/src/views/Ontology/components/OntologyExportModal.tsx new file mode 100644 index 00000000..a40beba2 --- /dev/null +++ b/web/src/views/Ontology/components/OntologyExportModal.tsx @@ -0,0 +1,144 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:10:46 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:10:46 + */ +import { forwardRef, useImperativeHandle, useState } from 'react'; +import { Form, App, Select, type SelectProps } from 'antd'; +import { useTranslation } from 'react-i18next'; + +import type { OntologyExportModalData, OntologyExportModalRef } from '../types' +import RbModal from '@/components/RbModal' +import { ontologyExport, getOntologyScenesUrl } from '@/api/ontology' +import CustomSelect from '@/components/CustomSelect'; + +const FormItem = Form.Item; + +/** + * Props for OntologyExportModal component + */ +interface OntologyExportModalProps { + /** Callback function to refresh parent list after export */ + refresh: () => void; +} + +/** + * Modal component for exporting ontology scenes + * Supports RDF/XML (.owl) and Turtle (.ttl) formats + */ +const OntologyExportModal = forwardRef(({ + refresh +}, ref) => { + // Hooks + const { t } = useTranslation(); + const { message } = App.useApp(); + const [form] = Form.useForm(); + + // State + const [visible, setVisible] = useState(false); + const [loading, setLoading] = useState(false) + const [fileName, setFileName] = useState('') + + /** + * Close modal and reset form state + */ + const handleClose = () => { + setVisible(false); + form.resetFields(); + setLoading(false) + }; + + /** + * Open the export modal + */ + const handleOpen = () => { + form.resetFields(); + setVisible(true); + }; + + /** + * Handle scene selection change to set export filename + * @param _value - Selected scene ID + * @param option - Selected option containing scene name + */ + const handleChange: SelectProps['onChange'] = (_value, option) => { + const name = Array.isArray(option) ? option[0]?.children : option?.children; + setFileName(String(name || '')); + } + + /** + * Validate and submit form data to export ontology + * Downloads file with appropriate extension based on format + */ + const handleSave = () => { + form + .validateFields() + .then((values) => { + setLoading(true) + ontologyExport(values, `${fileName}.${values.format === 'rdfxml' ?'owl' : 'ttl'}`, () => { + message.success(t('common.exportSuccess')); + handleClose(); + refresh(); + setLoading(false) + }) + }) + .catch((err) => { + console.log('err', err) + }); + } + + /** + * Expose methods to parent component via ref + */ + useImperativeHandle(ref, () => ({ + handleOpen, + })); + + return ( + +
+ + + + + + + + + + + + + + +
+
+ ); +}); + +export default OntologyImportModal; \ No newline at end of file diff --git a/web/src/views/Ontology/components/OntologyModal.tsx b/web/src/views/Ontology/components/OntologyModal.tsx index 82eebbd2..a4c203ed 100644 --- a/web/src/views/Ontology/components/OntologyModal.tsx +++ b/web/src/views/Ontology/components/OntologyModal.tsx @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:10:28 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:10:28 + */ import { forwardRef, useImperativeHandle, useState } from 'react'; import { Form, Input, App } from 'antd'; import { useTranslation } from 'react-i18next'; @@ -8,21 +14,34 @@ import { createOntologyScene, updateOntologyScene } from '@/api/ontology' const FormItem = Form.Item; +/** + * Props for OntologyModal component + */ interface OntologyModalProps { + /** Callback function to refresh parent list after save */ refresh: () => void; } +/** + * Modal component for creating or editing ontology scenes + * Provides form interface for scene name and description + */ const OntologyModal = forwardRef(({ refresh }, ref) => { + // Hooks const { t } = useTranslation(); const { message } = App.useApp(); + const [form] = Form.useForm(); + + // State const [visible, setVisible] = useState(false); const [editVo, setEditVo] = useState(null) - const [form] = Form.useForm(); const [loading, setLoading] = useState(false) - // 封装取消方法,添加关闭弹窗逻辑 + /** + * Close modal and reset form state + */ const handleClose = () => { setVisible(false); form.resetFields(); @@ -30,6 +49,10 @@ const OntologyModal = forwardRef(({ setEditVo(null) }; + /** + * Open modal for creating or editing + * @param vo - Optional ontology item data for edit mode + */ const handleOpen = (vo?: OntologyItem) => { if (vo) { setEditVo(vo); @@ -39,7 +62,11 @@ const OntologyModal = forwardRef(({ } setVisible(true); }; - // 封装保存方法,添加提交逻辑 + + /** + * Validate and submit form data + * Creates new scene or updates existing one based on editVo + */ const handleSave = () => { form .validateFields() @@ -59,7 +86,9 @@ const OntologyModal = forwardRef(({ }); } - // 暴露给父组件的方法 + /** + * Expose methods to parent component via ref + */ useImperativeHandle(ref, () => ({ handleOpen, })); diff --git a/web/src/views/Ontology/components/PageHeader.tsx b/web/src/views/Ontology/components/PageHeader.tsx index 81009596..087a6d49 100644 --- a/web/src/views/Ontology/components/PageHeader.tsx +++ b/web/src/views/Ontology/components/PageHeader.tsx @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:10:24 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:10:56 + */ import { type FC, type ReactNode } from 'react'; import { useNavigate } from 'react-router-dom'; import { Layout, Button } from 'antd'; @@ -6,11 +12,23 @@ import logoutIcon from '@/assets/images/logout_hover.svg' const { Header } = Layout; +/** + * Props for PageHeader component + */ interface ConfigHeaderProps { + /** Page title/name */ name?: string; + /** Subtitle content displayed below the title */ subTitle?: ReactNode | string; + /** Extra content displayed on the right side */ extra?: ReactNode; } + +/** + * Page header component for ontology pages + * Displays title, subtitle, back button and extra actions + * @param props - Component props + */ const PageHeader: FC = ({ name, subTitle, @@ -19,6 +37,9 @@ const PageHeader: FC = ({ const { t } = useTranslation(); const navigate = useNavigate(); + /** + * Navigate back to previous page + */ const goBack = () => { navigate(-1) } diff --git a/web/src/views/Ontology/index.tsx b/web/src/views/Ontology/index.tsx index 4d32dfa6..42a6544f 100644 --- a/web/src/views/Ontology/index.tsx +++ b/web/src/views/Ontology/index.tsx @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:10:15 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:10:15 + */ import { type FC, useState, useRef, type MouseEvent } from 'react'; import { useNavigate } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; @@ -5,29 +11,57 @@ import { Row, Col, Button, Flex, Divider, Space, App, Tooltip } from 'antd' import SearchInput from '@/components/SearchInput'; import OntologyModal from './components/OntologyModal' -import type { OntologyModalRef, OntologyItem, Query } from './types' +import type { OntologyModalRef, OntologyItem, Query, OntologyImportModalRef, OntologyExportModalRef } from './types' import RbCard from '@/components/RbCard/Card' import Tag from '@/components/Tag' import PageScrollList, { type PageScrollListRef } from '@/components/PageScrollList' import { getOntologyScenesUrl, deleteOntologyScene } from '@/api/ontology' import { formatDateTime } from '@/utils/format' +import OntologyImportModal from './components/OntologyImportModal' +import OntologyExportModal from './components/OntologyExportModal' +/** + * Ontology management page component + * Displays a list of ontology scenes with search, create, import, export functionality + */ const Ontology: FC = () => { + // Hooks const { t } = useTranslation(); const navigate = useNavigate() const { modal, message } = App.useApp(); + + // State const [query, setQuery] = useState({}); + + // Refs const scrollListRef = useRef(null) const entityModalRef = useRef(null) + const ontologyImportModalRef = useRef(null) + const ontologyExportModalRef = useRef(null) + /** + * Open modal to create a new ontology scene + */ const handleCreate = () => { entityModalRef.current?.handleOpen() } + + /** + * Open modal to edit an existing ontology scene + * @param record - The ontology item to edit + * @param e - Mouse event to prevent propagation + */ const handleEdit = (record: OntologyItem, e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); entityModalRef.current?.handleOpen(record) } + + /** + * Delete an ontology scene with confirmation + * @param item - The ontology item to delete + * @param e - Mouse event to prevent propagation + */ const handleDelete = (item: OntologyItem, e: MouseEvent) => { e.preventDefault(); e.stopPropagation(); @@ -45,9 +79,35 @@ const Ontology: FC = () => { } }) } + + /** + * Navigate to ontology detail page + * @param record - The ontology item to view + */ const handleJump = (record: OntologyItem) => { navigate(`/ontology/${record.scene_id}`) } + + /** + * Refresh the ontology list + */ + const handleRefresh = () => { + scrollListRef.current?.refresh() + } + + /** + * Open export modal + */ + const handleExport = () => { + ontologyExportModalRef.current?.handleOpen() + } + + /** + * Open import modal + */ + const handleImport = () => { + ontologyImportModalRef.current?.handleOpen() + } return ( <> @@ -60,9 +120,17 @@ const Ontology: FC = () => { /> - + + + + + @@ -124,7 +192,15 @@ const Ontology: FC = () => { scrollListRef.current?.refresh()} + refresh={handleRefresh} + /> + + ) diff --git a/web/src/views/Ontology/pages/Detail.tsx b/web/src/views/Ontology/pages/Detail.tsx index b7123c5c..a61a83a2 100644 --- a/web/src/views/Ontology/pages/Detail.tsx +++ b/web/src/views/Ontology/pages/Detail.tsx @@ -1,3 +1,9 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:10:20 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:10:20 + */ import { type FC, useEffect, useState, useRef } from 'react' import { useParams } from 'react-router-dom'; import { useTranslation } from 'react-i18next'; @@ -12,22 +18,35 @@ import SearchInput from '@/components/SearchInput'; import OntologyClassExtractModal from '../components/OntologyClassExtractModal' import BodyWrapper from '@/components/Empty/BodyWrapper' +/** + * Ontology detail page component + * Displays and manages classes within a specific ontology scene + */ const Detail: FC = () => { + // Hooks const { t } = useTranslation(); const { id } = useParams() const { modal, message } = App.useApp() + + // Refs const ontologyClassModalRef = useRef(null) const ontologyClassExtractModalRef = useRef(null) + + // State const [query, setQuery] = useState<{ class_name?: string; }>({}); const [loading, setLoading] = useState(false) const [data, setData] = useState({} as OntologyClassData) + // Fetch data when component mounts or dependencies change useEffect(() => { getData() }, [id, query]) + /** + * Fetch ontology class list data + */ const getData = () => { if (!id) return; setLoading(true) @@ -42,6 +61,11 @@ const Detail: FC = () => { setLoading(false) }) } + + /** + * Delete an ontology class with confirmation + * @param item - The class item to delete + */ const handleDelete = (item: OntologyClassItem) => { modal.confirm({ title: t('common.confirmDeleteDesc', { name: item.class_name }), @@ -57,9 +81,17 @@ const Detail: FC = () => { } }) } + + /** + * Open modal to add a new class + */ const handleAdd = () => { ontologyClassModalRef.current?.handleOpen(data.scene_id) } + + /** + * Open modal to extract classes using LLM + */ const handleExtract = () => { ontologyClassExtractModalRef.current?.handleOpen(data) } diff --git a/web/src/views/Ontology/types.ts b/web/src/views/Ontology/types.ts index fc9af2ea..d78d8464 100644 --- a/web/src/views/Ontology/types.ts +++ b/web/src/views/Ontology/types.ts @@ -1,79 +1,214 @@ +/* + * @Author: ZhaoYing + * @Date: 2026-02-03 14:10:10 + * @Last Modified by: ZhaoYing + * @Last Modified time: 2026-02-03 14:10:10 + */ +/** + * Query parameters for ontology list pagination and filtering + */ export interface Query { + /** Number of items per page */ pagesize?: number; + /** Current page number */ page?: number; + /** Scene name for filtering */ scene_name?: string; } +/** + * Ontology scene item data structure + */ export interface OntologyItem { + /** Unique identifier for the scene */ scene_id: string; + /** Name of the ontology scene */ scene_name: string; + /** Description of the ontology scene */ scene_description: string; + /** Number of entity types in the scene */ type_num: number; + /** Array of entity type names */ entity_type: string[]; + /** Associated workspace identifier */ workspace_id: string; + /** Creation timestamp */ created_at: number; + /** Last update timestamp */ updated_at: number; + /** Total count of classes in the scene */ classes_count: number; } +/** + * Form data for creating/editing ontology scene + */ export interface OntologyModalData { + /** Scene name */ scene_name: string; + /** Scene description */ scene_description: string; } +/** + * Ref methods exposed by OntologyModal component + */ export interface OntologyModalRef { + /** + * Open the modal for creating or editing + * @param data - Optional ontology item data for editing mode + */ handleOpen: (data?: OntologyItem) => void; } +/** + * Ontology class item data structure + */ export interface OntologyClassItem { + /** Unique identifier for the class */ class_id: string; + /** Name of the class */ class_name: string; + /** Description of the class */ class_description: string; + /** Associated scene identifier */ scene_id: string; + /** Creation timestamp */ created_at: number; + /** Last update timestamp */ updated_at: number; } +/** + * Response data structure for ontology class list + */ export interface OntologyClassData { + /** Total number of classes */ total: number; + /** Scene identifier */ scene_id: string; + /** Scene name */ scene_name: string; + /** Scene description */ scene_description: string; + /** Array of class items */ items: OntologyClassItem[]; } +/** + * Data structure for adding a new class + */ export interface AddClassItem { + /** Name of the class to add */ class_name: string; + /** Description of the class to add */ class_description: string; } +/** + * Form data for creating ontology classes + */ export interface OntologyClassModalData { + /** Target scene identifier */ scene_id: string; + /** Array of classes to create */ classes: AddClassItem[] } +/** + * Ref methods exposed by OntologyClassModal component + */ export interface OntologyClassModalRef { + /** + * Open the modal for adding classes + * @param scene_id - Target scene identifier + */ handleOpen: (scene_id: string) => void; } +/** + * Form data for extracting ontology classes using LLM + */ export interface OntologyClassExtractModalData { + /** LLM model identifier */ llm_id: string; + /** Target scene identifier */ scene_id: string; + /** Scenario description for extraction */ scenario: string; - domain: string; // scene_name + /** Domain name (same as scene_name) */ + domain: string; } +/** + * Ref methods exposed by OntologyClassExtractModal component + */ export interface OntologyClassExtractModalRef { + /** + * Open the modal for extracting classes + * @param vo - Ontology class data containing scene information + */ handleOpen: (vo: OntologyClassData) => void; } +/** + * Extracted class item from LLM + */ export interface ExtractClassItem { + /** Unique identifier for the extracted class */ id: string; + /** English name of the class */ name: string; + /** Chinese name of the class */ name_chinese: string; + /** Description of the class */ description: string; + /** Example instances of the class */ examples: string[]; + /** Parent class name if exists */ parent_class: string | null; + /** Entity type classification */ entity_type: string; + /** Domain the class belongs to */ domain: string; } +/** + * Response data structure for class extraction + */ export interface ExtractData { + /** Domain name */ domain: string; + /** Number of classes extracted */ extracted_count: number; + /** Array of extracted class items */ classes: ExtractClassItem[] } +/** + * Ref methods exposed by OntologyImportModal component + */ +export interface OntologyImportModalRef { + /** Open the import modal */ + handleOpen: () => void; +} +/** + * Form data for importing ontology + */ +export interface OntologyImportModalData { + /** Name for the imported scene */ + scene_name: string; + /** Optional description for the imported scene */ + scene_description?: string; + /** File to import (OWL, TTL, RDF, XML formats) */ + file: any; +} +/** + * Ref methods exposed by OntologyExportModal component + */ +export interface OntologyExportModalRef { + /** Open the export modal */ + handleOpen: () => void; +} +/** + * Form data for exporting ontology + */ +export interface OntologyExportModalData { + /** Scene identifier to export */ + scene_id: string; + /** Export format: 'rdfxml' (.owl) or 'turtle' (.ttl) */ + format: 'rdfxml' | 'turtle'; +} \ No newline at end of file