@embedpdf-editor/react-chapter-viewer 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +366 -50
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +64 -6
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +636 -503
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,92 +1,408 @@
|
|
|
1
1
|
# @embedpdf-editor/react-chapter-viewer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
面向 React 的**章节 PDF 阅读器**统一入口:多章 PDF 纵向拼接滚动、划词高亮/划线、段落书签、选区笔记,以及可选缩放。
|
|
4
|
+
|
|
5
|
+
业务侧只需安装本包与 `react` / `react-dom`;PDFium(`@embedpdf/engines`)、`scheduler` 及阅读器依赖的 `@embedpdf/*` 插件由本包 **dependencies** 带入,**不必**在业务 `package.json` 里逐个声明 `@embedpdf/engines` 等。
|
|
6
|
+
|
|
7
|
+
> 组件样式以 **inline style** 为主,**不需要** Tailwind。
|
|
8
|
+
> 下文提供可直接复制到业务项目中的完整示例(不依赖仓库内 demo 工程)。
|
|
9
|
+
|
|
10
|
+
---
|
|
4
11
|
|
|
5
12
|
## 安装
|
|
6
13
|
|
|
7
14
|
```bash
|
|
8
|
-
pnpm add @embedpdf-editor/react-chapter-viewer
|
|
15
|
+
pnpm add @embedpdf-editor/react-chapter-viewer
|
|
16
|
+
# 或 npm / yarn
|
|
9
17
|
```
|
|
10
18
|
|
|
11
|
-
|
|
19
|
+
| 依赖 | 说明 |
|
|
20
|
+
|------|------|
|
|
21
|
+
| `react` `react-dom` | **peerDependencies**(建议 React 18+) |
|
|
22
|
+
| 本包 `dependencies` | 已包含 `@embedpdf/engines`、`scheduler`、章节插件与 `editor-engine` |
|
|
12
23
|
|
|
13
|
-
|
|
24
|
+
---
|
|
14
25
|
|
|
15
|
-
|
|
26
|
+
## Vite 项目(推荐)
|
|
16
27
|
|
|
17
|
-
|
|
28
|
+
若 dev 时出现 `scheduler` 解析失败,可合并本包提供的 Vite 片段(将 `scheduler` 指到本包依赖目录并加入预构建):
|
|
18
29
|
|
|
19
30
|
```ts
|
|
31
|
+
// vite.config.ts
|
|
20
32
|
import { defineConfig, mergeConfig } from 'vite';
|
|
33
|
+
import react from '@vitejs/plugin-react';
|
|
21
34
|
import { chapterViewerViteResolve } from '@embedpdf-editor/react-chapter-viewer/vite';
|
|
22
35
|
|
|
23
|
-
export default mergeConfig(
|
|
36
|
+
export default mergeConfig(
|
|
37
|
+
defineConfig({
|
|
38
|
+
plugins: [react()],
|
|
39
|
+
}),
|
|
40
|
+
chapterViewerViteResolve(),
|
|
41
|
+
);
|
|
24
42
|
```
|
|
25
43
|
|
|
26
|
-
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## 你需要准备的数据
|
|
47
|
+
|
|
48
|
+
阅读器**不内置**目录与 PDF 文件,全部由业务提供。
|
|
49
|
+
|
|
50
|
+
### 1. `ChapterManifest`(引擎用)
|
|
51
|
+
|
|
52
|
+
描述每一章对应的 PDF 与在「整本」中的页码区间:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import type { ChapterManifest } from '@embedpdf-editor/react-chapter-viewer';
|
|
56
|
+
|
|
57
|
+
const manifest: ChapterManifest = {
|
|
58
|
+
chapters: [
|
|
59
|
+
{
|
|
60
|
+
chapterId: '001_封面', // 唯一 ID,同时作为 documentId
|
|
61
|
+
title: '封面',
|
|
62
|
+
globalPageRange: [1, 1], // 在整本中的全局页(闭区间)
|
|
63
|
+
localPageRange: [0, 0], // 该 PDF 内 0-based 页(页数须与 global 一致)
|
|
64
|
+
source: { url: '/001_封面.pdf' }, // 或 buffer / load()
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
chapterId: '002_前言',
|
|
68
|
+
title: '前言',
|
|
69
|
+
globalPageRange: [2, 5],
|
|
70
|
+
localPageRange: [0, 3],
|
|
71
|
+
source: { url: '/002_前言.pdf' },
|
|
72
|
+
},
|
|
73
|
+
],
|
|
74
|
+
};
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**`ChapterSource` 三种写法:**
|
|
78
|
+
|
|
79
|
+
| 写法 | 场景 |
|
|
80
|
+
|------|------|
|
|
81
|
+
| `{ url: string }` | 静态或可直链的 PDF |
|
|
82
|
+
| `{ buffer: ArrayBuffer }` | 已下载的二进制 |
|
|
83
|
+
| `{ load: () => Promise<{ url } \| { buffer }> }` | 单章自定义拉取(鉴权等) |
|
|
84
|
+
|
|
85
|
+
若 manifest 里**未写** `source`,需传入全局加载器 `chapterPdfLoader`(实现 `IChapterPdfLoader.loadPdf(chapter)`)。
|
|
86
|
+
|
|
87
|
+
相邻章节的 `globalPageRange` **允许重叠**(例如上下章各含一页过渡页);默认按 `first-wins` 解析归属。
|
|
88
|
+
|
|
89
|
+
### 2. `ChapterViewerCatalog`(带目录树时)
|
|
90
|
+
|
|
91
|
+
左侧章节目录由业务构造,与 manifest 对应:
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import type { ChapterViewerCatalog } from '@embedpdf-editor/react-chapter-viewer';
|
|
95
|
+
|
|
96
|
+
const catalog: ChapterViewerCatalog = {
|
|
97
|
+
tree: [
|
|
98
|
+
{ id: '001_封面', title: '封面', startPage: 1, endPage: 1 },
|
|
99
|
+
{
|
|
100
|
+
id: '002_前言',
|
|
101
|
+
title: '前言',
|
|
102
|
+
startPage: 2,
|
|
103
|
+
endPage: 5,
|
|
104
|
+
children: [/* 可选子节点 */],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
manifest,
|
|
108
|
+
};
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
`tree` 仅用于 UI;真正加载 PDF 以 `manifest.chapters` 为准。
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 快速开始:`<ChapterPdfViewer />`
|
|
116
|
+
|
|
117
|
+
适合「单容器铺满、manifest 已就绪」的页面。推荐只传 **`options`**(不必再拆 `editorOptions` + `features`,也不必包一层 `callbacks`)。
|
|
27
118
|
|
|
28
119
|
```tsx
|
|
29
120
|
import {
|
|
30
121
|
usePdfiumEngine,
|
|
31
122
|
ChapterPdfViewer,
|
|
32
|
-
type
|
|
123
|
+
type ChapterManifest,
|
|
33
124
|
} from '@embedpdf-editor/react-chapter-viewer';
|
|
34
125
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
enabled: true,
|
|
38
|
-
styles: {
|
|
39
|
-
underline: { color: '#dc2626', offsetY: 3, thickness: 1.5 },
|
|
40
|
-
},
|
|
41
|
-
},
|
|
42
|
-
zoom: { enabled: false },
|
|
43
|
-
selectionToolbar: {
|
|
44
|
-
extraActions: [{ id: 'translate', label: '翻译', order: 10 }],
|
|
45
|
-
},
|
|
46
|
-
};
|
|
126
|
+
export function Reader({ manifest }: { manifest: ChapterManifest }) {
|
|
127
|
+
const { engine, isLoading, error } = usePdfiumEngine();
|
|
47
128
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
if (!engine) return null;
|
|
129
|
+
if (error) return <div>引擎失败:{error.message}</div>;
|
|
130
|
+
if (isLoading || !engine) return <div>正在加载 PDFium…</div>;
|
|
51
131
|
|
|
52
132
|
return (
|
|
53
|
-
<
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
133
|
+
<div style={{ height: '100vh' }}>
|
|
134
|
+
<ChapterPdfViewer
|
|
135
|
+
engine={engine}
|
|
136
|
+
options={{
|
|
137
|
+
manifest,
|
|
138
|
+
bookmarks: {
|
|
139
|
+
load: () => fetchBookmarks(),
|
|
140
|
+
persist: (list) => saveBookmarks(list),
|
|
141
|
+
onRequestRemove: async (b) => {
|
|
142
|
+
await api.deleteBookmark(b.id);
|
|
143
|
+
return true;
|
|
144
|
+
},
|
|
145
|
+
},
|
|
146
|
+
notes: {
|
|
147
|
+
loadNotes: () => fetchNotes(),
|
|
60
148
|
onRequestCreateNote: ({ draft, complete }) => {
|
|
61
|
-
|
|
149
|
+
openCreateModal(draft).then((noteId) => complete(noteId));
|
|
62
150
|
},
|
|
63
|
-
onRequestEditNote: (noteId) =>
|
|
64
|
-
loadNotes: () => api.loadNotes(),
|
|
151
|
+
onRequestEditNote: (noteId) => openEditModal(noteId),
|
|
65
152
|
onDeleteNote: (id) => api.deleteNote(id),
|
|
66
153
|
},
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}}
|
|
74
|
-
/>
|
|
154
|
+
features: {
|
|
155
|
+
zoom: { pageWidth: 800 },
|
|
156
|
+
},
|
|
157
|
+
}}
|
|
158
|
+
/>
|
|
159
|
+
</div>
|
|
75
160
|
);
|
|
76
161
|
}
|
|
77
162
|
```
|
|
78
163
|
|
|
79
|
-
|
|
164
|
+
### `features` 简写
|
|
80
165
|
|
|
81
|
-
|
|
82
|
-
|
|
166
|
+
| 写法 | 含义 |
|
|
167
|
+
|------|------|
|
|
168
|
+
| 省略 | 划线、书签、笔记、选区浮窗、缩放 **全部开启** |
|
|
169
|
+
| `markup: false` | 关闭划词高亮/划线 |
|
|
170
|
+
| `zoom: false` | 关闭缩放 |
|
|
171
|
+
| `zoom: { pageWidth: 800 }` | 按宽度适配 |
|
|
83
172
|
|
|
84
|
-
|
|
173
|
+
**交互说明(默认开启时):**
|
|
85
174
|
|
|
86
|
-
|
|
87
|
-
|
|
175
|
+
- 划词 → 浮窗:高亮、下划线、波浪线、删除线、笔记
|
|
176
|
+
- 鼠标移到文本行末 → 显示「添加书签」;已加书签点击 → 删除确认(走 `onRequestRemove`)
|
|
177
|
+
- 笔记区域悬停 → 编辑 / 删除
|
|
178
|
+
- **缩放**:`features.zoom.enabled !== false` 时,在 PDF 滚动区域内 **`Ctrl/Cmd + 滚轮`** 或 **双指捏合**(触控板)缩放;缩放写入 core 的 `document.scale`,PDF 与书签/笔记 overlay 同步变化(不是只放大外层 div)
|
|
179
|
+
|
|
180
|
+
> 普通滚轮用于上下滚动章节,不会触发缩放;Mac 触控板请按住 Ctrl 或双指捏合。
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## 进阶:自定义布局(目录 + 首章预加载)
|
|
185
|
+
|
|
186
|
+
需要**左侧章节目录**、或要在挂载视口前**先 `ensureChapterLoaded`** 时,不要用一站式 `<ChapterPdfViewer />`,改用:
|
|
187
|
+
|
|
188
|
+
`createChapterViewerBundle` → `EmbedPDF` → `ChapterTreePanel` + `PdfChapterViewport`
|
|
189
|
+
|
|
190
|
+
**步骤简述:**
|
|
191
|
+
|
|
192
|
+
1. 用 `createChapterViewerBundle(options)` 得到 `{ plugins, features }`(`options` 字段与上一节 `ChapterPdfViewer` 的 `options` 相同)。
|
|
193
|
+
2. 用 `EmbedPDF` 挂载 `plugins`,在 `pluginsReady` 后再渲染子树。
|
|
194
|
+
3. 在 `EmbedPDF` 子树内用 `useCapability(ChapterManagerPlugin.id)` 对**首章**(或当前章)调用 `ensureChapterLoaded`。
|
|
195
|
+
4. 章节状态为 `loaded` 后再挂载 `PdfChapterViewport`,并传入 **`bundle.features`**(不要另写一套 `features`)。
|
|
196
|
+
|
|
197
|
+
**完整示例(可复制):**
|
|
198
|
+
|
|
199
|
+
```tsx
|
|
200
|
+
import { useEffect, useMemo, useState } from 'react';
|
|
201
|
+
import {
|
|
202
|
+
usePdfiumEngine,
|
|
203
|
+
EmbedPDF,
|
|
204
|
+
useCapability,
|
|
205
|
+
ChapterManagerPlugin,
|
|
206
|
+
createChapterViewerBundle,
|
|
207
|
+
ChapterTreePanel,
|
|
208
|
+
PdfChapterViewport,
|
|
209
|
+
type ChapterViewerCatalog,
|
|
210
|
+
type ChapterViewerOptions,
|
|
211
|
+
} from '@embedpdf-editor/react-chapter-viewer';
|
|
212
|
+
|
|
213
|
+
type ReaderProps = {
|
|
214
|
+
catalog: ChapterViewerCatalog;
|
|
215
|
+
notes: ChapterViewerOptions['notes'];
|
|
216
|
+
bookmarks: ChapterViewerOptions['bookmarks'];
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
export function ChapterReaderWithSidebar({ catalog, notes, bookmarks }: ReaderProps) {
|
|
220
|
+
const { engine, isLoading, error } = usePdfiumEngine();
|
|
221
|
+
const bundle = useMemo(
|
|
222
|
+
() =>
|
|
223
|
+
createChapterViewerBundle({
|
|
224
|
+
manifest: catalog.manifest,
|
|
225
|
+
notes,
|
|
226
|
+
bookmarks,
|
|
227
|
+
features: { zoom: { pageWidth: 800 } },
|
|
228
|
+
}),
|
|
229
|
+
[catalog.manifest, notes, bookmarks],
|
|
230
|
+
);
|
|
231
|
+
const firstChapterId = catalog.manifest.chapters[0]?.chapterId ?? '';
|
|
232
|
+
|
|
233
|
+
if (error) return <div>引擎失败:{error.message}</div>;
|
|
234
|
+
if (isLoading || !engine) return <div>正在加载 PDFium…</div>;
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<div style={{ height: '100vh', display: 'flex', flexDirection: 'column' }}>
|
|
238
|
+
<EmbedPDF engine={engine} plugins={bundle.plugins}>
|
|
239
|
+
{({ pluginsReady }) =>
|
|
240
|
+
pluginsReady ? (
|
|
241
|
+
<ChapterWorkspace
|
|
242
|
+
tree={catalog.tree}
|
|
243
|
+
firstChapterId={firstChapterId}
|
|
244
|
+
features={bundle.features}
|
|
245
|
+
/>
|
|
246
|
+
) : (
|
|
247
|
+
<div>正在初始化插件…</div>
|
|
248
|
+
)
|
|
249
|
+
}
|
|
250
|
+
</EmbedPDF>
|
|
251
|
+
</div>
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function ChapterWorkspace({
|
|
256
|
+
tree,
|
|
257
|
+
firstChapterId,
|
|
258
|
+
features,
|
|
259
|
+
}: {
|
|
260
|
+
tree: ChapterViewerCatalog['tree'];
|
|
261
|
+
firstChapterId: string;
|
|
262
|
+
features: ReturnType<typeof createChapterViewerBundle>['features'];
|
|
263
|
+
}) {
|
|
264
|
+
const [activeChapterId, setActiveChapterId] = useState(firstChapterId);
|
|
265
|
+
const [chapterReady, setChapterReady] = useState(false);
|
|
266
|
+
const { provides: chapterManager } = useCapability<ChapterManagerPlugin>(
|
|
267
|
+
ChapterManagerPlugin.id,
|
|
268
|
+
);
|
|
269
|
+
|
|
270
|
+
useEffect(() => {
|
|
271
|
+
if (!chapterManager || !firstChapterId) return;
|
|
272
|
+
|
|
273
|
+
let cancelled = false;
|
|
274
|
+
setChapterReady(false);
|
|
275
|
+
|
|
276
|
+
const unsub = chapterManager.onChapterStatusChange(() => {
|
|
277
|
+
if (cancelled) return;
|
|
278
|
+
if (chapterManager.getChapterStatus(firstChapterId) === 'loaded') {
|
|
279
|
+
setChapterReady(true);
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
void chapterManager.ensureChapterLoaded(firstChapterId).then((status) => {
|
|
284
|
+
if (!cancelled && status === 'loaded') setChapterReady(true);
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
return () => {
|
|
288
|
+
cancelled = true;
|
|
289
|
+
unsub();
|
|
290
|
+
};
|
|
291
|
+
}, [chapterManager, firstChapterId]);
|
|
292
|
+
|
|
293
|
+
return (
|
|
294
|
+
<div style={{ display: 'flex', flex: 1, minHeight: 0, gap: 12 }}>
|
|
295
|
+
<ChapterTreePanel
|
|
296
|
+
tree={tree}
|
|
297
|
+
activeChapterId={activeChapterId}
|
|
298
|
+
onActiveChapterChange={setActiveChapterId}
|
|
299
|
+
/>
|
|
300
|
+
<div style={{ flex: 1, minWidth: 0, minHeight: 0, position: 'relative' }}>
|
|
301
|
+
{chapterReady ? (
|
|
302
|
+
<div style={{ position: 'absolute', inset: 0 }}>
|
|
303
|
+
<PdfChapterViewport features={features} />
|
|
304
|
+
</div>
|
|
305
|
+
) : (
|
|
306
|
+
<div>正在加载章节 PDF…</div>
|
|
307
|
+
)}
|
|
308
|
+
</div>
|
|
309
|
+
</div>
|
|
310
|
+
);
|
|
311
|
+
}
|
|
88
312
|
```
|
|
89
313
|
|
|
90
|
-
|
|
314
|
+
切换目录项时,可对新的 `activeChapterId` 同样调用 `ensureChapterLoaded`,再在 `loaded` 后更新视口(`ChapterTreePanel` 内部也会触发章节加载,按产品需求二选一或组合使用即可)。
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## 笔记与书签回调
|
|
319
|
+
|
|
320
|
+
直接写在 `options.notes` / `options.bookmarks`(不再嵌套 `callbacks`)。
|
|
321
|
+
|
|
322
|
+
### 笔记 `options.notes`
|
|
323
|
+
|
|
324
|
+
| 回调 | 说明 |
|
|
325
|
+
|------|------|
|
|
326
|
+
| `loadNotes` | 初始化加载已有笔记 |
|
|
327
|
+
| `onRequestCreateNote` | **推荐**:宿主弹窗后 `complete(noteId)` |
|
|
328
|
+
| `onCreateNote` | 内置创建(与上一项二选一) |
|
|
329
|
+
| `onRequestEditNote` / `onUpdateNote` / `onDeleteNote` | 编辑、更新、删除 |
|
|
330
|
+
|
|
331
|
+
### 书签 `options.bookmarks`
|
|
332
|
+
|
|
333
|
+
| 回调 | 说明 |
|
|
334
|
+
|------|------|
|
|
335
|
+
| `load` | 加载已有书签 |
|
|
336
|
+
| `persist` | 增删改后持久化 |
|
|
337
|
+
| `onRequestRemove` | 删除确认,返回 `true` 后从渲染层移除 |
|
|
338
|
+
|
|
339
|
+
---
|
|
340
|
+
|
|
341
|
+
## 内部默认(无需配置)
|
|
342
|
+
|
|
343
|
+
以下由引擎内置,业务**不用**再写:`overlapStrategy`、`passwordProvider`、`prefetchChapters`、`loadDefaultStampLibrary`、`toolbar`(Annotate 模式栏已关闭)、`placeholderPageWidth` 等。需要改时再使用进阶 API `createPdfChapterEditor`。
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
## 主要导出
|
|
348
|
+
|
|
349
|
+
| 导出 | 用途 |
|
|
350
|
+
|------|------|
|
|
351
|
+
| `ChapterPdfViewer` | 一站式阅读器(传 `options`) |
|
|
352
|
+
| `createChapterViewerBundle` | 生成 `{ plugins, features }`(自定义布局) |
|
|
353
|
+
| `PdfChapterViewport` | 仅视口(需在 `EmbedPDF` 内) |
|
|
354
|
+
| `ChapterTreePanel` | 章节目录树 |
|
|
355
|
+
| `usePdfiumEngine` | PDFium 引擎 |
|
|
356
|
+
| `ChapterViewerOptions` | `options` 的类型 |
|
|
357
|
+
| `ChapterViewerConfig` | `options.features` 的类型 |
|
|
358
|
+
| `ChapterManifest` / `ChapterDescriptor` / `IChapterPdfLoader` | 数据与加载契约 |
|
|
359
|
+
| `ChapterViewerCatalog` / `ChapterTreeNode` | 目录树类型 |
|
|
360
|
+
| `applySelectionMarkup` | 编程式应用划线(高级) |
|
|
361
|
+
| `chapterViewerViteResolve` | Vite 配置辅助(`/vite` 子路径) |
|
|
362
|
+
|
|
363
|
+
类型详见 `dist/index.d.ts`。
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
367
|
+
## 接入检查清单
|
|
368
|
+
|
|
369
|
+
在业务项目中接入时,请确认:
|
|
370
|
+
|
|
371
|
+
1. **静态资源**:`manifest` 里每章 `source.url` 可被浏览器访问(或实现 `chapterPdfLoader` / `load()`)。
|
|
372
|
+
2. **目录数据**:若使用 `ChapterTreePanel`,自行从后端组装 `ChapterViewerCatalog`(`tree` + `manifest` 字段一致)。
|
|
373
|
+
3. **首章加载**:自定义布局下,首章 `ensureChapterLoaded` 返回 `loaded` 后再挂载 `PdfChapterViewport`。
|
|
374
|
+
4. **笔记 UI**:`onRequestCreateNote` 需在宿主弹窗后调用 `complete(noteId)`。
|
|
375
|
+
|
|
376
|
+
本包随 npm 发布,**不包含**可运行的示例站点或 mock 数据;请以上文代码片段为模板,在业务仓库中创建页面并放置自有 PDF。
|
|
377
|
+
|
|
378
|
+
---
|
|
379
|
+
|
|
380
|
+
## 其他框架
|
|
381
|
+
|
|
382
|
+
| 包 | 说明 |
|
|
383
|
+
|----|------|
|
|
384
|
+
| `@embedpdf-editor/vue3-chapter-viewer` | Vue 3 组件封装 |
|
|
385
|
+
| `@embedpdf-editor/vue2-chapter-viewer` | Vue 2.6+(或 `@embedpdf-editor/chapter-snippet` Web Component) |
|
|
386
|
+
|
|
387
|
+
Vue 3 包同样只需安装对应包 + `vue`;视口内核与 React 版能力对齐。
|
|
388
|
+
|
|
389
|
+
---
|
|
390
|
+
|
|
391
|
+
## 常见问题
|
|
392
|
+
|
|
393
|
+
**Q:页面一直「正在加载」?**
|
|
394
|
+
确认 `manifest` 中每章 `source.url` 可访问,或 `chapterPdfLoader` / `ensureChapterLoaded` 返回 `loaded`。首章未加载完成时不要提前挂载 `PdfChapterViewport`。
|
|
395
|
+
|
|
396
|
+
**Q:需要用户安装 `@embedpdf/engines` 吗?**
|
|
397
|
+
不需要,已作为本包依赖。只需 `react` / `react-dom`。
|
|
398
|
+
|
|
399
|
+
**Q:如何关闭缩放?**
|
|
400
|
+
`features={{ ...DEFAULT_CHAPTER_VIEWER_FEATURES, zoom: { enabled: false } }}`。
|
|
401
|
+
|
|
402
|
+
**Q:`zoom.enabled: true` 但缩放手势没反应?**
|
|
403
|
+
1. 确认使用 `<ChapterPdfViewer />` 或 `<PdfChapterViewport features={...} />`(缩放逻辑在 `PdfChapterViewport` 内)。
|
|
404
|
+
2. 在 PDF 区域使用 **Ctrl/Cmd + 滚轮**,不要用普通滚轮。
|
|
405
|
+
3. 重新构建 `@embedpdf-editor/editor-engine` 与 `@embedpdf-editor/react-chapter-viewer` 后硬刷新浏览器。
|
|
91
406
|
|
|
92
|
-
|
|
407
|
+
**Q:笔记弹窗想完全自定义?**
|
|
408
|
+
使用 `onRequestCreateNote` + `complete(noteId)`,不要实现 `onCreateNote`。
|