diff --git a/ui/src/api/API_README.md b/ui/src/api/API_README.md index 3dd107ddfe0..778455644bd 100644 --- a/ui/src/api/API_README.md +++ b/ui/src/api/API_README.md @@ -565,7 +565,10 @@ Workspace、System 资源管理及 System 共享资源的 `knowledge.ts` 提供 `//document/` 查询文档详情,支持分段页刷新和直接访问。 `workspace/knowledge/paragraph.ts` 维护普通 Workspace 文档分段接口,前缀为 `/workspace//knowledge//document//paragraph`。 -`getParagraphList` 查询完整分段列表,支持 `title`、`content`,服务端按 `position`、创建时间排序; +`getParagraphList` 查询完整分段列表,供位置调整定位相邻分段; +`getParagraphPage(knowledgeId, documentId, page, query?)` 查询 `//`, +返回 `ResponsePage`,供分段详情滚动加载。两者均支持 `title`、`content`, +服务端按 `position`、创建时间排序; `postParagraph` 提交 `title`、`content`、`is_active` 和可选的 `problem_list: [{ content }]`, 返回新增分段详情,默认插入首位并触发索引。 新增载荷可传 `position`,在目标位置插入,用于当前分段上方添加。 diff --git a/ui/src/api/admin/workspace/knowledge/paragraph.ts b/ui/src/api/admin/workspace/knowledge/paragraph.ts index 7c46442cc63..d97ef164eba 100644 --- a/ui/src/api/admin/workspace/knowledge/paragraph.ts +++ b/ui/src/api/admin/workspace/knowledge/paragraph.ts @@ -1,4 +1,5 @@ import { del, get, post, put } from '../../core/request' +import type { ParamsPage, ResponsePage } from '../../core/types' import type { ParagraphCreatePayload, ParagraphDetail, @@ -17,6 +18,10 @@ const getPrefix = (knowledgeId: string, documentId: string) => const getParagraphList = (knowledgeId: string, documentId: string, query?: ParagraphQuery) => get(getPrefix(knowledgeId, documentId), { ...query }) +/** 分页查询文档分段,供详情页滚动加载。 */ +const getParagraphPage = (knowledgeId: string, documentId: string, page: ParamsPage, query?: ParagraphQuery) => + get>(`${getPrefix(knowledgeId, documentId)}/${page.currentPage}/${page.pageSize}`, { ...query }) + /** 在指定位置新增分段,默认首位,由服务端触发索引。 */ const postParagraph = (knowledgeId: string, documentId: string, payload: ParagraphCreatePayload) => post(getPrefix(knowledgeId, documentId), payload) @@ -74,6 +79,7 @@ const putParagraphPosition = (knowledgeId: string, documentId: string, paragraph export default { getParagraphList, + getParagraphPage, postParagraph, getParagraphDetail, putParagraph, diff --git a/ui/src/assets/iconfont.js b/ui/src/assets/iconfont.js index 6cd4d281dbd..b801a0765ad 100644 --- a/ui/src/assets/iconfont.js +++ b/ui/src/assets/iconfont.js @@ -1 +1 @@ -window._iconfont_svg_string_5200172='',(h=>{var a=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var o,v,t,i,c,d=function(a,l){l.parentNode.insertBefore(a,l)};if(a&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}o=function(){var a,l=document.createElement("div");l.innerHTML=h._iconfont_svg_string_5200172,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?d(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),o()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(t=o,i=h.document,c=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,e())})}function e(){c||(c=!0,t())}function m(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(m,50)}e()}})(window); \ No newline at end of file +window._iconfont_svg_string_5200172='',(h=>{var a=(l=(l=document.getElementsByTagName("script"))[l.length-1]).getAttribute("data-injectcss"),l=l.getAttribute("data-disable-injectsvg");if(!l){var o,v,t,i,c,d=function(a,l){l.parentNode.insertBefore(a,l)};if(a&&!h.__iconfont__svg__cssinject__){h.__iconfont__svg__cssinject__=!0;try{document.write("")}catch(a){console&&console.log(a)}}o=function(){var a,l=document.createElement("div");l.innerHTML=h._iconfont_svg_string_5200172,(l=l.getElementsByTagName("svg")[0])&&(l.setAttribute("aria-hidden","true"),l.style.position="absolute",l.style.width=0,l.style.height=0,l.style.overflow="hidden",l=l,(a=document.body).firstChild?d(l,a.firstChild):a.appendChild(l))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(v=function(){document.removeEventListener("DOMContentLoaded",v,!1),o()},document.addEventListener("DOMContentLoaded",v,!1)):document.attachEvent&&(t=o,i=h.document,c=!1,m(),i.onreadystatechange=function(){"complete"==i.readyState&&(i.onreadystatechange=null,e())})}function e(){c||(c=!0,t())}function m(){try{i.documentElement.doScroll("left")}catch(a){return void setTimeout(m,50)}e()}})(window); \ No newline at end of file diff --git a/ui/src/components/COMPONENT_README.md b/ui/src/components/COMPONENT_README.md index 29a485bb6a8..de26143e3e6 100644 --- a/ui/src/components/COMPONENT_README.md +++ b/ui/src/components/COMPONENT_README.md @@ -266,8 +266,10 @@ Symbol ID 变化时同步引用。 Element Plus 的 `v-infinite-scroll`。组件通过 `v-model` 管理已经加载的列表数据,通过 `load` 传入按页请求方法,并在内部管理页码、首次加载、数据追加、加载状态、结束状态和过期请求。请求 方法接收 `{ currentPage, pageSize }`,返回包含 `records`、`current`、`size` 和 `total` 的分页结果。 -默认每页加载 30 条,可通过 `pageSize` 修改。组件挂载后自动加载第一页,之后只在底部哨兵随 -用户滚动进入可视区域时加载下一页。查询条件变化时通过组件暴露的 `reset()` 重新加载。 +每页数量通过 `pageSize` 配置。组件挂载后自动加载第一页;每页成功加载非空数据后,等待 DOM +更新并重新观察底部哨兵,仍在可视预加载区域内时串行加载下一页,直到哨兵离开或数据加载完毕。 +请求失败、空页或响应页码不匹配时不自动续载,避免循环请求;卸载时使未完成请求失效。 +查询条件变化时通过组件暴露的 `reset()` 重新加载。 首次加载或 `reset()` 请求第一页时,组件只显示加载状态;第一页完成且列表为空后才渲染 `empty` 插槽,避免请求过程中短暂显示空状态。已有数据时继续渲染默认插槽,并在触底请求期间保留列表。 diff --git a/ui/src/components/global/mk-infinite-scroll/index.vue b/ui/src/components/global/mk-infinite-scroll/index.vue index fce14df5df5..0c5a05df3f6 100644 --- a/ui/src/components/global/mk-infinite-scroll/index.vue +++ b/ui/src/components/global/mk-infinite-scroll/index.vue @@ -1,5 +1,5 @@ + + diff --git a/ui/src/views/document-detail/components/ButtonGenerateQuestions.vue b/ui/src/views/document-detail/action-dropdown/generate-questions/BatchGenerateQuestionsAction.vue similarity index 85% rename from ui/src/views/document-detail/components/ButtonGenerateQuestions.vue rename to ui/src/views/document-detail/action-dropdown/generate-questions/BatchGenerateQuestionsAction.vue index d8db767b3d3..07742242d4f 100644 --- a/ui/src/views/document-detail/components/ButtonGenerateQuestions.vue +++ b/ui/src/views/document-detail/action-dropdown/generate-questions/BatchGenerateQuestionsAction.vue @@ -5,7 +5,7 @@ import type { RelatedQuestionsConfig } from '@/api/types' import GenerateQuestionsDialog from '@/components/business/generate-questions/GenerateQuestionsDialog.vue' import { MsgSuccess } from '@/utils/message' -defineOptions({ name: 'ButtonGenerateQuestions' }) +defineOptions({ name: 'BatchGenerateQuestionsAction' }) const props = defineProps<{ api: typeof ParagraphApi knowledgeId: string @@ -14,7 +14,6 @@ const props = defineProps<{ disabled?: boolean }>() const emit = defineEmits<{ refresh: [] }>() -defineSlots<{ default?(props: { open: () => unknown; disabled: boolean }): unknown }>() // 打开时固定本次生成问题的分段与所属文档。 const loading = ref(false) @@ -70,10 +69,8 @@ function handleClosed() {