116 lines
3.9 KiB
Markdown
116 lines
3.9 KiB
Markdown
根据你的需求(PDF 内容精确引用到思维导图节点,且支持双向跳转),结合项目技术栈(Next.js + TypeScript),推荐以下 PDF 组件方案:
|
||||
|
|
|
|||
|
|
### 推荐组件:`@react-pdf-viewer/core`(搭配 `@react-pdf-viewer/selection-mode` 插件)
|
|||
|
|
|
|||
|
|
#### 核心优势:
|
|||
|
|
1. **精确内容选取**:支持页面级、文本片段级选择,可获取选中内容的页码、坐标、文本内容
|
|||
|
|
2. **双向链接能力**:可生成带定位信息的锚点(如 `#page=3&selection=xxx`),实现思维导图点击跳转
|
|||
|
|
3. **React 生态适配**:完美兼容 Next.js 15 和 TypeScript,支持 SSR/SSG
|
|||
|
|
4. **轻量可扩展**:核心包体积小,按需加载插件(如文本选择、搜索、缩放)
|
|||
|
|
|
|||
|
|
#### 实现思路:
|
|||
|
|
|
|||
|
|
1. **PDF 查看器集成**
|
|||
|
|
```tsx
|
|||
|
|
// components/PDFViewer.tsx
|
|||
|
|
import { Viewer, Worker } from '@react-pdf-viewer/core';
|
|||
|
|
import { selectionModePlugin } from '@react-pdf-viewer/selection-mode';
|
|||
|
|
import '@react-pdf-viewer/core/lib/styles/index.css';
|
|||
|
|
import '@react-pdf-viewer/selection-mode/lib/styles/index.css';
|
|||
|
|
|
|||
|
|
const selectionMode = selectionModePlugin();
|
|||
|
|
const { SelectionMode } = selectionMode;
|
|||
|
|
|
|||
|
|
interface PDFViewerProps {
|
|||
|
|
fileUrl: string;
|
|||
|
|
onSelect: (data: {
|
|||
|
|
text: string;
|
|||
|
|
pageNumber: number;
|
|||
|
|
position: { x: number; y: number };
|
|||
|
|
}) => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export default function PDFViewer({ fileUrl, onSelect }: PDFViewerProps) {
|
|||
|
|
return (
|
|||
|
|
<Worker workerUrl="https://unpkg.com/pdfjs-dist@3.4.120/build/pdf.worker.min.js">
|
|||
|
|
<SelectionMode
|
|||
|
|
onSelect={(args) => {
|
|||
|
|
if (args.selectionText) {
|
|||
|
|
onSelect({
|
|||
|
|
text: args.selectionText,
|
|||
|
|
pageNumber: args.pageIndex + 1, // 页码从1开始
|
|||
|
|
position: args.selectionRects[0]?.position || { x: 0, y: 0 },
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}}
|
|||
|
|
>
|
|||
|
|
<Viewer fileUrl={fileUrl} plugins={[selectionMode]} />
|
|||
|
|
</SelectionMode>
|
|||
|
|
</Worker>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **思维导图节点与 PDF 引用绑定**
|
|||
|
|
```tsx
|
|||
|
|
// 思维导图节点数据结构扩展
|
|||
|
|
interface MindMapNode {
|
|||
|
|
id: string;
|
|||
|
|
text: string;
|
|||
|
|
pdfReference?: {
|
|||
|
|
fileId: string; // 关联的PDF文件ID
|
|||
|
|
pageNumber: number;
|
|||
|
|
text: string; // 引用的文本内容
|
|||
|
|
position?: { x: number; y: number }; // 可选:更精确的定位
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 点击节点跳转PDF
|
|||
|
|
const handleNodeClick = (node: MindMapNode) => {
|
|||
|
|
if (node.pdfReference) {
|
|||
|
|
// 导航到PDF查看页并定位
|
|||
|
|
router.push(`/documents/${node.pdfReference.fileId}?page=${node.pdfReference.pageNumber}`);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
3. **PDF 选择内容插入思维导图**
|
|||
|
|
```tsx
|
|||
|
|
// 在PDF查看页中使用
|
|||
|
|
const PDFDocumentPage = ({ params }) => {
|
|||
|
|
const [mindMapNodes, setMindMapNodes] = useState<MindMapNode[]>([]);
|
|||
|
|
|
|||
|
|
const handlePDFSelect = (data) => {
|
|||
|
|
// 创建新节点并关联PDF引用
|
|||
|
|
const newNode: MindMapNode = {
|
|||
|
|
id: uuidv4(),
|
|||
|
|
text: data.text.substring(0, 50) + '...', // 截断显示
|
|||
|
|
pdfReference: {
|
|||
|
|
fileId: params.id,
|
|||
|
|
pageNumber: data.pageNumber,
|
|||
|
|
text: data.text,
|
|||
|
|
position: data.position,
|
|||
|
|
},
|
|||
|
|
};
|
|||
|
|
setMindMapNodes([...mindMapNodes, newNode]);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
return <PDFViewer fileUrl={pdfUrl} onSelect={handlePDFSelect} />;
|
|||
|
|
};
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
4. **双向跳转优化**
|
|||
|
|
- 在 PDF 查看页加载时,通过 URL 参数自动定位到指定页:
|
|||
|
|
```tsx
|
|||
|
|
const { page } = useSearchParams();
|
|||
|
|
const defaultPage = page ? parseInt(page.toString()) : 1;
|
|||
|
|
// 传递defaultPage给Viewer组件
|
|||
|
|
```
|
|||
|
|
- 思维导图节点显示引用标识(如 📄3 表示引用第3页),增强可视化关联
|
|||
|
|
|
|||
|
|
### 备选方案:
|
|||
|
|
如果需要更强大的 PDF 处理能力(如 MinerU 提取后的结构化内容引用),可结合:
|
|||
|
|
- **后端**:MinerU 提取 PDF 时记录「文本块-页码」映射关系
|
|||
|
|
- **前端**:通过 `@react-pdf-viewer` 定位到 MinerU 识别的文本块位置
|
|||
|
|
|
|||
|
|
该方案完全符合项目前后端分离架构,且能无缝集成到现有的思维导图(simple-mind-map)和文档系统中。
|