From 3ec80e18fb91cf9496d6922350a746ad17f72fbf Mon Sep 17 00:00:00 2001 From: wangdan-fit2cloud Date: Thu, 20 Aug 2026 18:51:03 +0800 Subject: [PATCH] feat: folder --- ui/AGENTS.md | 7 +- ui/package.json | 1 - ui/src/api/API_README.md | 21 +- ui/src/api/admin/system/auth-scan-setting.ts | 10 +- ui/src/api/admin/system/auth-setting.ts | 18 +- .../api/admin/system/chat-user-auth-scan.ts | 10 +- ui/src/api/admin/system/chat-user-auth.ts | 12 +- ui/src/api/admin/system/chat-user-groups.ts | 6 +- ui/src/api/admin/system/chat-user.ts | 10 +- ui/src/api/admin/system/email-setting.ts | 12 +- ui/src/api/admin/system/role.ts | 11 +- ui/src/api/admin/system/user-manage.ts | 19 +- ui/src/api/admin/system/workspace.ts | 6 +- ui/src/api/admin/workspace/folder.ts | 15 +- ui/src/api/admin/workspace/model/provider.ts | 6 +- ui/src/api/admin/workspace/tool/tool.ts | 31 +- ui/src/api/enums/folder.ts | 6 + ui/src/api/enums/index.ts | 6 + ui/src/api/enums/login.ts | 12 + ui/src/api/enums/model.ts | 7 + ui/src/api/enums/system-role.ts | 6 + ui/src/api/enums/tool.ts | 16 + ui/src/api/types/chat-user-groups.ts | 2 +- ui/src/api/types/chat-user.ts | 2 +- ui/src/api/types/folder.ts | 6 +- ui/src/api/types/index.ts | 10 +- ui/src/api/types/login.ts | 13 +- ui/src/api/types/system-authentication.ts | 6 +- ui/src/api/types/system-email.ts | 2 +- ui/src/api/types/system-role.ts | 9 +- ui/src/api/types/system-user.ts | 2 +- ui/src/api/types/system-workspace.ts | 2 +- ui/src/api/types/workspace-model.ts | 10 +- ui/src/api/types/workspace-tool.ts | 42 +- ui/src/components.d.ts | 2 + ui/src/components/COMPONENT_README.md | 15 + .../business/folder-tree/VirtualizedTree.vue | 28 +- .../components/business/folder-tree/index.vue | 391 +++++++++++------- .../components/business/folder-tree/types.ts | 17 +- .../business/model-select/index.vue | 5 +- .../components/global/mk-load-more/index.vue | 100 +++++ .../mk-source-card-action-dropdown.vue | 2 +- ui/src/constants/CONSTANT_README.md | 9 +- ui/src/constants/auth.ts | 3 +- ui/src/constants/folder.ts | 22 + ui/src/constants/index.ts | 4 + ui/src/constants/tool.ts | 10 + ui/src/layout/avatar-dropdown/index.vue | 2 +- ui/src/stores/user.ts | 2 +- ui/src/views/login/LoginView.vue | 3 +- ui/src/views/login/constants.ts | 3 +- ui/src/views/login/modes/AccountLogin.vue | 5 +- ui/src/views/login/modes/QrCodeLogin.vue | 10 +- ui/src/views/model/ModelView.vue | 48 +-- ui/src/views/model/components/ModelCard.vue | 4 +- .../views/model/components/ModelProvider.vue | 14 +- .../authentication/AuthenticationView.vue | 2 +- .../chat/authentication/EditSCANDrawer.vue | 11 +- .../chat/authentication/components/CAS.vue | 7 +- .../chat/authentication/components/LDAP.vue | 7 +- .../chat/authentication/components/OAuth2.vue | 7 +- .../chat/authentication/components/OIDC.vue | 7 +- .../authentication/components/SCANLogin.vue | 12 +- .../chat/user-groups/GroupsListView.vue | 16 +- .../system/chat/users/UserFromDrawer.vue | 6 +- .../views/system/chat/users/UserListView.vue | 13 +- .../chat/users/dialog/ImportUsersDialog.vue | 12 +- .../system/identity/roles/AddMemberDrawer.vue | 9 +- .../system/identity/roles/RoleListView.vue | 5 +- .../roles/components/RoleMemberList.vue | 9 +- .../roles/dialog/CreateOrUpdateRoleDialog.vue | 11 +- .../system/identity/users/UserFromDrawer.vue | 8 +- .../system/identity/users/UserListView.vue | 10 +- .../users/dialog/BatchSetUserRoleDialog.vue | 10 +- .../identity/workspaces/AddMemberDrawer.vue | 4 +- .../authentication/AuthenticationView.vue | 2 +- .../authentication/EditSCANDrawer.vue | 11 +- .../authentication/components/CAS.vue | 7 +- .../authentication/components/LDAP.vue | 7 +- .../components/LoginSetting.vue | 18 +- .../authentication/components/OAuth2.vue | 7 +- .../authentication/components/OIDC.vue | 7 +- .../authentication/components/SCANLogin.vue | 12 +- .../authentication/components/Saml2.vue | 7 +- .../settings/email/EmailSettingsView.vue | 8 +- ui/src/views/tool/ToolView.vue | 227 +++++----- ui/src/views/tool/components/ToolCard.vue | 16 +- ui/src/workflow-canvas/config/node-mapping.ts | 5 +- 88 files changed, 849 insertions(+), 704 deletions(-) create mode 100644 ui/src/api/enums/folder.ts create mode 100644 ui/src/api/enums/index.ts create mode 100644 ui/src/api/enums/login.ts create mode 100644 ui/src/api/enums/model.ts create mode 100644 ui/src/api/enums/system-role.ts create mode 100644 ui/src/api/enums/tool.ts create mode 100644 ui/src/components/global/mk-load-more/index.vue create mode 100644 ui/src/constants/folder.ts create mode 100644 ui/src/constants/index.ts create mode 100644 ui/src/constants/tool.ts diff --git a/ui/AGENTS.md b/ui/AGENTS.md index 531c490e454..617e3f9907c 100644 --- a/ui/AGENTS.md +++ b/ui/AGENTS.md @@ -55,7 +55,8 @@ ui/ │ │ ├── iconfont.js # Generated SVG Symbol resource; replace, do not edit │ │ ├── logo/ # Product and brand logos │ │ └── workflow/ # Workflow-related visual assets -│ ├── api/ # Request infrastructure, business APIs, and shared API types +│ ├── api/ # Request infrastructure, business APIs, enums, and shared API types +│ │ ├── enums/ # Backend fixed-value enums, exposed through one public entry │ │ ├── types/ # Types shared by APIs and their View or Component consumers │ │ └── API_README.md # API layering, placement, typing, and request rules │ ├── components/ # Shared UI and cross-page business components @@ -115,6 +116,8 @@ Structural responsibilities: route and page-level actions around `MkWorkflow`. - Put server communication in `api/`, isolate Admin and Chat request systems, and group business APIs by domain and resource according to `src/api/API_README.md`. +- Put backend fixed-value enums in `api/enums/` and import them through `@/api/enums`; keep + `api/types/` as the type-only public entry for API and UI consumers. - Put constants shared by most pages or multiple business modules in `constants/`, and split files by a specific domain. Keep constants used by only one page or feature with their owning code. - Put shared client state in `stores/`; component-local state should remain in the component. - Reuse the Pinia instance exported by `stores/index.ts`; application code accesses Store instances @@ -175,7 +178,7 @@ Keep these behaviors: - `base: './'` for v2-compatible static deployment. - `envDir: './env'`. - Proxy `/admin/api`, `/chat/api`, `/doc`, `/schema`, `/static`, and `oss` file paths to the backend. -- Keep Tailwind, Vue, Vue JSX, Vue DevTools, Element Plus auto import, and component auto registration plugins. +- Keep Tailwind, Vue, Vue JSX, Element Plus auto import, and component auto registration plugins. - Do not add `unplugin-vue-define-options`; Vue 3.5 already supports `defineOptions`. - Avoid adding `vite-plugin-html` unless multi-template requirements exceed Vite's built-in HTML env replacement. diff --git a/ui/package.json b/ui/package.json index 76a3fffe2dd..4b9e4d29d71 100644 --- a/ui/package.json +++ b/ui/package.json @@ -55,7 +55,6 @@ "unplugin-auto-import": "^21.0.0", "unplugin-vue-components": "^32.1.0", "vite": "^8.0.16", - "vite-plugin-vue-devtools": "^8.1.2", "vue-eslint-parser": "^10.4.1", "vue-tsc": "^3.3.5" }, diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index e450e83fd67..1b02d03d604 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -14,6 +14,9 @@ src/api/ │ ├── workspace/ # 工作空间业务接口 │ └── system/ # 系统管理业务接口 ├── chat/ # Chat 独立请求体系,当前预留 +├── enums/ # 后端固定枚举值 +│ ├── index.ts # API 枚举值的唯一导入入口 +│ └── .ts # 按明确业务域拆分的枚举值 ├── types/ # API 与 View/Component 共用的业务类型 │ ├── index.ts # API 公共类型的唯一导入入口 │ ├── common.ts # 多个 API 业务域共用的基础类型 @@ -46,9 +49,17 @@ src/api/ `LoginApi.postLogin()`;System Workspace API 使用 `import WorkspaceApi from '@/api/admin/system/workspace'`。 -## 类型组织 +## 枚举与类型组织 -API 类型统一在 `src/api` 范围内管理,相关规则由本文档统一维护。 +API 枚举与类型统一在 `src/api` 范围内管理,相关规则由本文档统一维护。 + +后端字段的固定枚举值放在 `src/api/enums/.ts`,使用 `as const` 对象声明,并通过 +`src/api/enums/index.ts` 统一导出。业务代码统一从 `@/api/enums` 导入运行时枚举值,不直接 +引用领域文件;不得重复使用裸字符串或另建同值常量。 + +`src/api/enums` 按明确业务域拆分文件,不创建收集无关枚举的通用文件。枚举的联合类型在对应的 +`src/api/types/.ts` 中由枚举对象派生,并继续通过 `@/api/types` 对外提供。例如 +`TOOL_TYPE` 从 `@/api/enums` 导入,`ToolType` 从 `@/api/types` 导入。 新增或移动类型时按以下顺序判断: @@ -68,11 +79,7 @@ API 类型统一在 `src/api` 范围内管理,相关规则由本文档统一 `import type { ... } from '@/api/types'` 导入,不写 `/index.ts`。 - `src/api/types/index.ts` 只负责导出各业务域类型,不直接声明类型。 - 新增类型前先搜索是否已有等价声明,优先复用或扩展已有类型。 -- 后端字段的固定协议值与其类型放在同一个 `src/api/types/.ts` 中:使用 - `as const` 对象声明运行时常量,并从该对象派生联合类型。协议常量是唯一数据源;新增、删除或 - 修改协议值只维护对应的 API 类型文件,业务判断不得重复使用裸字符串或另建同值常量。 -- 同时导出协议常量的类型模块在 `src/api/types/index.ts` 中使用 `export *`;只有纯类型模块使用 - `export type *`。 +- `src/api/types/index.ts` 只使用 `export type *` 导出类型,运行时值不得从 `@/api/types` 暴露。 - 同一业务边界内的重复类型提取到最近共同目录的 `common.ts`;`common.ts` 不得成为无关类型 的集合。 - API 与 View 或 Component 使用同一业务类型时只保留一份声明,不得在两层分别定义。 diff --git a/ui/src/api/admin/system/auth-scan-setting.ts b/ui/src/api/admin/system/auth-scan-setting.ts index 501a7261d8f..b79d7e8e84f 100644 --- a/ui/src/api/admin/system/auth-scan-setting.ts +++ b/ui/src/api/admin/system/auth-scan-setting.ts @@ -1,5 +1,5 @@ import { get, post, put } from '../core/request' -import type { QrLoginPlatform, QrLoginPlatformRequest } from '@/api/types' +import type { QrLoginPlatform, QrLoginPlatformPayload } from '@/api/types' const prefix = '/platform/source' @@ -9,13 +9,13 @@ const getQrLoginPlatforms = () => { } /** 保存扫码登录平台配置。 */ -const putQrLoginPlatform = (platform: QrLoginPlatformRequest) => { - return put(prefix, platform) +const putQrLoginPlatform = (payload: QrLoginPlatformPayload) => { + return put(prefix, payload) } /** 校验扫码登录平台配置是否可用。 */ -const postValidateQrLoginPlatform = (platform: QrLoginPlatformRequest) => { - return post(`${prefix}`, platform) +const postValidateQrLoginPlatform = (payload: QrLoginPlatformPayload) => { + return post(`${prefix}`, payload) } export default { diff --git a/ui/src/api/admin/system/auth-setting.ts b/ui/src/api/admin/system/auth-setting.ts index 0b7e1411493..f796646eb25 100644 --- a/ui/src/api/admin/system/auth-setting.ts +++ b/ui/src/api/admin/system/auth-setting.ts @@ -1,31 +1,31 @@ import { get, post, put } from '../core/request' -import type { AuthProviderSetting, AuthProviderType, LoginAuthSetting } from '@/api/types' +import type { AuthProviderSettingPayload, AuthProviderType, LoginAuthSettingPayload } from '@/api/types' const prefix = '/auth' /** 获取指定认证源配置。 */ const getAuthSetting = (authType: AuthProviderType) => { - return get>(`${prefix}/${authType}/detail`) + return get>(`${prefix}/${authType}/detail`) } /** 测试认证源连接。 */ -const postAuthSettingConnection = (setting: AuthProviderSetting) => { - return post(`${prefix}/connection`, setting) +const postAuthSettingConnection = (payload: AuthProviderSettingPayload) => { + return post(`${prefix}/connection`, payload) } /** 保存指定认证源配置。 */ -const putAuthSetting = (authType: AuthProviderType, setting: AuthProviderSetting) => { - return put(`${prefix}/${authType}/info`, setting) +const putAuthSetting = (authType: AuthProviderType, payload: AuthProviderSettingPayload) => { + return put(`${prefix}/${authType}/info`, payload) } /** 获取系统登录设置。 */ const getLoginSetting = () => { - return get(`${prefix}/setting`) + return get(`${prefix}/setting`) } /** 保存系统登录设置。 */ -const putLoginSetting = (setting: LoginAuthSetting) => { - return put(`${prefix}/setting`, setting) +const putLoginSetting = (payload: LoginAuthSettingPayload) => { + return put(`${prefix}/setting`, payload) } export default { diff --git a/ui/src/api/admin/system/chat-user-auth-scan.ts b/ui/src/api/admin/system/chat-user-auth-scan.ts index 7fdfc9c55b2..2a3f709dbba 100644 --- a/ui/src/api/admin/system/chat-user-auth-scan.ts +++ b/ui/src/api/admin/system/chat-user-auth-scan.ts @@ -1,5 +1,5 @@ import { get, post, put } from '../core/request' -import type { QrLoginPlatform, QrLoginPlatformRequest } from '@/api/types' +import type { QrLoginPlatform, QrLoginPlatformPayload } from '@/api/types' const prefix = '/chat_user/auth/platform/source' @@ -9,13 +9,13 @@ const getQrLoginPlatforms = () => { } /** 保存对话用户扫码登录平台配置。 */ -const postQrLoginPlatform = (platform: QrLoginPlatformRequest) => { - return post(prefix, platform) +const postQrLoginPlatform = (payload: QrLoginPlatformPayload) => { + return post(prefix, payload) } /** 校验对话用户扫码登录平台配置是否可用。 */ -const putValidateQrLoginPlatform = (platform: QrLoginPlatformRequest) => { - return put(prefix, platform) +const putValidateQrLoginPlatform = (payload: QrLoginPlatformPayload) => { + return put(prefix, payload) } export default { diff --git a/ui/src/api/admin/system/chat-user-auth.ts b/ui/src/api/admin/system/chat-user-auth.ts index d55895700f8..3cb4276cc40 100644 --- a/ui/src/api/admin/system/chat-user-auth.ts +++ b/ui/src/api/admin/system/chat-user-auth.ts @@ -1,21 +1,21 @@ import { get, post, put } from '../core/request' -import type { AuthProviderSetting, AuthProviderType, LoginAuthSetting } from '@/api/types' +import type { AuthProviderSettingPayload, AuthProviderType } from '@/api/types' const prefix = '/chat_user/auth' /** 获取对话用户指定认证源配置。 */ const getAuthSetting = (authType: AuthProviderType) => { - return get>(`${prefix}/${authType}/detail`) + return get>(`${prefix}/${authType}/detail`) } /** 测试对话用户认证源连接。 */ -const postAuthSettingConnection = (setting: AuthProviderSetting) => { - return post(`${prefix}/connection`, setting) +const postAuthSettingConnection = (payload: AuthProviderSettingPayload) => { + return post(`${prefix}/connection`, payload) } /** 保存对话用户指定认证源配置。 */ -const putAuthSetting = (authType: AuthProviderType, setting: AuthProviderSetting) => { - return put(`${prefix}/${authType}/info`, setting) +const putAuthSetting = (authType: AuthProviderType, payload: AuthProviderSettingPayload) => { + return put(`${prefix}/${authType}/info`, payload) } export default { getAuthSetting, diff --git a/ui/src/api/admin/system/chat-user-groups.ts b/ui/src/api/admin/system/chat-user-groups.ts index b8f66cb9ca8..27b91e79aef 100644 --- a/ui/src/api/admin/system/chat-user-groups.ts +++ b/ui/src/api/admin/system/chat-user-groups.ts @@ -2,7 +2,7 @@ import { del, get, post } from '../core/request' import type { ParamsPage, ResponsePage } from '../core/types' import type { ChatUserGroupMember, - ChatUserGroupRequest, + ChatUserGroupPayload, ListItem, RequestParams, } from '@/api/types' @@ -15,8 +15,8 @@ const getChatUserGroups = () => { } /** 创建或重命名对话用户组。 */ -const postChatUserGroup = (group: ChatUserGroupRequest) => { - return post(prefix, group) +const postChatUserGroup = (payload: ChatUserGroupPayload) => { + return post(prefix, payload) } /** 删除对话用户组。 */ diff --git a/ui/src/api/admin/system/chat-user.ts b/ui/src/api/admin/system/chat-user.ts index 18c565c79d7..a6b3cfcb638 100644 --- a/ui/src/api/admin/system/chat-user.ts +++ b/ui/src/api/admin/system/chat-user.ts @@ -4,7 +4,7 @@ import type { BatchSetChatUserGroupsRequest, ChatUserBase, ChatUser, - ChatUserRequest, + ChatUserPayload, ChatUserSyncResult, ChatUserUpdateRequest, RequestParams, @@ -26,13 +26,13 @@ const getChatUserPage = (page: ParamsPage, query?: RequestParams) => { } /** 创建对话用户。 */ -const postChatUser = (user: ChatUserRequest) => { - return post(prefix, user) +const postChatUser = (payload: ChatUserPayload) => { + return post(prefix, payload) } /** 编辑对话用户。 */ -const putChatUser = (userId: string, user: ChatUserUpdateRequest) => { - return put(`${prefix}/${userId}`, user) +const putChatUser = (userId: string, payload: ChatUserUpdateRequest) => { + return put(`${prefix}/${userId}`, payload) } /** 修改对话用户密码。 */ diff --git a/ui/src/api/admin/system/email-setting.ts b/ui/src/api/admin/system/email-setting.ts index d0d341d944c..e43591e2d7c 100644 --- a/ui/src/api/admin/system/email-setting.ts +++ b/ui/src/api/admin/system/email-setting.ts @@ -1,21 +1,21 @@ import { get, post, put } from '../core/request' -import type { EmailSetting } from '@/api/types' +import type { EmailSettingPayload } from '@/api/types' const prefix = '/email_setting' /** 获取邮箱设置。 */ const getEmailSetting = () => { - return get>(prefix) + return get>(prefix) } /** 测试邮箱设置是否可用。 */ -const postEmailSettingTest = (setting: EmailSetting) => { - return post(prefix, setting) +const postEmailSettingTest = (payload: EmailSettingPayload) => { + return post(prefix, payload) } /** 保存邮箱设置。 */ -const putEmailSetting = (setting: EmailSetting) => { - return put(prefix, setting) +const putEmailSetting = (payload: EmailSettingPayload) => { + return put(prefix, payload) } export default { diff --git a/ui/src/api/admin/system/role.ts b/ui/src/api/admin/system/role.ts index 4ca172d7326..56488537321 100644 --- a/ui/src/api/admin/system/role.ts +++ b/ui/src/api/admin/system/role.ts @@ -6,9 +6,8 @@ import type { RoleItem, RoleMember, RolePermissionModule, - SaveRoleRequest, + RolePayload, SaveRolePermissionRequest, - RoleType, } from '@/api/types' const prefix = '/system/role' @@ -19,8 +18,8 @@ const getRoleList = () => { } /** 创建或重命名自定义角色。 */ -const postRole = (role: SaveRoleRequest) => { - return post(prefix, role) +const postRole = (payload: RolePayload) => { + return post(prefix, payload) } /** 删除自定义角色。 */ @@ -49,8 +48,8 @@ const getRoleMemberList = (roleId: string, page: ParamsPage, query?: RequestPara } /** 为指定角色添加成员。 */ -const postRoleMembers = (roleId: string, request: CreateRoleMembersRequest) => { - return post(`${prefix}/${roleId}/add_member`, request) +const postRoleMembers = (roleId: string, payload: CreateRoleMembersRequest) => { + return post(`${prefix}/${roleId}/add_member`, payload) } /** 从指定角色移除成员。 */ diff --git a/ui/src/api/admin/system/user-manage.ts b/ui/src/api/admin/system/user-manage.ts index 51ea213d11c..bcba3adec54 100644 --- a/ui/src/api/admin/system/user-manage.ts +++ b/ui/src/api/admin/system/user-manage.ts @@ -3,9 +3,8 @@ import type { ResponsePage, ParamsPage, PasswordRequest } from '../core/types' import type { RequestParams, SystemUser, - SystemUserRequest, + SystemUserPayload, SystemUserUpdateRequest, - SystemUserOption, BatchSetUserRolesRequest, BatchSetUserWorkspaceRolesRequest, } from '@/api/types' @@ -18,13 +17,13 @@ const getUserManagePage = (page: ParamsPage, query?: RequestParams) => { } /** 创建系统用户。 */ -const postUser = (user: SystemUserRequest) => { - return post(prefix, user) +const postUser = (payload: SystemUserPayload) => { + return post(prefix, payload) } /** 编辑系统用户。 */ -const putUser = (userId: string, user: SystemUserUpdateRequest) => { - return put(`${prefix}/${userId}`, user) +const putUser = (userId: string, payload: SystemUserUpdateRequest) => { + return put(`${prefix}/${userId}`, payload) } /** 修改系统用户密码。 */ @@ -43,13 +42,13 @@ const postBatchDeleteUsers = (userIds: string[]) => { } /** 专业版批量设置系统用户角色。 */ -const postBatchSetUserRoles = (request: BatchSetUserRolesRequest) => { - return post(`${prefix}/batch/add_role`, request) +const postBatchSetUserRoles = (payload: BatchSetUserRolesRequest) => { + return post(`${prefix}/batch/add_role`, payload) } /** 企业版批量设置系统用户角色及工作空间。 */ -const postBatchSetUserWorkspaceRoles = (request: BatchSetUserWorkspaceRolesRequest) => { - return post(`${prefix}/batch/add_role_ee`, request) +const postBatchSetUserWorkspaceRoles = (payload: BatchSetUserWorkspaceRolesRequest) => { + return post(`${prefix}/batch/add_role_ee`, payload) } diff --git a/ui/src/api/admin/system/workspace.ts b/ui/src/api/admin/system/workspace.ts index 4972b9dedb0..9c3c9f80a30 100644 --- a/ui/src/api/admin/system/workspace.ts +++ b/ui/src/api/admin/system/workspace.ts @@ -1,7 +1,7 @@ import { del, get, post } from '../core/request' import type { ParamsPage, ResponsePage } from '../core/types' import type { - CreateWorkspaceMemberParamsItem, + CreateWorkspaceMemberPayload, RequestParams, WorkspaceItem, WorkspaceMemberItem, @@ -41,8 +41,8 @@ const deleteWorkspace = (workspaceId: string) => { } /** 新增工作空间成员。 */ -const postWorkspaceMembers = (workspaceId: string, members: CreateWorkspaceMemberParamsItem[]) => { - return post( +const postWorkspaceMembers = (workspaceId: string, members: CreateWorkspaceMemberPayload[]) => { + return post( `${prefix}/${workspaceId}/add_member`, members, ) diff --git a/ui/src/api/admin/workspace/folder.ts b/ui/src/api/admin/workspace/folder.ts index 4c4a44c092b..c8c9cad1cb7 100644 --- a/ui/src/api/admin/workspace/folder.ts +++ b/ui/src/api/admin/workspace/folder.ts @@ -12,21 +12,16 @@ const getFolderTree = (source: FolderSource, query?: RequestParams) => { return get(getPrefix(source), query) } +/** 更新 Workspace 的文件夹。 */ +const putFolder = (folderId: string, source: FolderSource, payload?: FolderRequest) => { + return put(`${getPrefix(source)}/${folderId}`, payload) +} + /** 在指定资源模块中创建 Workspace 文件夹。 */ const postFolder = (source: FolderSource, payload: FolderRequest) => { return post(getPrefix(source), payload) } -/** 更新指定 Workspace 文件夹。 */ -const putFolder = ( - workspaceId: string, - source: FolderSource, - folderId: string, - payload: FolderRequest, -) => { - return put(`${getPrefix(source)}/${folderId}`, payload) -} - /** 删除指定 Workspace 文件夹及其中的资源。 */ const deleteFolder = (source: FolderSource, folderId: string) => { return del(`${getPrefix(source)}/${folderId}`) diff --git a/ui/src/api/admin/workspace/model/provider.ts b/ui/src/api/admin/workspace/model/provider.ts index c582a830f0b..a24447c3b35 100644 --- a/ui/src/api/admin/workspace/model/provider.ts +++ b/ui/src/api/admin/workspace/model/provider.ts @@ -1,16 +1,16 @@ import { get } from '../../core/request' -import type { BaseModelOption, DynamicFormField, ModelProvider, ModelTypeOption } from '@/api/types' +import type { BaseModelOption, DynamicFormField, ModelProviderItem, ModelTypeOption } from '@/api/types' const prefix = '/provider' /** 获取全部模型供应商。 */ const getProviderList = () => { - return get(prefix) + return get(prefix) } /** 获取支持指定模型类型的供应商。 */ const getProviderListByModelType = (modelType: string) => { - return get(prefix, { model_type: modelType }) + return get(prefix, { model_type: modelType }) } /** 获取创建模型所需的动态表单。 */ diff --git a/ui/src/api/admin/workspace/tool/tool.ts b/ui/src/api/admin/workspace/tool/tool.ts index a740860d36c..6af9df560ab 100644 --- a/ui/src/api/admin/workspace/tool/tool.ts +++ b/ui/src/api/admin/workspace/tool/tool.ts @@ -1,12 +1,6 @@ import { del, get, post, put } from '../../core/request' import type { ParamsPage, ResponsePage } from '../../core/types' -import type { - ToolCatalogResponse, - ToolListQuery, - ToolPayload, - ToolTreeResponse, - WorkspaceTool, -} from '@/api/types' +import type { RequestParams, ToolPayload, WorkspaceTool } from '@/api/types' import { getWorkspaceId } from '@/utils/workspace-context' const getPrefix = () => { @@ -15,21 +9,16 @@ const getPrefix = () => { } /** 获取工具分页列表。 */ -const getToolPage = (page: ParamsPage, query?: ToolListQuery) => { +const getToolPage = (page: ParamsPage, query?: RequestParams) => { return get>( `${getPrefix()}/${page.currentPage}/${page.pageSize}`, query, ) } -/** 获取当前目录下的工具和子文件夹。 */ -const getToolTree = (query?: ToolListQuery) => { - return get(getPrefix(), query) -} - -/** 获取工作空间全部工具以及已授权的共享工具。 */ -const getToolCatalog = (query?: ToolListQuery) => { - return get(`${getPrefix()}/tool_list`, query) +/** 更新工作空间工具。 */ +const putTool = (toolId: string, payload: ToolPayload) => { + return put(`${getPrefix()}/${toolId}`, payload) } /** 创建工作空间工具。 */ @@ -37,11 +26,6 @@ const postTool = (payload: ToolPayload) => { return post(getPrefix(), payload) } -/** 更新工作空间工具。 */ -const putTool = (toolId: string, payload: ToolPayload) => { - return put(`${getPrefix()}/${toolId}`, payload) -} - /** 获取工具详情。 */ const getToolDetail = (toolId: string) => { return get(`${getPrefix()}/${toolId}`) @@ -73,11 +57,10 @@ const putBatchMoveTools = (toolIds: string[], folderId: string) => { } export default { + getToolPage, deleteTool, - getToolCatalog, getToolDetail, - getToolPage, - getToolTree, + postTool, postToolTestConnection, putBatchDeleteTools, diff --git a/ui/src/api/enums/folder.ts b/ui/src/api/enums/folder.ts new file mode 100644 index 00000000000..563ab1c3e1f --- /dev/null +++ b/ui/src/api/enums/folder.ts @@ -0,0 +1,6 @@ +/** 后端文件夹资源来源枚举值。 */ +export const FOLDER_SOURCE = { + APPLICATION: 'APPLICATION', + KNOWLEDGE: 'KNOWLEDGE', + TOOL: 'TOOL', +} as const diff --git a/ui/src/api/enums/index.ts b/ui/src/api/enums/index.ts new file mode 100644 index 00000000000..7cbb9094f45 --- /dev/null +++ b/ui/src/api/enums/index.ts @@ -0,0 +1,6 @@ +/** API 枚举值的唯一公共入口。 */ +export * from './folder' +export * from './login' +export * from './system-role' +export * from './model' +export * from './tool' diff --git a/ui/src/api/enums/login.ts b/ui/src/api/enums/login.ts new file mode 100644 index 00000000000..97185f8f2fd --- /dev/null +++ b/ui/src/api/enums/login.ts @@ -0,0 +1,12 @@ +/** 后端登录方式枚举值;新增或修改登录方式时以此处为唯一数据源。 */ +export const LOGIN_METHOD = { + CAS: 'CAS', + DINGTALK: 'dingtalk', + LDAP: 'LDAP', + LARK: 'lark', + LOCAL: 'LOCAL', + OAUTH2: 'OAuth2', + OIDC: 'OIDC', + SAML2: 'SAML2', + WECOM: 'wecom', +} as const diff --git a/ui/src/api/enums/model.ts b/ui/src/api/enums/model.ts new file mode 100644 index 00000000000..8cc8066b3cf --- /dev/null +++ b/ui/src/api/enums/model.ts @@ -0,0 +1,7 @@ +/** 后端 Workspace 模型状态枚举值。 */ +export const MODEL_STATUS = { + DOWNLOAD: 'DOWNLOAD', + ERROR: 'ERROR', + PAUSE_DOWNLOAD: 'PAUSE_DOWNLOAD', + SUCCESS: 'SUCCESS', +} as const diff --git a/ui/src/api/enums/system-role.ts b/ui/src/api/enums/system-role.ts new file mode 100644 index 00000000000..9638f264a4b --- /dev/null +++ b/ui/src/api/enums/system-role.ts @@ -0,0 +1,6 @@ +/** 后端角色类型枚举值;新增或修改角色类型时以此处为唯一数据源。 */ +export const ROLE_TYPE = { + ADMIN: 'ADMIN', + USER: 'USER', + WORKSPACE_MANAGE: 'WORKSPACE_MANAGE', +} as const diff --git a/ui/src/api/enums/tool.ts b/ui/src/api/enums/tool.ts new file mode 100644 index 00000000000..3ccb47742cb --- /dev/null +++ b/ui/src/api/enums/tool.ts @@ -0,0 +1,16 @@ +/** 后端 Workspace 工具作用域枚举值。 */ +export const TOOL_SCOPE = { + INTERNAL: 'INTERNAL', + SHARED: 'SHARED', + WORKSPACE: 'WORKSPACE', +} as const + +/** 后端 Workspace 工具类型枚举值。 */ +export const TOOL_TYPE = { + CUSTOM: 'CUSTOM', + DATA_SOURCE: 'DATA_SOURCE', + INTERNAL: 'INTERNAL', + MCP: 'MCP', + SKILL: 'SKILL', + WORKFLOW: 'WORKFLOW', +} as const diff --git a/ui/src/api/types/chat-user-groups.ts b/ui/src/api/types/chat-user-groups.ts index dab41dd5386..78055cca2fa 100644 --- a/ui/src/api/types/chat-user-groups.ts +++ b/ui/src/api/types/chat-user-groups.ts @@ -6,7 +6,7 @@ export interface ChatUserGroupMember extends ChatUserBase { user_group_relation_id: string } -export interface ChatUserGroupRequest { +export interface ChatUserGroupPayload { id?: string name: string } diff --git a/ui/src/api/types/chat-user.ts b/ui/src/api/types/chat-user.ts index 05046afb137..8e4f7f087ff 100644 --- a/ui/src/api/types/chat-user.ts +++ b/ui/src/api/types/chat-user.ts @@ -17,7 +17,7 @@ export interface ChatUser extends ChatUserBase { user_group_names: string[] } -export interface ChatUserRequest { +export interface ChatUserPayload { username: string nick_name: string email: string diff --git a/ui/src/api/types/folder.ts b/ui/src/api/types/folder.ts index 6e095501632..4978b6a17a6 100644 --- a/ui/src/api/types/folder.ts +++ b/ui/src/api/types/folder.ts @@ -1,10 +1,6 @@ /** Workspace 下多个资源模块共用的文件夹业务类型。 */ -export const FOLDER_SOURCE = { - APPLICATION: 'APPLICATION', - KNOWLEDGE: 'KNOWLEDGE', - TOOL: 'TOOL', -} as const +import { FOLDER_SOURCE } from '@/api/enums' export type FolderSource = (typeof FOLDER_SOURCE)[keyof typeof FOLDER_SOURCE] diff --git a/ui/src/api/types/index.ts b/ui/src/api/types/index.ts index 430090e517e..3c56ef60270 100644 --- a/ui/src/api/types/index.ts +++ b/ui/src/api/types/index.ts @@ -2,14 +2,14 @@ export type * from './common' export type * from './chat-user' export type * from './chat-user-groups' -export * from './login' +export type * from './login' export type * from './system-user' export type * from './system-user-groups' export type * from './system-workspace' export type * from './system-authentication' export type * from './system-operate-log' -export * from './system-role' +export type * from './system-role' export type * from './system-email' -export * from './workspace-model' -export * from './folder' -export * from './workspace-tool' +export type * from './workspace-model' +export type * from './folder' +export type * from './workspace-tool' diff --git a/ui/src/api/types/login.ts b/ui/src/api/types/login.ts index 037095d87c2..60235583b08 100644 --- a/ui/src/api/types/login.ts +++ b/ui/src/api/types/login.ts @@ -1,17 +1,6 @@ /** 登录 API 与登录页面共同使用的业务类型。 */ -/** 后端登录方式协议值;新增或修改登录方式时以此处为唯一数据源。 */ -export const LOGIN_METHOD = { - CAS: 'CAS', - DINGTALK: 'dingtalk', - LDAP: 'LDAP', - LARK: 'lark', - LOCAL: 'LOCAL', - OAUTH2: 'OAuth2', - OIDC: 'OIDC', - SAML2: 'SAML2', - WECOM: 'wecom', -} as const +import { LOGIN_METHOD } from '@/api/enums' export type LoginMethod = (typeof LOGIN_METHOD)[keyof typeof LOGIN_METHOD] diff --git a/ui/src/api/types/system-authentication.ts b/ui/src/api/types/system-authentication.ts index 57196ccbed0..932e971e684 100644 --- a/ui/src/api/types/system-authentication.ts +++ b/ui/src/api/types/system-authentication.ts @@ -3,14 +3,14 @@ import type { QrCodeProvider } from '@/api/types/login' export type AuthProviderType = 'LDAP' | 'CAS' | 'OIDC' | 'OAuth2' | 'SAML2' -export interface AuthProviderSetting { +export interface AuthProviderSettingPayload { id?: string auth_type: AuthProviderType config: Record is_active: boolean } -export interface LoginAuthSetting { +export interface LoginAuthSettingPayload { auth_types?: OptionItem[] default_value: string failed_attempts: number @@ -31,7 +31,7 @@ export interface QrLoginPlatform { is_valid: boolean } -export interface QrLoginPlatformRequest { +export interface QrLoginPlatformPayload { config: Record isActive: boolean key: QrCodeProvider diff --git a/ui/src/api/types/system-email.ts b/ui/src/api/types/system-email.ts index 786ceee94ba..45b127c7789 100644 --- a/ui/src/api/types/system-email.ts +++ b/ui/src/api/types/system-email.ts @@ -1,4 +1,4 @@ -export interface EmailSetting { +export interface EmailSettingPayload { email_host: string email_host_password: string email_host_user: string diff --git a/ui/src/api/types/system-role.ts b/ui/src/api/types/system-role.ts index 27616b976d4..6a69c1a8eba 100644 --- a/ui/src/api/types/system-role.ts +++ b/ui/src/api/types/system-role.ts @@ -1,9 +1,4 @@ -/** 后端角色类型协议值;新增或修改角色类型时以此处为唯一数据源。 */ -export const ROLE_TYPE = { - ADMIN: 'ADMIN', - USER: 'USER', - WORKSPACE_MANAGE: 'WORKSPACE_MANAGE', -} as const +import { ROLE_TYPE } from '@/api/enums' export type RoleType = (typeof ROLE_TYPE)[keyof typeof ROLE_TYPE] @@ -35,7 +30,7 @@ export interface RolePermissionModule { children: RolePermissionFeature[] } -export interface SaveRoleRequest { +export interface RolePayload { role_id?: string role_name: string role_type?: RoleType diff --git a/ui/src/api/types/system-user.ts b/ui/src/api/types/system-user.ts index 8594dd5a675..c71ff6bb153 100644 --- a/ui/src/api/types/system-user.ts +++ b/ui/src/api/types/system-user.ts @@ -17,7 +17,7 @@ export interface BatchSetUserWorkspaceRolesRequest { role_setting: SystemUserRoleAssignment[] } -export interface SystemUserRequest { +export interface SystemUserPayload { id?: string username: string email: string diff --git a/ui/src/api/types/system-workspace.ts b/ui/src/api/types/system-workspace.ts index cd9c3ccdf2f..d29061af0d5 100644 --- a/ui/src/api/types/system-workspace.ts +++ b/ui/src/api/types/system-workspace.ts @@ -5,7 +5,7 @@ export interface WorkspaceItem { user_count?: number } -export interface CreateWorkspaceMemberParamsItem { +export interface CreateWorkspaceMemberPayload { user_ids: string[] role_ids: string[] } diff --git a/ui/src/api/types/workspace-model.ts b/ui/src/api/types/workspace-model.ts index 3fbd631477c..cc439b89c93 100644 --- a/ui/src/api/types/workspace-model.ts +++ b/ui/src/api/types/workspace-model.ts @@ -1,15 +1,10 @@ /** Workspace 模型与工具页面共用的业务类型。 */ -export const MODEL_STATUS = { - DOWNLOAD: 'DOWNLOAD', - ERROR: 'ERROR', - PAUSE_DOWNLOAD: 'PAUSE_DOWNLOAD', - SUCCESS: 'SUCCESS', -} as const +import { MODEL_STATUS } from '@/api/enums' export type ModelStatus = (typeof MODEL_STATUS)[keyof typeof MODEL_STATUS] -export interface ModelProvider { +export interface ModelProviderItem { icon: string name: string provider: string @@ -36,7 +31,6 @@ export interface WorkspaceUserOption { nick_name: string } - export interface ModelPayload { credential: Record model_name: string diff --git a/ui/src/api/types/workspace-tool.ts b/ui/src/api/types/workspace-tool.ts index 1fcaa1b9419..4894cf11c53 100644 --- a/ui/src/api/types/workspace-tool.ts +++ b/ui/src/api/types/workspace-tool.ts @@ -1,21 +1,6 @@ /** Workspace 工具列表和工具维护共用的业务类型。 */ -import type { WorkspaceFolder } from './folder' - -export const TOOL_SCOPE = { - INTERNAL: 'INTERNAL', - SHARED: 'SHARED', - WORKSPACE: 'WORKSPACE', -} as const - -export const TOOL_TYPE = { - CUSTOM: 'CUSTOM', - DATA_SOURCE: 'DATA_SOURCE', - INTERNAL: 'INTERNAL', - MCP: 'MCP', - SKILL: 'SKILL', - WORKFLOW: 'WORKFLOW', -} as const +import { TOOL_SCOPE, TOOL_TYPE } from '@/api/enums' export type ToolScope = (typeof TOOL_SCOPE)[keyof typeof TOOL_SCOPE] export type ToolType = (typeof TOOL_TYPE)[keyof typeof TOOL_TYPE] @@ -44,26 +29,6 @@ export interface WorkspaceTool { workspace_id: string } -export interface ToolCatalogResponse { - shared_tools: WorkspaceTool[] - tools: WorkspaceTool[] -} - -export interface ToolTreeResponse { - folders: WorkspaceFolder[] - tools: WorkspaceTool[] -} - -export interface ToolListQuery { - create_user?: string - folder_id?: string - name?: string - scope?: ToolScope - tool_type?: ToolType - 'tool_type_list[]'?: ToolType[] - [key: string]: unknown -} - export interface ToolPayload { code?: string desc?: string | null @@ -77,8 +42,3 @@ export interface ToolPayload { scope?: ToolScope tool_type?: ToolType } - -export interface AddStoreToolPayload { - folder_id?: string | null - name?: string -} diff --git a/ui/src/components.d.ts b/ui/src/components.d.ts index 7f84e02e11b..1aaecfee17b 100644 --- a/ui/src/components.d.ts +++ b/ui/src/components.d.ts @@ -23,6 +23,7 @@ declare module 'vue' { MkEmpty: typeof import('./components/global/mk-empty/index.vue')['default'] MkFilterableDropdown: typeof import('./components/global/mk-filterable-dropdown/index.vue')['default'] MkIcon: typeof import('./components/global/mk-icon/index.vue')['default'] + MkLoadMore: typeof import('./components/global/mk-load-more/index.vue')['default'] MkSearchInput: typeof import('./components/global/mk-search-input/index.vue')['default'] MkStatusLabel: typeof import('./components/global/mk-status-label/index.vue')['default'] MkTable: typeof import('./components/global/mk-table/index.vue')['default'] @@ -46,6 +47,7 @@ declare global { const MkEmpty: typeof import('./components/global/mk-empty/index.vue')['default'] const MkFilterableDropdown: typeof import('./components/global/mk-filterable-dropdown/index.vue')['default'] const MkIcon: typeof import('./components/global/mk-icon/index.vue')['default'] + const MkLoadMore: typeof import('./components/global/mk-load-more/index.vue')['default'] const MkSearchInput: typeof import('./components/global/mk-search-input/index.vue')['default'] const MkStatusLabel: typeof import('./components/global/mk-status-label/index.vue')['default'] const MkTable: typeof import('./components/global/mk-table/index.vue')['default'] diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md index 398d3b0e16a..13a69718dd3 100644 --- a/ui/src/components/COMPONENT_README.md +++ b/ui/src/components/COMPONENT_README.md @@ -65,6 +65,8 @@ src/components/ │ │ └── index.vue # 带搜索过滤和滚动列表的下拉选择 │ ├── mk-icon/ │ │ └── index.vue # SVG Symbol 与 Element Plus 图标统一入口 +│ ├── mk-load-more/ +│ │ └── index.vue # 分页列表滚动触底加载与结束状态 │ ├── mk-view-layout/ │ │ ├── index.vue # 路由页面标题、操作区和内容区统一结构 │ │ └── layout-aside.vue # 页面可选左侧栏结构 @@ -366,6 +368,19 @@ Element Plus Dropdown 属性和事件通过 `$attrs` 传入,并暴露 `handleO ``` +### MkLoadMore + +分页列表的滚动触底加载组件,使用 `IntersectionObserver` 监听组件所在滚动区域的底部,不依赖 +Element Plus 的 `v-infinite-scroll`。通过 `paginationConfig` 接收 `currentPage`、`pageSize` 和 +`total`,组件内部判断是否加载完成;接近底部时,`load` 事件返回下一页页码。`loading` 用于阻止 +重复请求并展示加载状态,`disabled` 可临时禁用加载,`distance` 设置提前触发距离且默认为 +`200px`。数据不足以撑满滚动区域时会继续加载,直至填满或全部加载完成。`loading` 和 `finished` +插槽可覆盖默认提示。 + +```vue + +``` + ### MkSearchInput 带默认搜索图标的输入框。`placeholder` 默认为“搜索”,组件固定支持清空;`v-model`、其他属性 diff --git a/ui/src/components/business/folder-tree/VirtualizedTree.vue b/ui/src/components/business/folder-tree/VirtualizedTree.vue index 19481e3d45d..598c1d07629 100644 --- a/ui/src/components/business/folder-tree/VirtualizedTree.vue +++ b/ui/src/components/business/folder-tree/VirtualizedTree.vue @@ -52,6 +52,7 @@ const emit = defineEmits<{ nodeClick: [folder: FolderItem] }>() +// 过滤树 function filterFolders(folders: FolderTreeNode[], keyword: string): FolderTreeNode[] { if (!keyword) return folders @@ -69,6 +70,15 @@ const filteredTreeData = computed(() => filterFolders(props.data as FolderTreeNode[], props.filterText.trim().toLocaleLowerCase()), ) +// 点击展开或选择 +function handleNodeClick(stat: FolderStat) { + if (stat.data.disabled) return + if (stat.data.id === props.currentNodeKey) { + stat.open = !stat.open + return + } + emit('nodeClick', stat.data) +} function containsCurrentFolder(folder: FolderTreeNode): boolean { return ( folder.id === props.currentNodeKey || @@ -82,15 +92,7 @@ function handleStat(stat: T): T { return stat } -function handleNodeClick(stat: FolderStat) { - if (stat.data.disabled) return - if (stat.data.id === props.currentNodeKey) { - stat.open = !stat.open - return - } - emit('nodeClick', stat.data) -} - +// 拖拽 function buildNodeDropArgs() { const draggingNode = dragContext.dragNode as FolderStat | null const targetInfo = dragContext.targetInfo as DropTargetInfo | null @@ -165,6 +167,10 @@ function handleAfterDrop() { diff --git a/ui/src/components/business/folder-tree/index.vue b/ui/src/components/business/folder-tree/index.vue index 2012b38cfe5..2cb1c36605e 100644 --- a/ui/src/components/business/folder-tree/index.vue +++ b/ui/src/components/business/folder-tree/index.vue @@ -1,30 +1,25 @@