diff --git a/ui/src/api/admin/workspace/model/model.ts b/ui/src/api/admin/workspace/model/model.ts new file mode 100644 index 00000000000..50788f82860 --- /dev/null +++ b/ui/src/api/admin/workspace/model/model.ts @@ -0,0 +1,76 @@ +import { del, get, post, put } from '../../core/request' +import type { + DynamicFormField, + ModelListQuery, + ModelPayload, + SelectableModelResponse, + WorkspaceModel, +} from '@/api/types' + +const getPrefix = (workspaceId: string) => `/workspace/${workspaceId}/model` + +/** 获取工作空间模型列表。 */ +const getModelList = (workspaceId: string, query?: ModelListQuery) => { + return get(getPrefix(workspaceId), query) +} + +/** 获取工作空间可选模型,并区分工作空间模型与共享模型。 */ +const getSelectableModelList = (workspaceId: string, query?: ModelListQuery) => { + return get(`/workspace/${workspaceId}/model_list`, query) +} + +/** 获取模型参数表单。 */ +const getModelParamsForm = (workspaceId: string, modelId: string) => { + return get(`${getPrefix(workspaceId)}/${modelId}/model_params_form`) +} + +/** 创建工作空间模型。 */ +const postModel = (workspaceId: string, payload: ModelPayload) => { + return post(getPrefix(workspaceId), payload) +} + +/** 更新工作空间模型。 */ +const putModel = (workspaceId: string, modelId: string, payload: Partial) => { + return put, WorkspaceModel>(`${getPrefix(workspaceId)}/${modelId}`, payload) +} + +/** 保存模型参数配置。 */ +const putModelParamsForm = (workspaceId: string, modelId: string, payload: DynamicFormField[]) => { + return put( + `${getPrefix(workspaceId)}/${modelId}/model_params_form`, + payload, + ) +} + +/** 获取包含认证信息的模型详情。 */ +const getModelDetail = (workspaceId: string, modelId: string) => { + return get(`${getPrefix(workspaceId)}/${modelId}`) +} + +/** 获取不包含认证信息的模型元数据。 */ +const getModelMeta = (workspaceId: string, modelId: string) => { + return get(`${getPrefix(workspaceId)}/${modelId}/meta`) +} + +/** 暂停本地模型下载。 */ +const putPauseModelDownload = (workspaceId: string, modelId: string) => { + return put(`${getPrefix(workspaceId)}/${modelId}/pause_download`) +} + +/** 删除工作空间模型。 */ +const deleteModel = (workspaceId: string, modelId: string) => { + return del(`${getPrefix(workspaceId)}/${modelId}`) +} + +export default { + deleteModel, + getModelDetail, + getModelList, + getModelMeta, + getModelParamsForm, + getSelectableModelList, + postModel, + putModel, + putModelParamsForm, + putPauseModelDownload, +} diff --git a/ui/src/api/admin/workspace/model/provider.ts b/ui/src/api/admin/workspace/model/provider.ts new file mode 100644 index 00000000000..c582a830f0b --- /dev/null +++ b/ui/src/api/admin/workspace/model/provider.ts @@ -0,0 +1,54 @@ +import { get } from '../../core/request' +import type { BaseModelOption, DynamicFormField, ModelProvider, ModelTypeOption } from '@/api/types' + +const prefix = '/provider' + +/** 获取全部模型供应商。 */ +const getProviderList = () => { + return get(prefix) +} + +/** 获取支持指定模型类型的供应商。 */ +const getProviderListByModelType = (modelType: string) => { + return get(prefix, { model_type: modelType }) +} + +/** 获取创建模型所需的动态表单。 */ +const getModelCreateForm = (provider: string, modelType: string, modelName: string) => { + return get(`${prefix}/model_form`, { + model_name: modelName, + model_type: modelType, + provider, + }) +} + +/** 获取供应商支持的模型类型。 */ +const getModelTypeList = (provider: string) => { + return get(`${prefix}/model_type_list`, { provider }) +} + +/** 获取供应商指定类型下的基础模型。 */ +const getBaseModelList = (provider: string, modelType: string) => { + return get(`${prefix}/model_list`, { + model_type: modelType, + provider, + }) +} + +/** 获取基础模型的参数表单。 */ +const getBaseModelParamsForm = (provider: string, modelType: string, modelName: string) => { + return get(`${prefix}/model_params_form`, { + model_name: modelName, + model_type: modelType, + provider, + }) +} + +export default { + getBaseModelList, + getBaseModelParamsForm, + getModelCreateForm, + getModelTypeList, + getProviderList, + getProviderListByModelType, +} diff --git a/ui/src/api/admin/workspace/tool/store.ts b/ui/src/api/admin/workspace/tool/store.ts new file mode 100644 index 00000000000..5d1befd77b5 --- /dev/null +++ b/ui/src/api/admin/workspace/tool/store.ts @@ -0,0 +1,54 @@ +import { get, post } from '../../core/request' +import type { AddStoreToolPayload, WorkspaceTool } from '@/api/types' +import type { RequestParams } from '@/api/types' + +/** 获取系统内置工具。 */ +const getInternalToolList = (query?: RequestParams) => { + return get('/workspace/internal/tool', query) +} + +/** 获取工具商店列表。 */ +const getStoreToolList = (query?: RequestParams) => { + return get('/workspace/store/tool', query) +} + +/** 获取知识库模板商店列表。 */ +const getStoreKnowledgeList = (query?: RequestParams) => { + return get('/workspace/store/knowledge_template', query) +} + +/** 获取工作流工具模板商店列表。 */ +const getStoreToolWorkflowList = (query?: RequestParams) => { + return get('/workspace/store/tool_workflow_template', query) +} + +/** 获取应用模板商店列表。 */ +const getStoreApplicationList = (query?: RequestParams) => { + return get('/workspace/store/application_template', query) +} + +/** 将系统内置工具添加到工作空间。 */ +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, +} diff --git a/ui/src/api/admin/workspace/tool/tool.ts b/ui/src/api/admin/workspace/tool/tool.ts new file mode 100644 index 00000000000..50568ba4bd0 --- /dev/null +++ b/ui/src/api/admin/workspace/tool/tool.ts @@ -0,0 +1,109 @@ +import { del, get, post, put } from '../../core/request' +import type { ParamsPage, ResponsePage } from '../../core/types' +import type { + ToolCatalogResponse, + ToolFolder, + ToolFolderPayload, + ToolListQuery, + ToolPayload, + ToolTreeResponse, + WorkspaceTool, +} from '@/api/types' + +const getPrefix = (workspaceId: string) => `/workspace/${workspaceId}/tool` +const getFolderPrefix = (workspaceId: string) => `/workspace/${workspaceId}/TOOL/folder` + +/** 获取当前目录下的工具和子文件夹。 */ +const getToolTree = (workspaceId: string, query?: ToolListQuery) => { + return get(getPrefix(workspaceId), query) +} + +/** 获取工作空间全部工具以及已授权的共享工具。 */ +const getToolCatalog = (workspaceId: string, query?: ToolListQuery) => { + return get(`${getPrefix(workspaceId)}/tool_list`, query) +} + +/** 获取工具分页列表。 */ +const getToolPage = (workspaceId: string, page: ParamsPage, query?: ToolListQuery) => { + return get>( + `${getPrefix(workspaceId)}/${page.currentPage}/${page.pageSize}`, + query, + ) +} + +/** 获取工具文件夹树。 */ +const getToolFolderTree = (workspaceId: string) => { + return get(getFolderPrefix(workspaceId)) +} + +/** 创建工具文件夹。 */ +const postToolFolder = (workspaceId: string, payload: ToolFolderPayload) => { + return post(getFolderPrefix(workspaceId), payload) +} + +/** 更新工具文件夹。 */ +const putToolFolder = (workspaceId: string, folderId: string, payload: ToolFolderPayload) => { + return put(`${getFolderPrefix(workspaceId)}/${folderId}`, payload) +} + +/** 删除工具文件夹及其内容。 */ +const deleteToolFolder = (workspaceId: string, folderId: string) => { + return del(`${getFolderPrefix(workspaceId)}/${folderId}`) +} + +/** 创建工作空间工具。 */ +const postTool = (workspaceId: string, payload: ToolPayload) => { + return post(getPrefix(workspaceId), payload) +} + +/** 更新工作空间工具。 */ +const putTool = (workspaceId: string, toolId: string, payload: ToolPayload) => { + return put(`${getPrefix(workspaceId)}/${toolId}`, payload) +} + +/** 获取工具详情。 */ +const getToolDetail = (workspaceId: string, toolId: string) => { + return get(`${getPrefix(workspaceId)}/${toolId}`) +} + +/** 删除工作空间工具。 */ +const deleteTool = (workspaceId: string, toolId: string) => { + return del(`${getPrefix(workspaceId)}/${toolId}`) +} + +/** 测试工具配置是否可连接。 */ +const postToolTestConnection = (workspaceId: string, payload: ToolPayload) => { + return post(`${getPrefix(workspaceId)}/test_connection`, payload) +} + +/** 批量删除工作空间工具。 */ +const putBatchDeleteTools = (workspaceId: string, toolIds: string[]) => { + return put<{ id_list: string[] }, boolean>(`${getPrefix(workspaceId)}/batch_delete`, { + id_list: toolIds, + }) +} + +/** 批量移动工作空间工具。 */ +const putBatchMoveTools = (workspaceId: string, toolIds: string[], folderId: string) => { + return put<{ folder_id: string; id_list: string[] }, boolean>( + `${getPrefix(workspaceId)}/batch_move`, + { folder_id: folderId, id_list: toolIds }, + ) +} + +export default { + deleteTool, + deleteToolFolder, + getToolCatalog, + getToolDetail, + getToolFolderTree, + getToolPage, + getToolTree, + postTool, + postToolFolder, + postToolTestConnection, + putBatchDeleteTools, + putBatchMoveTools, + putTool, + putToolFolder, +} diff --git a/ui/src/api/types/index.ts b/ui/src/api/types/index.ts index ea30261778a..334e88a14a5 100644 --- a/ui/src/api/types/index.ts +++ b/ui/src/api/types/index.ts @@ -10,3 +10,4 @@ export type * from './system-authentication' export type * from './system-operate-log' export * from './system-role' export type * from './system-email' +export * from './workspace-resources' diff --git a/ui/src/api/types/workspace-resources.ts b/ui/src/api/types/workspace-resources.ts new file mode 100644 index 00000000000..55b53213d13 --- /dev/null +++ b/ui/src/api/types/workspace-resources.ts @@ -0,0 +1,146 @@ +/** Workspace 模型与工具页面共用的业务类型。 */ + +export const MODEL_STATUS = { + DOWNLOAD: 'DOWNLOAD', + ERROR: 'ERROR', + PAUSE_DOWNLOAD: 'PAUSE_DOWNLOAD', + SUCCESS: 'SUCCESS', +} as const + +export type ModelStatus = (typeof MODEL_STATUS)[keyof typeof MODEL_STATUS] + +export interface ModelProvider { + icon: string + name: string + provider: string +} + +export interface WorkspaceModel { + create_time?: string + id: string + meta?: Record + model_name: string + model_type: string + name: string + nick_name?: string + provider: string + source?: 'shared' | 'workspace' + status: ModelStatus + user_id?: string + username?: string + workspace_id?: string +} + +export interface ModelListQuery { + create_user?: string + model_name?: string + model_type?: string + name?: string + provider?: string + [key: string]: unknown +} + +export interface SelectableModelResponse { + model: WorkspaceModel[] + shared_model: WorkspaceModel[] +} + +export interface ModelPayload { + credential: Record + model_name: string + model_type: string + name: string + provider: string +} + +export interface DynamicFormField { + default_value?: unknown + field: string + label: string + required?: boolean + [key: string]: unknown +} + +export interface BaseModelOption { + desc?: string + model_type: string + name: string +} + +export interface ModelTypeOption { + key: string + value: string +} + +export type ToolType = 'CUSTOM' | 'DATA_SOURCE' | 'INTERNAL' | 'MCP' | 'SKILL' | 'WORKFLOW' + +export interface WorkspaceTool { + create_time?: string + desc?: string + folder_id?: string + icon?: string + id: string + is_active: boolean + name: string + nick_name?: string + scope?: 'SHARED' | 'WORKSPACE' + source?: 'shared' | 'workspace' + tool_type: ToolType + version?: string + [key: string]: unknown +} + +export interface ToolFolder { + children?: ToolFolder[] + create_time?: string + desc?: string + id: string + name: string + parent_id?: string + update_time?: string + user_id?: string + workspace_id: string +} + +export interface ToolListQuery { + create_user?: string + folder_id?: string + name?: string + scope?: 'SHARED' | 'WORKSPACE' + tool_type?: ToolType | '' + [key: string]: unknown +} + +export interface ToolTreeResponse { + folders: ToolFolder[] + tools: WorkspaceTool[] +} + +export interface ToolCatalogResponse { + shared_tools: WorkspaceTool[] + tools: WorkspaceTool[] +} + +export interface ToolPayload { + code?: string + desc?: string + folder_id?: string + icon?: string + input_field_list?: unknown[] + is_active?: boolean + name?: string + tool_type?: ToolType + [key: string]: unknown +} + +export interface ToolFolderPayload { + desc?: string + name: string + parent_id?: string +} + +export interface AddStoreToolPayload { + folder_id: string + name: string + [key: string]: unknown +} diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md index 9702e5a0c6d..7a67d08b77e 100644 --- a/ui/src/components/COMPONENT_README.md +++ b/ui/src/components/COMPONENT_README.md @@ -64,7 +64,7 @@ src/components/ │ └── index.vue # 标签折叠和剩余标签浮层 ├── mk-search-list/ │ ├── index.vue # 搜索框与剩余空间滚动列表,手动导入 -│ └── mk-list-item.vue # 列表与业务分组列表复用的私有行结构 +│ └── mk-list-item.vue # 列表与业务分组列表复用的行结构 ├── mk-form-list/ │ └── index.vue # 可动态增删的表单行列表,手动导入 ├── mk-workspace-dropdown/ @@ -78,6 +78,7 @@ Vue 模板中使用,不需要手动导入。其他共享组件必须从具体 ```ts import MkSearchList from '@/components/mk-search-list/index.vue' +import MkListItem from '@/components/mk-search-list/mk-list-item.vue' import MkFormList from '@/components/mk-form-list/index.vue' import MkWorkspaceDropdown from '@/components/mk-workspace-dropdown/index.vue' import MkWorkspaceRelationTags from '@/components/mk-workspace-relation-tags/index.vue' @@ -125,11 +126,20 @@ Element Plus 使用 `ElOnlyChild` 处理浮层触发器。`el-tooltip`、`el-pop ### MkCollapse -带标题触发器和展开过渡动画的内容折叠组件。通过 `v-model` 控制展开状态,默认展开;点击标题行 -会切换状态,默认插槽放置折叠内容。 +带标题触发器和展开过渡动画的内容折叠组件。组件内部维护展开状态,`default-expanded` 设置初始 +状态且默认为 `true`;点击标题行会切换状态,不向外回传展开状态。默认插槽放置折叠内容,标题 +默认使用 `title` 属性,也可通过 `label` 插槽自定义;标题触发层可通过 `trigger-class` 和 +`trigger-style` 自定义 class 和行内样式。 ```vue - + +
折叠内容
+
+ + +
折叠内容
``` @@ -260,7 +270,7 @@ Element Plus Dropdown 属性和事件通过 `$attrs` 传入,并暴露 `handleO ``` @@ -269,14 +279,23 @@ Element Plus Dropdown 属性和事件通过 `$attrs` 传入,并暴露 `handleO 路由页面的通用内容结构,统一提供满高弹性布局及可选左侧栏。标题优先使用 `title` Prop,未传入 时读取当前路由的 `meta.title`。`aside` 和默认作用域插槽都提供 `title` 与对应区域的 `Header` -包装组件,页面可以在同一个插槽中组织标题、内容和空状态,并只写一次业务状态判断。组件不统一 -处理内容滚动,需要滚动的页面自行使用 `el-scrollbar`。传入 `aside` 插槽后才会渲染左侧栏;左右 -结构上方的独立内容放入 `top` 插槽。页面加载状态通过 +包装组件;插槽未渲染 `Header` 时,组件会自动显示当前标题,页面需要添加操作区或自定义标题时 +再显式渲染 `Header`。Header 固定在主内容区顶部,其余默认插槽内容由组件统一放入 +`el-scrollbar`,页面不要再为整个主内容区嵌套滚动容器;Tabs、树或其他局部区域需要独立滚动时 +仍可自行使用 `el-scrollbar`。页面可以在同一个插槽中组织标题、内容和空状态,并只写一次业务 +状态判断。传入 `aside` 插槽后才会渲染左侧栏;左右结构上方的独立内容放入 `top` 插槽。页面加载状态通过 `loading` Prop 传入,由组件将 Element Plus Loading 遮罩绑定到整个布局根节点。默认插槽没有 -声明作用域参数时会自动显示当前标题;使用 `#default="{ Header }"` 后则由页面通过 -`` 控制标题,空状态分支不会自动补充标题。 +渲染 `Header` 时会自动显示当前标题;显式渲染后则由页面控制标题内容。 + +主内容区固定保留 `px-6` 水平留白。内置滚动容器会抵消两侧留白,再为滚动内容补回 `24px` +水平间距,使滚动条贴齐主区域右边界,并允许 `MkTable` 的底部操作栏延伸至完整主区域宽度。 ```vue + + + + +