mirror of
https://github.com/langgenius/dify.git
synced 2026-03-13 04:49:41 +08:00
feat: add pagination for chunk list
This commit is contained in:
parent
c541d4aaa8
commit
1c5464dbef
@ -0,0 +1,32 @@
|
||||
import React, { type FC } from 'react'
|
||||
import Drawer from '@/app/components/base/drawer'
|
||||
|
||||
type IFullScreenDrawerProps = {
|
||||
isOpen: boolean
|
||||
onClose?: () => void
|
||||
fullScreen: boolean
|
||||
children: React.ReactNode
|
||||
}
|
||||
|
||||
const FullScreenDrawer: FC<IFullScreenDrawerProps> = ({
|
||||
isOpen,
|
||||
onClose = () => {},
|
||||
fullScreen,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
<Drawer
|
||||
isOpen={isOpen}
|
||||
onClose={onClose}
|
||||
panelClassname={`!p-0 ${fullScreen
|
||||
? '!max-w-full !w-full'
|
||||
: 'mt-16 mr-2 mb-2 !max-w-[560px] !w-[560px] border-[0.5px] border-components-panel-border rounded-xl'}`}
|
||||
mask={false}
|
||||
unmount
|
||||
footer={null}
|
||||
>
|
||||
{children}
|
||||
</Drawer>)
|
||||
}
|
||||
|
||||
export default FullScreenDrawer
|
||||
@ -11,12 +11,12 @@ import SegmentList from './segment-list'
|
||||
import DisplayToggle from './display-toggle'
|
||||
import BatchAction from './batch-action'
|
||||
import SegmentDetail from './segment-detail'
|
||||
import { mockChildSegments } from './mock-data'
|
||||
import SegmentCard from './segment-card'
|
||||
import ChildSegmentList from './child-segment-list'
|
||||
import FullScreenDrawer from './full-screen-drawer'
|
||||
import Pagination from '@/app/components/base/pagination'
|
||||
import cn from '@/utils/classnames'
|
||||
import { formatNumber } from '@/utils/format'
|
||||
import Drawer from '@/app/components/base/drawer'
|
||||
import Divider from '@/app/components/base/divider'
|
||||
import Input from '@/app/components/base/input'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
@ -24,12 +24,14 @@ import type { Item } from '@/app/components/base/select'
|
||||
import { SimpleSelect } from '@/app/components/base/select'
|
||||
import { updateSegment } from '@/service/datasets'
|
||||
import type { ChildChunkDetail, SegmentDetailModel, SegmentUpdater } from '@/models/datasets'
|
||||
import NewSegmentModal from '@/app/components/datasets/documents/detail/new-segment-modal'
|
||||
import NewSegment from '@/app/components/datasets/documents/detail/new-segment'
|
||||
import { useEventEmitterContextContext } from '@/context/event-emitter'
|
||||
import Checkbox from '@/app/components/base/checkbox'
|
||||
import { useChildSegmentList, useDeleteSegment, useDisableSegment, useEnableSegment, useSegmentList } from '@/service/knowledge/use-segment'
|
||||
import { Chunk } from '@/app/components/base/icons/src/public/knowledge'
|
||||
|
||||
const DEFAULT_LIMIT = 10
|
||||
|
||||
type SegmentListContextValue = {
|
||||
isCollapsed: boolean
|
||||
toggleCollapsed: () => void
|
||||
@ -99,9 +101,8 @@ const Completed: FC<ICompletedProps> = ({
|
||||
const [selectedSegmentIds, setSelectedSegmentIds] = useState<string[]>([])
|
||||
const { eventEmitter } = useEventEmitterContextContext()
|
||||
const [isCollapsed, setIsCollapsed] = useState(true)
|
||||
// todo: pagination
|
||||
const [currentPage, setCurrentPage] = useState(1)
|
||||
const [limit, setLimit] = useState(10)
|
||||
const [currentPage, setCurrentPage] = useState(1) // start from 1
|
||||
const [limit, setLimit] = useState(DEFAULT_LIMIT)
|
||||
const [fullScreen, setFullScreen] = useState(false)
|
||||
|
||||
const { run: handleSearch } = useDebounceFn(() => {
|
||||
@ -135,8 +136,6 @@ const Completed: FC<ICompletedProps> = ({
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
// setSegments(mockSegments.data)
|
||||
// todo: remove mock data
|
||||
if (segmentListData)
|
||||
setSegments(segmentListData.data || [])
|
||||
}, [segmentListData])
|
||||
@ -156,15 +155,14 @@ const Completed: FC<ICompletedProps> = ({
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
setChildSegments(mockChildSegments.data)
|
||||
// todo: remove mock data
|
||||
// if (childChunkListData)
|
||||
// setChildSegments(childChunkListData.data || [])
|
||||
if (childChunkListData)
|
||||
setChildSegments(childChunkListData.data || [])
|
||||
}, [childChunkListData])
|
||||
|
||||
const resetList = useCallback(() => {
|
||||
setSegments([])
|
||||
refreshSegmentList()
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [])
|
||||
|
||||
const onClickCard = (detail: SegmentDetailModel, isEditMode = false) => {
|
||||
@ -362,16 +360,18 @@ const Completed: FC<ICompletedProps> = ({
|
||||
archived={archived}
|
||||
/>
|
||||
}
|
||||
{/* Pagination */}
|
||||
<Pagination
|
||||
current={currentPage - 1}
|
||||
onChange={cur => setCurrentPage(cur + 1)}
|
||||
total={segmentListData?.total || 0}
|
||||
limit={limit}
|
||||
onLimitChange={limit => setLimit(limit)}
|
||||
/>
|
||||
{/* Edit or view segment detail */}
|
||||
<Drawer
|
||||
<FullScreenDrawer
|
||||
isOpen={currSegment.showModal}
|
||||
onClose={() => {}}
|
||||
panelClassname={`!p-0 ${fullScreen
|
||||
? '!max-w-full !w-full'
|
||||
: 'mt-16 mr-2 mb-2 !max-w-[560px] !w-[560px] border-[0.5px] border-components-panel-border rounded-xl'}`}
|
||||
mask={false}
|
||||
unmount
|
||||
footer={null}
|
||||
fullScreen={fullScreen}
|
||||
>
|
||||
<SegmentDetail
|
||||
segInfo={currSegment.segInfo ?? { id: '' }}
|
||||
@ -379,14 +379,18 @@ const Completed: FC<ICompletedProps> = ({
|
||||
onUpdate={handleUpdateSegment}
|
||||
onCancel={onCloseDrawer}
|
||||
/>
|
||||
</Drawer>
|
||||
</FullScreenDrawer>
|
||||
{/* Create New Segment */}
|
||||
<NewSegmentModal
|
||||
isShow={showNewSegmentModal}
|
||||
docForm={docForm}
|
||||
onCancel={() => onNewSegmentModalChange(false)}
|
||||
onSave={resetList}
|
||||
/>
|
||||
<FullScreenDrawer
|
||||
isOpen={showNewSegmentModal}
|
||||
fullScreen={fullScreen}
|
||||
>
|
||||
<NewSegment
|
||||
docForm={docForm}
|
||||
onCancel={() => onNewSegmentModalChange(false)}
|
||||
onSave={resetList}
|
||||
/>
|
||||
</FullScreenDrawer>
|
||||
{/* Batch Action Buttons */}
|
||||
{selectedSegmentIds.length > 0
|
||||
&& <BatchAction
|
||||
|
||||
@ -1,369 +0,0 @@
|
||||
import type { ChildChunkType } from '@/models/datasets'
|
||||
|
||||
export const mockSegments = {
|
||||
data: [
|
||||
{
|
||||
id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
position: 1,
|
||||
document_id: '887985f1-ca0c-4805-8e9f-34cbc4738a3c',
|
||||
content: 'Dify 云服务 · 自托管 · 文档 · (需用英文)常见问题解答 / 联系团队\n\nDify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等,让您可以快速从原型到生产。以下是其核心功能列表:\n\n1. 工作流: 在画布上构建和测试功能强大的 AI 工作流程,利用以下所有功能以及更多功能。\n\nhttps://github.com/langgenius/dify/assets/13230914/356df23e-1604-483d-80a6-9517ece318aa\n\n2. 全面的模型支持: 与数百种专有/开源 LLMs 以及数十种推理提供商和自托管解决方案无缝集成,涵盖 GPT、Mistral、Llama3 以及任何与 OpenAI API 兼容的模型。完整的支持模型提供商列表可在此处找到。\n\n3. Prompt IDE: 用于制作提示、比较模型性能以及向基于聊天的应用程序添加其他功能(如文本转语音)的直观界面。\n\n4. RAG Pipeline: 广泛的 RAG 功能,涵盖从文档摄入到检索的所有内容,支持从 PDF、PPT 和其他常见文档格式中提取文本的开箱即用的支持。\n\n5. Agent 智能体: 您可以基于 LLM 函数调用或 ReAct 定义 Agent,并为 Agent 添加预构建或自定义工具。Dify 为 AI Agent 提供了50多种内置工具,如谷歌搜索、DALL·E、Stable Diffusion 和 WolframAlpha 等。',
|
||||
answer: '',
|
||||
word_count: 672,
|
||||
tokens: 481,
|
||||
keywords: [
|
||||
'功能',
|
||||
'AI',
|
||||
'LLM',
|
||||
'模型',
|
||||
'文档',
|
||||
'Agent',
|
||||
'开源',
|
||||
'Dify',
|
||||
'支持',
|
||||
'RAG',
|
||||
],
|
||||
index_node_id: 'b67972c2-4a95-4e46-bf8e-f32535bfc483',
|
||||
index_node_hash: '40ead185f2ec6a451da09e99f4f5a7438df4542590090660b7f2f40099220cf0',
|
||||
hit_count: 0,
|
||||
enabled: true,
|
||||
disabled_at: 1732081062,
|
||||
disabled_by: '',
|
||||
status: 'completed',
|
||||
created_by: '573cfc4a-4ff1-43d2-b3e9-46ff1def08c5',
|
||||
created_at: 1732081062,
|
||||
indexing_at: 1732081061,
|
||||
completed_at: 1732081064,
|
||||
error: null,
|
||||
stopped_at: 1732081062,
|
||||
child_chunks: [
|
||||
// {
|
||||
// id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6d',
|
||||
// position: 1,
|
||||
// segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
// content: 'Dify 云服务 · 自托管 · 文档 · (需用英文)常见问题解答 / 联系团队\n\n',
|
||||
// word_count: 45,
|
||||
// created_at: 1732081062,
|
||||
// type: 'automatic' as ChildChunkType,
|
||||
// },
|
||||
// {
|
||||
// id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6c',
|
||||
// position: 2,
|
||||
// segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
// content: 'Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等,让您可以快速从原型到生产。',
|
||||
// word_count: 79,
|
||||
// created_at: 1732081062,
|
||||
// type: 'automatic' as ChildChunkType,
|
||||
// },
|
||||
],
|
||||
},
|
||||
{
|
||||
id: '4c701023-90a6-4df9-bc26-49cfb701badc',
|
||||
position: 2,
|
||||
document_id: '887985f1-ca0c-4805-8e9f-34cbc4738a3c',
|
||||
content: '6. LLMOps: 随时间监视和分析应用程序日志和性能。您可以根据生产数据和标注持续改进提示、数据集和模型。\n\n7. 后端即服务: 所有 Dify 的功能都带有相应的 API,因此您可以轻松地将 Dify 集成到自己的业务逻辑中。\n\n功能比较',
|
||||
answer: '',
|
||||
word_count: 122,
|
||||
tokens: 104,
|
||||
keywords: [
|
||||
'标注',
|
||||
'API',
|
||||
'Dify',
|
||||
'集成',
|
||||
'LLMOps',
|
||||
'后端',
|
||||
'应用程序',
|
||||
'数据',
|
||||
'日志',
|
||||
'功能',
|
||||
],
|
||||
index_node_id: 'fd5a3ea6-c726-41cb-bf0f-00da11f7cab9',
|
||||
index_node_hash: '4e3f5f693e9e43734c12613bbb9971eae154be7765fd0b91fb7263b1755319b8',
|
||||
hit_count: 0,
|
||||
enabled: false,
|
||||
disabled_at: 1732081062,
|
||||
disabled_by: '',
|
||||
status: 'completed',
|
||||
created_by: '573cfc4a-4ff1-43d2-b3e9-46ff1def08c5',
|
||||
created_at: 1732081062,
|
||||
indexing_at: 1732081061,
|
||||
completed_at: 1732081064,
|
||||
error: null,
|
||||
stopped_at: 1732081062,
|
||||
},
|
||||
{
|
||||
id: '070f9780-1819-43fc-b976-780db8e19ed9',
|
||||
position: 3,
|
||||
document_id: '887985f1-ca0c-4805-8e9f-34cbc4738a3c',
|
||||
content: '功能 Dify.AI LangChain Flowise OpenAI Assistant API 编程方法 API + 应用程序导向 Python 代码 应用程序导向 API 导向 支持的 LLMs 丰富多样 丰富多样 丰富多样 仅限 OpenAI RAG引擎 ✅ ✅ ✅ ✅ Agent ✅ ✅ ❌ ✅ 工作流 ✅ ❌ ✅ ❌ 可观测性 ✅ ✅ ❌ ❌ 企业功能(SSO/访问控制) ✅ ❌ ❌ ❌ 本地部署 ✅ ✅ ✅ ❌',
|
||||
answer: '',
|
||||
word_count: 214,
|
||||
tokens: 158,
|
||||
keywords: [
|
||||
'导向',
|
||||
'API',
|
||||
'Dify',
|
||||
'OpenAI',
|
||||
'AI',
|
||||
'多样',
|
||||
'LangChain',
|
||||
'应用程序',
|
||||
'Flowise',
|
||||
'丰富',
|
||||
],
|
||||
index_node_id: 'a3c7a2bd-003a-4667-a4a8-2da6c27cd887',
|
||||
index_node_hash: 'e824b23aa039ebc6a6b34a366251235bd81ad72535c2ea66fab949b1f78a65dc',
|
||||
hit_count: 0,
|
||||
enabled: true,
|
||||
disabled_at: 1732081062,
|
||||
disabled_by: '',
|
||||
status: 'completed',
|
||||
created_by: '573cfc4a-4ff1-43d2-b3e9-46ff1def08c5',
|
||||
created_at: 1732081062,
|
||||
indexing_at: 1732081061,
|
||||
completed_at: 1732081064,
|
||||
error: null,
|
||||
stopped_at: 1732081062,
|
||||
},
|
||||
{
|
||||
id: 'c817f359-d927-4987-b940-e040251b10e1',
|
||||
position: 4,
|
||||
document_id: '887985f1-ca0c-4805-8e9f-34cbc4738a3c',
|
||||
content: '使用 Dify\n\n云 我们提供 Dify 云服务,任何人都可以零设置尝试。它提供了自部署版本的所有功能,并在沙盒计划中包含 200 次免费的 GPT-4 调用。\n\n自托管 Dify 社区版 使用这个入门指南快速在您的环境中运行 Dify。 使用我们的文档进行进一步的参考和更深入的说明。\n\n面向企业/组织的 Dify 我们提供额外的面向企业的功能。给我们发送电子邮件讨论企业需求。\n\n对于使用 AWS 的初创公司和中小型企业,请查看 AWS Marketplace 上的 Dify 高级版,并使用一键部署到您自己的 AWS VPC。它是一个价格实惠的 AMI 产品,提供了使用自定义徽标和品牌创建应用程序的选项。\n\n保持领先\n\n在 GitHub 上给 Dify Star,并立即收到新版本的通知。\n\n安装社区版\n\n系统要求\n\n在安装 Dify 之前,请确保您的机器满足以下最低系统要求:\n\nCPU >= 2 Core\n\nRAM >= 4 GiB\n\n快速启动\n\n启动 Dify 服务器的最简单方法是运行我们的 docker-compose.yml 文件。在运行安装命令之前,请确保您的机器上安装了 Docker 和 Docker Compose:\n\nbash cd docker cp .env.example .env docker compose up -d\n\n运行后,可以在浏览器上访问 http://localhost/install 进入 Dify 控制台并开始初始化安装操作。\n\n自定义配置',
|
||||
answer: '',
|
||||
word_count: 650,
|
||||
tokens: 427,
|
||||
keywords: [
|
||||
'Docker',
|
||||
'Dify',
|
||||
'env',
|
||||
'AWS',
|
||||
'docker',
|
||||
'自定义',
|
||||
'使用',
|
||||
'确保您',
|
||||
'安装',
|
||||
'compose',
|
||||
],
|
||||
index_node_id: '2af623f5-dea6-4b6b-a147-17f9e76ac1dd',
|
||||
index_node_hash: '7570a716c175c92b47658536e3c0df7dce8bac30b09cd33fb4333299874ebb0d',
|
||||
hit_count: 0,
|
||||
enabled: true,
|
||||
disabled_at: 1732081062,
|
||||
disabled_by: '',
|
||||
status: 'completed',
|
||||
created_by: '573cfc4a-4ff1-43d2-b3e9-46ff1def08c5',
|
||||
created_at: 1732081062,
|
||||
indexing_at: 1732081061,
|
||||
completed_at: 1732081064,
|
||||
error: null,
|
||||
stopped_at: 1732081062,
|
||||
},
|
||||
{
|
||||
id: 'c2cbfe0b-304c-40c2-9980-7d39d65e5b18',
|
||||
position: 5,
|
||||
document_id: '887985f1-ca0c-4805-8e9f-34cbc4738a3c',
|
||||
content: '运行后,可以在浏览器上访问 http://localhost/install 进入 Dify 控制台并开始初始化安装操作。\n\n自定义配置\n\n如果您需要自定义配置,请参考 .env.example 文件中的注释,并更新 .env 文件中对应的值。此外,您可能需要根据您的具体部署环境和需求对 docker-compose.yaml 文件本身进行调整,例如更改镜像版本、端口映射或卷挂载。完成任何更改后,请重新运行 docker-compose up -d。您可以在此处找到可用环境变量的完整列表。\n\n使用 Helm Chart 部署\n\n使用 Helm Chart 版本或者 YAML 文件,可以在 Kubernetes 上部署 Dify。\n\nHelm Chart by @LeoQuote\n\nHelm Chart by @BorisPolonsky\n\nYAML 文件 by @Winson-030\n\n使用 Terraform 部署\n\n使用 terraform 一键将 Dify 部署到云平台\n\nAzure Global\n\nAzure Terraform by @nikawang\n\nGoogle Cloud\n\nGoogle Cloud Terraform by @sotazum\n\nStar History\n\nContributing\n\n对于那些想要贡献代码的人,请参阅我们的贡献指南。 同时,请考虑通过社交媒体、活动和会议来支持 Dify 的分享。\n\n我们正在寻找贡献者来帮助将Dify翻译成除了中文和英文之外的其他语言。如果您有兴趣帮助,请参阅我们的i18n README获取更多信息,并在我们的Discord社区服务器的global-users频道中留言。\n\nContributors\n\n社区与支持',
|
||||
answer: '',
|
||||
word_count: 751,
|
||||
tokens: 424,
|
||||
keywords: [
|
||||
'Terraform',
|
||||
'Dify',
|
||||
'env',
|
||||
'Chart',
|
||||
'自定义',
|
||||
'docker',
|
||||
'Helm',
|
||||
'部署',
|
||||
'请参阅',
|
||||
'文件',
|
||||
],
|
||||
index_node_id: 'e8b230c2-1ab6-4e70-b317-c50479b284d1',
|
||||
index_node_hash: '1efe0128dc40d87f3cd57855e872e4b67f20cc71a6c52732bfd67cd5bdcff65e',
|
||||
hit_count: 0,
|
||||
enabled: true,
|
||||
disabled_at: 1732081062,
|
||||
disabled_by: '',
|
||||
status: 'completed',
|
||||
created_by: '573cfc4a-4ff1-43d2-b3e9-46ff1def08c5',
|
||||
created_at: 1732081062,
|
||||
indexing_at: 1732081061,
|
||||
completed_at: 1732081064,
|
||||
error: null,
|
||||
stopped_at: 1732081062,
|
||||
},
|
||||
{
|
||||
id: '0dcea77f-657d-4765-bc4a-a71806bede29',
|
||||
position: 6,
|
||||
document_id: '887985f1-ca0c-4805-8e9f-34cbc4738a3c',
|
||||
content: 'Contributors\n\n社区与支持\n\n我们欢迎您为 Dify 做出贡献,以帮助改善 Dify。包括:提交代码、问题、新想法,或分享您基于 Dify 创建的有趣且有用的 AI 应用程序。同时,我们也欢迎您在不同的活动、会议和社交媒体上分享 Dify。\n\nGithub Discussion. 👉:分享您的应用程序并与社区交流。\n\nGitHub Issues。👉:使用 Dify.AI 时遇到的错误和问题,请参阅贡献指南。\n\n电子邮件支持。👉:关于使用 Dify.AI 的问题。\n\nDiscord。👉:分享您的应用程序并与社区交流。\n\nX(Twitter)。👉:分享您的应用程序并与社区交流。\n\n商业许可。👉:有关商业用途许可 Dify.AI 的商业咨询。\n\n微信 👉:扫描下方二维码,添加微信好友,备注 Dify,我们将邀请您加入 Dify 社区。\n\n安全问题\n\n为了保护您的隐私,请避免在 GitHub 上发布安全问题。发送问题至 security@dify.ai,我们将为您做更细致的解答。\n\nLicense\n\n本仓库遵循 Dify Open Source License 开源协议,该许可证本质上是 Apache 2.0,但有一些额外的限制。',
|
||||
answer: '',
|
||||
word_count: 525,
|
||||
tokens: 388,
|
||||
keywords: [
|
||||
'问题',
|
||||
'Dify',
|
||||
'分享',
|
||||
'AI',
|
||||
'GitHub',
|
||||
'微信',
|
||||
'应用程序',
|
||||
'社区',
|
||||
'欢迎您',
|
||||
'License',
|
||||
],
|
||||
index_node_id: '3d17802d-9316-4e0d-9e9e-179f12e9830c',
|
||||
index_node_hash: 'd7d3093eb73803bdbfabe811e33ff60c8b75c15340f9046cac53b2e02fa07203',
|
||||
hit_count: 0,
|
||||
enabled: true,
|
||||
disabled_at: 1732081062,
|
||||
disabled_by: '',
|
||||
status: 'completed',
|
||||
created_by: '573cfc4a-4ff1-43d2-b3e9-46ff1def08c5',
|
||||
created_at: 1732081062,
|
||||
indexing_at: 1732081061,
|
||||
completed_at: 1732081064,
|
||||
error: null,
|
||||
stopped_at: 1732081062,
|
||||
},
|
||||
],
|
||||
doc_form: 'text_model',
|
||||
has_more: false,
|
||||
limit: 10,
|
||||
total: 6,
|
||||
}
|
||||
|
||||
export const mockChildSegments = {
|
||||
data: [
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6d',
|
||||
position: 1,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 云服务 · 自托管 · 文档 · (需用英文)常见问题解答 / 联系团队\n\n',
|
||||
word_count: 45,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6c',
|
||||
position: 2,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等,让您可以快速从原型到生产。',
|
||||
word_count: 79,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6d',
|
||||
position: 1,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 云服务 · 自托管 · 文档 · (需用英文)常见问题解答 / 联系团队\n\n',
|
||||
word_count: 45,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6c',
|
||||
position: 2,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等,让您可以快速从原型到生产。',
|
||||
word_count: 79,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6d',
|
||||
position: 1,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 云服务 · 自托管 · 文档 · (需用英文)常见问题解答 / 联系团队\n\n',
|
||||
word_count: 45,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6c',
|
||||
position: 2,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等,让您可以快速从原型到生产。',
|
||||
word_count: 79,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6d',
|
||||
position: 1,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 云服务 · 自托管 · 文档 · (需用英文)常见问题解答 / 联系团队\n\n',
|
||||
word_count: 45,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6c',
|
||||
position: 2,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等,让您可以快速从原型到生产。',
|
||||
word_count: 79,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6d',
|
||||
position: 1,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 云服务 · 自托管 · 文档 · (需用英文)常见问题解答 / 联系团队\n\n',
|
||||
word_count: 45,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6c',
|
||||
position: 2,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等,让您可以快速从原型到生产。',
|
||||
word_count: 79,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6d',
|
||||
position: 1,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 云服务 · 自托管 · 文档 · (需用英文)常见问题解答 / 联系团队\n\n',
|
||||
word_count: 45,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6c',
|
||||
position: 2,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等,让您可以快速从原型到生产。',
|
||||
word_count: 79,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6d',
|
||||
position: 1,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 云服务 · 自托管 · 文档 · (需用英文)常见问题解答 / 联系团队\n\n',
|
||||
word_count: 45,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
{
|
||||
id: 'f3c7e7b6-5e7e-4c8d-9a0b-8f7e1c1f7a6c',
|
||||
position: 2,
|
||||
segment_id: '12aa196a-cf47-4962-a64a-7d927ed9b0ea',
|
||||
content: 'Dify 是一个开源的 LLM 应用开发平台。其直观的界面结合了 AI 工作流、RAG 管道、Agent、模型管理、可观测性功能等,让您可以快速从原型到生产。',
|
||||
word_count: 79,
|
||||
created_at: 1732081062,
|
||||
type: 'automatic' as ChildChunkType,
|
||||
},
|
||||
],
|
||||
total: 2,
|
||||
total_pages: 10,
|
||||
page: 1,
|
||||
limit: 10,
|
||||
}
|
||||
@ -14,6 +14,8 @@ import { formatNumber } from '@/utils/format'
|
||||
import Confirm from '@/app/components/base/confirm'
|
||||
import cn from '@/utils/classnames'
|
||||
import Badge from '@/app/components/base/badge'
|
||||
import { isAfter } from '@/utils/time'
|
||||
import Tooltip from '@/app/components/base/tooltip'
|
||||
|
||||
const Dot = React.memo(() => {
|
||||
return (
|
||||
@ -115,6 +117,8 @@ const SegmentCard: FC<ISegmentCardProps> = ({
|
||||
answer,
|
||||
keywords,
|
||||
child_chunks = [],
|
||||
created_at,
|
||||
updated_at,
|
||||
} = detail as Required<ISegmentCardProps>['detail']
|
||||
const [showModal, setShowModal] = useState(false)
|
||||
const isCollapsed = useSegmentListContext(s => s.isCollapsed)
|
||||
@ -132,10 +136,11 @@ const SegmentCard: FC<ISegmentCardProps> = ({
|
||||
return mode === 'hierarchical' && parentMode === 'full-doc'
|
||||
}, [mode, parentMode])
|
||||
|
||||
// todo: change to real logic
|
||||
const chunkEdited = useMemo(() => {
|
||||
return mode !== 'hierarchical' || parentMode !== 'full-doc'
|
||||
}, [mode, parentMode])
|
||||
if (mode === 'hierarchical' && parentMode === 'full-doc')
|
||||
return false
|
||||
return isAfter(updated_at * 1000, created_at * 1000)
|
||||
}, [mode, parentMode, updated_at, created_at])
|
||||
|
||||
const textOpacity = useMemo(() => {
|
||||
return enabled ? '' : 'opacity-50 group-hover/card:opacity-100'
|
||||
@ -203,22 +208,32 @@ const SegmentCard: FC<ISegmentCardProps> = ({
|
||||
rounded-[10px] border-[0.5px] border-components-actionbar-border bg-components-actionbar-bg shadow-md backdrop-blur-[5px]">
|
||||
{!archived && (
|
||||
<>
|
||||
<div
|
||||
className='shrink-0 w-6 h-6 flex items-center justify-center rounded-lg hover:bg-state-base-hover cursor-pointer'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onClickEdit?.()
|
||||
}}>
|
||||
<RiEditLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
<div className='shrink-0 w-6 h-6 flex items-center justify-center rounded-lg hover:bg-state-destructive-hover cursor-pointer group/delete'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setShowModal(true)
|
||||
}
|
||||
}>
|
||||
<RiDeleteBinLine className='w-4 h-4 text-text-tertiary group-hover/delete:text-text-destructive' />
|
||||
</div>
|
||||
<Tooltip
|
||||
popupContent='Edit'
|
||||
popupClassName='text-text-secondary system-xs-medium'
|
||||
>
|
||||
<div
|
||||
className='shrink-0 w-6 h-6 flex items-center justify-center rounded-lg hover:bg-state-base-hover cursor-pointer'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
onClickEdit?.()
|
||||
}}>
|
||||
<RiEditLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip
|
||||
popupContent='Delete'
|
||||
popupClassName='text-text-secondary system-xs-medium'
|
||||
>
|
||||
<div className='shrink-0 w-6 h-6 flex items-center justify-center rounded-lg hover:bg-state-destructive-hover cursor-pointer group/delete'
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
setShowModal(true)
|
||||
}
|
||||
}>
|
||||
<RiDeleteBinLine className='w-4 h-4 text-text-tertiary group-hover/delete:text-text-destructive' />
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Divider type="vertical" className="h-3.5 bg-divider-regular" />
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -63,6 +63,8 @@ const SegmentDetail: FC<ISegmentDetailProps> = ({
|
||||
})
|
||||
|
||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.s`, (e) => {
|
||||
if (loading)
|
||||
return
|
||||
e.preventDefault()
|
||||
handleSave()
|
||||
}
|
||||
@ -164,7 +166,7 @@ const SegmentDetail: FC<ISegmentDetailProps> = ({
|
||||
<div className='flex items-center gap-x-2'>
|
||||
<SegmentIndexTag positionId={segInfo?.position || ''} />
|
||||
<span className='text-text-quaternary system-xs-medium'>·</span>
|
||||
<span className='text-text-tertiary system-xs-medium'>{formatNumber(segInfo?.word_count as number)} {t('datasetDocuments.segment.characters')}</span>
|
||||
<span className='text-text-tertiary system-xs-medium'>{formatNumber(isEditMode ? question.length : segInfo?.word_count as number)} {t('datasetDocuments.segment.characters')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
@ -197,4 +199,4 @@ const SegmentDetail: FC<ISegmentDetailProps> = ({
|
||||
)
|
||||
}
|
||||
|
||||
export default SegmentDetail
|
||||
export default React.memo(SegmentDetail)
|
||||
|
||||
@ -234,7 +234,7 @@ const DocumentDetail: FC<Props> = ({ datasetId, documentId }) => {
|
||||
<div className='flex flex-row flex-1' style={{ height: 'calc(100% - 4rem)' }}>
|
||||
{isDetailLoading
|
||||
? <Loading type='app' />
|
||||
: <div className={`h-full w-full flex flex-col ${embedding ? 'px-6 py-3 sm:py-12 sm:px-16' : `relative pb-[30px] pt-3 ${isFullDocMode ? 'pl-11' : 'pl-5'} pr-11`}`}>
|
||||
: <div className={`h-full w-full flex flex-col ${embedding ? 'px-6 py-3 sm:py-12 sm:px-16' : `relative pt-3 ${isFullDocMode ? 'pl-11' : 'pl-5'} pr-11`}`}>
|
||||
{embedding
|
||||
? <Embedding detail={documentDetail} detailUpdate={detailMutate} />
|
||||
: <Completed
|
||||
|
||||
@ -6,7 +6,6 @@ import { useParams } from 'next/navigation'
|
||||
import { RiCloseLine, RiExpandDiagonalLine } from '@remixicon/react'
|
||||
import { useKeyPress } from 'ahooks'
|
||||
import { SegmentIndexTag, useSegmentListContext } from './completed'
|
||||
import Drawer from '@/app/components/base/drawer'
|
||||
import Button from '@/app/components/base/button'
|
||||
import AutoHeightTextarea from '@/app/components/base/auto-height-textarea/common'
|
||||
import { ToastContext } from '@/app/components/base/toast'
|
||||
@ -19,14 +18,12 @@ import { getKeyboardKeyCodeBySystem, getKeyboardKeyNameBySystem } from '@/app/co
|
||||
import Divider from '@/app/components/base/divider'
|
||||
|
||||
type NewSegmentModalProps = {
|
||||
isShow: boolean
|
||||
onCancel: () => void
|
||||
docForm: string
|
||||
onSave: () => void
|
||||
}
|
||||
|
||||
const NewSegmentModal: FC<NewSegmentModalProps> = ({
|
||||
isShow,
|
||||
onCancel,
|
||||
docForm,
|
||||
onSave,
|
||||
@ -86,6 +83,8 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
|
||||
})
|
||||
|
||||
useKeyPress(`${getKeyboardKeyCodeBySystem('ctrl')}.s`, (e) => {
|
||||
if (loading)
|
||||
return
|
||||
e.preventDefault()
|
||||
handleSave()
|
||||
}
|
||||
@ -167,58 +166,47 @@ const NewSegmentModal: FC<NewSegmentModalProps> = ({
|
||||
}
|
||||
|
||||
return (
|
||||
<Drawer
|
||||
isOpen={isShow}
|
||||
onClose={() => {}}
|
||||
panelClassname={`!p-0 ${fullScreen
|
||||
? '!max-w-full !w-full'
|
||||
: 'mt-16 mr-2 mb-2 !max-w-[560px] !w-[560px] border-[0.5px] border-components-panel-border rounded-xl'}`}
|
||||
mask={false}
|
||||
unmount
|
||||
footer={null}
|
||||
>
|
||||
<div className={'flex flex-col h-full'}>
|
||||
<div className={classNames('flex items-center justify-between', fullScreen ? 'py-3 pr-4 pl-6 border border-divider-subtle' : 'pt-3 pr-3 pl-4')}>
|
||||
<div className='flex flex-col'>
|
||||
<div className='text-text-primary system-xl-semibold'>{
|
||||
docForm === 'qa_model'
|
||||
? t('datasetDocuments.segment.newQaSegment')
|
||||
: t('datasetDocuments.segment.addChunk')
|
||||
}</div>
|
||||
<div className='flex items-center gap-x-2'>
|
||||
<SegmentIndexTag label={'New Chunk'} />
|
||||
<span className='text-text-quaternary system-xs-medium'>·</span>
|
||||
<span className='text-text-tertiary system-xs-medium'>{formatNumber(question.length)} {t('datasetDocuments.segment.characters')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
{fullScreen && (
|
||||
<>
|
||||
{renderActionButtons()}
|
||||
<Divider type='vertical' className='h-3.5 bg-divider-regular ml-4 mr-2' />
|
||||
</>
|
||||
)}
|
||||
<div className='w-8 h-8 flex justify-center items-center p-1.5 cursor-pointer mr-1' onClick={toggleFullScreen}>
|
||||
<RiExpandDiagonalLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
<div className='w-8 h-8 flex justify-center items-center p-1.5 cursor-pointer' onClick={handleCancel}>
|
||||
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
<div className={'flex flex-col h-full'}>
|
||||
<div className={classNames('flex items-center justify-between', fullScreen ? 'py-3 pr-4 pl-6 border border-divider-subtle' : 'pt-3 pr-3 pl-4')}>
|
||||
<div className='flex flex-col'>
|
||||
<div className='text-text-primary system-xl-semibold'>{
|
||||
docForm === 'qa_model'
|
||||
? t('datasetDocuments.segment.newQaSegment')
|
||||
: t('datasetDocuments.segment.addChunk')
|
||||
}</div>
|
||||
<div className='flex items-center gap-x-2'>
|
||||
<SegmentIndexTag label={'New Chunk'} />
|
||||
<span className='text-text-quaternary system-xs-medium'>·</span>
|
||||
<span className='text-text-tertiary system-xs-medium'>{formatNumber(question.length)} {t('datasetDocuments.segment.characters')}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className={classNames('flex grow overflow-hidden', fullScreen ? 'w-full flex-row justify-center px-6 pt-6 gap-x-8' : 'flex-col gap-y-1 py-3 px-4')}>
|
||||
<div className={classNames('break-all overflow-y-auto whitespace-pre-line', fullScreen ? 'w-1/2' : 'grow')}>
|
||||
{renderContent()}
|
||||
<div className='flex items-center'>
|
||||
{fullScreen && (
|
||||
<>
|
||||
{renderActionButtons()}
|
||||
<Divider type='vertical' className='h-3.5 bg-divider-regular ml-4 mr-2' />
|
||||
</>
|
||||
)}
|
||||
<div className='w-8 h-8 flex justify-center items-center p-1.5 cursor-pointer mr-1' onClick={toggleFullScreen}>
|
||||
<RiExpandDiagonalLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
<div className='w-8 h-8 flex justify-center items-center p-1.5 cursor-pointer' onClick={handleCancel}>
|
||||
<RiCloseLine className='w-4 h-4 text-text-tertiary' />
|
||||
</div>
|
||||
{renderKeywords()}
|
||||
</div>
|
||||
{!fullScreen && (
|
||||
<div className='flex items-center justify-end p-4 pt-3 border-t-[1px] border-t-divider-subtle'>
|
||||
{renderActionButtons()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Drawer>
|
||||
<div className={classNames('flex grow overflow-hidden', fullScreen ? 'w-full flex-row justify-center px-6 pt-6 gap-x-8' : 'flex-col gap-y-1 py-3 px-4')}>
|
||||
<div className={classNames('break-all overflow-y-auto whitespace-pre-line', fullScreen ? 'w-1/2' : 'grow')}>
|
||||
{renderContent()}
|
||||
</div>
|
||||
{renderKeywords()}
|
||||
</div>
|
||||
{!fullScreen && (
|
||||
<div className='flex items-center justify-end p-4 pt-3 border-t-[1px] border-t-divider-subtle'>
|
||||
{renderActionButtons()}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
@ -281,7 +281,6 @@ export const OperationAction: FC<{
|
||||
<Tooltip
|
||||
popupContent={t('datasetDocuments.list.action.settings')}
|
||||
popupClassName='text-text-secondary system-xs-medium'
|
||||
needsDelay
|
||||
>
|
||||
<button
|
||||
className={cn('rounded-lg mr-2 cursor-pointer',
|
||||
|
||||
@ -451,6 +451,7 @@ export type SegmentDetailModel = {
|
||||
stopped_at: number
|
||||
answer?: string
|
||||
child_chunks?: ChildChunkDetail[]
|
||||
updated_at: number
|
||||
}
|
||||
|
||||
export type SegmentsResponse = {
|
||||
|
||||
5
web/utils/time.ts
Normal file
5
web/utils/time.ts
Normal file
@ -0,0 +1,5 @@
|
||||
import dayjs, { type ConfigType } from 'dayjs'
|
||||
|
||||
export const isAfter = (date: ConfigType, compare: ConfigType) => {
|
||||
return dayjs(date).isAfter(dayjs(compare))
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user