diff --git a/ui/AGENTS.md b/ui/AGENTS.md index 9c5cfd7d74f..f01b466ffff 100644 --- a/ui/AGENTS.md +++ b/ui/AGENTS.md @@ -67,7 +67,7 @@ ui/ │ │ └── / # Less-frequent shared components imported explicitly │ ├── constants/ # Cross-feature constants grouped by domain │ │ └── CONSTANT_README.md # Shared constant placement and naming rules -│ ├── layout/ # Shared application shells, headers, sidebars, and layout types +│ ├── layout/ # Shared application shells, headers, sidebars, types, and helpers │ ├── locales/ # Reserved for internationalization messages and locale setup │ ├── workflow-canvas/ # LogicFlow canvas and workflow domain components │ │ ├── component/ # Reusable workflow canvas UI @@ -124,6 +124,10 @@ Structural responsibilities: through `useStore()`. Store modules may import a specific Store directly when coordinating state internally to avoid circular access through the aggregate entry. - Put functions shared by most pages or multiple business modules in `utils/`; detailed placement, file organization, and naming rules are maintained in `src/utils/UTILS_README.md`. +- Treat application, knowledge, model, and tool as the four special resource domains that can span + Workspace, System resource management, and System shared resources. Their route scope, API + selection, reusable cards, and context-specific Actions follow `ROUTE_README.md`, + `API_README.md`, and `VIEW_README.md`; do not extend this three-scope pattern to ordinary pages. - Import source assets through `src/assets/`; use `public/` only when a file must retain its original filename and URL. ## Entry Points diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index aef6a8e4cd3..cb099642282 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -6,21 +6,32 @@ Admin API;Chat 目录作为独立体系预留。 ```text src/api/ ├── admin/ -│ ├── core/ # Admin 请求基础能力 -│ │ ├── request.ts # Axios 实例、HTTP 方法与统一响应解包 -│ │ └── types.ts # Admin 请求协议和 loading 类型 -│ ├── auth/ # Admin 登录认证接口 -│ │ └── types.ts # 认证 API 与认证 Store 共用类型 -│ ├── workspace/ # 工作空间业务接口 -│ └── system/ # 系统管理业务接口 -├── chat/ # Chat 独立请求体系,当前预留 -├── enums/ # 后端固定枚举值 -│ ├── index.ts # API 枚举值的唯一导入入口 -│ └── .ts # 按明确业务域拆分的枚举值 -├── types/ # API 与 View/Component 共用的业务类型 -│ ├── index.ts # API 公共类型的唯一导入入口 -│ ├── common.ts # 多个 API 业务域共用的基础类型 -│ └── .ts # 按明确业务域拆分的共享类型 +│ ├── auth/ # Admin 登录认证与当前用户接口 +│ │ └── types.ts # 认证 API 与认证 Store 共用类型 +│ ├── core/ # Admin 请求基础能力 +│ │ ├── request.ts # Axios 实例、HTTP 方法与统一响应解包 +│ │ └── types.ts # Admin 请求协议和 loading 类型 +│ ├── system/ # 系统管理业务接口 +│ │ ├── chat-user/ # 对话用户、用户组及认证接口 +│ │ ├── settings/ # 登录认证、邮件与外观设置接口 +│ │ ├── shared-resources/ # System 共享资源接口 +│ │ └── .ts # 其他 System 单资源接口 +│ ├── workspace/ # 工作空间业务接口 +│ │ ├── application/ # 智能体接口 +│ │ ├── knowledge/ # 知识库接口 +│ │ ├── model/ # 模型接口 +│ │ ├── tool/ # 工具及工具商店接口 +│ │ └── .ts # 工作空间公共资源接口 +│ └── provider.ts # Workspace 与 System 共用的模型供应商接口 +├── chat/ # Chat 独立请求体系,当前预留 +│ └── README.md # Chat API 边界与后续实现说明 +├── enums/ # 后端固定枚举值 +│ ├── index.ts # API 枚举值的唯一导入入口 +│ └── .ts # 按明确业务域拆分的枚举值 +├── types/ # API 与 View/Component 共用的业务类型 +│ ├── index.ts # API 公共类型的唯一导入入口 +│ ├── common.ts # 多个 API 业务域共用的基础类型 +│ └── .ts # 按明确业务域拆分的共享类型 └── API_README.md ``` @@ -35,9 +46,12 @@ src/api/ ## 业务接口组织 -- 业务 API 按一级业务域、二级资源文件归类,例如 `workspace/agent.ts` 和 - `system/workspace.ts`。 -- 一类资源的增删改查放在同一个文件,不创建同名业务文件夹或汇总所有业务接口的 `api.ts`。 +- 业务 API 先按 Admin 入口下的 `auth`、`workspace`、`system` 等一级业务域归类。Workspace 和 + System 内部可继续按明确的功能域建立子目录,例如 `workspace/application/`、 + `workspace/tool/`、`system/chat-user/` 和 `system/settings/`;不需要分组的单资源接口直接放在 + 所属一级业务域下。 +- 一类资源的增删改查放在同一个最终资源文件中。调用方直接导入该文件,不为业务目录创建聚合 + 入口,也不创建汇总所有业务接口的 `api.ts`。 - 每个业务接口函数必须添加简短的 JSDoc,说明接口的业务作用;注释应描述“获取什么”“保存什么” 或“对哪个资源执行什么操作”,不重复参数类型、请求方法等代码已经清楚表达的信息。 - 每个业务接口使用 `const` 声明的箭头函数,不单独具名导出;在文件末尾通过 @@ -47,7 +61,20 @@ src/api/ `admin/core/request.ts` 等请求基础设施可按职责提供具名导出。 例如从 `login.ts` 使用 `import LoginApi from '@/api/admin/auth/login'`,再调用 `LoginApi.postLogin()`;System Workspace API 使用 - `import WorkspaceApi from '@/api/admin/system/workspace'`。 + `import WorkspaceApi from '@/api/admin/system/workspace'`;System 登录设置使用 + `import AuthSettingApi from '@/api/admin/system/settings/auth-setting'`。 + +### 四类特殊资源 API + +`application`、`knowledge`、`model`、`tool` 是需要同时考虑 Workspace、System 资源管理和 +System 共享资源的四类特殊资源。其接口按真实后端边界分别维护在 `admin/workspace/` 与 +`admin/system/` 下,不把不同范围的 URL 合并为页面侧 API Map,也不让卡片或 Action 根据路由拼接 +System 接口地址。 + +页面根据路由 `resourceScope` 选择当前范围的完整业务 API 对象,并将其传给需要请求的 Card +Action、Drawer 或 Dialog。复用方直接使用 `typeof XxxApi` 约束完整 API 对象;不要为每组 Action +额外维护逐方法接口,例如 `ModelActionApi`,也不要使用不断扩展的 `Pick`。 +仅展示数据的组件不接收 API。 ## 枚举与类型组织 diff --git a/ui/src/api/admin/workspace/model/provider.ts b/ui/src/api/admin/model-provider.ts similarity index 97% rename from ui/src/api/admin/workspace/model/provider.ts rename to ui/src/api/admin/model-provider.ts index c5c93397053..413b0ed3e9a 100644 --- a/ui/src/api/admin/workspace/model/provider.ts +++ b/ui/src/api/admin/model-provider.ts @@ -1,4 +1,4 @@ -import { get } from '../../core/request' +import { get } from './core/request' import type { BaseModelOption, DynamicFormField, @@ -36,7 +36,6 @@ const getBaseModelParamsForm = (provider: string, modelType: string, modelName: }) } - /** 获取供应商支持的模型类型。 */ const getModelTypeList = (provider: string) => { return get(`${prefix}/model_type_list`, { provider }) diff --git a/ui/src/api/admin/system/chat-user-auth-scan.ts b/ui/src/api/admin/system/chat-user/chat-user-auth-scan.ts similarity index 93% rename from ui/src/api/admin/system/chat-user-auth-scan.ts rename to ui/src/api/admin/system/chat-user/chat-user-auth-scan.ts index 2a3f709dbba..f1719987be0 100644 --- a/ui/src/api/admin/system/chat-user-auth-scan.ts +++ b/ui/src/api/admin/system/chat-user/chat-user-auth-scan.ts @@ -1,4 +1,4 @@ -import { get, post, put } from '../core/request' +import { get, post, put } from '../../core/request' import type { QrLoginPlatform, QrLoginPlatformPayload } from '@/api/types' const prefix = '/chat_user/auth/platform/source' diff --git a/ui/src/api/admin/system/chat-user-auth.ts b/ui/src/api/admin/system/chat-user/chat-user-auth.ts similarity index 94% rename from ui/src/api/admin/system/chat-user-auth.ts rename to ui/src/api/admin/system/chat-user/chat-user-auth.ts index 3cb4276cc40..7424beef1ba 100644 --- a/ui/src/api/admin/system/chat-user-auth.ts +++ b/ui/src/api/admin/system/chat-user/chat-user-auth.ts @@ -1,4 +1,4 @@ -import { get, post, put } from '../core/request' +import { get, post, put } from '../../core/request' import type { AuthProviderSettingPayload, AuthProviderType } from '@/api/types' const prefix = '/chat_user/auth' diff --git a/ui/src/api/admin/system/chat-user-groups.ts b/ui/src/api/admin/system/chat-user/chat-user-groups.ts similarity index 92% rename from ui/src/api/admin/system/chat-user-groups.ts rename to ui/src/api/admin/system/chat-user/chat-user-groups.ts index dc9fb10da3c..4a234a7299f 100644 --- a/ui/src/api/admin/system/chat-user-groups.ts +++ b/ui/src/api/admin/system/chat-user/chat-user-groups.ts @@ -1,5 +1,5 @@ -import { del, get, post } from '../core/request' -import type { ParamsPage, ResponsePage } from '../core/types' +import { del, get, post } from '../../core/request' +import type { ParamsPage, ResponsePage } from '../../core/types' import type { ChatUserGroupMember, ChatUserGroupPayload, ListItem, Dict } from '@/api/types' const prefix = '/system/group' diff --git a/ui/src/api/admin/system/chat-user.ts b/ui/src/api/admin/system/chat-user/chat-user.ts similarity index 97% rename from ui/src/api/admin/system/chat-user.ts rename to ui/src/api/admin/system/chat-user/chat-user.ts index f70007728bd..d8eb5cd0978 100644 --- a/ui/src/api/admin/system/chat-user.ts +++ b/ui/src/api/admin/system/chat-user/chat-user.ts @@ -1,5 +1,5 @@ -import { del, get, post, put } from '../core/request' -import type { ParamsPage, ResponsePage, PasswordRequest } from '../core/types' +import { del, get, post, put } from '../../core/request' +import type { ParamsPage, ResponsePage, PasswordRequest } from '../../core/types' import type { BatchSetChatUserQuotaRequest, BatchSetChatUserQuotaResult, diff --git a/ui/src/api/admin/system/auth-scan-setting.ts b/ui/src/api/admin/system/settings/auth-scan-setting.ts similarity index 93% rename from ui/src/api/admin/system/auth-scan-setting.ts rename to ui/src/api/admin/system/settings/auth-scan-setting.ts index b79d7e8e84f..f01e9b988f9 100644 --- a/ui/src/api/admin/system/auth-scan-setting.ts +++ b/ui/src/api/admin/system/settings/auth-scan-setting.ts @@ -1,4 +1,4 @@ -import { get, post, put } from '../core/request' +import { get, post, put } from '../../core/request' import type { QrLoginPlatform, QrLoginPlatformPayload } from '@/api/types' const prefix = '/platform/source' diff --git a/ui/src/api/admin/system/auth-setting.ts b/ui/src/api/admin/system/settings/auth-setting.ts similarity index 95% rename from ui/src/api/admin/system/auth-setting.ts rename to ui/src/api/admin/system/settings/auth-setting.ts index f796646eb25..7aed58f4897 100644 --- a/ui/src/api/admin/system/auth-setting.ts +++ b/ui/src/api/admin/system/settings/auth-setting.ts @@ -1,4 +1,4 @@ -import { get, post, put } from '../core/request' +import { get, post, put } from '../../core/request' import type { AuthProviderSettingPayload, AuthProviderType, LoginAuthSettingPayload } from '@/api/types' const prefix = '/auth' diff --git a/ui/src/api/admin/system/email-setting.ts b/ui/src/api/admin/system/settings/email-setting.ts similarity index 92% rename from ui/src/api/admin/system/email-setting.ts rename to ui/src/api/admin/system/settings/email-setting.ts index e43591e2d7c..b55d1edea7b 100644 --- a/ui/src/api/admin/system/email-setting.ts +++ b/ui/src/api/admin/system/settings/email-setting.ts @@ -1,4 +1,4 @@ -import { get, post, put } from '../core/request' +import { get, post, put } from '../../core/request' import type { EmailSettingPayload } from '@/api/types' const prefix = '/email_setting' diff --git a/ui/src/api/admin/system/theme-setting.ts b/ui/src/api/admin/system/settings/theme-setting.ts similarity index 66% rename from ui/src/api/admin/system/theme-setting.ts rename to ui/src/api/admin/system/settings/theme-setting.ts index e09ade6a570..a9ed6b1aff4 100644 --- a/ui/src/api/admin/system/theme-setting.ts +++ b/ui/src/api/admin/system/settings/theme-setting.ts @@ -1,5 +1,5 @@ -import { get, put } from '../core/request' -import type { ThemeInfo } from '../auth/types' +import { get, put } from '../../core/request' +import type { ThemeInfo } from '../../auth/types' /** 保存系统外观主题设置。 */ const putThemeSetting = (payload: FormData) => { diff --git a/ui/src/api/admin/system/shared-resources/model.ts b/ui/src/api/admin/system/shared-resources/model.ts new file mode 100644 index 00000000000..cb3f6edbe07 --- /dev/null +++ b/ui/src/api/admin/system/shared-resources/model.ts @@ -0,0 +1,64 @@ +import { del, get, post, put } from '../../core/request' +import type { Dict, DynamicFormField, ModelItem, ModelPayload } from '@/api/types' + +const prefix = '/system/shared/model' + +/** + * 获得模型列表 + * @params 参数 name, model_type, model_name + */ +const getModelList = (query?: Dict) => { + return get(prefix, query) +} + +/** 创建 System 共享模型。 */ +const postModel = (payload: ModelPayload) => { + return post(prefix, payload) +} + +/** 获取包含认证信息的 System 共享模型详情。 */ +const getModelDetail = (modelId: string) => { + return get(`${prefix}/${modelId}`) +} + +/** 更新 System 共享模型。 */ +const putModel = (modelId: string, payload: Partial) => { + return put, ModelItem>(`${prefix}/${modelId}`, payload) +} + +/** 删除 System 共享模型。 */ +const deleteModel = (modelId: string) => { + return del(`${prefix}/${modelId}`) +} + +/** 获取 System 共享模型参数表单。 */ +const getModelParamsForm = (modelId: string) => { + return get(`${prefix}/${modelId}/model_params_form`) +} + +/** 保存 System 共享模型参数表单。 */ +const putModelParamsForm = (modelId: string, payload: DynamicFormField[]) => { + return put(`${prefix}/${modelId}/model_params_form`, payload) +} + +/** 获取不包含认证信息的 System 共享模型元数据。 */ +const getModelMeta = (modelId: string) => { + return get(`${prefix}/${modelId}/meta`) +} + +/** 暂停 System 共享本地模型下载。 */ +const putPauseModelDownload = (modelId: string) => { + return put(`${prefix}/${modelId}/pause_download`) +} + +export default { + deleteModel, + getModelDetail, + getModelList, + getModelMeta, + getModelParamsForm, + postModel, + putModel, + putModelParamsForm, + putPauseModelDownload, +} diff --git a/ui/src/api/admin/tool-store.ts b/ui/src/api/admin/tool-store.ts new file mode 100644 index 00000000000..6c82edbee90 --- /dev/null +++ b/ui/src/api/admin/tool-store.ts @@ -0,0 +1,35 @@ +import { get, post } from './core/request' +import type { Dict, ToolItem, ToolStoreResponse } from '@/api/types' + +/** 获取系统内置工具。 */ +const getInternalToolList = (query?: Dict) => { + return get('/workspace/internal/tool', query) +} + +/** 获取工具商店列表。 */ +const getStoreToolList = (query?: Dict) => { + return get('/workspace/store/tool', query) +} + +/** 获取知识库模板商店列表。 */ +const getStoreKnowledgeList = (query?: Dict) => { + return get('/workspace/store/knowledge_template', query) +} + +/** 获取工作流工具模板商店列表。 */ +const getStoreToolWorkflowList = (query?: Dict) => { + return get('/workspace/store/tool_workflow_template', query) +} + +/** 获取应用模板商店列表。 */ +const getStoreApplicationList = (query?: Dict) => { + return get('/workspace/store/application_template', query) +} + +export default { + getInternalToolList, + getStoreApplicationList, + getStoreKnowledgeList, + getStoreToolList, + getStoreToolWorkflowList, +} diff --git a/ui/src/api/admin/workspace/application/application.ts b/ui/src/api/admin/workspace/application/application.ts index 04c503f42cf..99c7f16d3cd 100644 --- a/ui/src/api/admin/workspace/application/application.ts +++ b/ui/src/api/admin/workspace/application/application.ts @@ -1,7 +1,7 @@ import { get, post, put } from '../../core/request' import type { ParamsPage, ResponsePage } from '../../core/types' import type { ApplicationDetail, ApplicationFormPayload, Dict } from '@/api/types' -import { getWorkspaceId } from '@/utils/workspace-context' +import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = () => { const workspaceId = getWorkspaceId() diff --git a/ui/src/api/admin/workspace/common.ts b/ui/src/api/admin/workspace/common.ts index 891cf1b0bbc..e5a490ac731 100644 --- a/ui/src/api/admin/workspace/common.ts +++ b/ui/src/api/admin/workspace/common.ts @@ -1,6 +1,6 @@ import { get } from '../core/request' import type { Dict, WorkspaceUserOption } from '@/api/types' -import { getWorkspaceId } from '@/utils/workspace-context' +import { getWorkspaceId } from '@/utils/resource-context' /** 获取当前工作空间下的用户选项。 */ const getAllUsers = (query?: Dict) => { diff --git a/ui/src/api/admin/workspace/folder.ts b/ui/src/api/admin/workspace/folder.ts index ac0bc7cbe2b..1277918dadd 100644 --- a/ui/src/api/admin/workspace/folder.ts +++ b/ui/src/api/admin/workspace/folder.ts @@ -1,6 +1,6 @@ import { del, get, post, put } from '../core/request' import type { Dict, FolderSource, FolderItem, FolderPayload } from '@/api/types' -import { getWorkspaceId } from '@/utils/workspace-context' +import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = (source: FolderSource) => { const workspaceId = getWorkspaceId() diff --git a/ui/src/api/admin/workspace/knowledge/knowledge.ts b/ui/src/api/admin/workspace/knowledge/knowledge.ts index 943621bdd53..3a37b838628 100644 --- a/ui/src/api/admin/workspace/knowledge/knowledge.ts +++ b/ui/src/api/admin/workspace/knowledge/knowledge.ts @@ -1,7 +1,7 @@ import { del, get } from '../../core/request' import type { ParamsPage, ResponsePage } from '../../core/types' import type { Dict, KnowledgeItem } from '@/api/types' -import { getWorkspaceId } from '@/utils/workspace-context' +import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = () => { const workspaceId = getWorkspaceId() diff --git a/ui/src/api/admin/workspace/model/model.ts b/ui/src/api/admin/workspace/model/model.ts index 28a73dfea6a..bb171300c35 100644 --- a/ui/src/api/admin/workspace/model/model.ts +++ b/ui/src/api/admin/workspace/model/model.ts @@ -1,6 +1,6 @@ import { del, get, post, put } from '../../core/request' import type { Dict, DynamicFormField, ModelPayload, ModelItem } from '@/api/types' -import { getWorkspaceId } from '@/utils/workspace-context' +import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = () => { const workspaceId = getWorkspaceId() diff --git a/ui/src/api/admin/workspace/shared.ts b/ui/src/api/admin/workspace/shared.ts index 68c6b8a6d32..6abef947c9c 100644 --- a/ui/src/api/admin/workspace/shared.ts +++ b/ui/src/api/admin/workspace/shared.ts @@ -1,7 +1,7 @@ import { get } from '../core/request' import type { ParamsPage, ResponsePage } from '../core/types' import type { Dict, KnowledgeItem, ModelItem, ToolItem } from '@/api/types' -import { getWorkspaceId } from '@/utils/workspace-context' +import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = () => { const workspaceId = getWorkspaceId() diff --git a/ui/src/api/admin/workspace/tool/store.ts b/ui/src/api/admin/workspace/tool/store.ts index 19d06e514e6..fb8a41b12a6 100644 --- a/ui/src/api/admin/workspace/tool/store.ts +++ b/ui/src/api/admin/workspace/tool/store.ts @@ -1,53 +1,40 @@ -import { get, post } from '../../core/request' -import type { Dict, ToolItem } from '@/api/types' - -/** 获取系统内置工具。 */ -const getInternalToolList = (query?: Dict) => { - return get('/workspace/internal/tool', query) -} - -/** 获取工具商店列表。 */ -const getStoreToolList = (query?: Dict) => { - return get('/workspace/store/tool', query) +import { post } from '../../core/request' +import type { + AddInternalToolPayload, + AddStoreToolPayload, + ToolItem, + UpdateStoreToolPayload, +} from '@/api/types' +import { getWorkspaceId } from '@/utils/resource-context' + +const getToolPrefix = () => { + const workspaceId = getWorkspaceId() + return `/workspace/${workspaceId}/tool` } -/** 获取知识库模板商店列表。 */ -const getStoreKnowledgeList = (query?: Dict) => { - return get('/workspace/store/knowledge_template', query) +/** 将系统内置工具添加到当前工作空间。 */ +const postInternalTool = (toolId: string, payload: AddInternalToolPayload) => { + return post( + `${getToolPrefix()}/${toolId}/add_internal_tool`, + payload, + ) } -/** 获取工作流工具模板商店列表。 */ -const getStoreToolWorkflowList = (query?: Dict) => { - return get('/workspace/store/tool_workflow_template', query) +/** 将商店工具添加到当前工作空间。 */ +const postStoreTool = (toolId: string, payload: AddStoreToolPayload) => { + return post(`${getToolPrefix()}/${toolId}/add_store_tool`, payload) } -/** 获取应用模板商店列表。 */ -const getStoreApplicationList = (query?: Dict) => { - return get('/workspace/store/application_template', query) +/** 将工作空间中的商店工具更新到最新版本。 */ +const postStoreToolUpdate = (toolId: string, payload: UpdateStoreToolPayload) => { + return post( + `${getToolPrefix()}/${toolId}/update_store_tool`, + payload, + ) } -// /** 将系统内置工具添加到工作空间。 */ -// const postInternalTool = (workspaceId: string, toolId: string, payload: AddStoreToolPayload) => { -// return post( -// `/workspace/${workspaceId}/tool/${toolId}/add_internal_tool`, -// payload, -// ) -// } - -// /** 将商店工具添加到工作空间。 */ -// const postStoreTool = (workspaceId: string, toolId: string, payload: AddStoreToolPayload) => { -// return post( -// `/workspace/${workspaceId}/tool/${toolId}/add_store_tool`, -// payload, -// ) -// } - export default { - getInternalToolList, - getStoreApplicationList, - getStoreKnowledgeList, - getStoreToolList, - getStoreToolWorkflowList, - // postInternalTool, - // postStoreTool, + postInternalTool, + postStoreTool, + postStoreToolUpdate, } diff --git a/ui/src/api/admin/workspace/tool/tool.ts b/ui/src/api/admin/workspace/tool/tool.ts index ad2f7f2cd3c..837e8a8f7ec 100644 --- a/ui/src/api/admin/workspace/tool/tool.ts +++ b/ui/src/api/admin/workspace/tool/tool.ts @@ -1,7 +1,7 @@ import { del, getExportFile, get, post, put } from '../../core/request' import type { ParamsPage, ResponsePage } from '../../core/types' import type { Dict, ToolDebugPayload, ToolItem, ToolPayload, ToolPylintIssue } from '@/api/types' -import { getWorkspaceId } from '@/utils/workspace-context' +import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = () => { const workspaceId = getWorkspaceId() @@ -41,6 +41,18 @@ const postToolImport = (file: File, folderId: string) => { return post(`${getPrefix()}/import`, payload) } +/** 上传 Skill 压缩包并返回临时文件 ID。 */ +const postSkillFile = (file: File) => { + const payload = new FormData() + payload.append('file', file) + return post(`${getPrefix()}/upload_skill_file`, payload) +} + +/** 下载 Skill 工具的压缩包。 */ +const downloadSkillFile = (toolId: string, fileName: string) => { + return getExportFile(fileName, `${getPrefix()}/${toolId}/download_skill_file`) +} + /** 导出工作空间工具文件。 */ const exportTool = (toolId: string, toolName: string) => { return getExportFile(`${toolName}.tool`, `${getPrefix()}/${toolId}/export`) @@ -56,7 +68,7 @@ const postToolPylint = (code: string) => { // return postStream(`${p}${getPrefix()}/generate_code`, data) // } -/** 调试自定义工具代码并返回运行结果。 */ +/** 调试普通工具代码并返回运行结果。 */ const postToolDebug = (payload: ToolDebugPayload) => { return post(`${getPrefix()}/debug`, payload) } @@ -86,10 +98,12 @@ export default { exportTool, deleteTool, getToolDetail, + downloadSkillFile, postTool, postToolDebug, postToolImport, + postSkillFile, postToolPylint, postToolTestConnection, putBatchDeleteTools, diff --git a/ui/src/api/types/tool.ts b/ui/src/api/types/tool.ts index cb80f952bc5..47ac65ded7a 100644 --- a/ui/src/api/types/tool.ts +++ b/ui/src/api/types/tool.ts @@ -1,6 +1,7 @@ /** Workspace 工具列表和工具维护共用的业务类型。 */ import { TOOL_SCOPE, TOOL_TYPE } from '@/api/enums' +import type { DynamicFormField } from './model' export type ToolScope = (typeof TOOL_SCOPE)[keyof typeof TOOL_SCOPE] export type ToolType = (typeof TOOL_TYPE)[keyof typeof TOOL_TYPE] @@ -16,18 +17,6 @@ export interface ToolInputField { type: ToolInputFieldType } -export interface ToolInitField { - attrs?: Record - default_value?: unknown - field: string - input_type: string - label: string - option_list?: Record[] - props_info?: Record - required: boolean - show_default_value?: boolean -} - export interface ToolDebugField extends ToolInputField { value: string } @@ -35,7 +24,7 @@ export interface ToolDebugField extends ToolInputField { export interface ToolDebugPayload { code: string debug_field_list: ToolDebugField[] - init_field_list: ToolInitField[] + init_field_list: DynamicFormField[] init_params: Record input_field_list: ToolInputField[] } @@ -58,9 +47,10 @@ export interface ToolItem { create_time?: string desc?: string | null folder_id?: string + fileList?: ToolFile[] icon?: string id: string - init_field_list?: ToolInitField[] + init_field_list?: DynamicFormField[] init_params?: Record | string | null input_field_list?: ToolInputField[] is_active: boolean @@ -75,19 +65,86 @@ export interface ToolItem { update_time?: string user_id?: string | null version?: string | null + work_flow?: Record workspace_id: string } +export interface ToolFile { + name: string + size?: number + uid?: number | string +} + export interface ToolPayload { code?: string desc?: string | null folder_id?: string | null icon?: string - init_field_list?: ToolInitField[] + init_field_list?: DynamicFormField[] init_params?: Record | null input_field_list?: ToolInputField[] is_active?: boolean name?: string scope?: ToolScope tool_type?: ToolType + work_flow?: Record + work_flow_template?: ToolStoreItem +} + +export interface ToolStoreVersion { + downloadUrl: string + name: string +} + +export interface ToolStoreTag { + key: string + name: string +} + +export type ToolStoreSource = 'internal' | 'store' + +export interface ToolStoreItem { + desc?: string | null + description?: string | null + downloadCallbackUrl?: string + downloadUrl?: string + downloads?: number + icon?: string + id: string + label?: string | null + name: string + readMe?: string + source: ToolStoreSource + tags?: string[] + tool_type: ToolType + version?: string | null + versions?: ToolStoreVersion[] +} + +export interface ToolStoreResponse { + additionalProperties: { + tags: ToolStoreTag[] + } + apps: Omit[] +} + +export interface AddInternalToolPayload { + folder_id: string + name: string +} + +export interface AddStoreToolPayload extends AddInternalToolPayload { + download_callback_url: string + download_url: string + icon: string + label: string + versions: ToolStoreVersion[] +} + +export interface UpdateStoreToolPayload { + download_callback_url: string + download_url: string + icon: string + label: string + versions: ToolStoreVersion[] } diff --git a/ui/src/assets/knowledge/icon_knowledge.svg b/ui/src/assets/knowledge/icon_knowledge.svg new file mode 100644 index 00000000000..34fe1b36f37 --- /dev/null +++ b/ui/src/assets/knowledge/icon_knowledge.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/ui/src/assets/knowledge/icon_web.svg b/ui/src/assets/knowledge/icon_web.svg new file mode 100644 index 00000000000..f958baf6153 --- /dev/null +++ b/ui/src/assets/knowledge/icon_web.svg @@ -0,0 +1,4 @@ + + + + diff --git a/ui/src/assets/knowledge/logo_lark.svg b/ui/src/assets/knowledge/logo_lark.svg new file mode 100644 index 00000000000..938c5055db6 --- /dev/null +++ b/ui/src/assets/knowledge/logo_lark.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/ui/src/components.d.ts b/ui/src/components.d.ts index ce77e2843c7..6ee62ee86e5 100644 --- a/ui/src/components.d.ts +++ b/ui/src/components.d.ts @@ -12,7 +12,9 @@ export {} /* prettier-ignore */ declare module 'vue' { export interface GlobalComponents { + KnowledgeIcon: typeof import('./components/global/mk-icon/KnowledgeIcon.vue')['default'] LayoutAside: typeof import('./components/global/mk-view-layout/layout-aside.vue')['default'] + LayoutBatchFooter: typeof import('./components/global/mk-view-layout/layout-batch-footer.vue')['default'] MkCollapse: typeof import('./components/global/mk-collapse/index.vue')['default'] MkComplexSearch: typeof import('./components/global/mk-complex-search/index.vue')['default'] MkDialog: typeof import('./components/global/mk-dialog/index.vue')['default'] @@ -40,7 +42,9 @@ declare module 'vue' { // For TSX support declare global { + const KnowledgeIcon: typeof import('./components/global/mk-icon/KnowledgeIcon.vue')['default'] const LayoutAside: typeof import('./components/global/mk-view-layout/layout-aside.vue')['default'] + const LayoutBatchFooter: typeof import('./components/global/mk-view-layout/layout-batch-footer.vue')['default'] const MkCollapse: typeof import('./components/global/mk-collapse/index.vue')['default'] const MkComplexSearch: typeof import('./components/global/mk-complex-search/index.vue')['default'] const MkDialog: typeof import('./components/global/mk-dialog/index.vue')['default'] diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md index 8fe5155b1dd..b3399924be6 100644 --- a/ui/src/components/COMPONENT_README.md +++ b/ui/src/components/COMPONENT_README.md @@ -71,6 +71,7 @@ src/components/ │ │ └── index.vue # 列表与业务分组列表复用的行结构 │ ├── mk-view-layout/ │ │ ├── index.vue # 路由页面标题、操作区和内容区统一结构 +│ │ ├── layout-batch-footer.vue # 页面与表格共用的批量选择底栏 │ │ └── layout-aside.vue # 页面可选左侧栏结构 │ ├── mk-search-input/ │ │ └── index.vue # 带默认搜索图标的输入框 @@ -230,7 +231,8 @@ ID 和样式类渲染 `title`。内容区域超出最大高度后显示滚动条 ### MkDrawer -全局抽屉组件,统一使用 `el-scrollbar` 包裹内容并为默认插槽提供 `p-6` 内边距。通过 +全局抽屉组件,默认通过 Element Plus 的 `append-to-body` 挂载到 `body`,避免受到父级容器的定位、 +层级和隐藏状态影响;统一使用 `el-scrollbar` 包裹内容并为默认插槽提供 `p-6` 内边距。通过 `content-class` 可以覆盖内容容器样式;全高布局可传入 `content-class="h-full p-0"`,再由 `MkViewLayout` 管理内边距和滚动区域。默认显示关闭按钮、关闭时销毁内容,同时禁止点击遮罩或按 Escape 关闭;这些默认行为可以通过同名 Props 覆盖。Element Plus Drawer 的其他属性和事件通过 @@ -365,6 +367,13 @@ Element Plus Dropdown 属性和事件通过 `$attrs` 传入,并暴露 `handleO 主内容区固定保留 `px-6` 水平留白。内置滚动容器会抵消两侧留白,再为滚动内容补回 `24px` 水平间距,使滚动条贴齐主区域右边界,并允许 `MkTable` 的底部操作栏延伸至完整主区域宽度。 +卡片等非表格列表需要批量操作时,页面在批量选择模式下渲染 `Footer`,通过 +`v-model:batch-selection` 绑定选中项唯一值数组,`batch-values` 传入当前列表全部唯一值,并提供 +`footer-batch-actions` 插槽中的业务按钮;布局会复用 `LayoutBatchFooter`,统一显示全选、半选、 +已选数量和取消按钮。点击取消会清空选择,并由 `Footer` 触发 `batch-cancel`。未提供 +`footer-batch-actions` 时,`Footer` 仍渲染普通底栏。`footer-batch-actions` 作用域同时提供 +`batchSelection`,业务按钮需要当前选择时可以直接读取。 + ```vue @@ -395,6 +404,32 @@ Element Plus Dropdown 属性和事件通过 `$attrs` 传入,并暴露 `handleO ``` +```vue + + + +``` + ### MkIcon 统一渲染 MaxKB SVG Symbol 和 Element Plus 图标: @@ -443,7 +478,7 @@ Element Plus 的 `v-infinite-scroll`。组件通过 `v-model` 管理已经加载 数据驱动使用时传入 `row`。`label-field` 默认读取 `name`,`index` 默认为 `0`;未提供默认插槽时 组件显示对应字段文本。`action` 和 `action-dropdown` 插槽需要配合 `row` 使用,并接收 `row`、 `index`。`action-dropdown` 由组件统一渲染 More 触发器、`MkDropdown` 和 `MkDropdownMenu`,插槽中 -直接放置 `MkDropdownItem`。 +直接放置 `MkDropdownItem`;插槽为空,或其中的条件菜单项均未渲染时,不显示 More 触发器。 ```vue @@ -528,8 +563,9 @@ Dialog、Drawer、Popover、嵌套区域等其他大、小表格均禁止开启 ``` 表格操作列需要 More 菜单时使用 `MkTableMoreDropdown`。组件统一提供点击型、右下定位的 More -按钮以及 `MkDropdownMenu`,默认插槽中直接放置 `MkDropdownItem`。其他 Dropdown 属性和事件通过 -`$attrs` 传入,菜单容器样式通过 `menu-class` 设置。 +按钮以及 `MkDropdownMenu`,默认插槽中直接放置 `MkDropdownItem`;插槽为空,或其中的条件菜单项 +均未渲染时,不显示 More 触发器。其他 Dropdown 属性和事件通过 `$attrs` 传入,菜单容器样式通过 +`menu-class` 设置。 ```vue @@ -540,8 +576,8 @@ Dialog、Drawer、Popover、嵌套区域等其他大、小表格均禁止开启 包含 `type="selection"` 的选择列时,选择数据会显示页面底部操作栏。批量按钮放入 `footer-batch-actions` 插槽,当前选择通过 `selection-change` 返回。组件暴露 `tableRef` 和 -`clearSelection()`。操作栏在 `MkViewLayout` 的主内容滚动区域内吸附于页面底部,不随表格内容 -滚出可视区域。 +`clearSelection()`。操作栏与 `MkViewLayout` 复用 `LayoutBatchFooter`,统一全选、半选、数量和 +取消行为;它在主内容滚动区域内吸附于页面底部,不随表格内容滚出可视区域。 ```vue @@ -652,7 +688,15 @@ const config = defineModel({ required: true }) `footer` 作用域提供 `Action` 和 `ActionDropdown`。左侧常驻内容与 `Action` 写在同一个插槽内; `Action` 是卡片悬浮或内部获得焦点时显示的右侧操作容器,可以只放开关或按钮。 需要 More 菜单时,再将 `ActionDropdown` 放入 `Action`,组件会统一渲染 `MkDropdownMenu`,默认 -插槽中直接放置 `MkDropdownItem`。 +插槽中直接放置 `MkDropdownItem`;插槽为空,或其中的条件菜单项均未渲染时,不显示 More 触发器。 +`ActionDropdown` 固定开启 `persistent`,避免菜单关闭后销毁由菜单项管理的 Drawer 或 Dialog; +浮层本身应在打开时按需挂载,并在 `closed` 后卸载,避免每张卡片都长期保留隐藏的浮层节点。 +传入 `selectable` 后,卡片进入选择模式:复选框固定在右上角,`tag` 插槽仍正常渲染并为复选框 +预留位置;点击卡片或复选框会通过 `selected` 事件返回新的选择状态,页面通过 `selected` Prop +传回当前状态,选中卡片统一显示主题色边框和浅色背景。业务内容需要在选择模式下由复选框替代时, +由业务卡片根据 `selectable` 控制该内容是否渲染;例如工具卡片选择时隐藏“更新版本”入口。 +选择模式下,`MkSourceCard` 提供的 `Action` 不渲染任何内容,业务卡片不需要重复判断 +`selectable`;选择集合、全选、批量接口和页面底部操作栏仍由使用页面管理。 需要自定义头部时可 通过 `icon`、`title`、`subtitle` 和 `tag` 插槽覆盖对应区域;`title` 插槽提供 `{ title }`, 便于在保留标题文案的同时追加状态图标等内容。 @@ -682,6 +726,19 @@ import MkSourceCard from '@/components/mk-source-card/index.vue' ``` +批量选择模式由页面显式控制: + +```vue + +

