feat(chunk): add pdf preview and locate functionality to chunk results

This commit is contained in:
2025-11-15 00:29:43 +08:00
parent 5bacd45419
commit ae9ca74dbc
9 changed files with 462 additions and 75 deletions

View File

@@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>RAG Empowerment System</title>
<title>TERES AI</title>
</head>
<body>
<div id="root"></div>

View File

@@ -11,14 +11,13 @@
"preview": "vite preview"
},
"dependencies": {
"@teres/iframe-bridge": "workspace:*",
"penpal": "^6.2.1",
"@emotion/react": "^11.14.0",
"@emotion/styled": "^11.14.1",
"@mui/icons-material": "^7.3.4",
"@mui/material": "^7.3.4",
"@mui/x-data-grid": "^8.14.0",
"@mui/x-date-pickers": "^8.14.0",
"@teres/iframe-bridge": "workspace:*",
"@xyflow/react": "^12.8.6",
"ahooks": "^3.9.5",
"axios": "^1.12.2",
@@ -29,6 +28,8 @@
"jsencrypt": "^3.5.4",
"lodash": "^4.17.21",
"loglevel": "^1.9.2",
"pdfjs-dist": "^5.4.394",
"penpal": "^6.2.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.64.0",

124
pnpm-lock.yaml generated
View File

@@ -59,6 +59,9 @@ importers:
loglevel:
specifier: ^1.9.2
version: 1.9.2
pdfjs-dist:
specifier: ^5.4.394
version: 5.4.394
penpal:
specifier: ^6.2.1
version: 6.2.2
@@ -2225,6 +2228,75 @@ packages:
peerDependencies:
workerize-loader: '*'
'@napi-rs/canvas-android-arm64@0.1.82':
resolution: {integrity: sha512-bvZhN0iI54ouaQOrgJV96H2q7J3ZoufnHf4E1fUaERwW29Rz4rgicohnAg4venwBJZYjGl5Yl3CGmlAl1LZowQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [android]
'@napi-rs/canvas-darwin-arm64@0.1.82':
resolution: {integrity: sha512-InuBHKCyuFqhNwNr4gpqazo5Xp6ltKflqOLiROn4hqAS8u21xAHyYCJRgHwd+a5NKmutFTaRWeUIT/vxWbU/iw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@napi-rs/canvas-darwin-x64@0.1.82':
resolution: {integrity: sha512-aQGV5Ynn96onSXcuvYb2y7TRXD/t4CL2EGmnGqvLyeJX1JLSNisKQlWN/1bPDDXymZYSdUqbXehj5qzBlOx+RQ==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@napi-rs/canvas-linux-arm-gnueabihf@0.1.82':
resolution: {integrity: sha512-YIUpmHWeHGGRhWitT1KJkgj/JPXPfc9ox8oUoyaGPxolLGPp5AxJkq8wIg8CdFGtutget968dtwmx71m8o3h5g==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
'@napi-rs/canvas-linux-arm64-gnu@0.1.82':
resolution: {integrity: sha512-AwLzwLBgmvk7kWeUgItOUor/QyG31xqtD26w1tLpf4yE0hiXTGp23yc669aawjB6FzgIkjh1NKaNS52B7/qEBQ==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@napi-rs/canvas-linux-arm64-musl@0.1.82':
resolution: {integrity: sha512-moZWuqepAwWBffdF4JDadt8TgBD02iMhG6I1FHZf8xO20AsIp9rB+p0B8Zma2h2vAF/YMjeFCDmW5un6+zZz9g==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@napi-rs/canvas-linux-riscv64-gnu@0.1.82':
resolution: {integrity: sha512-w9++2df2kG9eC9LWYIHIlMLuhIrKGQYfUxs97CwgxYjITeFakIRazI9LYWgVzEc98QZ9x9GQvlicFsrROV59MQ==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@napi-rs/canvas-linux-x64-gnu@0.1.82':
resolution: {integrity: sha512-lZulOPwrRi6hEg/17CaqdwWEUfOlIJuhXxincx1aVzsVOCmyHf+xFq4i6liJl1P+x2v6Iz2Z/H5zHvXJCC7Bwg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@napi-rs/canvas-linux-x64-musl@0.1.82':
resolution: {integrity: sha512-Be9Wf5RTv1w6GXlTph55K3PH3vsAh1Ax4T1FQY1UYM0QfD0yrwGdnJ8/fhqw7dEgMjd59zIbjJQC8C3msbGn5g==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@napi-rs/canvas-win32-x64-msvc@0.1.82':
resolution: {integrity: sha512-LN/i8VrvxTDmEEK1c10z2cdOTkWT76LlTGtyZe5Kr1sqoSomKeExAjbilnu1+oee5lZUgS5yfZ2LNlVhCeARuw==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@napi-rs/canvas@0.1.82':
resolution: {integrity: sha512-FGjyUBoF0sl1EenSiE4UV2WYu76q6F9GSYedq5EiOCOyGYoQ/Owulcv6rd7v/tWOpljDDtefXXIaOCJrVKem4w==}
engines: {node: '>= 10'}
'@napi-rs/nice-android-arm-eabi@1.1.1':
resolution: {integrity: sha512-kjirL3N6TnRPv5iuHw36wnucNqXAO46dzK9oPb0wj076R5Xm8PfUVA9nAFB5ZNMmfJQJVKACAPd/Z2KYMppthw==}
engines: {node: '>= 10'}
@@ -9139,6 +9211,10 @@ packages:
worker-loader:
optional: true
pdfjs-dist@5.4.394:
resolution: {integrity: sha512-9ariAYGqUJzx+V/1W4jHyiyCep6IZALmDzoaTLZ6VNu8q9LWi1/ukhzHgE2Xsx96AZi0mbZuK4/ttIbqSbLypg==}
engines: {node: '>=20.16.0 || >=22.3.0'}
penpal@6.2.2:
resolution: {integrity: sha512-RQD7hTx14/LY7QoS3tQYO3/fzVtwvZI+JeS5udgsu7FPaEDjlvfK9HBcme9/ipzSPKnrxSgacI9PI7154W62YQ==}
@@ -14104,6 +14180,50 @@ snapshots:
dependencies:
workerize-loader: 2.0.2(webpack@5.102.1(@swc/core@1.15.1)(esbuild@0.25.10))
'@napi-rs/canvas-android-arm64@0.1.82':
optional: true
'@napi-rs/canvas-darwin-arm64@0.1.82':
optional: true
'@napi-rs/canvas-darwin-x64@0.1.82':
optional: true
'@napi-rs/canvas-linux-arm-gnueabihf@0.1.82':
optional: true
'@napi-rs/canvas-linux-arm64-gnu@0.1.82':
optional: true
'@napi-rs/canvas-linux-arm64-musl@0.1.82':
optional: true
'@napi-rs/canvas-linux-riscv64-gnu@0.1.82':
optional: true
'@napi-rs/canvas-linux-x64-gnu@0.1.82':
optional: true
'@napi-rs/canvas-linux-x64-musl@0.1.82':
optional: true
'@napi-rs/canvas-win32-x64-msvc@0.1.82':
optional: true
'@napi-rs/canvas@0.1.82':
optionalDependencies:
'@napi-rs/canvas-android-arm64': 0.1.82
'@napi-rs/canvas-darwin-arm64': 0.1.82
'@napi-rs/canvas-darwin-x64': 0.1.82
'@napi-rs/canvas-linux-arm-gnueabihf': 0.1.82
'@napi-rs/canvas-linux-arm64-gnu': 0.1.82
'@napi-rs/canvas-linux-arm64-musl': 0.1.82
'@napi-rs/canvas-linux-riscv64-gnu': 0.1.82
'@napi-rs/canvas-linux-x64-gnu': 0.1.82
'@napi-rs/canvas-linux-x64-musl': 0.1.82
'@napi-rs/canvas-win32-x64-msvc': 0.1.82
optional: true
'@napi-rs/nice-android-arm-eabi@1.1.1':
optional: true
@@ -22626,6 +22746,10 @@ snapshots:
dommatrix: 1.0.3
web-streams-polyfill: 3.3.3
pdfjs-dist@5.4.394:
optionalDependencies:
'@napi-rs/canvas': 0.1.82
penpal@6.2.2: {}
performance-now@2.1.0: {}

View File

@@ -41,7 +41,7 @@ function MaterialUIApp() {
}
function App() {
useTitle('RAG Empowerment System');
useTitle('TERES AI');
return (
<MaterialUIApp />

View File

@@ -61,7 +61,7 @@ const Header = () => {
borderBottom: '1px solid #E5E5E5',
}}
>
<Box
{/* <Box
sx={{
fontSize: '1.2rem',
fontWeight: 'bold',
@@ -69,7 +69,7 @@ const Header = () => {
}}
>
RAG Empowerment System
</Box>
</Box> */}
<Box
sx={{

View File

@@ -84,7 +84,7 @@ const Sidebar = () => {
letterSpacing: '0.5px',
}}
>
T-Systems Enterprise
TERES AI
</Typography>
<List>

View File

@@ -59,10 +59,11 @@ interface ChunkListResultProps {
onPageChange: (page: number) => void;
onRefresh?: () => void;
docName?: string;
onLocate?: (chunk: IChunk) => void;
}
function ChunkListResult(props: ChunkListResultProps) {
const { doc_id, chunks, total, loading, page, pageSize, onPageChange, onRefresh } = props;
const { doc_id, chunks, total, loading, page, pageSize, onPageChange, onRefresh, onLocate } = props;
const { t } = useTranslation();
// 选择状态
@@ -149,9 +150,12 @@ function ChunkListResult(props: ChunkListResultProps) {
}, []);
// 图片点击放大
const handleImageClick = useCallback((imageUrl: string) => {
const handleImageClick = useCallback((imageUrl: string, chunk?: IChunk) => {
setPreviewImageUrl(imageUrl);
setImagePreviewOpen(true);
if (chunk) {
onLocate?.(chunk);
}
}, []);
// 编辑chunk
@@ -351,7 +355,7 @@ function ChunkListResult(props: ChunkListResultProps) {
<Grid container spacing={2}>
{chunks.map((chunk, index) => (
<Grid size={6} key={chunk.chunk_id}>
<Grid size={12} key={chunk.chunk_id}>
<Card
variant="outlined"
sx={{
@@ -383,6 +387,12 @@ function ChunkListResult(props: ChunkListResultProps) {
color={chunk.available_int === 1 ? 'success' : 'default'}
variant={chunk.available_int === 1 ? 'filled' : 'outlined'}
/>
{/* 定位到文档位置 */}
<Tooltip title={'定位'}>
<IconButton size="small" onClick={() => onLocate?.(chunk)}>
<ZoomInIcon />
</IconButton>
</Tooltip>
</Box>
{/* 主要内容区域 - 左右布局 */}
@@ -415,7 +425,7 @@ function ChunkListResult(props: ChunkListResultProps) {
}
}
}}
onClick={() => handleImageClick(`${import.meta.env.VITE_API_BASE_URL}/v1/document/image/${chunk.image_id}`)}
onClick={() => handleImageClick(`${import.meta.env.VITE_API_BASE_URL}/v1/document/image/${chunk.image_id}`, chunk)}
onMouseEnter={(e) => handleImageHover(e, `${import.meta.env.VITE_API_BASE_URL}/v1/document/image/${chunk.image_id}`)}
onMouseLeave={handleImageHoverClose}
>

View File

@@ -1,5 +1,16 @@
import React, { useState, useEffect } from 'react';
import { useSearchParams, useNavigate } from "react-router-dom";
import React, { useState, useEffect, useRef } from 'react';
// 使用 pdf.js 在左侧容器中渲染 PDF 页面,支持自由滑动与页码定位
import * as pdfjsLib from 'pdfjs-dist';
// 使用 Vite 的资源 URL 解析,将 pdf.js 的模块 worker 转成可访问 URL
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
import pdfjsWorkerUrl from 'pdfjs-dist/build/pdf.worker.min.mjs?url';
// 采用模块 Worker避免经典 worker 的 MIME/路径问题
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
pdfjsLib.GlobalWorkerOptions.workerPort = new Worker(pdfjsWorkerUrl, { type: 'module' });
import { useSearchParams } from "react-router-dom";
import {
Box,
Typography,
@@ -11,24 +22,31 @@ import {
Card,
CardContent
} from "@mui/material";
import { Search as SearchIcon, Visibility as VisibilityIcon } from '@mui/icons-material';
import { Search as SearchIcon, Download as DownloadIcon } from '@mui/icons-material';
import { useTranslation } from 'react-i18next';
import { useChunkList } from '@/hooks/chunk-hooks';
import ChunkListResult from './components/ChunkListResult';
import knowledgeService from '@/services/knowledge_service';
import type { IKnowledge, IKnowledgeFile } from '@/interfaces/database/knowledge';
import type { IKnowledge, IKnowledgeFile, IChunk } from '@/interfaces/database/knowledge';
import KnowledgeBreadcrumbs from '@/pages/knowledge/components/KnowledgeBreadcrumbs';
function ChunkParsedResult() {
const { t } = useTranslation();
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const kb_id = searchParams.get('kb_id');
const doc_id = searchParams.get('doc_id');
const [knowledgeBase, setKnowledgeBase] = useState<IKnowledge | null>(null);
const [document, setDocument] = useState<IKnowledgeFile | null>(null);
const [searchKeyword, setSearchKeyword] = useState('');
const [documentFile, setDocumentFile] = useState<Blob | null>(null);
const [fileUrl, setFileUrl] = useState<string>('');
const [fileLoading, setFileLoading] = useState(false);
const [previewOverrideUrl, setPreviewOverrideUrl] = useState<string>('');
const [focusPage, setFocusPage] = useState<number | null>(null);
const abortControllerRef = useRef<AbortController | null>(null);
const pdfContainerRef = useRef<HTMLDivElement | null>(null);
const [pdfRendered, setPdfRendered] = useState<boolean>(false);
// 使用chunk列表hook
const {
@@ -68,6 +86,9 @@ function ChunkParsedResult() {
setDocument(docArr[0]);
}
}
// 自动加载文档文件用于预览
await loadDocumentFile();
} catch (error) {
console.error('Failed to fetch data:', error);
}
@@ -83,11 +104,223 @@ function ChunkParsedResult() {
setCurrentPage(1);
};
// 处理查看文件
const handleViewFile = () => {
if (doc_id && kb_id) {
// 跳转到文件预览页面
navigate(`/chunk/document-preview/${kb_id}/${doc_id}`);
// 异步加载文档文件
const loadDocumentFile = async () => {
if (!doc_id || fileLoading) return;
try {
setFileLoading(true);
// 取消之前的请求
if (abortControllerRef.current) {
// abortControllerRef.current.abort();
}
// 创建新的AbortController
abortControllerRef.current = new AbortController();
const fileResponse = await knowledgeService.getDocumentFile({
doc_id
}, {
signal: abortControllerRef.current.signal
});
if (fileResponse.data instanceof Blob) {
setDocumentFile(fileResponse.data);
const url = URL.createObjectURL(fileResponse.data);
setFileUrl(url);
}
} catch (err: any) {
if (err?.name !== 'AbortError' && err?.name !== 'CanceledError') {
console.error('获取文档文件失败:', err);
}
} finally {
setFileLoading(false);
}
};
// 清理fileUrl
useEffect(() => {
return () => {
if (fileUrl) {
URL.revokeObjectURL(fileUrl);
}
if (abortControllerRef.current) {
abortControllerRef.current.abort();
}
};
}, [fileUrl]);
// 使用 pdf.js 渲染 PDF 到左侧容器,替代 iframe保证滚动与定位可用
useEffect(() => {
const renderPdf = async () => {
if (!documentFile || documentFile.type !== 'application/pdf') {
// 非 PDF 或无文件,不渲染 PDF
setPdfRendered(false);
return;
}
try {
const arrayBuffer = await documentFile.arrayBuffer();
// 使用模块 Worker 的端口,避免设置 workerSrc 导致的路径/MIME 问题
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const task = pdfjsLib.getDocument({ data: arrayBuffer });
const pdfDoc = await task.promise;
const container = pdfContainerRef.current;
if (!container) return;
container.innerHTML = '';
// 根据容器宽度自适应缩放
const containerRect = container.getBoundingClientRect();
const containerWidth = Math.max(1, Math.floor(containerRect.width || container.clientWidth || 800));
for (let pageNum = 1; pageNum <= pdfDoc.numPages; pageNum++) {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const page = await pdfDoc.getPage(pageNum);
const viewport = page.getViewport({ scale: 1 });
const scale = containerWidth / viewport.width;
const scaledViewport = page.getViewport({ scale });
const dpr = Math.max(1, window.devicePixelRatio || 1);
const canvas = window.document.createElement('canvas');
const ctx = canvas.getContext('2d');
canvas.style.display = 'block';
canvas.style.marginBottom = '12px';
canvas.style.width = `${Math.floor(scaledViewport.width)}px`;
canvas.style.height = `${Math.floor(scaledViewport.height)}px`;
canvas.width = Math.floor(scaledViewport.width * dpr);
canvas.height = Math.floor(scaledViewport.height * dpr);
if (ctx) {
// 提升清晰度:将绘制与 CSS 尺寸解耦并使用设备像素比
// @ts-ignore
ctx.imageSmoothingEnabled = true;
// @ts-ignore
ctx.imageSmoothingQuality = 'high';
}
// @ts-ignore
await page.render({
canvasContext: ctx as CanvasRenderingContext2D,
viewport: scaledViewport,
transform: dpr !== 1 ? [dpr, 0, 0, dpr, 0, 0] : undefined,
}).promise;
const pageWrapper = window.document.createElement('div');
pageWrapper.setAttribute('data-page-index', String(pageNum));
pageWrapper.appendChild(canvas);
container.appendChild(pageWrapper);
}
setPdfRendered(true);
} catch (err) {
console.error('渲染 PDF 过程中发生错误: ', err);
setPdfRendered(false);
}
};
renderPdf();
// 清理渲染内容,避免内存泄露
return () => {
const container = pdfContainerRef.current;
if (container) container.innerHTML = '';
};
// 仅在文件或其类型变化时重新渲染
}, [documentFile]);
// 当 focusPage 变化时,滚动到对应页(仅对 PDF 有效)
useEffect(() => {
if (documentFile?.type !== 'application/pdf' || !pdfRendered || !focusPage) return;
const container = pdfContainerRef.current;
if (!container) return;
const children = Array.from(container.children);
const target = children.find((el) => (el as HTMLElement).dataset?.pageIndex === String(focusPage));
if (target) {
(target as HTMLElement).scrollIntoView({ behavior: 'smooth', block: 'start' });
}
}, [focusPage, pdfRendered, documentFile]);
// 下载文件
const handleDownload = () => {
if (fileUrl && window.document) {
const link = window.document.createElement('a');
link.href = fileUrl;
link.download = document?.name || 'document';
window.document.body.appendChild(link);
link.click();
window.document.body.removeChild(link);
}
};
// 处理定位点击某个chunk后联动预览
const handleLocate = (chunk: IChunk) => {
// 使用 positions 的第一个元素的第一个值作为页码(若存在)
const page = Array.isArray(chunk.positions) && chunk.positions.length > 0 && Array.isArray(chunk.positions[0])
? Number(chunk.positions[0][0])
: null;
// 保持左侧预览为文档视图,定位仅改变 PDF 页码,不替换为切片图片
if (documentFile?.type === 'application/pdf') {
setFocusPage(page && !Number.isNaN(page) ? page : null);
setPreviewOverrideUrl('');
return;
}
// 非 PDF 文档暂不处理页码定位,保持可滚动预览
setPreviewOverrideUrl('');
setFocusPage(null);
};
// 渲染左侧预览
const renderPreview = () => {
// 如果有覆盖的图片URL直接显示图片
if (previewOverrideUrl) {
return (
<Box
component="img"
src={previewOverrideUrl}
alt={document?.name || t('chunkPage.documentPreview')}
sx={{ width: '100%', height: '100%', objectFit: 'contain', borderRadius: 1 }}
/>
);
}
if (!documentFile || !fileUrl) return null;
const fileType = documentFile.type;
if (fileType.startsWith('image/')) {
return (
<Box
component="img"
src={fileUrl}
alt={document?.name || t('chunkPage.documentPreview')}
sx={{ width: '100%', height: '100%', objectFit: 'contain', borderRadius: 1 }}
/>
);
} else if (fileType === 'application/pdf') {
// 使用 pdf.js 渲染的容器,支持滚动与页码定位
return (
<Box
ref={pdfContainerRef}
sx={{ width: '100%', height: '100%', overflow: 'auto', borderRadius: 1 }}
/>
);
} else if (fileType.startsWith('text/')) {
return (
<Box
component="iframe"
src={fileUrl}
sx={{ width: '100%', height: '100%', border: '1px solid', borderColor: 'grey.300', borderRadius: 1 }}
/>
);
} else {
return (
<Alert severity="info" sx={{ mt: 2 }}>
{t('chunkPage.fileTypeNotSupportedPreview')}
</Alert>
);
}
};
@@ -121,17 +354,18 @@ function ChunkParsedResult() {
},
]}
/>
{/* 页面标题和文档信息 */}
<Paper sx={{ p: 3, mb: 3 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', mb: 2 }}>
{/* 顶部信息与下载 */}
<Paper sx={{ p: 3, mb: 2 }}>
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start' }}>
<Box>
<Typography variant="h4" gutterBottom>
{document?.name || t('chunkPage.documentDetail')}
</Typography>
<Typography variant="body2" color="text.secondary">
{t('chunkPage.documentChunkResult')}
</Typography>
<Typography variant="body1" color="text.secondary">
{t('chunkPage.viewDocument')} "{document?.name}" {t('chunkPage.allChunkData')}
</Typography>
</Box>
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
<Card>
<CardContent sx={{ textAlign: 'center' }}>
<Typography variant="h4" color="primary">
@@ -142,19 +376,34 @@ function ChunkParsedResult() {
</Typography>
</CardContent>
</Card>
<Button
variant="outlined"
startIcon={<VisibilityIcon />}
onClick={handleViewFile}
sx={{ ml: 2 }}
>
{t('chunkPage.viewFile')}
{fileUrl && (
<Button variant="contained" startIcon={<DownloadIcon />} onClick={handleDownload}>
{t('chunkPage.downloadFile')}
</Button>
)}
</Box>
</Box>
</Paper>
{/* 搜索框 */}
<Paper sx={{ p: 3, mb: 3 }}>
{/* 左右布局:左预览,右切片结果 */}
<Box sx={{ display: 'flex', gap: 2 }}>
{/* 左侧文档预览 */}
<Box sx={{ flex: 1 }}>
<Paper sx={{ p: 2, height: '80vh' }}>
{/* 文件加载状态 */}
{fileLoading && (
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center', minHeight: '200px' }}>
<Alert severity="info">{t('chunkPage.loadingFile')}</Alert>
</Box>
)}
{renderPreview()}
</Paper>
</Box>
{/* 右侧切片结果与搜索 */}
<Box sx={{ flex: 1 }}>
<Paper sx={{ p: 2, height: '80vh', display: 'flex', flexDirection: 'column' }}>
<TextField
fullWidth
placeholder={t('chunkPage.searchChunkPlaceholder')}
@@ -168,9 +417,7 @@ function ChunkParsedResult() {
),
}}
/>
</Paper>
{/* Chunk列表结果 */}
<Box sx={{ flex: 1, overflow: 'auto', pt: 2 }}>
<ChunkListResult
doc_id={doc_id}
chunks={chunks}
@@ -182,8 +429,13 @@ function ChunkParsedResult() {
onPageChange={setCurrentPage}
onRefresh={refresh}
docName={document?.name}
onLocate={handleLocate}
/>
</Box>
</Paper>
</Box>
</Box>
</Box>
);
}

View File

@@ -73,7 +73,7 @@ const Login = () => {
<Card sx={{ width: '100%' }}>
<CardContent sx={{ p: 4 }}>
<Typography variant="subtitle1" color="text.secondary" sx={{ mb: 1, display: 'block' }}>
T-Systems Enterprise RAG Empowerment System
TERES AI
</Typography>
{/* 标签页 */}