diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index e77b17b531c..5ce5862657d 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -510,9 +510,24 @@ putDocument 统一更新名称、启停及设置,不按字段重复封装; | putBatchDeleteParagraphs | PUT batch_delete,`{ id_list }`,服务端清理关联与索引 | 标题最多 256,后端内容上限 102400;界面限制见 View 文档。 -问题候选在 knowledge/problem.ts 的 getProblemsPage,支持 content 搜索,类型 ParagraphProblem。 +问题候选在 knowledge/problem.ts 的 getProblemsPage,支持 content 搜索,分页记录类型为 ProblemItem。 Workspace 共享 Paragraph API 仅 getParagraphPage、getParagraphDetail,不补问题查询或维护。 +### 知识库问题 + +四范围 `knowledge/problem.ts` 使用各自知识库前缀追加 `//problem`, +由知识库及文档详情上下文的 `resourceApi.problem` 选择,不固定 Workspace。 +`getProblemsPage(knowledgeId, page, query)` 返回 `ResponsePage`,query 仅传 content。 +`ProblemItem` 包含 id、content、paragraph_count、hit_num、last_hit_time、create_time、update_time; +分段已有问题仍使用轻量 `ParagraphProblem`,不要求包含列表统计字段。 + +Workspace、System 资源管理、System 共享提供完整问题 API: +`postProblems` 直接提交内容字符串数组,`putProblem` 提交 `{ content }`,单项 DELETE 使用问题 ID, +`putBatchDeleteProblems` 直接提交问题 ID 数组;单个问题内容最多 256 字符。 +`getProblemParagraphs` 返回 `ProblemParagraph[]`,包含分段字段及 knowledge_id、document_name。 +取消关联复用 Paragraph API 的 `putUnassociateParagraphProblem`,不调用问题或分段删除接口。 +Workspace 共享仅有 `getProblemsPage`,不补造详情或写接口,不回退其他范围。 + ### 标签与文档关联 四范围 tags.ts 按上述知识库前缀维护;Workspace 共享只 getKnowledgeTags,其余范围提供维护和关联。 diff --git a/ui/src/api/admin/system/resource-management/knowledge/problem.ts b/ui/src/api/admin/system/resource-management/knowledge/problem.ts index e7c6a82eef2..bf2e3f7c109 100644 --- a/ui/src/api/admin/system/resource-management/knowledge/problem.ts +++ b/ui/src/api/admin/system/resource-management/knowledge/problem.ts @@ -1,15 +1,15 @@ import { del, get, post, put } from '@/api/admin/core/request' import type { ParamsPage, ResponsePage } from '@/api/admin/core/types' -import type { ParagraphProblem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' +import type { ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' const getPrefix = (knowledgeId: string) => `/system/resource/knowledge/${knowledgeId}/problem` /** 批量创建知识库问题。 */ const postProblems = (knowledgeId: string, contents: string[]) => post(getPrefix(knowledgeId), contents) -/** 分页搜索知识库问题,供分段选择已有问题进行关联。 */ +/** 分页搜索知识库问题,供问题列表及分段关联候选使用。 */ const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) => - get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) + get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) /** 编辑单个知识库问题的内容。 */ const putProblem = (knowledgeId: string, problemId: string, content: string) => diff --git a/ui/src/api/admin/system/shared-resources/knowledge/problem.ts b/ui/src/api/admin/system/shared-resources/knowledge/problem.ts index 34224284901..296822d727d 100644 --- a/ui/src/api/admin/system/shared-resources/knowledge/problem.ts +++ b/ui/src/api/admin/system/shared-resources/knowledge/problem.ts @@ -1,15 +1,15 @@ import { del, get, post, put } from '@/api/admin/core/request' import type { ParamsPage, ResponsePage } from '@/api/admin/core/types' -import type { ParagraphProblem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' +import type { ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' const getPrefix = (knowledgeId: string) => `/system/shared/knowledge/${knowledgeId}/problem` /** 批量创建知识库问题。 */ const postProblems = (knowledgeId: string, contents: string[]) => post(getPrefix(knowledgeId), contents) -/** 分页搜索知识库问题,供分段选择已有问题进行关联。 */ +/** 分页搜索知识库问题,供问题列表及分段关联候选使用。 */ const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) => - get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) + get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) /** 编辑单个知识库问题的内容。 */ const putProblem = (knowledgeId: string, problemId: string, content: string) => diff --git a/ui/src/api/admin/workspace/knowledge/problem.ts b/ui/src/api/admin/workspace/knowledge/problem.ts index 36e3b8254e5..7dc6a08c812 100644 --- a/ui/src/api/admin/workspace/knowledge/problem.ts +++ b/ui/src/api/admin/workspace/knowledge/problem.ts @@ -1,6 +1,6 @@ import { del, get, post, put } from '../../core/request' import type { ParamsPage, ResponsePage } from '../../core/types' -import type { ParagraphProblem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' +import type { ProblemItem, ProblemBatchAssociationPayload, ProblemParagraph } from '@/api/types' import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = (knowledgeId: string) => `/workspace/${getWorkspaceId()}/knowledge/${knowledgeId}/problem` @@ -8,9 +8,9 @@ const getPrefix = (knowledgeId: string) => `/workspace/${getWorkspaceId()}/knowl /** 批量创建知识库问题。 */ const postProblems = (knowledgeId: string, contents: string[]) => post(getPrefix(knowledgeId), contents) -/** 分页搜索知识库问题,供分段选择已有问题进行关联。 */ +/** 分页搜索知识库问题,供问题列表及分段关联候选使用。 */ const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) => - get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) + get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) /** 编辑单个知识库问题的内容。 */ const putProblem = (knowledgeId: string, problemId: string, content: string) => diff --git a/ui/src/api/admin/workspace/shared/knowledge/problem.ts b/ui/src/api/admin/workspace/shared/knowledge/problem.ts index 568ac3c32f4..dfc96ee8690 100644 --- a/ui/src/api/admin/workspace/shared/knowledge/problem.ts +++ b/ui/src/api/admin/workspace/shared/knowledge/problem.ts @@ -1,12 +1,12 @@ import { get } from '../../../core/request' import type { ParamsPage, ResponsePage } from '../../../core/types' -import type { ParagraphProblem } from '@/api/types' +import type { ProblemItem } from '@/api/types' import { getWorkspaceId } from '@/utils/resource-context' const getPrefix = (knowledgeId: string) => `/system/shared/workspace/${getWorkspaceId()}/knowledge/${knowledgeId}/problem` /** 分页查询工作空间共享知识库的问题,只提供只读访问。 */ const getProblemsPage = (knowledgeId: string, page: ParamsPage, query?: { content?: string }) => - get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) + get>(`${getPrefix(knowledgeId)}/${page.currentPage}/${page.pageSize}`, query) export default { getProblemsPage } diff --git a/ui/src/api/types/paragraph.ts b/ui/src/api/types/paragraph.ts index 7c5eb805977..c84fd35ae1f 100644 --- a/ui/src/api/types/paragraph.ts +++ b/ui/src/api/types/paragraph.ts @@ -20,6 +20,15 @@ export interface ParagraphProblem { content: string } +/** 知识库问题列表,包含关联数量和召回统计。 */ +export interface ProblemItem extends ParagraphProblem { + paragraph_count: number + hit_num: number + last_hit_time: string | null + create_time: string + update_time: string +} + /** 从问题详情查询到的关联分段,额外携带所属知识库与文档名称。 */ export interface ProblemParagraph extends ParagraphItem { knowledge_id: string diff --git a/ui/src/views/VIEW_README.md b/ui/src/views/VIEW_README.md index 7524163eb4d..703a878fc60 100644 --- a/ui/src/views/VIEW_README.md +++ b/ui/src/views/VIEW_README.md @@ -106,7 +106,7 @@ `knowledge-detail/context.ts` 仅通过 `provideKnowledgeDetailContext` 和 `useKnowledgeDetailContext` 公开详情、更新能力、ID、来源导航及完整 API。 -`resourceApi.knowledge / scheduledSync / document / paragraph / image / tags / termbase / chatUser / externalRetrieval` 按当前范围响应式读取,无需 `.value`, +`resourceApi.knowledge / scheduledSync / document / paragraph / image / tags / termbase / problem / chatUser / externalRetrieval` 按当前范围响应式读取,无需 `.value`, 不要在 setup 时解构具体 API。`workflow` 当前仅普通 Workspace 提供,其他范围不回退。 共有查询方法必填,部分范围缺失的方法可选调用;未发请求时不提示成功、不关闭、不刷新或修改快照,loading 正常清理。 不增加查询/维护双入口、接口别名或专属 Context hook,也不以方法存在性新增操作显隐规则。 @@ -207,9 +207,21 @@ System 资源管理或 System 共享 Knowledge API 的 `postKnowledgeHitTest`; 文档 `DocumentTags`、标签 Drawer 和批量 Action 各自维护添加弹窗;添加候选仅取前 100 组并本地搜索, 不禁选已关联值,后台去重。创建候选成功重载但不自动选择。删除仅解除文档关联,不删除知识库标签。 +### 问题管理 + +`question/` 使用 `resourceApi.problem`,默认每页 20 条,不提供表头排序,搜索将 content 传给后端。 +`ButtonCreateProblem` 内聚创建按钮和多行输入弹窗;快捷创建使用 `MkQuickCreate`,每个问题最多 256 字符。 +单项与批量删除直接提交,删除后末页为空时回退到最后有效页;名称编辑复用 `MkEditName`。 +点击数据行打开 `ProblemDetailDrawer`,勾选、名称编辑和操作按钮不触发行详情。 +详情支持问题名称快捷编辑、关联分段卡片和跨页上一个/下一个;卡片复用 `.mk-paragraph-card` 样式, +点击打开已有 `ParagraphDialog`,悬浮取消关联按钮仅解除当前关联,成功刷新卡片与列表计数。 +分段详情关闭后重查关联,保持编辑与解除关联后的数据一致。 +单项、批量、详情的“关联分段”入口暂时禁用,待交互确认后接入,不创建关联弹窗或发送关联请求。 +Workspace 共享只展示和搜索问题列表,不显示维护入口,也不打开缺少查询接口的问题详情。 + ## 文档分段 -`document-detail/context.ts` 装配四范围完整 Document/Paragraph API 和返回导航, +`document-detail/context.ts` 装配四范围完整 Document/Paragraph/Problem API 和返回导航, 无注入时按当前路由创建上下文,供导入预览复用。页面提供上下文;Action 接收选定 Paragraph API, `paragraph-dialog/` 直接读取上下文,不透传 API。共有查询必填,维护方法按真实差异可选。 Workspace 共享只读,隐藏创建、编辑、排序、关联修改及批量操作;缺少详情接口保留默认标题,不回退。 diff --git a/ui/src/views/document-detail/context.ts b/ui/src/views/document-detail/context.ts index 356cdfd0757..6dc7812b68a 100644 --- a/ui/src/views/document-detail/context.ts +++ b/ui/src/views/document-detail/context.ts @@ -9,14 +9,18 @@ import ParagraphApi from '@/api/admin/workspace/knowledge/paragraph' import SharedParagraphApi from '@/api/admin/workspace/shared/knowledge/paragraph' import SystemResourceParagraphApi from '@/api/admin/system/resource-management/knowledge/paragraph' import SystemSharedParagraphApi from '@/api/admin/system/shared-resources/knowledge/paragraph' +import ProblemApi from '@/api/admin/workspace/knowledge/problem' +import SharedProblemApi from '@/api/admin/workspace/shared/knowledge/problem' +import SystemResourceProblemApi from '@/api/admin/system/resource-management/knowledge/problem' +import SystemSharedProblemApi from '@/api/admin/system/shared-resources/knowledge/problem' import type { ResourceScope } from '@/router/admin/types' const resourceConfigs = { - workspace: { document: DocumentApi, paragraph: ParagraphApi }, - 'workspace-shared': { document: SharedDocumentApi, paragraph: SharedParagraphApi }, - 'system-resource': { document: SystemResourceDocumentApi, paragraph: SystemResourceParagraphApi }, - 'system-shared': { document: SystemSharedDocumentApi, paragraph: SystemSharedParagraphApi }, -} satisfies Record + workspace: { document: DocumentApi, paragraph: ParagraphApi, problem: ProblemApi }, + 'workspace-shared': { document: SharedDocumentApi, paragraph: SharedParagraphApi, problem: SharedProblemApi }, + 'system-resource': { document: SystemResourceDocumentApi, paragraph: SystemResourceParagraphApi, problem: SystemResourceProblemApi }, + 'system-shared': { document: SystemSharedDocumentApi, paragraph: SystemSharedParagraphApi, problem: SystemSharedProblemApi }, +} satisfies Record /** 共有查询方法保持必填,仅部分范围提供的方法标记为可选。 */ type ApiKeys = Api extends unknown ? keyof Api : never @@ -41,6 +45,9 @@ function createDocumentDetailContext() { get paragraph(): DocumentParagraphApi { return resourceConfig.value.paragraph }, + get problem(): OptionalApiMethods<(typeof resourceConfigs)[ResourceScope]['problem']> { + return resourceConfig.value.problem + }, }) function handleBack() { diff --git a/ui/src/views/document-detail/paragraph-dialog/components/ParagraphQuestions.vue b/ui/src/views/document-detail/paragraph-dialog/components/ParagraphQuestions.vue index 46776b8ad95..831f998753e 100644 --- a/ui/src/views/document-detail/paragraph-dialog/components/ParagraphQuestions.vue +++ b/ui/src/views/document-detail/paragraph-dialog/components/ParagraphQuestions.vue @@ -2,7 +2,6 @@ import { nextTick, onMounted, ref } from 'vue' import type { SelectInstance } from 'element-plus' import { useDocumentDetailContext } from '@/views/document-detail/context' -import ProblemApi from '@/api/admin/workspace/knowledge/problem' import type { ParagraphProblem } from '@/api/types' import type { ParagraphQuestionDraft } from '../types' import { isWorkspaceSharedResource } from '@/utils/resource-context' @@ -25,7 +24,8 @@ const optionLoading = ref(false) function loadProblemOptions(query = '') { optionLoading.value = true - return ProblemApi.getProblemsPage(props.knowledgeId, { currentPage: 1, pageSize: 100 }, query.trim() ? { content: query.trim() } : undefined) + return resourceApi.problem + .getProblemsPage(props.knowledgeId, { currentPage: 1, pageSize: 100 }, query.trim() ? { content: query.trim() } : undefined) .then(({ records }) => { problemOptions.value = records }) diff --git a/ui/src/views/knowledge-detail/context.ts b/ui/src/views/knowledge-detail/context.ts index 36711cfddcc..95800233965 100644 --- a/ui/src/views/knowledge-detail/context.ts +++ b/ui/src/views/knowledge-detail/context.ts @@ -25,6 +25,10 @@ import TermbaseApi from '@/api/admin/workspace/knowledge/termbase' import SharedTermbaseApi from '@/api/admin/workspace/shared/knowledge/termbase' import SystemResourceTermbaseApi from '@/api/admin/system/resource-management/knowledge/termbase' import SystemSharedTermbaseApi from '@/api/admin/system/shared-resources/knowledge/termbase' +import ProblemApi from '@/api/admin/workspace/knowledge/problem' +import SharedProblemApi from '@/api/admin/workspace/shared/knowledge/problem' +import SystemResourceProblemApi from '@/api/admin/system/resource-management/knowledge/problem' +import SystemSharedProblemApi from '@/api/admin/system/shared-resources/knowledge/problem' import WorkflowApi from '@/api/admin/workspace/knowledge/workflow' import ScheduledSyncApi from '@/api/admin/workspace/knowledge/scheduled-sync' import SharedScheduledSyncApi from '@/api/admin/workspace/shared/knowledge/scheduled-sync' @@ -51,6 +55,7 @@ interface KnowledgeResourceApi { readonly paragraph: OptionalApiMethods< typeof ParagraphApi | typeof SharedParagraphApi | typeof SystemResourceParagraphApi | typeof SystemSharedParagraphApi > + readonly problem: OptionalApiMethods readonly chatUser: OptionalApiMethods< typeof ChatUserApi | typeof SharedChatUserApi | typeof SystemResourceChatUserApi | typeof SystemSharedChatUserApi > @@ -93,6 +98,7 @@ const resourceConfigs = { image: ImageApi, tags: TagsApi, termbase: TermbaseApi, + problem: ProblemApi, workflow: WorkflowApi, }, listRouteName: 'workspace-knowledge-list', @@ -107,6 +113,7 @@ const resourceConfigs = { paragraph: SharedParagraphApi, tags: SharedTagsApi, termbase: SharedTermbaseApi, + problem: SharedProblemApi, }, listRouteName: 'workspace-knowledge-list', }, @@ -121,6 +128,7 @@ const resourceConfigs = { image: SystemResourceImageApi, tags: SystemResourceTagsApi, termbase: SystemResourceTermbaseApi, + problem: SystemResourceProblemApi, }, listRouteName: 'system-resource-knowledge', }, @@ -134,6 +142,7 @@ const resourceConfigs = { paragraph: SystemSharedParagraphApi, tags: SystemSharedTagsApi, termbase: SystemSharedTermbaseApi, + problem: SystemSharedProblemApi, }, listRouteName: 'system-shared-knowledge', }, @@ -183,6 +192,9 @@ function createKnowledgeDetailContext(state: KnowledgeDetailState): KnowledgeDet get termbase() { return resourceConfig.value.resourceApi.termbase }, + get problem() { + return resourceConfig.value.resourceApi.problem + }, get knowledge() { return resourceConfig.value.resourceApi.knowledge }, @@ -237,7 +249,11 @@ type ScopedResourceApi = { } type ScopedKnowledgeDetailContext = Omit & { - resourceApi: Omit, 'scheduledSync' | 'tags' | 'termbase' | 'document' | 'chatUser' | 'externalRetrieval' | 'image'> & { + resourceApi: Omit< + ScopedResourceApi, + 'scheduledSync' | 'tags' | 'termbase' | 'problem' | 'document' | 'chatUser' | 'externalRetrieval' | 'image' + > & { + readonly problem: OptionalApiMethods<(typeof resourceConfigs)[Scope]['resourceApi']['problem']> readonly image: KnowledgeResourceApi['image'] readonly chatUser: OptionalApiMethods<(typeof resourceConfigs)[Scope]['resourceApi']['chatUser']> readonly scheduledSync: OptionalApiMethods<(typeof resourceConfigs)[Scope]['resourceApi']['scheduledSync']> diff --git a/ui/src/views/knowledge-detail/question/components/ButtonCreateProblem.vue b/ui/src/views/knowledge-detail/question/components/ButtonCreateProblem.vue new file mode 100644 index 00000000000..92e593eaa55 --- /dev/null +++ b/ui/src/views/knowledge-detail/question/components/ButtonCreateProblem.vue @@ -0,0 +1,65 @@ + + + diff --git a/ui/src/views/knowledge-detail/question/components/ProblemDetailDrawer.vue b/ui/src/views/knowledge-detail/question/components/ProblemDetailDrawer.vue new file mode 100644 index 00000000000..1900be408ee --- /dev/null +++ b/ui/src/views/knowledge-detail/question/components/ProblemDetailDrawer.vue @@ -0,0 +1,138 @@ + + + diff --git a/ui/src/views/knowledge-detail/question/components/ProblemParagraphCard.vue b/ui/src/views/knowledge-detail/question/components/ProblemParagraphCard.vue new file mode 100644 index 00000000000..d6ad97ef5f7 --- /dev/null +++ b/ui/src/views/knowledge-detail/question/components/ProblemParagraphCard.vue @@ -0,0 +1,28 @@ + + + diff --git a/ui/src/views/knowledge-detail/question/index.vue b/ui/src/views/knowledge-detail/question/index.vue index 5db50a395dc..579c8984016 100644 --- a/ui/src/views/knowledge-detail/question/index.vue +++ b/ui/src/views/knowledge-detail/question/index.vue @@ -1,10 +1,260 @@