From 14f5aa8a8455999da18fc6b1245f9db307bf30cb Mon Sep 17 00:00:00 2001 From: wangdan-fit2cloud Date: Wed, 21 Oct 2026 14:40:12 +0800 Subject: [PATCH] feat: paragraph --- ui/src/styles/STYLE_README.md | 5 ++ ui/src/styles/app.scss | 17 +++++ ui/src/views/VIEW_README.md | 7 +- .../components/AssociateParagraphDialog.vue | 68 ++++++++++++------- .../components/ProblemDetailDrawer.vue | 40 +++++------ 5 files changed, 86 insertions(+), 51 deletions(-) diff --git a/ui/src/styles/STYLE_README.md b/ui/src/styles/STYLE_README.md index f5aa6abdf77..71749261111 100644 --- a/ui/src/styles/STYLE_README.md +++ b/ui/src/styles/STYLE_README.md @@ -74,6 +74,11 @@ Admin 和 Chat 共用。组件不单独恢复焦点 `outline`,包括通过隐 单元格最小宽度为 120px、最大宽度为 400px,正文正常换行并允许长链接折行;表头不换行, 避免多列时标题和正文被压缩成逐字纵向排列。滚动条沿用 Markdown 主题已有样式。 +## 数量徽标 + +`app.scss` 的 `.mk-badge` 用于行内数量展示,统一提供 16px 高度与最小宽度、水平居中、圆角、 +主题色背景和白色小字号文字;是否显示及数量内容由使用方控制。 + ## 右上角移除按钮 `app.scss` 的 button 分组提供 `.mk-remove-button`,用于图片、附件等内容的右上角移除入口。 diff --git a/ui/src/styles/app.scss b/ui/src/styles/app.scss index 5304e7f57ed..b3f22010e41 100644 --- a/ui/src/styles/app.scss +++ b/ui/src/styles/app.scss @@ -72,6 +72,23 @@ h6 { line-height: var(--mk-line-height-base); } +/* badge */ +.mk-badge { + align-items: center; + background-color: var(--mk-primary); + border-radius: 9999px; + color: white; + display: flex; + flex-shrink: 0; + font-size: 12px; + height: 16px; + justify-content: center; + line-height: 20px; + font-weight: 500; + min-width: 16px; + padding: 0 4px; +} + /* button */ // 资源菜单的导入按钮 .mk-import-button { diff --git a/ui/src/views/VIEW_README.md b/ui/src/views/VIEW_README.md index 06988ec4fa7..4b274c19d84 100644 --- a/ui/src/views/VIEW_README.md +++ b/ui/src/views/VIEW_README.md @@ -253,10 +253,11 @@ System 资源管理或 System 共享 Knowledge API 的 `postKnowledgeHitTest`; 详情支持问题名称快捷编辑、关联分段卡片和跨页上一个/下一个;卡片复用 `.mk-paragraph-card` 样式, 点击打开已有 `ParagraphDialog`,悬浮取消关联按钮仅解除当前关联,成功刷新卡片与列表计数。 分段详情关闭后重查关联,保持编辑与解除关联后的数据一致。关联卡片按接口来源字段显示图片、Web 或文件图标, -图片原图引用同时传给分段详情;加载失败支持重试。抽屉暴露 `refresh()`,由问题页在批量关联成功后刷新当前关联分段。 +图片原图引用同时传给分段详情;请求失败由请求层统一提示,保留当前关联内容。抽屉暴露 `refresh()`,由问题页在批量关联成功后刷新当前关联分段。 单项、批量、详情的“关联分段”入口统一使用 `AssociateParagraphDialog`,勾选仅维护本次选择, -跨文档、图片类型及搜索保留选择;确认后通过 `putBatchAssociateProblems` 一次提交问题与分段列表, -成功关闭并刷新问题列表,失败保留选择,取消不提交。弹窗不回填既有关联或执行解除关联,解除关联仍由问题详情维护。 +跨文档、图片类型及搜索保留选择;单个问题打开时查询已有关系并回填勾选,所有选项均可切换,批量问题打开时只维护新增选择。 +确认后通过 `putBatchAssociateProblems` 一次提交问题与新增分段列表;没有新增选择时直接关闭。 +成功关闭并刷新问题列表,失败保留选择,取消不提交。解除关联仍由问题详情维护。 Workspace 共享只展示和搜索问题列表,不显示维护入口,也不打开缺少查询接口的问题详情。 ## 文档分段 diff --git a/ui/src/views/knowledge-detail/problem/components/AssociateParagraphDialog.vue b/ui/src/views/knowledge-detail/problem/components/AssociateParagraphDialog.vue index 37e9b1dc5ec..bb8d658881c 100644 --- a/ui/src/views/knowledge-detail/problem/components/AssociateParagraphDialog.vue +++ b/ui/src/views/knowledge-detail/problem/components/AssociateParagraphDialog.vue @@ -26,6 +26,7 @@ const selectedDocumentId = ref('') const paragraphSearchText = ref('') const paragraphs = ref([]) const selectedParagraphs = ref([]) +const associatedParagraphIds = ref(new Set()) const infiniteScrollRef = useTemplateRef<{ reset: () => Promise }>('infiniteScrollRef') const paragraphLayoutRef = useTemplateRef>('paragraphLayoutRef') @@ -88,7 +89,7 @@ function handleParagraphSearch() { return refreshParagraphs() } -// 勾选仅维护本次批量关联的选择,确认后统一提交。 +// 已关联项回显后也可切换勾选,确认时只提交新增关联。 function handleToggleParagraph(paragraph: ParagraphItem) { if (operationLoading.value) return if (isSelected(paragraph.id)) { @@ -99,12 +100,17 @@ function handleToggleParagraph(paragraph: ParagraphItem) { } function handleSubmit() { - if (operationLoading.value || documentLoading.value || !problemIds.value.length || !selectedParagraphs.value.length) return + if (operationLoading.value || documentLoading.value || !problemIds.value.length) return + const paragraphReferences = selectedParagraphs.value.filter(({ paragraph_id }) => !associatedParagraphIds.value.has(paragraph_id)) + if (!paragraphReferences.length) { + visible.value = false + return + } operationLoading.value = true return resourceApi.problem .putBatchAssociateProblems(knowledgeId.value, { problem_id_list: [...problemIds.value], - paragraph_list: [...selectedParagraphs.value], + paragraph_list: paragraphReferences, }) .then(() => { MsgSuccess('关联成功') @@ -124,7 +130,14 @@ function open(targetProblemIds: string[]) { problemIds.value = [...targetProblemIds] visible.value = true documentLoading.value = true - return loadDocuments() + return Promise.all([ + loadDocuments(), + targetProblemIds.length === 1 ? resourceApi.problem.getProblemDetail(knowledgeId.value, targetProblemIds[0]) : [], + ]) + .then(([, problemParagraphs]) => { + associatedParagraphIds.value = new Set(problemParagraphs.map(({ id }) => id)) + selectedParagraphs.value = problemParagraphs.map(({ id, document_id }) => ({ paragraph_id: id, document_id })) + }) .catch(() => { // 请求层统一提示错误,失败时保持空列表。 }) @@ -141,6 +154,7 @@ function handleClosed() { paragraphSearchText.value = '' paragraphs.value = [] selectedParagraphs.value = [] + associatedParagraphIds.value.clear() resourceType.value = DOCUMENT_RESOURCE_TYPE.DOCUMENT } @@ -157,7 +171,6 @@ defineExpose({ open })
@@ -225,11 +240,12 @@ defineExpose({ open }) +

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

