askbot-dragon 1.8.35-beta → 1.8.36-beta
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.
|
@@ -88,7 +88,8 @@ export default {
|
|
|
88
88
|
pdf: null,
|
|
89
89
|
numPages: 0,
|
|
90
90
|
scale: 1, // 初始缩小比例
|
|
91
|
-
|
|
91
|
+
renderInProgress: false,
|
|
92
|
+
pages: [], // { pageNumber: number, loaded: boolean, canvas: ref, loading: boolean }
|
|
92
93
|
// 静态高亮(坐标采用顶部为原点)
|
|
93
94
|
highlights: [],
|
|
94
95
|
searchQuery: "",
|
|
@@ -178,22 +179,28 @@ export default {
|
|
|
178
179
|
loaded: false,
|
|
179
180
|
canvas: null,
|
|
180
181
|
loading: false,
|
|
181
|
-
retryCount: 0,
|
|
182
182
|
}));
|
|
183
183
|
// 清空搜索高亮
|
|
184
184
|
this.searchHighlights = [];
|
|
185
185
|
this.searchTermCount = 0;
|
|
186
186
|
this.currentSearchIndex = 0;
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
187
|
+
// 确保 DOM 更新完成后再设置 canvas 引用
|
|
188
|
+
await this.$nextTick();
|
|
189
|
+
await this.setCanvasRef();
|
|
190
|
+
}).catch(error => {
|
|
191
|
+
console.error('PDF 加载失败:', error);
|
|
190
192
|
})
|
|
191
193
|
|
|
192
194
|
},
|
|
193
195
|
async loadPdf() {
|
|
194
196
|
if (!this.pdfFile) return;
|
|
195
197
|
await this.initPdf();
|
|
196
|
-
|
|
198
|
+
// 确保 canvas 引用设置完成后再初始化观察器
|
|
199
|
+
await this.$nextTick();
|
|
200
|
+
// 延迟初始化观察器,确保所有 canvas 都已准备好
|
|
201
|
+
setTimeout(() => {
|
|
202
|
+
this.initObserver();
|
|
203
|
+
}, 200);
|
|
197
204
|
},
|
|
198
205
|
initObserver(type) {
|
|
199
206
|
if (this.observer) {
|
|
@@ -206,7 +213,7 @@ export default {
|
|
|
206
213
|
const index = Number(entry.target.getAttribute('data-index'));
|
|
207
214
|
const page = this.pages[index];
|
|
208
215
|
if (page && !page.loaded && !page.loading) {
|
|
209
|
-
|
|
216
|
+
this.renderPage(page.pageNumber);
|
|
210
217
|
}
|
|
211
218
|
this.updateCurrentPage();
|
|
212
219
|
}
|
|
@@ -218,7 +225,9 @@ export default {
|
|
|
218
225
|
threshold: 0.1,
|
|
219
226
|
}
|
|
220
227
|
);
|
|
221
|
-
|
|
228
|
+
// 确保 canvas 引用都已设置好后再开始观察
|
|
229
|
+
this.$nextTick(() => {
|
|
230
|
+
// 增加延迟确保所有 canvas 引用都已设置
|
|
222
231
|
setTimeout(() => {
|
|
223
232
|
this.pages.forEach((page, index) => {
|
|
224
233
|
const canvas = page.canvas;
|
|
@@ -227,7 +236,7 @@ export default {
|
|
|
227
236
|
this.observer.observe(canvas);
|
|
228
237
|
}
|
|
229
238
|
});
|
|
230
|
-
},
|
|
239
|
+
},500)
|
|
231
240
|
if (type !== 'changeScale' && type !== 'onResize'){
|
|
232
241
|
setTimeout(async () => {
|
|
233
242
|
let currentPage = this.split_paragraphs.length > 0 ? this.split_paragraphs[0] : null;
|
|
@@ -250,195 +259,161 @@ export default {
|
|
|
250
259
|
await this.updateCurrentPage();
|
|
251
260
|
}
|
|
252
261
|
},
|
|
253
|
-
async renderPage(pageNumber
|
|
262
|
+
async renderPage(pageNumber) {
|
|
254
263
|
if (!this.pdf) return;
|
|
255
264
|
const pageIndex = pageNumber - 1;
|
|
256
265
|
const pageData = this.pages[pageIndex];
|
|
257
266
|
if (!pageData) return;
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
// 如果已加载且不是重试,直接返回
|
|
262
|
-
if (pageData.loaded && !retry) return;
|
|
263
|
-
|
|
264
|
-
// 重试机制:最多重试 3 次
|
|
265
|
-
const maxRetries = 3;
|
|
266
|
-
if (retry && pageData.retryCount >= maxRetries) {
|
|
267
|
-
console.error(`Max retries reached for page ${pageNumber}`);
|
|
268
|
-
this.$set(pageData, 'loading', false);
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
|
|
267
|
+
// 如果已经在加载或已加载,避免重复渲染
|
|
268
|
+
if (pageData.loaded || pageData.loading) return;
|
|
269
|
+
|
|
272
270
|
this.pageNumber = pageNumber;
|
|
273
|
-
|
|
271
|
+
|
|
274
272
|
try {
|
|
273
|
+
// 标记为加载中,防止重复渲染
|
|
274
|
+
this.$set(pageData, 'loading', true);
|
|
275
|
+
|
|
275
276
|
const page = await this.pdf.getPage(pageNumber);
|
|
276
|
-
let pdfWidth = page.getViewport({ scale: 1}).width;
|
|
277
|
+
let pdfWidth = page.getViewport({ scale: 1 }).width;
|
|
277
278
|
const viewport = page.getViewport({ scale: this.calculateScale(pdfWidth) });
|
|
278
279
|
this.viewport = viewport;
|
|
279
|
-
|
|
280
|
+
|
|
280
281
|
const canvas = pageData.canvas;
|
|
281
282
|
if (!canvas) {
|
|
282
|
-
console.warn(
|
|
283
|
-
|
|
284
|
-
if (!retry) {
|
|
285
|
-
this.$set(pageData, 'retryCount', (pageData.retryCount || 0) + 1);
|
|
286
|
-
setTimeout(() => {
|
|
287
|
-
this.renderPage(pageNumber, true);
|
|
288
|
-
}, 200);
|
|
289
|
-
}
|
|
283
|
+
console.warn(`页面 ${pageNumber} 的 canvas 引用不存在`);
|
|
284
|
+
this.$set(pageData, 'loading', false);
|
|
290
285
|
return;
|
|
291
286
|
}
|
|
292
287
|
|
|
293
288
|
const context = canvas.getContext("2d");
|
|
294
289
|
if (!context) {
|
|
295
|
-
console.warn(
|
|
296
|
-
|
|
297
|
-
if (!retry) {
|
|
298
|
-
this.$set(pageData, 'retryCount', (pageData.retryCount || 0) + 1);
|
|
299
|
-
setTimeout(() => {
|
|
300
|
-
this.renderPage(pageNumber, true);
|
|
301
|
-
}, 200);
|
|
302
|
-
}
|
|
290
|
+
console.warn(`页面 ${pageNumber} 无法获取 canvas context`);
|
|
291
|
+
this.$set(pageData, 'loading', false);
|
|
303
292
|
return;
|
|
304
293
|
}
|
|
305
|
-
|
|
294
|
+
|
|
306
295
|
this.context = context;
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
296
|
+
|
|
297
|
+
// 计算输出缩放比例(与 vuePdf 保持一致)
|
|
298
|
+
const outputScale = this.calculateScale(pdfWidth);
|
|
299
|
+
|
|
300
|
+
// 计算实际显示的尺寸
|
|
301
|
+
let domWidth = Math.floor(viewport.width);
|
|
302
|
+
let domHeight = Math.floor(viewport.height);
|
|
303
|
+
|
|
304
|
+
// 设置 canvas 尺寸(与 vuePdf 保持一致,使用 outputScale)
|
|
305
|
+
// 注意:vuePdf 不使用 devicePixelRatio,为了保持一致,我们也只使用 outputScale
|
|
306
|
+
canvas.width = Math.floor(viewport.width * outputScale);
|
|
307
|
+
canvas.height = Math.floor(viewport.height * outputScale);
|
|
308
|
+
canvas.style.width = `${domWidth}px`;
|
|
309
|
+
canvas.style.height = `${domHeight}px`;
|
|
310
|
+
|
|
311
|
+
// 创建 transform 矩阵(与 vuePdf 保持一致)
|
|
312
|
+
const transform = outputScale !== 1
|
|
313
|
+
? [outputScale, 0, 0, outputScale, 0, 0]
|
|
314
|
+
: null;
|
|
315
|
+
|
|
316
|
+
// 清除旧的文本层
|
|
318
317
|
let hasDoc = document.getElementById('textLayer' + pageNumber);
|
|
319
|
-
if (hasDoc
|
|
318
|
+
if (hasDoc) {
|
|
320
319
|
hasDoc.parentNode.removeChild(hasDoc);
|
|
321
320
|
}
|
|
322
|
-
|
|
323
|
-
// 渲染 PDF
|
|
324
|
-
const renderTask = page.render({
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
321
|
+
|
|
322
|
+
// 渲染 PDF 页面到 canvas(传入 transform 确保正确渲染)
|
|
323
|
+
const renderTask = page.render({
|
|
324
|
+
canvasContext: context,
|
|
325
|
+
viewport: viewport,
|
|
326
|
+
transform: transform
|
|
327
|
+
});
|
|
328
|
+
await renderTask.promise;
|
|
329
|
+
|
|
330
|
+
// 渲染文本层(必须在高亮渲染之前,确保文本层在最上层)
|
|
331
|
+
try {
|
|
329
332
|
const textContent = await page.getTextContent();
|
|
330
|
-
|
|
331
|
-
// 创建文本层
|
|
332
333
|
const textDiv = document.createElement('div');
|
|
333
334
|
textDiv.setAttribute('class', 'textLayer');
|
|
334
335
|
textDiv.setAttribute('id', 'textLayer' + pageNumber);
|
|
336
|
+
// 关键:TextLayerBuilder 也需要传入 transform,确保文本位置正确
|
|
335
337
|
let textLayer = new TextLayerBuilder({
|
|
336
338
|
textLayerDiv: textDiv,
|
|
337
339
|
pageIndex: pageIndex,
|
|
338
340
|
viewport: viewport,
|
|
341
|
+
transform: transform, // 添加 transform 参数
|
|
339
342
|
});
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
textDiv.style.
|
|
343
|
-
//
|
|
344
|
-
|
|
345
|
-
textDiv.style.minHeight = `${viewport.height}px`; // 使用最小高度
|
|
346
|
-
textDiv.style.overflow = 'visible'; // 改为 visible,避免裁剪文字
|
|
343
|
+
// 使用 domWidth 和 domHeight(与 vuePdf 保持一致)
|
|
344
|
+
textDiv.style.width = `${domWidth}px`;
|
|
345
|
+
textDiv.style.height = `${domHeight}px`;
|
|
346
|
+
// 使用 hidden 但确保尺寸正确(与 vuePdf 保持一致)
|
|
347
|
+
textDiv.style.overflow = 'hidden';
|
|
347
348
|
textDiv.style.whiteSpace = 'pre-wrap';
|
|
348
349
|
textDiv.style.position = 'absolute';
|
|
349
350
|
textDiv.style.top = '0';
|
|
350
351
|
textDiv.style.left = '0';
|
|
351
352
|
textDiv.style.fontSize = '20px';
|
|
352
353
|
textDiv.style.color = 'black';
|
|
353
|
-
textDiv.style.pointerEvents = 'none'; // 确保文本层不阻挡交互
|
|
354
|
-
|
|
355
|
-
// 将文本 div 先添加到覆盖层中(TextLayerBuilder 需要 DOM 元素存在)
|
|
356
|
-
let pagesDiv = document.getElementById('canvas-wrapper' + pageIndex);
|
|
357
|
-
if (!pagesDiv) {
|
|
358
|
-
console.warn(`Canvas wrapper not found for page ${pageNumber}`);
|
|
359
|
-
return;
|
|
360
|
-
}
|
|
361
|
-
pagesDiv.appendChild(textDiv);
|
|
362
|
-
|
|
363
|
-
// 设置文本内容并渲染
|
|
364
354
|
textLayer.setTextContent(textContent);
|
|
365
355
|
textLayer.render();
|
|
366
|
-
|
|
367
|
-
//
|
|
356
|
+
|
|
357
|
+
// 渲染完成后,检查文本层实际内容高度,如果超出则调整
|
|
368
358
|
await this.$nextTick();
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
if (pagesDiv) {
|
|
384
|
-
pagesDiv.style.minHeight = `${textLayerHeight}px`;
|
|
385
|
-
}
|
|
386
|
-
// 调整 canvas 的显示高度(如果需要)
|
|
387
|
-
const canvas = pageData.canvas;
|
|
388
|
-
if (canvas && canvas.parentElement) {
|
|
389
|
-
canvas.parentElement.style.minHeight = `${textLayerHeight}px`;
|
|
359
|
+
const textLayerSpans = textDiv.querySelectorAll('span');
|
|
360
|
+
if (textLayerSpans.length > 0) {
|
|
361
|
+
let maxBottom = 0;
|
|
362
|
+
textLayerSpans.forEach(span => {
|
|
363
|
+
const rect = span.getBoundingClientRect();
|
|
364
|
+
const parentRect = textDiv.getBoundingClientRect();
|
|
365
|
+
const relativeBottom = rect.bottom - parentRect.top;
|
|
366
|
+
if (relativeBottom > maxBottom) {
|
|
367
|
+
maxBottom = relativeBottom;
|
|
368
|
+
}
|
|
369
|
+
});
|
|
370
|
+
// 如果文本内容超出当前高度,增加文本层高度(增加一些边距)
|
|
371
|
+
if (maxBottom > domHeight) {
|
|
372
|
+
textDiv.style.height = `${Math.ceil(maxBottom + 10)}px`;
|
|
390
373
|
}
|
|
391
374
|
}
|
|
392
|
-
|
|
393
|
-
//
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
// 渲染搜索高亮区域
|
|
398
|
-
this.renderSearchHighlights(context, pageNumber, viewport);
|
|
399
|
-
|
|
400
|
-
// 重置重试计数
|
|
401
|
-
this.$set(pageData, 'retryCount', 0);
|
|
402
|
-
// 更新页面状态
|
|
403
|
-
this.$set(pageData, 'loading', false);
|
|
404
|
-
this.$set(pageData, 'loaded', true);
|
|
405
|
-
}).catch((error) => {
|
|
406
|
-
console.error(`Error rendering page ${pageNumber}:`, error);
|
|
407
|
-
// 如果渲染失败,尝试重试
|
|
408
|
-
if (!retry && (pageData.retryCount || 0) < maxRetries) {
|
|
409
|
-
this.$set(pageData, 'retryCount', (pageData.retryCount || 0) + 1);
|
|
410
|
-
this.$set(pageData, 'loading', false);
|
|
411
|
-
// 延迟后重试
|
|
412
|
-
setTimeout(() => {
|
|
413
|
-
this.renderPage(pageNumber, true);
|
|
414
|
-
}, 500);
|
|
415
|
-
} else {
|
|
416
|
-
// 确保在错误时也更新状态
|
|
417
|
-
this.$set(pageData, 'loading', false);
|
|
418
|
-
this.$set(pageData, 'loaded', false);
|
|
375
|
+
|
|
376
|
+
// 将文本 div 添加到覆盖层中
|
|
377
|
+
let pagesDiv = document.getElementById('canvas-wrapper' + pageIndex);
|
|
378
|
+
if (pagesDiv) {
|
|
379
|
+
pagesDiv.appendChild(textDiv);
|
|
419
380
|
}
|
|
420
|
-
})
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
381
|
+
} catch (textError) {
|
|
382
|
+
console.warn(`页面 ${pageNumber} 文本层渲染失败:`, textError);
|
|
383
|
+
// 文本层渲染失败不影响页面继续渲染
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// 渲染已有高亮区域(静态highlights)
|
|
387
|
+
// 注意:高亮是在 canvas 上绘制的,不会覆盖文本层(文本层在 canvas 上方)
|
|
388
|
+
// 需要传入 outputScale,因为 canvas 使用了 transform 渲染
|
|
389
|
+
try {
|
|
390
|
+
this.renderHighlights(context, pageNumber, viewport, outputScale);
|
|
391
|
+
} catch (highlightError) {
|
|
392
|
+
console.warn(`页面 ${pageNumber} 高亮渲染失败:`, highlightError);
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
// 渲染搜索高亮区域
|
|
396
|
+
try {
|
|
397
|
+
this.renderSearchHighlights(context, pageNumber, viewport, outputScale);
|
|
398
|
+
} catch (searchHighlightError) {
|
|
399
|
+
console.warn(`页面 ${pageNumber} 搜索高亮渲染失败:`, searchHighlightError);
|
|
434
400
|
}
|
|
401
|
+
|
|
402
|
+
// 标记为已加载
|
|
403
|
+
this.$set(pageData, 'loading', false);
|
|
404
|
+
this.$set(pageData, 'loaded', true);
|
|
405
|
+
} catch (error) {
|
|
406
|
+
console.error(`页面 ${pageNumber} 渲染失败:`, error);
|
|
407
|
+
// 渲染失败时重置状态,允许重试
|
|
408
|
+
this.$set(pageData, 'loading', false);
|
|
409
|
+
this.$set(pageData, 'loaded', false);
|
|
435
410
|
}
|
|
436
411
|
},
|
|
437
412
|
calculateScale(pdfWidth) {
|
|
438
413
|
const containerWidth = document.getElementById('pdfViewPage').clientWidth - 50;
|
|
439
414
|
return (containerWidth / pdfWidth) * this.scale;
|
|
440
415
|
},
|
|
441
|
-
async renderHighlights(context, number,viewport) {
|
|
416
|
+
async renderHighlights(context, number, viewport, outputScale = 1) {
|
|
442
417
|
const pageHighlights = this.highlights.filter(item => (item.page === number ))
|
|
443
418
|
let highlights = this.mergedGroups(pageHighlights);
|
|
444
419
|
this.mergedGroupsList = this.mergedGroupsList.concat(highlights)
|
|
@@ -452,63 +427,60 @@ export default {
|
|
|
452
427
|
groups.push(position.slice(i, i + 4));
|
|
453
428
|
}
|
|
454
429
|
for (let i = 0;i<groups.length;i++){
|
|
455
|
-
this.drawHighlight(context, viewport, groups[i], fillStyle, false);
|
|
430
|
+
this.drawHighlight(context, viewport, groups[i], fillStyle, false, outputScale);
|
|
456
431
|
}
|
|
457
432
|
});
|
|
458
433
|
}
|
|
459
434
|
},
|
|
460
|
-
renderSearchHighlights(context, pageNumber, viewport) {
|
|
435
|
+
renderSearchHighlights(context, pageNumber, viewport, outputScale = 1) {
|
|
461
436
|
const pageHighlights = this.searchHighlights.filter(h => h.pageNumber === pageNumber);
|
|
462
437
|
pageHighlights.forEach(({ position }) => {
|
|
463
|
-
this.drawHighlight(context, viewport, position, "rgba(255, 255, 0, 0.4)", true);
|
|
438
|
+
this.drawHighlight(context, viewport, position, "rgba(255, 255, 0, 0.4)", true, outputScale);
|
|
464
439
|
});
|
|
465
440
|
},
|
|
466
|
-
drawHighlight(context, viewport, position, fillStyle, fromSearch = false) {
|
|
467
|
-
if (!context || !position || position.length < 4) return;
|
|
468
|
-
|
|
441
|
+
drawHighlight(context, viewport, position, fillStyle, fromSearch = false, outputScale = 1) {
|
|
469
442
|
const [x1, y1, x2, y2] = position;
|
|
443
|
+
// 使用 viewport.scale 计算 PDF 坐标到 viewport 坐标的转换
|
|
470
444
|
const scale = viewport.scale;
|
|
445
|
+
// PDF 页面的实际高度(未缩放)
|
|
471
446
|
const pageHeight = viewport.height / viewport.scale;
|
|
472
447
|
|
|
473
|
-
|
|
474
|
-
let
|
|
475
|
-
let
|
|
448
|
+
// 计算在 viewport 坐标系中的位置(PDF 坐标 → viewport 坐标)
|
|
449
|
+
let viewportX1 = x1 * scale;
|
|
450
|
+
let viewportX2 = x2 * scale;
|
|
451
|
+
let viewportY1, viewportY2;
|
|
476
452
|
|
|
477
453
|
if (fromSearch) {
|
|
478
|
-
// 搜索高亮:PDF
|
|
479
|
-
|
|
480
|
-
|
|
454
|
+
// 搜索高亮:PDF坐标系(左下原点),需要转换为顶部原点
|
|
455
|
+
viewportY1 = (pageHeight - y1) * scale;
|
|
456
|
+
viewportY2 = (pageHeight - y2) * scale;
|
|
481
457
|
} else {
|
|
482
|
-
// 静态 highlights
|
|
483
|
-
|
|
484
|
-
|
|
458
|
+
// 静态 highlights:顶部坐标原点,直接使用
|
|
459
|
+
viewportY1 = y1 * scale;
|
|
460
|
+
viewportY2 = y2 * scale;
|
|
485
461
|
}
|
|
486
462
|
|
|
487
|
-
//
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
463
|
+
// 由于 canvas 使用了 transform 渲染,PDF.js 会在内部应用 transform
|
|
464
|
+
// 但 context 的坐标系统是 canvas 的实际尺寸(viewport.width * outputScale)
|
|
465
|
+
// 而 viewport 坐标是基于 viewport.width 的,所以需要转换为 canvas 坐标
|
|
466
|
+
// 注意:outputScale 和 viewport.scale 是相同的值,所以这里只需要乘以 outputScale
|
|
467
|
+
// 但实际上 viewport.scale 已经应用了,所以这里应该直接使用 viewport 坐标
|
|
468
|
+
// 但 canvas 的实际尺寸是 viewport.width * outputScale,所以需要缩放
|
|
469
|
+
let canvasX1 = viewportX1 * outputScale;
|
|
470
|
+
let canvasX2 = viewportX2 * outputScale;
|
|
471
|
+
let canvasY1 = viewportY1 * outputScale;
|
|
472
|
+
let canvasY2 = viewportY2 * outputScale;
|
|
492
473
|
|
|
493
|
-
//
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
context.fillStyle = fillStyle;
|
|
504
|
-
context.fillRect(clippedX, clippedY, clippedWidth, clippedHeight);
|
|
505
|
-
}
|
|
506
|
-
} else {
|
|
507
|
-
// 正常绘制
|
|
508
|
-
context.beginPath();
|
|
509
|
-
context.fillStyle = fillStyle;
|
|
510
|
-
context.fillRect(minX, minY, width, height);
|
|
511
|
-
}
|
|
474
|
+
// 清除当前路径
|
|
475
|
+
context.beginPath();
|
|
476
|
+
|
|
477
|
+
context.fillStyle = fillStyle;
|
|
478
|
+
context.fillRect(
|
|
479
|
+
Math.min(canvasX1, canvasX2),
|
|
480
|
+
Math.min(canvasY1, canvasY2),
|
|
481
|
+
Math.abs(canvasX2 - canvasX1),
|
|
482
|
+
Math.abs(canvasY2 - canvasY1)
|
|
483
|
+
);
|
|
512
484
|
},
|
|
513
485
|
handleFileUpload(event) {
|
|
514
486
|
const file = event.target.files[0];
|
|
@@ -519,27 +491,26 @@ export default {
|
|
|
519
491
|
}
|
|
520
492
|
},
|
|
521
493
|
async setCanvasRef() {
|
|
522
|
-
//
|
|
494
|
+
// 等待 DOM 完全更新
|
|
523
495
|
await this.$nextTick();
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
});
|
|
496
|
+
|
|
497
|
+
if (!this.$refs.canvases || !Array.isArray(this.$refs.canvases)) {
|
|
498
|
+
console.warn('Canvas 引用未准备好,延迟重试');
|
|
499
|
+
// 如果 canvas 引用还没准备好,延迟重试
|
|
500
|
+
setTimeout(() => {
|
|
501
|
+
this.setCanvasRef();
|
|
502
|
+
}, 100);
|
|
503
|
+
return;
|
|
533
504
|
}
|
|
534
|
-
|
|
535
|
-
//
|
|
536
|
-
this.pages.
|
|
505
|
+
|
|
506
|
+
// 确保所有 canvas 引用都已设置
|
|
507
|
+
for (let index = 0; index < this.pages.length; index++) {
|
|
537
508
|
if (this.$refs.canvases && this.$refs.canvases[index]) {
|
|
538
509
|
this.$set(this.pages[index], 'canvas', this.$refs.canvases[index]);
|
|
539
510
|
} else {
|
|
540
|
-
console.warn(
|
|
511
|
+
console.warn(`页面 ${index + 1} 的 canvas 引用不存在`);
|
|
541
512
|
}
|
|
542
|
-
}
|
|
513
|
+
}
|
|
543
514
|
},
|
|
544
515
|
clearPages() {
|
|
545
516
|
this.pages = [];
|
|
@@ -621,8 +592,7 @@ export default {
|
|
|
621
592
|
zoomIn() {
|
|
622
593
|
this.updateCurrentPage();
|
|
623
594
|
// 检查是否有页面正在加载
|
|
624
|
-
|
|
625
|
-
if (hasLoadingPage) return;
|
|
595
|
+
if (this.pages.some(p => p.loading)) return;
|
|
626
596
|
const container = this.$refs.pdfContainer;
|
|
627
597
|
const currentScroll = container.scrollTop;
|
|
628
598
|
this.scale += 0.1;
|
|
@@ -631,8 +601,7 @@ export default {
|
|
|
631
601
|
zoomOut() {
|
|
632
602
|
this.updateCurrentPage();
|
|
633
603
|
// 检查是否有页面正在加载
|
|
634
|
-
|
|
635
|
-
if (hasLoadingPage) return;
|
|
604
|
+
if (this.pages.some(p => p.loading)) return;
|
|
636
605
|
const container = this.$refs.pdfContainer;
|
|
637
606
|
const currentScroll = container.scrollTop;
|
|
638
607
|
this.scale = Math.max(this.scale - 0.1, 0.1);
|
|
@@ -642,11 +611,10 @@ export default {
|
|
|
642
611
|
const pages = Vue.observable(this.pages)
|
|
643
612
|
pages.forEach((p) => {
|
|
644
613
|
p.loaded = false;
|
|
645
|
-
this.$set(p,
|
|
646
|
-
this.$set(p, 'retryCount', 0);
|
|
614
|
+
this.$set(p,'loading',false)
|
|
647
615
|
if (p.canvas) {
|
|
648
616
|
const ctx = p.canvas.getContext('2d');
|
|
649
|
-
ctx.clearRect(0,
|
|
617
|
+
ctx.clearRect(0,0,p.canvas.width,p.canvas.height);
|
|
650
618
|
}
|
|
651
619
|
});
|
|
652
620
|
|
|
@@ -679,8 +647,7 @@ export default {
|
|
|
679
647
|
async doSearch() {
|
|
680
648
|
if (!this.pdf || !this.searchQuery) return;
|
|
681
649
|
// 检查是否有页面正在加载
|
|
682
|
-
|
|
683
|
-
if (hasLoadingPage) return;
|
|
650
|
+
if (this.pages.some(p => p.loading)) return;
|
|
684
651
|
|
|
685
652
|
this.searchHighlights = [];
|
|
686
653
|
const numPages = this.numPages;
|
|
@@ -743,8 +710,7 @@ export default {
|
|
|
743
710
|
const currentScroll = container.scrollTop;
|
|
744
711
|
this.pages.forEach((p) => {
|
|
745
712
|
p.loaded = false;
|
|
746
|
-
this.$set(p,
|
|
747
|
-
this.$set(p, 'retryCount', 0);
|
|
713
|
+
this.$set(p,'loading',false)
|
|
748
714
|
if (p.canvas) {
|
|
749
715
|
const ctx = p.canvas.getContext("2d");
|
|
750
716
|
ctx.clearRect(0, 0, p.canvas.width, p.canvas.height);
|
|
@@ -775,18 +741,16 @@ export default {
|
|
|
775
741
|
clearTimeout(this.resizeTimeout);
|
|
776
742
|
this.resizeTimeout = setTimeout(() => {
|
|
777
743
|
// 检查是否有页面正在加载
|
|
778
|
-
|
|
779
|
-
if (hasLoadingPage) return;
|
|
744
|
+
if (this.pages.some(p => p.loading)) return;
|
|
780
745
|
const container = this.$refs.pdfContainer;
|
|
781
746
|
const currentScroll = container.scrollTop;
|
|
782
747
|
const pages = Vue.observable(this.pages)
|
|
783
748
|
pages.forEach((p) => {
|
|
784
749
|
p.loaded = false;
|
|
785
|
-
this.$set(p,
|
|
786
|
-
this.$set(p, 'retryCount', 0);
|
|
750
|
+
this.$set(p,'loading',false)
|
|
787
751
|
if (p.canvas) {
|
|
788
752
|
const ctx = p.canvas.getContext('2d');
|
|
789
|
-
ctx.clearRect(0,
|
|
753
|
+
ctx.clearRect(0,0,p.canvas.width,p.canvas.height);
|
|
790
754
|
}
|
|
791
755
|
});
|
|
792
756
|
this.$nextTick(() => {
|
|
@@ -874,15 +838,11 @@ export default {
|
|
|
874
838
|
display: flex;
|
|
875
839
|
justify-content: center;
|
|
876
840
|
width: 100%;
|
|
877
|
-
min-height: fit-content; /* 确保容器能容纳所有内容 */
|
|
878
|
-
overflow: visible; /* 允许内容超出,避免裁剪 */
|
|
879
841
|
}
|
|
880
842
|
|
|
881
843
|
.canvas-wrapper {
|
|
882
844
|
position: relative;
|
|
883
845
|
width: 100%;
|
|
884
|
-
min-height: 100%; /* 确保容器有足够高度 */
|
|
885
|
-
overflow: visible; /* 允许内容超出,避免裁剪 */
|
|
886
846
|
}
|
|
887
847
|
|
|
888
848
|
/* 在loading时覆盖canvas显示加载动画 */
|