diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index 653cb606c09..5cf1da13489 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -572,8 +572,14 @@ Workspace、System 资源管理及 System 共享资源的 `knowledge.ts` 提供 `hit_num` 和 `last_hit_time`。`putParagraph` 向同一路径提交标题和正文,解包后端三元素编辑响应的 首项为最新详情;不提交 `problem_list`,关联关系通过专属接口维护。 `postParagraphProblem` 向 `//problem` 提交 `{ content }`,创建或复用问题并关联分段; +`getParagraphProblems` GET 同一路径,返回当前分段的 `ParagraphProblem[]`,用于关联变更后刷新。 +`putAssociateParagraphProblem` PUT 到 `/association`,通过查询参数传递 `paragraph_id`、`problem_id`, +不传请求体,用于关联已存在的问题。 `putUnassociateParagraphProblem` PUT 到 `/unassociation`,以查询参数传递 `paragraph_id`、 `problem_id`,不传请求体,仅解除当前分段的关联。 +`workspace/knowledge/problem.ts` 的 `getProblemsPage(knowledgeId, page, query?)` 查询知识库问题分页, +路径为 `/workspace//knowledge//problem//`, +支持 `content` 搜索,返回 `ResponsePage`。分段添加问题沿用 v2 查询第一页 100 条候选。 `putMigrateParagraphs` 向分段前缀下的 `/migrate/knowledge//document/` 提交 PUT `{ id_list }`,将所选分段迁移到目标文档;目标不能为当前文档。 `putGenerateParagraphQuestions` 向分段前缀下的 `/batch_generate_related` 提交 PUT diff --git a/ui/src/api/admin/workspace/knowledge/paragraph.ts b/ui/src/api/admin/workspace/knowledge/paragraph.ts index 513fe4c39f7..bd194b5b606 100644 --- a/ui/src/api/admin/workspace/knowledge/paragraph.ts +++ b/ui/src/api/admin/workspace/knowledge/paragraph.ts @@ -36,6 +36,14 @@ const putParagraph = (knowledgeId: string, documentId: string, paragraphId: stri const postParagraphProblem = (knowledgeId: string, documentId: string, paragraphId: string, content: string) => post<{ content: string }, ParagraphProblem>(`${getPrefix(knowledgeId, documentId)}/${paragraphId}/problem`, { content }) +/** 获取当前分段关联的全部问题。 */ +const getParagraphProblems = (knowledgeId: string, documentId: string, paragraphId: string) => + get(`${getPrefix(knowledgeId, documentId)}/${paragraphId}/problem`) + +/** 将知识库中的已有问题关联到当前分段。 */ +const putAssociateParagraphProblem = (knowledgeId: string, documentId: string, paragraphId: string, problemId: string) => + put(`${getPrefix(knowledgeId, documentId)}/association`, undefined, { paragraph_id: paragraphId, problem_id: problemId }) + /** 解除当前分段的问题关联,保留问题及其他分段的关联。 */ const putUnassociateParagraphProblem = (knowledgeId: string, documentId: string, paragraphId: string, problemId: string) => put(`${getPrefix(knowledgeId, documentId)}/unassociation`, undefined, { paragraph_id: paragraphId, problem_id: problemId }) @@ -46,7 +54,7 @@ const putMigrateParagraphs = (knowledgeId: string, documentId: string, targetKno id_list: paragraphIds, }) -/** 批量/单个为所选分段提交关联问题生成任务。 */ +/** 批量/单个为生成问题。 */ const putGenerateParagraphQuestions = (knowledgeId: string, documentId: string, payload: ParagraphGeneratePayload) => put(`${getPrefix(knowledgeId, documentId)}/batch_generate_related`, payload) @@ -60,6 +68,8 @@ export default { getParagraphDetail, putParagraph, postParagraphProblem, + getParagraphProblems, + putAssociateParagraphProblem, putUnassociateParagraphProblem, putMigrateParagraphs, putGenerateParagraphQuestions, diff --git a/ui/src/api/admin/workspace/knowledge/problem.ts b/ui/src/api/admin/workspace/knowledge/problem.ts new file mode 100644 index 00000000000..5b6cfbcdacb --- /dev/null +++ b/ui/src/api/admin/workspace/knowledge/problem.ts @@ -0,0 +1,10 @@ +import { get } from '../../core/request' +import type { ParamsPage, ResponsePage } from '../../core/types' +import type { ParagraphProblem } from '@/api/types' +import { getWorkspaceId } from '@/utils/resource-context' + +/** 分页搜索知识库问题,供分段选择已有问题进行关联。 */ +const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) => + get>(`/workspace/${getWorkspaceId()}/knowledge/${knowledgeId}/problem/${page.currentPage}/${page.pageSize}`, query) + +export default { getProblemsPage } diff --git a/ui/src/styles/element-plus.scss b/ui/src/styles/element-plus.scss index f791b1cf6cb..11321778501 100644 --- a/ui/src/styles/element-plus.scss +++ b/ui/src/styles/element-plus.scss @@ -1262,7 +1262,7 @@ .el-textarea { --el-input-placeholder-color: var(--mk-N500); --el-input-text-color: var(--el-text-color-primary); - &:has(.el-input__inner[readonly]) { + &:has(.el-textarea__inner[readonly]) { --el-input-focus-border-color: var(--el-input-border-color); --el-input-hover-border-color: var(--el-input-border-color); } diff --git a/ui/src/views/VIEW_README.md b/ui/src/views/VIEW_README.md index 300f5cb9da0..ace2e3e3ae7 100644 --- a/ui/src/views/VIEW_README.md +++ b/ui/src/views/VIEW_README.md @@ -195,7 +195,7 @@ src/views/knowledge/ │ ├── GenerateQuestionsAction.vue # 知识库生成关联问题 │ ├── sync-knowledge-action/ # Web 知识库同步入口与方式选择弹窗 │ ├── KeywordIndexKnowledgeAction.vue # 分词索引入口 -│ ├── mcp-config-action/ # MCP 配置入口与专属只读弹窗 +│ ├── McpConfigKnowledgeAction.vue # MCP 配置查询、展示与复制 │ ├── ExportKnowledgeAction.vue # Excel、文档 ZIP 与知识库包导出 │ ├── MoveKnowledgeAction.vue │ └── DeleteKnowledgeAction.vue @@ -235,7 +235,8 @@ src/views/knowledge/ `workspaceId`。点击阻止事件冒泡,避免同时进入卡片详情;Workspace 共享知识库仍不展示操作菜单。 “设置”之后依次组合 `KeywordIndexKnowledgeAction` 和 `McpConfigKnowledgeAction`,接收完整 Knowledge API 并复用页面操作 loading。分词索引不增加确认弹窗,调用成功后只提示“操作成功”; -MCP 配置沿用工具的只读文本与悬浮复制交互,专属弹窗按需挂载、关闭后卸载。 +`McpConfigKnowledgeAction` 内直接查询 MCP 配置字符串,通过 `MkDialog` 只读展示并提供悬浮复制, +配置为空时禁用复制,关闭动画结束后清理配置;弹窗外壳由 `MkDialog` 按需挂载和卸载。 两者接口暂为本地占位,MCP 返回空文本,分词索引模拟成功,尚不执行服务端操作。 `ExportKnowledgeAction` 接收完整 Knowledge API,暂不增加权限判断;悬停“导出”展开右侧 子菜单,分别导出文档 Excel、文档 ZIP 和可再次导入的知识库包。 @@ -318,9 +319,7 @@ src/views/tool/ │ ├── InitParamAction.vue │ ├── MoveToolAction.vue │ ├── ToolWorkflowAction.vue -│ └── mcp-config-action/ -│ ├── McpConfigAction.vue -│ └── McpConfigDialog.vue # MCP 工具配置查看弹窗 +│ └── McpConfigAction.vue # MCP 配置查询、展示与复制 └── tool-store/ # 工具商店列表、详情与添加流程 ├── component/ │ └── ToolStoreCard.vue # 商店工具卡片及详情、添加入口 @@ -362,7 +361,9 @@ Workspace 的自定义工具和工作流工具菜单 工具列表页面通过 `ToolCard` 的 `actions` 和 `action-dropdown` 插槽组合 操作;编辑 Action 负责按 `TOOL_TYPE` 打开对应类型表单,启动参数 Action 获取工具详情后打开 -`InitParamDialog`,MCP 配置 Action 获取 MCP 工具详情后打开 `McpConfigDialog`。工作流 Action 进入 +`InitParamDialog`,`McpConfigAction` 获取 MCP 工具详情后,在 Action 内通过 `MkDialog` 只读展示 +`code ?? ''` 并提供悬浮复制,配置为空时禁用复制,关闭动画结束后清理配置。 +弹窗外壳由 `MkDialog` 按需挂载和卸载。工作流 Action 进入 独立的工具工作流画布;创建工作流工具后直接进入该画布,其他类型表单创建成功后通过 `refresh` 事件刷新列表。编辑成功后通过 `update` 事件返回接口响应的完整工具数据,由页面局部更新对应卡片;不要 把不同工具类型的字段重新合并到一个通用表单中。所有调用 `postTool` 创建工具的流程在接口成功后 @@ -894,18 +895,26 @@ Action 仍通过上层 `action-dropdown/index.ts` 导出,弹窗仅在目录内 内容必填且最多 100000 字符。正文使用 `MdEditor`,默认关闭预览,提供 Markdown 工具栏及字数统计; 通过必填的 `knowledgeId` 将图片上传到当前知识库,复用 `FileApi` 和 `FILE_SOURCE_TYPE.KNOWLEDGE`。 提交由 Dialog 编排。 -- `ParagraphPreview` 只展示标题、Markdown 正文、召回次数和最近召回时间,通过 `edit` 事件通知 Dialog。 -- `ParagraphQuestions` 管理问题草稿、校验、添加和移除,问题必填、去除首尾空格、最多 256 字符且不重复。 - 无 `paragraphId` 时维护本地草稿并随新增分段提交;已有分段通过专属接口即时添加或解除关联, - 不删除问题本身。通过 `v-model:loading`、`v-model:adding` 与 Dialog 协调操作状态。 - -两个 Dialog 和三个组件均提供默认 `false` 的 `disabled`,表示业务只读:表单不可修改、问题操作隐藏, -详情不展示编辑入口,Dialog 方法同时阻止保存;编辑 Dialog 在只读时强制详情模式,动态切为只读也返回详情。 -提交状态继续使用内容/按钮 loading,不作为表单 `disabled`;问题组件的 `blocked` 仅协调加载与保存期间的操作入口。 +- `ParagraphPreview` 只展示标题、Markdown 正文、召回次数和最近召回时间;Dialog 管理使用 `mk-paragraph-card` 样式的详情卡片容器及绝对定位的编辑按钮。 +- `ParagraphQuestions` 沿用 v2 的添加及移除流程:点击加号显示 `el-select`,使用远程搜索、 + `allow-create` 和首项默认选择,选择已有问题或输入新问题后立即添加;失焦取消输入。 + 问题去除首尾空格、非空、最多 256 字符且不重复,已关联候选不可重复选择。 + 候选通过 `ProblemApi.getProblemsPage` 查询第一页 100 条;已有分段分别调用创建或关联接口, + 添加或解除关联成功后重新查询当前分段问题。列表使用卡片,左侧显示问题文字,右侧提供移除按钮;移除只解除关联, + 不删除问题本身。无 `paragraphId` 时维护 `{ content }` 本地草稿并随新增分段提交。 + 问题输入状态和请求 loading 均在组件内部维护,不向 Dialog 传递 `adding` 或 `loading`, + 不阻止分段创建、正文保存、取消或关闭。新增分段只提交已加入列表的问题草稿; + 问题请求失败保留输入与现有问题。 + +两个 Dialog、`ParagraphForm` 和 `ParagraphQuestions` 均不提供或传递 `disabled` Prop; +详情模式通过悬浮编辑入口进入表单,问题区始终提供添加与移除入口。 +每个 Dialog 统一使用一个 `loading` 管理自身加载和提交、内容遮罩、按钮状态及重复操作保护; +问题请求使用子组件内部独立的 `loading`,只覆盖右侧问题区。提交状态不作为表单 `disabled`。 两个 Dialog 均接收完整 Paragraph API 和知识库/文档 ID,打开时固定目标,点击时挂载、关闭动画结束后卸载。 -详情加载失败提供重试;请求失败保留草稿。正文保存不提交 `problem_list`,取消编辑不撤销即时保存的问题关联。 +详情加载失败提供重试;请求失败保留草稿。正文保存不提交 `problem_list`,也不使用保存响应回填问题列表, +避免覆盖并行的问题操作;取消编辑不撤销即时保存的问题关联。 新增成功关闭弹窗并通知页面清空搜索、重新查询服务端排序;编辑成功通知页面刷新当前筛选结果。 -分段卡片在非批量模式提供“详情”入口,批量生成问题继续使用 `components/ButtonGenerateQuestions.vue`。 +分段卡片在非批量模式点击打开详情弹窗,批量模式点击切换勾选;批量生成问题继续使用 `components/ButtonGenerateQuestions.vue`。 Workspace 共享及 System 分段入口暂未接入,不回退 Workspace API。 System 资源管理与共享资源已注册共用知识库详情路由, @@ -1199,6 +1208,10 @@ Layout 保留标题栏固定位置和外层间距,页面负责内部布局。 策略页的“生成预览”按当前策略解析文件,进入 `upload-document/DocumentImportPreview.vue`, 使用 `MkViewLayout` 的侧栏切换文档、正文展示分段,复用其主区滚动和固定标题; 支持本地编辑和删除,编辑须保存或取消后再导入。 +分段编辑使用同目录的 `EditParagraphDialog.vue`,复用添加分段的 `ParagraphForm` 与 +`ParagraphQuestions`,提供 Markdown 编辑、知识库图片上传和关联问题草稿;不传入已有分段 ID, +保存仅更新当前导入草稿,不创建分段或立即写入问题关联,取消不修改预览。 +编辑弹窗按需挂载,关闭动画结束后卸载;关联问题随最终导入的 `problem_list` 提交。 预览仍属于第二步,返回策略页保留设置,重新生成时以新的解析结果替换预览。 预览导入直接批量创建编辑后的分段,保留来源文件及处理策略,不再次解析。 策略页“开始导入”将已上传的 `file_id_list` 与当前 `doc_strategy` 一次提交到 `document/import`, diff --git a/ui/src/views/knowledge-detail/library/document/upload-document/DocumentImportPreview.vue b/ui/src/views/knowledge-detail/library/document/upload-document/DocumentImportPreview.vue index a87e2fe94c9..11cd0a9bca0 100644 --- a/ui/src/views/knowledge-detail/library/document/upload-document/DocumentImportPreview.vue +++ b/ui/src/views/knowledge-detail/library/document/upload-document/DocumentImportPreview.vue @@ -1,39 +1,45 @@ @@ -84,33 +89,17 @@ function handleDeleteParagraph(index: number) {
@@ -119,4 +108,11 @@ function handleDeleteParagraph(index: number) { + diff --git a/ui/src/views/knowledge-detail/library/document/upload-document/EditParagraphDialog.vue b/ui/src/views/knowledge-detail/library/document/upload-document/EditParagraphDialog.vue new file mode 100644 index 00000000000..d30661d82ab --- /dev/null +++ b/ui/src/views/knowledge-detail/library/document/upload-document/EditParagraphDialog.vue @@ -0,0 +1,83 @@ + + + diff --git a/ui/src/views/knowledge-detail/library/document/upload-document/UploadDocumentDrawer.vue b/ui/src/views/knowledge-detail/library/document/upload-document/UploadDocumentDrawer.vue index 2695c472c4d..24330f25e53 100644 --- a/ui/src/views/knowledge-detail/library/document/upload-document/UploadDocumentDrawer.vue +++ b/ui/src/views/knowledge-detail/library/document/upload-document/UploadDocumentDrawer.vue @@ -163,7 +163,7 @@ defineExpose({ open })
- +

上传文档

diff --git a/ui/src/views/knowledge-detail/paragraph/index.vue b/ui/src/views/knowledge-detail/paragraph/index.vue index b86aa6a1a10..cc6c405bd47 100644 --- a/ui/src/views/knowledge-detail/paragraph/index.vue +++ b/ui/src/views/knowledge-detail/paragraph/index.vue @@ -76,9 +76,12 @@ function handleSelectBatchParagraph(paragraphId: string, selected: boolean) { selectedParagraphIds.value = selected ? [...selectedParagraphIds.value, paragraphId] : selectedParagraphIds.value.filter((id) => id !== paragraphId) } -function handleClickParagraphCard(paragraphId: string) { - if (!batchMode.value) return - handleSelectBatchParagraph(paragraphId, !selectedParagraphIds.value.includes(paragraphId)) +function handleClickParagraphCard(paragraph: ParagraphItem) { + if (batchMode.value) { + handleSelectBatchParagraph(paragraph.id, !selectedParagraphIds.value.includes(paragraph.id)) + return + } + return handleOpenParagraphDetail(paragraph) } // 迁移、删除成功后仅移除本次操作的分段,不重新查询接口。 @@ -222,11 +225,10 @@ onMounted(() => { shadow="never" class="mk-paragraph-card group relative cursor-pointer" :class="{ selectable: batchMode, active: batchMode && selectedParagraphIds.includes(paragraph.id) }" - @click="handleClickParagraphCard(paragraph.id)" + @click="handleClickParagraphCard(paragraph)" >

{{ paragraph.title || '-' }}

- - + -import { computed, ref } from 'vue' +import { ref } from 'vue' import type ParagraphApi from '@/api/admin/workspace/knowledge/paragraph' import type { ParagraphUpdatePayload } from '@/api/types' import type { ParagraphQuestionDraft } from './types' @@ -8,15 +8,11 @@ import ParagraphQuestions from './components/ParagraphQuestions.vue' import { MsgSuccess } from '@/utils/message' defineOptions({ name: 'CreateParagraphDialog' }) -const props = withDefaults( - defineProps<{ - api: typeof ParagraphApi - knowledgeId: string - documentId: string - disabled?: boolean - }>(), - { disabled: false }, -) +const props = defineProps<{ + api: typeof ParagraphApi + knowledgeId: string + documentId: string +}>() const emit = defineEmits<{ success: [mode: 'create']; closed: [] }>() /* 添加弹窗按需挂载,草稿在关闭后随实例释放。 */ @@ -26,10 +22,7 @@ const targetDocumentId = ref('') const paragraphFormRef = ref>() const paragraphForm = ref({ title: '', content: '' }) const relatedQuestions = ref([]) -const questionAdding = ref(false) -const questionLoading = ref(false) -const saving = ref(false) -const busy = computed(() => saving.value || questionLoading.value) +const loading = ref(false) function open() { targetKnowledgeId.value = props.knowledgeId @@ -38,20 +31,20 @@ function open() { } function close() { - if (!busy.value) visible.value = false + if (!loading.value) visible.value = false } function handleBeforeClose(done: () => void) { - if (!busy.value) done() + if (!loading.value) done() } function handleSubmit() { - if (props.disabled || busy.value || questionAdding.value) return + if (loading.value) return void paragraphFormRef.value ?.validate() .then((valid) => { - if (!valid || props.disabled || busy.value || questionAdding.value) return - saving.value = true + if (!valid || loading.value) return + loading.value = true return props.api .postParagraph(targetKnowledgeId.value, targetDocumentId.value, { title: paragraphForm.value.title.trim(), @@ -65,7 +58,7 @@ function handleSubmit() { emit('success', 'create') }) .finally(() => { - saving.value = false + loading.value = false }) }) .catch(() => {}) @@ -75,25 +68,25 @@ defineExpose({ open, close }) diff --git a/ui/src/views/knowledge-detail/paragraph/paragraph-dialog/ParagraphDialog.vue b/ui/src/views/knowledge-detail/paragraph/paragraph-dialog/ParagraphDialog.vue index d8189b533c9..224e76385c5 100644 --- a/ui/src/views/knowledge-detail/paragraph/paragraph-dialog/ParagraphDialog.vue +++ b/ui/src/views/knowledge-detail/paragraph/paragraph-dialog/ParagraphDialog.vue @@ -1,5 +1,5 @@ diff --git a/ui/src/views/knowledge/knowledge-card/action-dropdown/index.ts b/ui/src/views/knowledge/knowledge-card/action-dropdown/index.ts index 0698ea5748b..dd530df1c07 100644 --- a/ui/src/views/knowledge/knowledge-card/action-dropdown/index.ts +++ b/ui/src/views/knowledge/knowledge-card/action-dropdown/index.ts @@ -3,7 +3,7 @@ export { default as GenerateQuestionsAction } from './GenerateQuestionsAction.vu export { default as SyncKnowledgeAction } from './sync-knowledge-action/SyncKnowledgeAction.vue' export { default as EmbeddingKnowledgeAction } from './EmbeddingKnowledgeAction.vue' export { default as KeywordIndexKnowledgeAction } from './KeywordIndexKnowledgeAction.vue' -export { default as McpConfigKnowledgeAction } from './mcp-config-action/McpConfigKnowledgeAction.vue' +export { default as McpConfigKnowledgeAction } from './McpConfigKnowledgeAction.vue' export { default as ExportKnowledgeAction } from './ExportKnowledgeAction.vue' export { default as MoveKnowledgeAction } from './MoveKnowledgeAction.vue' export { default as DeleteKnowledgeAction } from './DeleteKnowledgeAction.vue' diff --git a/ui/src/views/knowledge/knowledge-card/action-dropdown/mcp-config-action/KnowledgeMcpConfigDialog.vue b/ui/src/views/knowledge/knowledge-card/action-dropdown/mcp-config-action/KnowledgeMcpConfigDialog.vue deleted file mode 100644 index 8a49ffe0883..00000000000 --- a/ui/src/views/knowledge/knowledge-card/action-dropdown/mcp-config-action/KnowledgeMcpConfigDialog.vue +++ /dev/null @@ -1,46 +0,0 @@ - - - - - diff --git a/ui/src/views/tool/tool-card/action-dropdown/mcp-config-action/McpConfigAction.vue b/ui/src/views/tool/tool-card/action-dropdown/McpConfigAction.vue similarity index 58% rename from ui/src/views/tool/tool-card/action-dropdown/mcp-config-action/McpConfigAction.vue rename to ui/src/views/tool/tool-card/action-dropdown/McpConfigAction.vue index 2605f3fbae0..a413bc847ba 100644 --- a/ui/src/views/tool/tool-card/action-dropdown/mcp-config-action/McpConfigAction.vue +++ b/ui/src/views/tool/tool-card/action-dropdown/McpConfigAction.vue @@ -1,10 +1,10 @@ @@ -40,5 +41,13 @@ function handleDialogClosed() { {{ label }} - + +
+ + + + + +
+
diff --git a/ui/src/views/tool/tool-card/action-dropdown/index.ts b/ui/src/views/tool/tool-card/action-dropdown/index.ts index c7094b211f1..2e36e966580 100644 --- a/ui/src/views/tool/tool-card/action-dropdown/index.ts +++ b/ui/src/views/tool/tool-card/action-dropdown/index.ts @@ -3,7 +3,7 @@ export { default as DeleteToolAction } from './DeleteToolAction.vue' export { default as EditToolAction } from './EditToolAction.vue' export { default as ExportToolAction } from './ExportToolAction.vue' export { default as InitParamAction } from './InitParamAction.vue' -export { default as McpConfigAction } from './mcp-config-action/McpConfigAction.vue' +export { default as McpConfigAction } from './McpConfigAction.vue' export { default as MoveToolAction } from './MoveToolAction.vue' export { default as ToolWorkflowAction } from './ToolWorkflowAction.vue' export { default as AuthorizeToolAction } from './AuthorizeToolAction.vue' diff --git a/ui/src/views/tool/tool-card/action-dropdown/mcp-config-action/McpConfigDialog.vue b/ui/src/views/tool/tool-card/action-dropdown/mcp-config-action/McpConfigDialog.vue deleted file mode 100644 index 467aeb1df0e..00000000000 --- a/ui/src/views/tool/tool-card/action-dropdown/mcp-config-action/McpConfigDialog.vue +++ /dev/null @@ -1,39 +0,0 @@ - - - -