{{ paragraph.content.length }} 字符

diff --git a/ui/src/views/knowledge-detail/problem/components/ProblemDetailDrawer.vue b/ui/src/views/knowledge-detail/problem/components/ProblemDetailDrawer.vue index 00a0d70e81c..9737ed9aa96 100644 --- a/ui/src/views/knowledge-detail/problem/components/ProblemDetailDrawer.vue +++ b/ui/src/views/knowledge-detail/problem/components/ProblemDetailDrawer.vue @@ -18,14 +18,12 @@ const emit = defineEmits<{ previous: []; next: []; refresh: []; closed: []; asso const { knowledgeId, resourceApi } = useKnowledgeDetailContext({ resourceScopes: ['workspace', 'system-resource', 'system-shared'] as const }) const visible = ref(true) const loading = ref(false) -const loadFailed = ref(false) const paragraphs = ref([]) const selectedParagraph = ref() const paragraphDialogRef = ref>() -// 关联分段查询:同步数量,失败时保留当前内容并支持重试。 +// 关联分段查询:同步数量,失败时保留当前内容。 function loadParagraphs() { - loadFailed.value = false return resourceApi.problem .getProblemDetail(knowledgeId.value, problem.value.id) .then((records) => { @@ -33,7 +31,7 @@ function loadParagraphs() { problem.value.paragraph_count = records.length }) .catch(() => { - loadFailed.value = true + // 请求层统一提示错误,失败时保留当前关联。 }) } @@ -121,18 +119,20 @@ defineExpose({ refresh: handleLoadParagraphs })
- - - - 重新加载 - +