工具描述

+
+``` + 只需要悬浮显示开关时,可以不使用 `ActionDropdown`: ```vue @@ -768,8 +825,10 @@ Props 和模型: - `data` 应传入全量数据;组件内的 50 条分批仅用于控制 DOM 渲染数量,不替代后端分页。 - 搜索词或数据源变化时,渲染范围和滚动位置会重置到第一批。 - 提供 `action-dropdown` 时,组件内部使用固定 More 按钮和点击型、非 Teleport 的 - `MkDropdown`,并统一包裹 `MkDropdownMenu`;插槽中直接放置 `MkDropdownItem`。 -- `action` 和 `action-dropdown` 二选一;同时提供时优先渲染 `action-dropdown`,不渲染 `action`。 + `MkDropdown`,并统一包裹 `MkDropdownMenu`;插槽中直接放置 `MkDropdownItem`,无可渲染菜单项时 + 不显示 More 触发器。 +- `action` 和 `action-dropdown` 二选一;同时提供且下拉插槽有可渲染菜单项时优先渲染 + `action-dropdown`,否则渲染 `action`。 #### 默认字段示例 diff --git a/ui/src/components/business/folder-tree/index.vue b/ui/src/components/business/folder-tree/index.vue index a4920687a20..f55b6c19d22 100644 --- a/ui/src/components/business/folder-tree/index.vue +++ b/ui/src/components/business/folder-tree/index.vue @@ -9,7 +9,7 @@ import { MsgSuccess, MsgConfirm } from '@/utils/message' import VirtualizedTree from './VirtualizedTree.vue' import FolderFormDialog from './FolderFormDialog.vue' import { useStore } from '@/stores' -import { getWorkspaceId } from '@/utils/workspace-context' +import { getWorkspaceId } from '@/utils/resource-context' defineOptions({ name: 'FolderTree' }) @@ -344,24 +344,25 @@ defineExpose({ refresh: loadFolders, openCreate: handleOpenCreateFolder })
- - - {{ folderEntries.shared.name }} - - - +
+ + + {{ folderEntries.shared.name }} + + + +
+import { resetUrl } from '@/utils/common' +defineOptions({ name: 'KnowledgeIcon' }) +withDefaults( + defineProps<{ + icon?: string + size?: number | string + type?: KnowledgeType + }>(), + { + size: 24, + }, +) + + diff --git a/ui/src/components/global/mk-list-item/index.vue b/ui/src/components/global/mk-list-item/index.vue index 38d41c1782e..877c3622c08 100644 --- a/ui/src/components/global/mk-list-item/index.vue +++ b/ui/src/components/global/mk-list-item/index.vue @@ -1,5 +1,6 @@