@embedpdf-editor/vue3-chapter-viewer 0.3.0 → 0.3.1
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 +212 -215
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +70 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +967 -937
- package/dist/index.js.map +1 -1
- package/package.json +6 -6
- package/src/vite.ts +2 -2
package/README.md
CHANGED
|
@@ -1,299 +1,296 @@
|
|
|
1
1
|
# @embedpdf-editor/vue3-chapter-viewer
|
|
2
2
|
|
|
3
|
-
Vue 3
|
|
3
|
+
Vue 3 版章节 PDF 阅读器。它把多份章节 PDF 组织成一本连续滚动的书,并内置划线/高亮、附注、段落书签、章节目录和标注导入导出能力。
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
底层使用 `@embedpdf/core` 插件体系和 PDFium 引擎;Vue 3 包只负责提供 SFC 组件、composition hooks 和类型导出。
|
|
6
6
|
|
|
7
7
|
## 安装
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
pnpm add @embedpdf-editor/vue3-chapter-viewer
|
|
10
|
+
pnpm add @embedpdf-editor/vue3-chapter-viewer
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
## Vite
|
|
13
|
+
## Vite
|
|
14
|
+
|
|
15
|
+
包内导出的是一段 Vite 配置,不是 Vite plugin。它用于补齐 `scheduler` alias 和 PDFium 引擎的预构建配置;`@embedpdf/engines` 已经是本包依赖,业务项目不需要单独安装。
|
|
14
16
|
|
|
15
17
|
```ts
|
|
16
18
|
// vite.config.ts
|
|
17
|
-
import { defineConfig } from 'vite';
|
|
19
|
+
import { defineConfig, mergeConfig } from 'vite';
|
|
18
20
|
import vue from '@vitejs/plugin-vue';
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
export default
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
import { chapterViewerViteResolve } from '@embedpdf-editor/vue3-chapter-viewer/vite';
|
|
22
|
+
|
|
23
|
+
export default mergeConfig(
|
|
24
|
+
defineConfig({
|
|
25
|
+
plugins: [vue()],
|
|
26
|
+
}),
|
|
27
|
+
chapterViewerViteResolve(),
|
|
28
|
+
);
|
|
24
29
|
```
|
|
25
30
|
|
|
26
|
-
|
|
31
|
+
如果不用 `mergeConfig`,需要手动合并 `resolve.alias` 和 `optimizeDeps.include`,不要直接覆盖现有配置。
|
|
27
32
|
|
|
28
|
-
##
|
|
33
|
+
## 快速开始
|
|
34
|
+
|
|
35
|
+
推荐只传 `options`。`editorOptions` 和 `features` 仍兼容,但已作为低层/旧写法保留。
|
|
29
36
|
|
|
30
37
|
```vue
|
|
31
38
|
<script setup lang="ts">
|
|
32
|
-
import { ref } from 'vue';
|
|
33
39
|
import {
|
|
34
40
|
ChapterPdfViewer,
|
|
35
41
|
usePdfiumEngine,
|
|
36
|
-
|
|
37
|
-
DEFAULT_CHAPTER_VIEWER_FEATURES,
|
|
38
|
-
CallbackPasswordProvider,
|
|
42
|
+
type ChapterViewerOptions,
|
|
39
43
|
} from '@embedpdf-editor/vue3-chapter-viewer';
|
|
40
|
-
import type { ChapterManifest, NoteAnchor } from '@embedpdf-editor/vue3-chapter-viewer';
|
|
41
|
-
|
|
42
|
-
const { engine, isLoading } = usePdfiumEngine({ wasmUrl: '/pdfium.wasm' });
|
|
43
|
-
|
|
44
|
-
const manifest: ChapterManifest = {
|
|
45
|
-
chapters: [
|
|
46
|
-
{
|
|
47
|
-
chapterId: 'ch-1',
|
|
48
|
-
title: '第一章',
|
|
49
|
-
globalPageRange: [1, 10],
|
|
50
|
-
localPageRange: [0, 9],
|
|
51
|
-
source: { url: '/pdfs/ch1.pdf' },
|
|
52
|
-
encrypted: true, // 可选:业务标记该章为加密 PDF
|
|
53
|
-
},
|
|
54
|
-
],
|
|
55
|
-
};
|
|
56
44
|
|
|
57
|
-
const
|
|
58
|
-
|
|
45
|
+
const { engine, isLoading, error } = usePdfiumEngine();
|
|
46
|
+
|
|
47
|
+
const options: ChapterViewerOptions = {
|
|
48
|
+
manifest: {
|
|
49
|
+
chapters: [
|
|
50
|
+
{
|
|
51
|
+
chapterId: 'ch-1',
|
|
52
|
+
title: '第一章',
|
|
53
|
+
globalPageRange: [1, 12],
|
|
54
|
+
localPageRange: [0, 11],
|
|
55
|
+
source: { url: '/pdfs/chapter-1.pdf' },
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
},
|
|
59
59
|
notes: {
|
|
60
|
-
loadNotes: async () => []
|
|
61
|
-
onCreateNote: async (
|
|
60
|
+
loadNotes: async () => [],
|
|
61
|
+
onCreateNote: async () => ({ noteId: crypto.randomUUID() }),
|
|
62
|
+
onUpdateNote: async () => {},
|
|
63
|
+
onDeleteNote: async () => {},
|
|
62
64
|
},
|
|
63
65
|
bookmarks: {
|
|
64
66
|
load: async () => [],
|
|
65
67
|
persist: async () => {},
|
|
68
|
+
onRequestRemove: async () => true,
|
|
66
69
|
},
|
|
67
|
-
features:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
features: {
|
|
71
|
+
markup: true,
|
|
72
|
+
bookmarks: true,
|
|
73
|
+
notes: true,
|
|
74
|
+
selectionToolbar: true,
|
|
75
|
+
zoom: { pageWidth: 720 },
|
|
76
|
+
},
|
|
77
|
+
};
|
|
71
78
|
</script>
|
|
72
79
|
|
|
73
80
|
<template>
|
|
74
|
-
<
|
|
81
|
+
<div class="reader">
|
|
82
|
+
<div v-if="error">PDF engine failed: {{ error.message }}</div>
|
|
83
|
+
<div v-else-if="isLoading || !engine">Loading PDF engine...</div>
|
|
84
|
+
<ChapterPdfViewer
|
|
85
|
+
v-else
|
|
86
|
+
:engine="engine"
|
|
87
|
+
:options="options"
|
|
88
|
+
class-name="reader-fill"
|
|
89
|
+
viewport-class-name="reader-viewport"
|
|
90
|
+
/>
|
|
91
|
+
</div>
|
|
75
92
|
</template>
|
|
93
|
+
|
|
94
|
+
<style scoped>
|
|
95
|
+
.reader {
|
|
96
|
+
height: 100vh;
|
|
97
|
+
min-height: 0;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
.reader-fill {
|
|
101
|
+
height: 100%;
|
|
102
|
+
min-height: 0;
|
|
103
|
+
}
|
|
104
|
+
</style>
|
|
76
105
|
```
|
|
77
106
|
|
|
78
|
-
|
|
107
|
+
## `ChapterViewerOptions`
|
|
79
108
|
|
|
80
|
-
|
|
109
|
+
`options` 是三端渲染器的统一配置入口。
|
|
81
110
|
|
|
82
111
|
| 字段 | 说明 |
|
|
83
|
-
|
|
84
|
-
| `
|
|
85
|
-
| `
|
|
86
|
-
| `
|
|
87
|
-
| `
|
|
88
|
-
| `
|
|
89
|
-
| `
|
|
90
|
-
|
|
112
|
+
| --- | --- |
|
|
113
|
+
| `manifest` | 章节清单,描述每章 PDF 和全局页码关系 |
|
|
114
|
+
| `notes` | 附注加载、创建、更新、删除回调 |
|
|
115
|
+
| `bookmarks` | 段落书签加载、持久化、删除确认回调 |
|
|
116
|
+
| `chapterPdfLoader` | 可选:全局 PDF 预处理/拉取(见下文) |
|
|
117
|
+
| `overlapStrategy` | 可选:同一全局页多章节重叠时,用先/后出现的章节 PDF 渲染 |
|
|
118
|
+
| `features` | 可选:功能开关与样式配置,省略时使用默认能力 |
|
|
119
|
+
|
|
120
|
+
### Manifest
|
|
91
121
|
|
|
92
|
-
|
|
122
|
+
```ts
|
|
123
|
+
type ChapterManifest = {
|
|
124
|
+
chapters: Array<{
|
|
125
|
+
chapterId: string;
|
|
126
|
+
title: string;
|
|
127
|
+
globalPageRange: [number, number];
|
|
128
|
+
localPageRange: [number, number];
|
|
129
|
+
source?: { url: string } | { buffer: ArrayBuffer } | { load: () => Promise<{ url: string } | { buffer: ArrayBuffer }> };
|
|
130
|
+
encrypted?: boolean;
|
|
131
|
+
ownedGlobalPages?: number[];
|
|
132
|
+
}>;
|
|
133
|
+
totalGlobalPages?: number;
|
|
134
|
+
};
|
|
135
|
+
```
|
|
93
136
|
|
|
94
|
-
|
|
137
|
+
`globalPageRange` 和 `localPageRange` 都是闭区间,且页数必须一致。`chapterId` 同时作为内部 `documentId`,需要在整本书内唯一。
|
|
95
138
|
|
|
96
|
-
###
|
|
139
|
+
### 嵌套目录树与重叠页
|
|
97
140
|
|
|
98
|
-
`
|
|
141
|
+
侧栏目录支持**任意层级**(`ChapterTreePanel` + `ChapterTreeNode.children`)。引擎滚动用的 `manifest.chapters` 是**扁平列表**:父节点页范围可包含子节点,相邻/重叠全局页由 **owner 策略** 决定只渲染一个章节的 PDF,避免同页画两遍。
|
|
99
142
|
|
|
100
143
|
```ts
|
|
101
144
|
import {
|
|
102
|
-
|
|
103
|
-
|
|
145
|
+
buildChapterViewerCatalog,
|
|
146
|
+
overlapStrategyForSamePageOwner,
|
|
147
|
+
type ChapterViewerOptions,
|
|
104
148
|
} from '@embedpdf-editor/vue3-chapter-viewer';
|
|
105
|
-
// StaticPasswordProvider / UiPromptPasswordProvider 从 @embedpdf-editor/chapter-core 引入
|
|
106
149
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
150
|
+
// 从业务树生成 tree + manifest(深度优先 flatten,顺序影响重叠页归属)
|
|
151
|
+
const { tree, manifest } = buildChapterViewerCatalog([
|
|
152
|
+
{ chapterId: 'part-1', title: '第一篇', startPage: 1, endPage: 50, source: { url: '/p1.pdf' } },
|
|
153
|
+
{
|
|
154
|
+
chapterId: 'sec-1',
|
|
155
|
+
title: '1.1 节',
|
|
156
|
+
startPage: 5,
|
|
157
|
+
endPage: 20,
|
|
158
|
+
source: { url: '/s1.pdf' },
|
|
159
|
+
children: [/* ... */],
|
|
160
|
+
},
|
|
161
|
+
]);
|
|
112
162
|
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
163
|
+
const options: ChapterViewerOptions = {
|
|
164
|
+
manifest,
|
|
165
|
+
overlapStrategy: overlapStrategyForSamePageOwner('last'), // 重叠页用 manifest 中「后出现」的章节
|
|
166
|
+
// overlapStrategy: { kind: 'first-wins' }, // 默认:先出现的章节
|
|
167
|
+
notes: { /* ... */ },
|
|
168
|
+
bookmarks: { /* ... */ },
|
|
169
|
+
};
|
|
116
170
|
```
|
|
117
171
|
|
|
118
|
-
|
|
172
|
+
| `overlapStrategy` | 同一全局页多个章节条目时 |
|
|
173
|
+
| --- | --- |
|
|
174
|
+
| `{ kind: 'first-wins' }`(默认) | 使用 **manifest 中先出现** 的章节的 PDF |
|
|
175
|
+
| `{ kind: 'last-wins' }` | 使用 **后出现** 的章节(目录 flatten 后,重叠页常以「当前页最后一节」为准) |
|
|
176
|
+
| `{ kind: 'explicit' }` | 各章在 `ownedGlobalPages` 上声明归属页 |
|
|
177
|
+
| `{ kind: 'custom', resolve }` | 完全自定义 |
|
|
119
178
|
|
|
120
|
-
|
|
121
|
-
|----|------|
|
|
122
|
-
| `CallbackPasswordProvider` | 异步回调 `(chapter, attempt) => string \| null`,最常用 |
|
|
123
|
-
| `StaticPasswordProvider` | 构造时传入 `Record<chapterId, password>`,适合 SSO 一次解锁多章 |
|
|
124
|
-
| `UiPromptPasswordProvider` | `onPrompt(chapter, attempt)` 通知 UI,UI 调 `submit(chapterId, password \| null)` 完成 Promise |
|
|
179
|
+
简写:`overlapStrategyForSamePageOwner('first' | 'last')`。
|
|
125
180
|
|
|
126
|
-
|
|
181
|
+
**注意**:仅作分组、没有独立 PDF 的父节点不要放进 flatten 后的 manifest;只保留需要加载 PDF 的节点。Demo:`examples/chapter-viewer-demo-vue3/src/demo/load-catalog.ts`。
|
|
127
182
|
|
|
128
|
-
###
|
|
183
|
+
### PDF 加载与预处理
|
|
129
184
|
|
|
130
|
-
|
|
185
|
+
打开章节前,引擎按以下优先级解析 PDF(`ChapterManagerPlugin.resolvePdfPayload`):
|
|
131
186
|
|
|
132
|
-
|
|
187
|
+
1. `chapter.source.url` / `source.buffer` — 直接使用
|
|
188
|
+
2. `chapter.source.load()` — **按章**异步预处理,返回 `{ url }` 或 `{ buffer }`
|
|
189
|
+
3. `chapterPdfLoader.loadPdf(chapter)` — **全局**统一逻辑
|
|
133
190
|
|
|
134
|
-
|
|
191
|
+
**按章 `source.load`(单章解密、鉴权 URL):**
|
|
135
192
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
```jsonc
|
|
193
|
+
```ts
|
|
139
194
|
{
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
"label": "用户可见标题",
|
|
152
|
-
"metadata": { "任意业务字段": true },
|
|
153
|
-
"anchor": {
|
|
154
|
-
"chapterId": "ch-1",
|
|
155
|
-
"localPageIndex": 0,
|
|
156
|
-
"globalPageIndex": 1,
|
|
157
|
-
"globalPageNumber": 1,
|
|
158
|
-
"rectPdfCoord": { "origin": { "x": 0, "y": 0 }, "size": { "width": 100, "height": 20 } },
|
|
159
|
-
"rectsPdfCoord": [],
|
|
160
|
-
"markerAnchor": { "x": 100, "y": 20 }
|
|
161
|
-
},
|
|
162
|
-
"createdAt": 1717243200000
|
|
163
|
-
}
|
|
164
|
-
],
|
|
165
|
-
// ---------- 附注(plugin-note)----------
|
|
166
|
-
"notes": [
|
|
167
|
-
{
|
|
168
|
-
"noteId": "note-uuid",
|
|
169
|
-
"chapterId": "ch-1",
|
|
170
|
-
"globalPageIndex": 1,
|
|
171
|
-
"globalPageNumber": 1,
|
|
172
|
-
"localPageIndex": 0,
|
|
173
|
-
"rectsPdfCoord": [{ "origin": { "x": 0, "y": 0 }, "size": { "width": 200, "height": 16 } }],
|
|
174
|
-
"endAnchor": { "x": 200, "y": 16 },
|
|
175
|
-
"selectedText": "被选中的原文",
|
|
176
|
-
"content": "用户笔记正文"
|
|
177
|
-
}
|
|
178
|
-
],
|
|
179
|
-
// ---------- 划线 / 高亮 / 图章等(@embedpdf/plugin-annotation)----------
|
|
180
|
-
"markup": [
|
|
181
|
-
{
|
|
182
|
-
// 与 AnnotationTransferItem.annotation 同结构(类型、页码、几何、颜色等)
|
|
183
|
-
"annotation": {
|
|
184
|
-
"id": "ann-uuid",
|
|
185
|
-
"type": "highlight",
|
|
186
|
-
"pageIndex": 0,
|
|
187
|
-
"rect": { "origin": { "x": 0, "y": 0 }, "size": { "width": 100, "height": 12 } }
|
|
188
|
-
},
|
|
189
|
-
// 图章等二进制附件:导出时 ArrayBuffer → base64
|
|
190
|
-
"ctx": {
|
|
191
|
-
"dataBase64": "iVBORw0KGgo...",
|
|
192
|
-
"mimeType": "image/png"
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
]
|
|
196
|
-
}
|
|
197
|
-
}
|
|
195
|
+
chapterId: 'ch-1',
|
|
196
|
+
title: '第一章',
|
|
197
|
+
globalPageRange: [1, 10],
|
|
198
|
+
localPageRange: [0, 9],
|
|
199
|
+
source: {
|
|
200
|
+
load: async () => {
|
|
201
|
+
const bytes = await fetchDecryptedPdf('ch-1');
|
|
202
|
+
return { buffer: bytes };
|
|
203
|
+
// 或 return { url: URL.createObjectURL(blob) };
|
|
204
|
+
},
|
|
205
|
+
},
|
|
198
206
|
}
|
|
199
207
|
```
|
|
200
208
|
|
|
201
|
-
|
|
209
|
+
**全局 `chapterPdfLoader`(所有章走同一套 API):**
|
|
202
210
|
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
```jsonc
|
|
206
|
-
{
|
|
207
|
-
"chapterId": "ch-1",
|
|
208
|
-
"bookmarks": [],
|
|
209
|
-
"notes": [],
|
|
210
|
-
"markup": []
|
|
211
|
-
}
|
|
212
|
-
```
|
|
211
|
+
```ts
|
|
212
|
+
import type { ChapterViewerOptions, IChapterPdfLoader } from '@embedpdf-editor/vue3-chapter-viewer';
|
|
213
213
|
|
|
214
|
-
|
|
214
|
+
const loader: IChapterPdfLoader = {
|
|
215
|
+
async loadPdf(chapter) {
|
|
216
|
+
const res = await api.getChapterPdf(chapter.chapterId);
|
|
217
|
+
return { url: res.signedUrl };
|
|
218
|
+
// 或 return { buffer: await res.arrayBuffer() };
|
|
219
|
+
},
|
|
220
|
+
};
|
|
215
221
|
|
|
216
|
-
|
|
222
|
+
const options: ChapterViewerOptions = {
|
|
223
|
+
manifest: { chapters: [/* 可省略各条 source */] },
|
|
224
|
+
chapterPdfLoader: loader,
|
|
225
|
+
notes: { /* ... */ },
|
|
226
|
+
bookmarks: { /* ... */ },
|
|
227
|
+
};
|
|
228
|
+
```
|
|
217
229
|
|
|
218
|
-
|
|
219
|
-
|------|------|------|
|
|
220
|
-
| `mode` | `replace` | `replace`:先清空该章对应数据再写入;`merge`:与现有合并(书签按 anchor 去重) |
|
|
221
|
-
| `ensureChapterLoaded` | `true` | 导入划线前确保该章 PDF 已打开 |
|
|
222
|
-
| `bookmarks` / `notes` / `markup` | `true` | 控制是否处理对应字段 |
|
|
223
|
-
| `persistNotes` | — | 导入完成后回调,参数为**全书**附注列表,便于写 localStorage / 后端 |
|
|
224
|
-
| `persistBookmarks` | — | 同上,段落书签列表 |
|
|
230
|
+
## Worker 与 WASM
|
|
225
231
|
|
|
226
|
-
|
|
232
|
+
`usePdfiumEngine()` 默认使用 `@embedpdf/engines` 内置的 PDFium CDN wasm 地址,并默认启用 worker:
|
|
227
233
|
|
|
228
234
|
```ts
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
exportChapterAnnotations,
|
|
232
|
-
exportAllChapterAnnotations,
|
|
233
|
-
importChapterAnnotations,
|
|
234
|
-
importChapterAnnotationsArchive,
|
|
235
|
-
chapterAnnotationsArchiveToJson,
|
|
236
|
-
parseChapterAnnotationsArchiveJson,
|
|
237
|
-
chapterAnnotationsSnapshotToJson,
|
|
238
|
-
parseChapterAnnotationsSnapshotJson,
|
|
239
|
-
downloadChapterAnnotationsArchive,
|
|
240
|
-
downloadChapterAnnotationsSnapshot,
|
|
241
|
-
} from '@embedpdf-editor/vue3-chapter-viewer';
|
|
242
|
-
|
|
243
|
-
const { registry } = useRegistry();
|
|
235
|
+
usePdfiumEngine();
|
|
236
|
+
```
|
|
244
237
|
|
|
245
|
-
|
|
246
|
-
const json = chapterAnnotationsArchiveToJson(archive);
|
|
238
|
+
如果业务要求内网部署、固定版本或自定义 CDN,再传入自托管地址:
|
|
247
239
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
persistNotes: (all) => myStore.saveNotes(all),
|
|
251
|
-
persistBookmarks: (all) => myStore.saveBookmarks(all),
|
|
252
|
-
});
|
|
240
|
+
```ts
|
|
241
|
+
usePdfiumEngine({ wasmUrl: '/assets/pdfium.wasm', worker: true });
|
|
253
242
|
```
|
|
254
243
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
| `exportAllChapterAnnotations(registry, options?)` | 导出全书归档 |
|
|
259
|
-
| `importChapterAnnotations(registry, snapshot, options?)` | 导入单章 |
|
|
260
|
-
| `importChapterAnnotationsArchive(registry, archive, options?)` | 导入全书 |
|
|
261
|
-
| `chapterAnnotationsArchiveToJson` / `parseChapterAnnotationsArchiveJson` | 序列化 / 反序列化 |
|
|
262
|
-
| `chapterAnnotationsSnapshotToJson` / `parseChapterAnnotationsSnapshotJson` | 单章版 |
|
|
263
|
-
| `downloadChapterAnnotationsArchive` / `downloadChapterAnnotationsSnapshot` | 触发浏览器下载 `.json` |
|
|
244
|
+
使用 `worker: true` 时,部署环境需要允许 worker 加载 wasm。若只在 worker 模式失败,可先用 `worker: false` 定位资源或响应头问题。
|
|
245
|
+
|
|
246
|
+
## 进阶组合
|
|
264
247
|
|
|
265
|
-
|
|
248
|
+
内置 `ChapterPdfViewer` 已完成插件注册和章节视口渲染。需要插入自定义 shell、目录、工具栏或直接访问 registry 时,可以使用低层组合:
|
|
266
249
|
|
|
267
|
-
|
|
250
|
+
```vue
|
|
251
|
+
<script setup lang="ts">
|
|
252
|
+
import {
|
|
253
|
+
EmbedPDF,
|
|
254
|
+
PdfChapterViewport,
|
|
255
|
+
createChapterViewerBundle,
|
|
256
|
+
} from '@embedpdf-editor/vue3-chapter-viewer';
|
|
268
257
|
|
|
269
|
-
|
|
258
|
+
const { plugins, features } = createChapterViewerBundle(options);
|
|
259
|
+
</script>
|
|
270
260
|
|
|
271
|
-
|
|
261
|
+
<template>
|
|
262
|
+
<EmbedPDF :engine="engine" :plugins="plugins">
|
|
263
|
+
<template #default="{ pluginsReady }">
|
|
264
|
+
<PdfChapterViewport v-if="pluginsReady" :features="features" />
|
|
265
|
+
</template>
|
|
266
|
+
</EmbedPDF>
|
|
267
|
+
</template>
|
|
268
|
+
```
|
|
272
269
|
|
|
273
|
-
|
|
274
|
-
- **书签**:`bookmarks.load`、`persist`、`onRequestRemove`,类型 `ParagraphBookmarkCallbacks`。
|
|
270
|
+
`createChapterViewerEditorOptions()` 只生成旧式 editor 配置,不含 `features`;新代码优先使用 `options` 或 `createChapterViewerBundle(options)`。
|
|
275
271
|
|
|
276
|
-
##
|
|
272
|
+
## 标注导入导出
|
|
277
273
|
|
|
278
|
-
|
|
279
|
-
|------|------|
|
|
280
|
-
| `ChapterPdfViewer` | 开箱即用阅读器壳 |
|
|
281
|
-
| `PdfChapterViewport` / `ChapterTreePanel` | 自定义布局 |
|
|
282
|
-
| `usePdfiumEngine` | 加载 PDFium WASM |
|
|
283
|
-
| `useRegistry` / `EmbedPDF` / `useCapability` | 插件注册表与能力 |
|
|
284
|
-
| `createChapterViewerEditorOptions` | 推荐配置入口 |
|
|
285
|
-
| `CallbackPasswordProvider` | 密码回调(更多 Provider 见 `@embedpdf-editor/chapter-core`) |
|
|
286
|
-
| 标注 IO 函数与类型 | 见上表 |
|
|
274
|
+
包内直接导出章节标注 IO:
|
|
287
275
|
|
|
288
|
-
|
|
276
|
+
```ts
|
|
277
|
+
import {
|
|
278
|
+
exportAllChapterAnnotations,
|
|
279
|
+
importChapterAnnotationsArchive,
|
|
280
|
+
chapterAnnotationsArchiveToJson,
|
|
281
|
+
parseChapterAnnotationsArchiveJson,
|
|
282
|
+
useRegistry,
|
|
283
|
+
} from '@embedpdf-editor/vue3-chapter-viewer';
|
|
284
|
+
```
|
|
289
285
|
|
|
290
|
-
|
|
291
|
-
|----|------|
|
|
292
|
-
| `@embedpdf-editor/react-chapter-viewer` | React 18,API 与本包对称 |
|
|
293
|
-
| `@embedpdf-editor/vue2-chapter-viewer` | Vue 2.6,内部为 `chapter-snippet` Web Component |
|
|
286
|
+
导出格式包含 `bookmarks`、`notes`、`markup`,版本常量为 `CHAPTER_ANNOTATIONS_ARCHIVE_VERSION`。导入时可选择 `mode: 'replace' | 'merge'`,并可传 `persistNotes` / `persistBookmarks` 把导入结果写回业务存储。
|
|
294
287
|
|
|
295
|
-
##
|
|
288
|
+
## 常见问题
|
|
296
289
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
290
|
+
| 现象 | 处理 |
|
|
291
|
+
| --- | --- |
|
|
292
|
+
| 一直停在引擎加载 | 如果传了自定义 `wasmUrl`,检查地址是否 200、MIME/跨源头是否正确 |
|
|
293
|
+
| 只显示空白 | 确认外层容器有高度,且章节 `source.url` 可访问 |
|
|
294
|
+
| 页码或滚动错位 | 检查 `globalPageRange` 与 `localPageRange` 页数是否一致 |
|
|
295
|
+
| 划词笔记没有保存 | 实现 `notes.onCreateNote` 或自定义 `onRequestCreateNote` 流程 |
|
|
296
|
+
| 书签删除无效 | `bookmarks.onRequestRemove` 需要返回 `true` 才会删除 |
|