diff --git a/ui/AGENTS.md b/ui/AGENTS.md index ae80a85e2c3..9795b43096d 100644 --- a/ui/AGENTS.md +++ b/ui/AGENTS.md @@ -208,6 +208,9 @@ Keep these behaviors: ## Frontend Implementation Notes +- Newly implemented frontend features must not add permission checks or permission-based visibility + or disabled states until the user explicitly requests them. Preserve existing permission logic + outside the requested scope; page-specific guidance is maintained in `src/views/VIEW_README.md`. - Prefer existing project structure and naming. - Do not modify shared components under `src/components/` without an explicit user instruction to change those components. Follow the modification boundary in `src/components/COMPONENT_README.md`. diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index 13fa7572082..1d98488fc46 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -112,6 +112,14 @@ Action、Drawer 或 Dialog。复用方直接使用 `typeof XxxApi` 约束完整 ### 知识库维护 +`exportKnowledgeExcel`、`exportKnowledgeZip`、`exportKnowledge` 分别通过 GET 请求知识库 +`//export`、`export_zip`、`export_knowledge`,复用 `getExportFile` 下载。 +三种结果分别为文档 Excel、包含图片的文档 ZIP 和可导入创建的知识库 ZIP;优先使用服务端文件名。 + +`postKnowledgeImport(file, folderId)` 将文件与 `folder_id` 组装为 FormData,POST 到 +`/workspace//knowledge/import_knowledge`,响应为 `{ knowledge_id, type }`。 +后端校验知识库导出包并创建资源;导入成功后的用户权限和列表刷新由调用页面负责。 + `workspace/knowledge/knowledge.ts` 与 `workspace/shared.ts` 的 `getAllKnowledge(query)` 分别查询工作空间及共享知识库的非分页列表,返回 `KnowledgeItem[]`,用于关联知识库选择等 需要全量选项的场景。原有 `getKnowledgePage` 继续用于分页列表。 @@ -121,6 +129,10 @@ Action、Drawer 或 Dialog。复用方直接使用 `typeof XxxApi` 约束完整 组装为 `{ id_list, folder_id }`,`putBatchDeleteKnowledge` 将 ID 数组组装为 `{ id_list }`, 分别使用 PUT 请求 `batch_move` 和 `batch_delete`。页面和 Action 负责类型判断及 loading。 +`putReEmbeddingKnowledge` 使用 PUT 请求 `//embedding` 重新向量化。 +设置页更换向量模型时先确认、保存,再调用该接口;Web、飞书配置通过 `meta` 提交,保留未编辑的 +已有配置,文件数量与大小限制仍作为知识库顶层字段提交。 + 知识库创建通过 `postKnowledge`、`postWebKnowledge` 分别提交到 `/base`、`/web`; `postLarkKnowledge` 沿用飞书扩展接口 `/lark/save`,当前开源后端未包含该实现。 基础创建字段由 `KnowledgeCreatePayload` 统一维护,Web、飞书请求扩展对应类型;工作流创建 diff --git a/ui/src/api/admin/core/request.ts b/ui/src/api/admin/core/request.ts index c1976f42d5c..94bb14c02c6 100644 --- a/ui/src/api/admin/core/request.ts +++ b/ui/src/api/admin/core/request.ts @@ -93,15 +93,6 @@ async function getResponseErrorMessage(error: unknown) { return responseData?.message } -async function getFetchErrorMessage(response: Response) { - const responseText = await response.text() - try { - const responseData = JSON.parse(responseText) as Partial> - return responseData.message || responseText - } catch { - return responseText - } -} async function downloadExportResponse(response: AxiosResponse, fileName: string, mimeType = 'application/octet-stream') { if (response.data.type.includes('application/json')) { diff --git a/ui/src/api/admin/workspace/knowledge/knowledge.ts b/ui/src/api/admin/workspace/knowledge/knowledge.ts index 22f0af20fc4..525cf059646 100644 --- a/ui/src/api/admin/workspace/knowledge/knowledge.ts +++ b/ui/src/api/admin/workspace/knowledge/knowledge.ts @@ -1,4 +1,4 @@ -import { del, get, post, put } from '../../core/request' +import { del, get, getExportFile, post, put } from '../../core/request' import type { ParamsPage, ResponsePage } from '../../core/types' import type { Dict, KnowledgeDetail, KnowledgeItem, KnowledgeCreatePayload, WebKnowledgeCreatePayload, LarkKnowledgeCreatePayload } from '@/api/types' import { getWorkspaceId } from '@/utils/resource-context' @@ -52,6 +52,11 @@ const putLarkKnowledge = (knowledgeId: string, payload: Partial) return put, KnowledgeItem>(`${getPrefix()}/lark/${knowledgeId}`, payload) } +/** 对知识库中的文档重新向量化。 */ +const putReEmbeddingKnowledge = (knowledgeId: string) => { + return put(`${getPrefix()}/${knowledgeId}/embedding`) +} + /** 批量删除工作空间知识库。 */ const putBatchDeleteKnowledge = (knowledgeIds: string[]) => { return put<{ id_list: string[] }, boolean>(`${getPrefix()}/batch_delete`, { id_list: knowledgeIds }) @@ -62,12 +67,33 @@ const putBatchMoveKnowledge = (knowledgeIds: string[], folderId: string) => { return put<{ id_list: string[]; folder_id: string }, boolean>(`${getPrefix()}/batch_move`, { id_list: knowledgeIds, folder_id: folderId }) } -/** 导入知识库文件。 */ -const importKnowledgeBundle = (payload: FormData) => { - return post(`${getPrefix()}/import_knowledge`, payload) +/** 将知识库文档导出为 Excel。 */ +const exportKnowledgeExcel = (knowledgeId: string, knowledgeName: string) => { + return getExportFile(`${knowledgeName}.xlsx`, `${getPrefix()}/${knowledgeId}/export`) +} + +/** 将知识库文档及图片导出为 ZIP。 */ +const exportKnowledgeZip = (knowledgeId: string, knowledgeName: string) => { + return getExportFile(`${knowledgeName}.zip`, `${getPrefix()}/${knowledgeId}/export_zip`) +} + +/** 导出可用于导入创建的知识库压缩包。 */ +const exportKnowledge = (knowledgeId: string, knowledgeName: string) => { + return getExportFile(`${knowledgeName}.zip`, `${getPrefix()}/${knowledgeId}/export_knowledge`) +} + +/** 导入知识库文件并在指定文件夹创建知识库。 */ +const postKnowledgeImport = (file: File, folderId: string) => { + const payload = new FormData() + payload.append('file', file) + payload.append('folder_id', folderId) + return post(`${getPrefix()}/import_knowledge`, payload) } export default { + exportKnowledgeExcel, + exportKnowledgeZip, + exportKnowledge, postKnowledge, postWebKnowledge, postLarkKnowledge, @@ -77,7 +103,8 @@ export default { getKnowledgePage, putKnowledge, putLarkKnowledge, + putReEmbeddingKnowledge, putBatchDeleteKnowledge, putBatchMoveKnowledge, - importKnowledgeBundle, + postKnowledgeImport, } diff --git a/ui/src/api/enums/knowledge.ts b/ui/src/api/enums/knowledge.ts index ba4c6d15307..2ca645e553f 100644 --- a/ui/src/api/enums/knowledge.ts +++ b/ui/src/api/enums/knowledge.ts @@ -1,5 +1,5 @@ /** 后端知识库类型。 */ -export const KNOWLEDGE_TYPE = { BASE: 0, WEB: 1, LARK: 2, YUQUE: 3, WORKFLOW: 4 } as const +export const KNOWLEDGE_TYPE = { BASE: 0, WEB: 1, LARK: 2, WORKFLOW: 4 } as const /** 知识库分段检索模式。 */ export const KNOWLEDGE_SEARCH_MODE = { EMBEDDING: 'embedding', KEYWORDS: 'keywords', BLEND: 'blend' } as const diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md index 08ba00338d8..cae3637b042 100644 --- a/ui/src/components/COMPONENT_README.md +++ b/ui/src/components/COMPONENT_README.md @@ -79,12 +79,15 @@ Admin、Chat 入口调用 `configureMarkdownEditor()` 配置本地高亮、KaTeX ### MkCollapse +`trigger` 默认 `header`,点击标题区域切换;设置为 `indicator` 时,仅点击箭头按钮切换, +标题文字和空白区域不触发。箭头按钮支持键盘操作。 + `v-model:expanded` 可控制展开状态;未绑定时沿用 `defaultExpanded` 的内部状态。 标题折叠区,`title` 或 `label` 插槽提供标题,默认插槽提供内容。 -`defaultExpanded` 默认 `true`,仅设置初始状态;点击标题内部切换,当前不提供展开状态事件。 +`defaultExpanded` 默认 `true`,仅设置初始状态;展开状态通过 `v-model:expanded` 同步。 `indicatorPosition` 默认 `before`,可设为 `after`;`triggerClass`、`triggerStyle` 调整触发区。 -内容使用 `v-if`,收起会卸载内部组件。 +内容使用 `v-show`,收起时保留内部组件及表单状态。 ### MkEmpty @@ -523,6 +526,10 @@ Skills 场景传入 `[TOOL_TYPE.SKILL]`,并通过 `title` 指定标题。两 ### SelectModel +`teleported` 默认为 `false`,画布节点内保留此默认值,使下拉浮层跟随画布。 +画布外的表单、Dialog 和 Drawer 显式传入 `teleported`(即 `true`),将下拉浮层挂载到 body, +避免被滚动容器裁剪。动态表单 Model 字段可通过透传属性配置该值。 + 按供应商分组展示模型,单选 `v-model` 为字符串,`multiple` 模式为字符串数组,变化时触发 `change`。`options` 为 `ModelItem[]`,`providerOptions` 为 `ModelProviderItem[]`,模型列表和供应商列表均由使用方查询。 需要接口加载的模型选项统一使用 `getModelListWithShared`,共享标签读取 `source: 'shared'`。 diff --git a/ui/src/components/business/select-model/index.vue b/ui/src/components/business/select-model/index.vue index 12b686f02b8..2a4aa67fd81 100644 --- a/ui/src/components/business/select-model/index.vue +++ b/ui/src/components/business/select-model/index.vue @@ -29,6 +29,7 @@ const props = withDefaults( canAdd?: boolean modelParams?: Record disabled?: boolean + teleported?: boolean multiple?: Multiple }>(), { @@ -38,6 +39,7 @@ const props = withDefaults( canAdd: false, modelParams: () => ({}), disabled: false, + teleported: false, }, ) @@ -129,7 +131,7 @@ function openModelParams() { filterable :loading="loading" :multiple="multiple" - :teleported="false" + :teleported="teleported" > (), - { defaultExpanded: true, indicatorPosition: 'before' }, + { defaultExpanded: true, indicatorPosition: 'before', trigger: 'header' }, ) defineSlots<{ default?: () => unknown; label?: () => unknown }>() @@ -30,26 +31,29 @@ const expanded = computed({ diff --git a/ui/src/constants/CONSTANT_README.md b/ui/src/constants/CONSTANT_README.md index 965dfda4d4c..3b43cdd6442 100644 --- a/ui/src/constants/CONSTANT_README.md +++ b/ui/src/constants/CONSTANT_README.md @@ -21,7 +21,7 @@ API 枚举值。例如角色类型和登录方式以 `src/api/enums` 为唯一 只维护它们的展示文案。 知识库类型的接口值保持为 `KNOWLEDGE_TYPE` 中的数字。`constants/knowledge.ts` 提供 -`KNOWLEDGE_TYPE_MAP`,将数字转换为 `BASE`、`WEB`、`LARK`、`YUQUE`、`WORKFLOW` 字符串, +`KNOWLEDGE_TYPE_MAP`,将数字转换为 `BASE`、`WEB`、`LARK`、`WORKFLOW` 字符串, 供前端判断;`KNOWLEDGE_TYPE_LABELS` 按这些字符串提供展示文案。提交接口时仍使用数字枚举值。 字符串标识统一由 `KNOWLEDGE_TYPE_KEY` 维护,映射、文案和业务判断均引用该常量,避免重复写裸字符串。 diff --git a/ui/src/constants/knowledge.ts b/ui/src/constants/knowledge.ts index b7c55872893..28e83da2fb7 100644 --- a/ui/src/constants/knowledge.ts +++ b/ui/src/constants/knowledge.ts @@ -15,7 +15,6 @@ export const KNOWLEDGE_TYPE_MAP = { [KNOWLEDGE_TYPE.BASE]: KNOWLEDGE_TYPE_KEY.BASE, [KNOWLEDGE_TYPE.WEB]: KNOWLEDGE_TYPE_KEY.WEB, [KNOWLEDGE_TYPE.LARK]: KNOWLEDGE_TYPE_KEY.LARK, - [KNOWLEDGE_TYPE.YUQUE]: KNOWLEDGE_TYPE_KEY.YUQUE, [KNOWLEDGE_TYPE.WORKFLOW]: KNOWLEDGE_TYPE_KEY.WORKFLOW, } as const satisfies Record @@ -24,6 +23,5 @@ export const KNOWLEDGE_TYPE_LABELS = { [KNOWLEDGE_TYPE_KEY.BASE]: '通用类型', [KNOWLEDGE_TYPE_KEY.WEB]: 'web站点类型', [KNOWLEDGE_TYPE_KEY.LARK]: '飞书类型', - [KNOWLEDGE_TYPE_KEY.YUQUE]: '语雀类型', [KNOWLEDGE_TYPE_KEY.WORKFLOW]: '工作流类型', } as const satisfies Record<(typeof KNOWLEDGE_TYPE_MAP)[KnowledgeType], string> diff --git a/ui/src/layout/ResourceDetailLayout.vue b/ui/src/layout/ResourceDetailLayout.vue index 2ee2933dcab..a802e460631 100644 --- a/ui/src/layout/ResourceDetailLayout.vue +++ b/ui/src/layout/ResourceDetailLayout.vue @@ -34,7 +34,7 @@ function navigateToDetailMenu(detailMenuItem: LayoutMenuItem) {