@koi-br/ocr-web-sdk 1.0.45 → 1.0.47

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.
@@ -12,7 +12,12 @@
12
12
  <div class="toolbar-group">
13
13
  <span class="scale-text"> {{ Math.round(scale * 100) }}% </span>
14
14
  <div class="toolbar-divider"></div>
15
- <ATooltip v-if="showResetButton" mini position="bottom" content="重置">
15
+ <ATooltip
16
+ v-if="showResetButton"
17
+ mini
18
+ position="bottom"
19
+ content="重置"
20
+ >
16
21
  <AButton size="small" type="outline" @click="reset">
17
22
  <RefreshCcw :size="16" />
18
23
  </AButton>
@@ -91,7 +96,7 @@
91
96
  <AButton
92
97
  size="small"
93
98
  type="outline"
94
- style="padding-right: 0px"
99
+ style="padding-right: 0px;"
95
100
  :disabled="currentPage >= totalPages"
96
101
  @click="goToNextPage"
97
102
  >
@@ -116,9 +121,9 @@
116
121
  <div class="loading-spinner"></div>
117
122
  <div class="loading-text">加载中...</div>
118
123
  </div>
119
-
120
- <div
121
- class="image-wrapper-container"
124
+
125
+ <div
126
+ class="image-wrapper-container"
122
127
  :style="containerStyle"
123
128
  :class="{ 'image-hidden': !isImageReady && autoFitWidth }"
124
129
  >
@@ -157,10 +162,7 @@
157
162
 
158
163
  <!-- 文本图层(用于文本块选择和定位) -->
159
164
  <div
160
- v-if="
161
- getPageBlocksData(pageIndex + 1) &&
162
- getPageBlocksData(pageIndex + 1).length > 0
163
- "
165
+ v-if="getPageBlocksData(pageIndex + 1) && getPageBlocksData(pageIndex + 1).length > 0"
164
166
  :ref="(el) => setTextLayerRef(el, pageIndex + 1)"
165
167
  class="text-layer"
166
168
  ></div>
@@ -358,6 +360,11 @@ const props = defineProps({
358
360
  type: Boolean,
359
361
  default: true, // 默认不显示,保持向后兼容(isDownload 的默认值)
360
362
  },
363
+ // 是否启用文本块的 hover 效果(高亮和显示批注按钮)
364
+ enableBlockHover: {
365
+ type: Boolean,
366
+ default: true,
367
+ },
361
368
  });
362
369
 
363
370
  const emit = defineEmits<{
@@ -405,22 +412,22 @@ const isImageReady = ref(false); // 标记图片是否已准备好显示(自
405
412
  watch(
406
413
  () => imageUrls.value,
407
414
  (newUrls, oldUrls) => {
408
- console.log("[ImagePreview] imageUrls changed:", {
415
+ console.log('[ImagePreview] imageUrls changed:', {
409
416
  newUrls: newUrls?.length,
410
417
  oldUrls: oldUrls?.length,
411
418
  autoFitWidth: props.autoFitWidth,
412
419
  isImageReady: isImageReady.value,
413
420
  isCalculatingAutoFit: isCalculatingAutoFit.value,
414
421
  });
415
-
422
+
416
423
  // 如果有新的图片URL,且启用自适应宽度,立即隐藏图片
417
424
  if (newUrls && newUrls.length > 0 && props.autoFitWidth) {
418
- console.log("[ImagePreview] 设置图片隐藏,等待自适应宽度计算");
425
+ console.log('[ImagePreview] 设置图片隐藏,等待自适应宽度计算');
419
426
  isImageReady.value = false;
420
427
  isCalculatingAutoFit.value = true;
421
428
  } else if (!props.autoFitWidth) {
422
429
  // 如果没有启用自适应宽度,立即显示
423
- console.log("[ImagePreview] 未启用自适应宽度,立即显示图片");
430
+ console.log('[ImagePreview] 未启用自适应宽度,立即显示图片');
424
431
  isImageReady.value = true;
425
432
  isCalculatingAutoFit.value = false;
426
433
  }
@@ -486,10 +493,10 @@ const imageSize = computed({
486
493
  // 在自适应宽度模式下,使用第一页的尺寸;否则使用当前页的尺寸
487
494
  const scaledImageSize = computed(() => {
488
495
  // 如果启用自适应宽度,使用第一页的尺寸作为基准
489
- const baseSize = props.autoFitWidth
490
- ? imageSizes.get(1) || { width: 0, height: 0 }
496
+ const baseSize = props.autoFitWidth
497
+ ? (imageSizes.get(1) || { width: 0, height: 0 })
491
498
  : imageSize.value;
492
-
499
+
493
500
  if (baseSize.width === 0 || baseSize.height === 0) {
494
501
  return { width: 0, height: 0 };
495
502
  }
@@ -535,7 +542,6 @@ const annotationInput = ref(""); // 批注输入内容
535
542
  const currentAnnotationBlock = ref<{
536
543
  bbox: [number, number, number, number];
537
544
  content: string;
538
- annotationId?: string; // 已有批注的ID(如果存在)
539
545
  } | null>(null); // 当前正在添加批注的文本块
540
546
  const annotationPopupRef = ref<HTMLElement>(); // 批注弹窗引用
541
547
 
@@ -585,7 +591,7 @@ const getPageScaledSize = (pageNo: number) => {
585
591
  const getPageContainerStyle = (pageNo: number) => {
586
592
  const scaledSize = getPageScaledSize(pageNo);
587
593
  return {
588
- height: scaledSize.height > 0 ? `${scaledSize.height}px` : "auto",
594
+ height: scaledSize.height > 0 ? `${scaledSize.height}px` : 'auto',
589
595
  };
590
596
  };
591
597
 
@@ -883,7 +889,7 @@ const switchToPage = (page: number) => {
883
889
  ) as HTMLElement;
884
890
  if (pageElement) {
885
891
  // 标记这是翻页滚动,不应该被同步滚动干扰
886
- containerRef.value.dataset.pageScrolling = "true";
892
+ containerRef.value.dataset.pageScrolling = 'true';
887
893
  pageElement.scrollIntoView({ behavior: "smooth", block: "start" });
888
894
  // 更新 lastScrollTop,确保滚动方向判断准确
889
895
  nextTick(() => {
@@ -918,7 +924,7 @@ const reset = () => {
918
924
  annotationInput.value = "";
919
925
  activeBlockDiv.value = null;
920
926
  isHighlighted.value = false;
921
-
927
+
922
928
  // 清除已渲染页面集合
923
929
  renderedPages.value.clear();
924
930
  };
@@ -929,7 +935,7 @@ const original = () => {
929
935
 
930
936
  // 计算自适应宽度的缩放比例
931
937
  const calculateAutoFitScale = () => {
932
- console.log("[ImagePreview] calculateAutoFitScale 开始:", {
938
+ console.log('[ImagePreview] calculateAutoFitScale 开始:', {
933
939
  autoFitWidth: props.autoFitWidth,
934
940
  hasContainerRef: !!containerRef.value,
935
941
  containerRect: containerRef.value?.getBoundingClientRect(),
@@ -938,19 +944,16 @@ const calculateAutoFitScale = () => {
938
944
  minScale: props.minScale,
939
945
  maxScale: props.maxScale,
940
946
  });
941
-
947
+
942
948
  if (!props.autoFitWidth || !containerRef.value) {
943
- console.log("[ImagePreview] calculateAutoFitScale 返回 1 (条件不满足)");
949
+ console.log('[ImagePreview] calculateAutoFitScale 返回 1 (条件不满足)');
944
950
  return 1;
945
951
  }
946
952
 
947
953
  // 使用第一页的图片尺寸作为基准(所有页面使用相同的缩放比例)
948
954
  const firstPageSize = imageSizes.get(1);
949
955
  if (!firstPageSize || firstPageSize.width === 0) {
950
- console.log(
951
- "[ImagePreview] calculateAutoFitScale 返回 1 (第一页尺寸无效)",
952
- firstPageSize
953
- );
956
+ console.log('[ImagePreview] calculateAutoFitScale 返回 1 (第一页尺寸无效)', firstPageSize);
954
957
  return 1;
955
958
  }
956
959
 
@@ -960,10 +963,7 @@ const calculateAutoFitScale = () => {
960
963
  const containerWidth = containerRect.width - 4;
961
964
 
962
965
  if (containerWidth <= 0) {
963
- console.log(
964
- "[ImagePreview] calculateAutoFitScale 返回 1 (容器宽度无效)",
965
- containerWidth
966
- );
966
+ console.log('[ImagePreview] calculateAutoFitScale 返回 1 (容器宽度无效)', containerWidth);
967
967
  return 1;
968
968
  }
969
969
 
@@ -974,21 +974,15 @@ const calculateAutoFitScale = () => {
974
974
  const imageWidth = isRotated ? firstPageSize.height : firstPageSize.width;
975
975
 
976
976
  if (imageWidth <= 0) {
977
- console.log(
978
- "[ImagePreview] calculateAutoFitScale 返回 1 (图片宽度无效)",
979
- imageWidth
980
- );
977
+ console.log('[ImagePreview] calculateAutoFitScale 返回 1 (图片宽度无效)', imageWidth);
981
978
  return 1;
982
979
  }
983
980
 
984
981
  // 计算缩放比例,使图片宽度完全适应容器宽度
985
982
  const calculatedScale = containerWidth / imageWidth;
986
- const finalScale = Math.max(
987
- props.minScale,
988
- Math.min(props.maxScale, calculatedScale)
989
- );
990
-
991
- console.log("[ImagePreview] calculateAutoFitScale 计算结果:", {
983
+ const finalScale = Math.max(props.minScale, Math.min(props.maxScale, calculatedScale));
984
+
985
+ console.log('[ImagePreview] calculateAutoFitScale 计算结果:', {
992
986
  containerWidth,
993
987
  imageWidth,
994
988
  calculatedScale,
@@ -1002,8 +996,8 @@ const calculateAutoFitScale = () => {
1002
996
  // 图片加载完成处理
1003
997
  const onImageLoad = (event: Event, pageNum: number) => {
1004
998
  const img = event.target as HTMLImageElement;
1005
-
1006
- console.log("[ImagePreview] 图片加载完成:", {
999
+
1000
+ console.log('[ImagePreview] 图片加载完成:', {
1007
1001
  pageNum,
1008
1002
  naturalWidth: img.naturalWidth,
1009
1003
  naturalHeight: img.naturalHeight,
@@ -1011,7 +1005,7 @@ const onImageLoad = (event: Event, pageNum: number) => {
1011
1005
  isImageReady: isImageReady.value,
1012
1006
  isCalculatingAutoFit: isCalculatingAutoFit.value,
1013
1007
  });
1014
-
1008
+
1015
1009
  // 存储该页的图片尺寸
1016
1010
  imageSizes.set(pageNum, {
1017
1011
  width: img.naturalWidth,
@@ -1020,10 +1014,10 @@ const onImageLoad = (event: Event, pageNum: number) => {
1020
1014
 
1021
1015
  // 如果是第一页且启用自适应宽度,计算并设置初始缩放比例
1022
1016
  if (pageNum === 1 && props.autoFitWidth) {
1023
- console.log("[ImagePreview] 第一页加载完成,开始计算自适应宽度");
1017
+ console.log('[ImagePreview] 第一页加载完成,开始计算自适应宽度');
1024
1018
  // 重置用户缩放标记
1025
1019
  isUserZooming.value = false;
1026
-
1020
+
1027
1021
  // 确保图片是隐藏的(watch 已经设置了,这里再次确认)
1028
1022
  if (!isImageReady.value) {
1029
1023
  isCalculatingAutoFit.value = true;
@@ -1031,7 +1025,7 @@ const onImageLoad = (event: Event, pageNum: number) => {
1031
1025
 
1032
1026
  // 设置超时保护,防止一直显示 loading(最多等待 3 秒)
1033
1027
  const timeoutId = setTimeout(() => {
1034
- console.warn("自适应宽度计算超时,强制显示图片");
1028
+ console.warn('自适应宽度计算超时,强制显示图片');
1035
1029
  isCalculatingAutoFit.value = false;
1036
1030
  isImageReady.value = true;
1037
1031
  }, 3000);
@@ -1042,51 +1036,38 @@ const onImageLoad = (event: Event, pageNum: number) => {
1042
1036
  // 添加小延迟确保容器完全渲染
1043
1037
  setTimeout(() => {
1044
1038
  try {
1045
- console.log("[ImagePreview] onImageLoad: 开始计算自适应宽度...");
1039
+ console.log('[ImagePreview] onImageLoad: 开始计算自适应宽度...');
1046
1040
  const autoScale = calculateAutoFitScale();
1047
- console.log("[ImagePreview] onImageLoad: 自适应宽度计算结果:", {
1041
+ console.log('[ImagePreview] onImageLoad: 自适应宽度计算结果:', {
1048
1042
  autoScale,
1049
1043
  containerRef: !!containerRef.value,
1050
- containerWidth:
1051
- containerRef.value?.getBoundingClientRect()?.width,
1044
+ containerWidth: containerRef.value?.getBoundingClientRect()?.width,
1052
1045
  firstPageSize: imageSizes.get(1),
1053
1046
  });
1054
-
1047
+
1055
1048
  if (autoScale > 0) {
1056
1049
  scale.value = autoScale;
1057
1050
  initialAutoFitScale.value = autoScale; // 记录初始自适应缩放比例
1058
1051
  // 记录当前容器宽度,用于后续 resize 检查
1059
1052
  if (containerRef.value) {
1060
- lastContainerWidth =
1061
- containerRef.value.getBoundingClientRect().width;
1053
+ lastContainerWidth = containerRef.value.getBoundingClientRect().width;
1062
1054
  }
1063
- console.log(
1064
- "[ImagePreview] onImageLoad: 缩放比例已设置:",
1065
- autoScale
1066
- );
1055
+ console.log('[ImagePreview] onImageLoad: 缩放比例已设置:', autoScale);
1067
1056
  } else {
1068
- console.warn(
1069
- "[ImagePreview] onImageLoad: 计算出的缩放比例无效:",
1070
- autoScale
1071
- );
1057
+ console.warn('[ImagePreview] onImageLoad: 计算出的缩放比例无效:', autoScale);
1072
1058
  }
1073
1059
  } catch (error) {
1074
- console.error(
1075
- "[ImagePreview] onImageLoad: 计算自适应宽度失败:",
1076
- error
1077
- );
1060
+ console.error('[ImagePreview] onImageLoad: 计算自适应宽度失败:', error);
1078
1061
  } finally {
1079
1062
  // 清除超时保护
1080
1063
  clearTimeout(timeoutId);
1081
- console.log(
1082
- "[ImagePreview] onImageLoad: 更新状态: isCalculatingAutoFit = false, isImageReady = true"
1083
- );
1064
+ console.log('[ImagePreview] onImageLoad: 更新状态: isCalculatingAutoFit = false, isImageReady = true');
1084
1065
  // 无论计算结果如何,都要更新状态,避免一直显示 loading
1085
1066
  isCalculatingAutoFit.value = false;
1086
1067
  // 使用 requestAnimationFrame 确保在下一帧显示,避免闪烁
1087
1068
  requestAnimationFrame(() => {
1088
1069
  isImageReady.value = true;
1089
- console.log("[ImagePreview] onImageLoad: 图片已准备好显示");
1070
+ console.log('[ImagePreview] onImageLoad: 图片已准备好显示');
1090
1071
  });
1091
1072
  }
1092
1073
  }, 100); // 增加延迟,确保所有图片都已加载
@@ -1097,7 +1078,7 @@ const onImageLoad = (event: Event, pageNum: number) => {
1097
1078
  isImageReady.value = true;
1098
1079
  isCalculatingAutoFit.value = false;
1099
1080
  }
1100
-
1081
+
1101
1082
  // 如果第一页已经加载完成,且当前页不是第一页,也应用自适应宽度
1102
1083
  if (pageNum > 1 && props.autoFitWidth && initialAutoFitScale.value !== null) {
1103
1084
  // 确保后续页面也使用相同的缩放比例
@@ -1301,6 +1282,11 @@ const renderTextLayer = (pageNum?: number) => {
1301
1282
 
1302
1283
  // Hover 和点击事件
1303
1284
  blockDiv.addEventListener("mouseenter", (e) => {
1285
+ // 如果禁用了 hover 效果,不执行相关逻辑
1286
+ if (!props.enableBlockHover) {
1287
+ return;
1288
+ }
1289
+
1304
1290
  // 取消之前的隐藏定时器
1305
1291
  if (hideTimer) {
1306
1292
  clearTimeout(hideTimer);
@@ -1310,7 +1296,7 @@ const renderTextLayer = (pageNum?: number) => {
1310
1296
  // 清除当前页面所有文本块的高亮样式(除了当前文本块)
1311
1297
  // 这样可以防止多个文本块同时高亮的竞态条件
1312
1298
  clearAllHighlights(blockDiv);
1313
-
1299
+
1314
1300
  // 清除跳转高亮标志(如果之前是通过跳转高亮的)
1315
1301
  if (isHighlighted.value) {
1316
1302
  isHighlighted.value = false;
@@ -1323,23 +1309,11 @@ const renderTextLayer = (pageNum?: number) => {
1323
1309
  const existingAnnotation = getAnnotationForBlock(bbox, targetPage);
1324
1310
  if (existingAnnotation) {
1325
1311
  // 如果有批注,保持批注背景色,但添加蓝色边框表示 hover
1326
- blockDiv.style.setProperty(
1327
- "background-color",
1328
- "rgba(255, 243, 205, 0.5)",
1329
- "important"
1330
- );
1331
- blockDiv.style.setProperty(
1332
- "border",
1333
- "2px solid rgba(30, 144, 255, 0.8)",
1334
- "important"
1335
- );
1312
+ blockDiv.style.setProperty("background-color", "rgba(255, 243, 205, 0.5)", "important");
1313
+ blockDiv.style.setProperty("border", "2px solid rgba(30, 144, 255, 0.8)", "important");
1336
1314
  blockDiv.style.setProperty("border-radius", "3px", "important");
1337
1315
  blockDiv.style.setProperty("padding", "1px 3px", "important");
1338
- blockDiv.style.setProperty(
1339
- "box-shadow",
1340
- "0 0 0 2px rgba(30, 144, 255, 0.6), 0 1px 2px rgba(255, 193, 7, 0.25)",
1341
- "important"
1342
- );
1316
+ blockDiv.style.setProperty("box-shadow", "0 0 0 2px rgba(30, 144, 255, 0.6), 0 1px 2px rgba(255, 193, 7, 0.25)", "important");
1343
1317
  } else {
1344
1318
  // 如果没有批注,先清除可能残留的样式,再设置 hover 样式(与 PdfPreview 保持一致)
1345
1319
  blockDiv.style.removeProperty("background-color");
@@ -1358,6 +1332,10 @@ const renderTextLayer = (pageNum?: number) => {
1358
1332
  });
1359
1333
 
1360
1334
  blockDiv.addEventListener("mouseleave", () => {
1335
+ // 如果禁用了 hover 效果,不执行隐藏逻辑
1336
+ if (!props.enableBlockHover) {
1337
+ return;
1338
+ }
1361
1339
  // 延迟隐藏,给用户时间移动到批注按钮
1362
1340
  hideAnnotationButton();
1363
1341
  });
@@ -1412,7 +1390,7 @@ const showAnnotationButtonForBlock = (
1412
1390
 
1413
1391
  try {
1414
1392
  const bbox = JSON.parse(bboxStr) as [number, number, number, number];
1415
-
1393
+
1416
1394
  // 从 blocksData 中查找对应的 content
1417
1395
  const pageBlocksData = getPageBlocksData(currentPage.value);
1418
1396
  const blockData = pageBlocksData.find((block) => {
@@ -1425,7 +1403,7 @@ const showAnnotationButtonForBlock = (
1425
1403
  Math.abs(y2 - bbox[3]) < tolerance
1426
1404
  );
1427
1405
  });
1428
-
1406
+
1429
1407
  if (!blockData) return;
1430
1408
 
1431
1409
  const rect = blockDiv.getBoundingClientRect();
@@ -1513,10 +1491,7 @@ const showAnnotationButtonForBlock = (
1513
1491
  * @param excludeBlockDiv 要排除的文本块(不清除它的样式)
1514
1492
  * @param pageNum 要清除的页码,如果不提供则从 excludeBlockDiv 中获取
1515
1493
  */
1516
- const clearAllHighlights = (
1517
- excludeBlockDiv?: HTMLElement,
1518
- pageNum?: number
1519
- ) => {
1494
+ const clearAllHighlights = (excludeBlockDiv?: HTMLElement, pageNum?: number) => {
1520
1495
  // 确定要清除的页码
1521
1496
  let targetPage = pageNum;
1522
1497
  if (!targetPage && excludeBlockDiv) {
@@ -1550,23 +1525,11 @@ const clearAllHighlights = (
1550
1525
 
1551
1526
  if (existingAnnotation) {
1552
1527
  // 如果有批注,恢复批注样式(不使用 hover 样式)
1553
- el.style.setProperty(
1554
- "background-color",
1555
- "rgba(255, 243, 205, 0.5)",
1556
- "important"
1557
- );
1558
- el.style.setProperty(
1559
- "border",
1560
- "1px solid rgba(255, 193, 7, 0.7)",
1561
- "important"
1562
- );
1528
+ el.style.setProperty("background-color", "rgba(255, 243, 205, 0.5)", "important");
1529
+ el.style.setProperty("border", "1px solid rgba(255, 193, 7, 0.7)", "important");
1563
1530
  el.style.setProperty("border-radius", "3px", "important");
1564
1531
  el.style.setProperty("padding", "1px 3px", "important");
1565
- el.style.setProperty(
1566
- "box-shadow",
1567
- "0 1px 2px rgba(255, 193, 7, 0.25)",
1568
- "important"
1569
- );
1532
+ el.style.setProperty("box-shadow", "0 1px 2px rgba(255, 193, 7, 0.25)", "important");
1570
1533
  } else {
1571
1534
  // 如果没有批注,清除所有高亮样式
1572
1535
  el.style.backgroundColor = "transparent";
@@ -1630,29 +1593,17 @@ const restoreBlockStyle = (blockDiv: HTMLElement) => {
1630
1593
 
1631
1594
  if (existingAnnotation) {
1632
1595
  // 如果有批注,保持批注样式(使用 !important 确保优先级)
1633
- blockDiv.style.setProperty(
1634
- "background-color",
1635
- "rgba(255, 243, 205, 0.5)",
1636
- "important"
1637
- );
1638
- blockDiv.style.setProperty(
1639
- "border",
1640
- "1px solid rgba(255, 193, 7, 0.7)",
1641
- "important"
1642
- );
1596
+ blockDiv.style.setProperty("background-color", "rgba(255, 243, 205, 0.5)", "important");
1597
+ blockDiv.style.setProperty("border", "1px solid rgba(255, 193, 7, 0.7)", "important");
1643
1598
  blockDiv.style.setProperty("border-radius", "3px", "important");
1644
1599
  blockDiv.style.setProperty("padding", "1px 3px", "important");
1645
- blockDiv.style.setProperty(
1646
- "box-shadow",
1647
- "0 1px 2px rgba(255, 193, 7, 0.25)",
1648
- "important"
1649
- );
1650
-
1600
+ blockDiv.style.setProperty("box-shadow", "0 1px 2px rgba(255, 193, 7, 0.25)", "important");
1601
+
1651
1602
  // 确保元素可见
1652
1603
  blockDiv.style.setProperty("display", "block", "important");
1653
1604
  blockDiv.style.setProperty("visibility", "visible", "important");
1654
1605
  blockDiv.style.setProperty("opacity", "1", "important");
1655
-
1606
+
1656
1607
  console.log("restoreBlockStyle: 已设置批注样式", {
1657
1608
  backgroundColor: blockDiv.style.backgroundColor,
1658
1609
  border: blockDiv.style.border,
@@ -1702,7 +1653,12 @@ const hideAnnotationButton = () => {
1702
1653
  const bboxStr = activeBlockDiv.value.dataset.bbox;
1703
1654
  if (bboxStr) {
1704
1655
  try {
1705
- const bbox = JSON.parse(bboxStr) as [number, number, number, number];
1656
+ const bbox = JSON.parse(bboxStr) as [
1657
+ number,
1658
+ number,
1659
+ number,
1660
+ number
1661
+ ];
1706
1662
  const existingAnnotation = getAnnotationForBlock(bbox);
1707
1663
  if (existingAnnotation) {
1708
1664
  // 如果有批注,恢复批注样式
@@ -1845,7 +1801,6 @@ const openAnnotationInput = (e?: Event) => {
1845
1801
  currentAnnotationBlock.value = {
1846
1802
  bbox,
1847
1803
  content: selectedText,
1848
- annotationId: existingAnnotation?.id, // 保存已有批注的ID
1849
1804
  };
1850
1805
 
1851
1806
  // 确保弹窗显示
@@ -1955,7 +1910,7 @@ const closeAnnotationInput = () => {
1955
1910
  currentAnnotationBlock.value = null;
1956
1911
  annotationInput.value = "";
1957
1912
  showAnnotationPopup.value = false;
1958
-
1913
+
1959
1914
  // 关闭批注输入弹窗后,恢复文本块的样式
1960
1915
  if (activeBlockDiv.value && !isHighlighted.value) {
1961
1916
  restoreBlockStyle(activeBlockDiv.value);
@@ -1987,22 +1942,15 @@ const saveAnnotation = () => {
1987
1942
  return;
1988
1943
  }
1989
1944
 
1990
- const { bbox, content, annotationId } = currentAnnotationBlock.value;
1945
+ const { bbox, content } = currentAnnotationBlock.value;
1991
1946
  const annotationContent = annotationInput.value.trim();
1992
1947
 
1993
- // 优先使用保存的ID查找批注,如果没有ID则使用bbox查找(向后兼容)
1994
- let existingAnnotation: AnnotationInfo | null = null;
1995
- if (annotationId && props.annotations) {
1996
- existingAnnotation =
1997
- props.annotations.find((ann) => ann.id === annotationId) || null;
1998
- }
1999
- // 如果没有通过ID找到,则使用bbox查找(向后兼容)
2000
- if (!existingAnnotation) {
2001
- existingAnnotation = getAnnotationForBlock(bbox);
2002
- }
1948
+ // 检查是否已有批注
1949
+ const existingAnnotation = getAnnotationForBlock(bbox);
2003
1950
 
2004
1951
  const annotation: AnnotationInfo = {
2005
- id: existingAnnotation?.id || "",
1952
+ id:
1953
+ existingAnnotation?.id || "",
2006
1954
  blockBbox: bbox,
2007
1955
  blockContent: content,
2008
1956
  blockPage: currentPage.value,
@@ -2017,7 +1965,7 @@ const saveAnnotation = () => {
2017
1965
  } else {
2018
1966
  // 添加新批注
2019
1967
  emit("annotation-add", annotation);
2020
- Message.success("批注已添加");
1968
+ // Message.success("批注已添加");
2021
1969
  }
2022
1970
 
2023
1971
  // 关闭输入框
@@ -2034,9 +1982,9 @@ const saveAnnotation = () => {
2034
1982
  */
2035
1983
  const handleScroll = (e: Event) => {
2036
1984
  const container = e.target as HTMLElement;
2037
-
1985
+
2038
1986
  // 检查是否是同步滚动触发的
2039
- const isSyncing = container?.dataset?.syncingScroll === "true";
1987
+ const isSyncing = container?.dataset?.syncingScroll === 'true';
2040
1988
  if (isSyncing) {
2041
1989
  // 即使是被同步滚动触发的,也应该立即更新页码(但不触发翻页动画)
2042
1990
  // 使用 requestAnimationFrame 确保在浏览器渲染后立即更新
@@ -2055,7 +2003,7 @@ const handleScroll = (e: Event) => {
2055
2003
  // 如果没有启用滚动翻页,立即清除标记
2056
2004
  delete container.dataset.syncingScroll;
2057
2005
  }
2058
-
2006
+
2059
2007
  // 同步滚动时,不执行其他逻辑(如隐藏批注按钮等)
2060
2008
  return;
2061
2009
  }
@@ -2096,6 +2044,7 @@ const handleScroll = (e: Event) => {
2096
2044
  }
2097
2045
  };
2098
2046
 
2047
+
2099
2048
  // 记录上次滚动位置,用于判断滚动方向
2100
2049
  let lastScrollTop = 0;
2101
2050
 
@@ -2302,7 +2251,7 @@ const highlightPosition = (
2302
2251
  const isVisible = isElementVisible(elementRef, containerRef.value);
2303
2252
  if (!isVisible) {
2304
2253
  // 标记这是定位滚动,不应该被同步滚动干扰
2305
- containerRef.value.dataset.pageScrolling = "true";
2254
+ containerRef.value.dataset.pageScrolling = 'true';
2306
2255
  elementRef.scrollIntoView({ behavior: "smooth", block: "center" });
2307
2256
  // 延迟清除标记,确保滚动完成
2308
2257
  setTimeout(() => {
@@ -2367,9 +2316,7 @@ const jumpToPosition = (
2367
2316
  retryCount++;
2368
2317
  setTimeout(tryHighlight, retryDelay);
2369
2318
  } else {
2370
- console.warn(
2371
- `无法找到并高亮指定位置: 页码 ${pageNum}, bbox: [${bbox.join(", ")}]`
2372
- );
2319
+ console.warn(`无法找到并高亮指定位置: 页码 ${pageNum}, bbox: [${bbox.join(", ")}]`);
2373
2320
  }
2374
2321
  };
2375
2322
 
@@ -2479,7 +2426,7 @@ const handleContainerResize = () => {
2479
2426
  lastContainerWidth = currentWidth;
2480
2427
  }
2481
2428
 
2482
- console.log("[ImagePreview] handleContainerResize 被调用:", {
2429
+ console.log('[ImagePreview] handleContainerResize 被调用:', {
2483
2430
  autoFitWidth: props.autoFitWidth,
2484
2431
  isUserZooming: isUserZooming.value,
2485
2432
  isImageReady: isImageReady.value,
@@ -2498,25 +2445,23 @@ const handleContainerResize = () => {
2498
2445
  }
2499
2446
 
2500
2447
  // 宽度变化时不显示 loading,只更新缩放比例(避免看起来像重新加载)
2501
- console.log("[ImagePreview] handleContainerResize: 开始重新计算");
2448
+ console.log('[ImagePreview] handleContainerResize: 开始重新计算');
2502
2449
 
2503
2450
  // 立即计算并应用新的缩放比例,避免过渡期间露出底色
2504
2451
  // 使用 requestAnimationFrame 确保在浏览器重绘前更新
2505
2452
  requestAnimationFrame(() => {
2506
2453
  try {
2507
- console.log(
2508
- "[ImagePreview] handleContainerResize: 开始计算自适应宽度..."
2509
- );
2454
+ console.log('[ImagePreview] handleContainerResize: 开始计算自适应宽度...');
2510
2455
  const newScale = calculateAutoFitScale();
2511
- console.log("[ImagePreview] handleContainerResize: 计算结果:", newScale);
2512
-
2456
+ console.log('[ImagePreview] handleContainerResize: 计算结果:', newScale);
2457
+
2513
2458
  if (newScale > 0) {
2514
2459
  // 即使变化很小也立即更新,确保过渡期间图片始终填满容器
2515
2460
  scale.value = newScale;
2516
2461
  initialAutoFitScale.value = newScale;
2517
2462
  }
2518
2463
  } catch (error) {
2519
- console.error("[ImagePreview] handleContainerResize: 计算失败:", error);
2464
+ console.error('[ImagePreview] handleContainerResize: 计算失败:', error);
2520
2465
  }
2521
2466
 
2522
2467
  // 在过渡动画完成后再次检查,确保最终状态正确(处理过渡动画期间的连续变化)
@@ -2528,13 +2473,10 @@ const handleContainerResize = () => {
2528
2473
  initialAutoFitScale.value = finalScale;
2529
2474
  }
2530
2475
  } catch (error) {
2531
- console.error(
2532
- "[ImagePreview] handleContainerResize: 最终计算失败:",
2533
- error
2534
- );
2476
+ console.error('[ImagePreview] handleContainerResize: 最终计算失败:', error);
2535
2477
  } finally {
2536
2478
  // 计算完成,重置标记(不改变图片显示状态,因为宽度变化时不应该显示loading)
2537
- console.log("[ImagePreview] handleContainerResize: 更新状态完成");
2479
+ console.log('[ImagePreview] handleContainerResize: 更新状态完成');
2538
2480
  isResizing = false; // 重置标记
2539
2481
  }
2540
2482
  }, 350); // 350ms 延迟,略大于过渡动画时间(300ms),确保过渡完成后稳定
@@ -2553,14 +2495,14 @@ onMounted(() => {
2553
2495
  // 隐藏图片,显示 loading
2554
2496
  isImageReady.value = false;
2555
2497
  isCalculatingAutoFit.value = true;
2556
-
2498
+
2557
2499
  // 设置超时保护,防止一直显示 loading(最多等待 3 秒)
2558
2500
  const timeoutId = setTimeout(() => {
2559
- console.warn("自适应宽度计算超时,强制显示图片");
2501
+ console.warn('自适应宽度计算超时,强制显示图片');
2560
2502
  isCalculatingAutoFit.value = false;
2561
2503
  isImageReady.value = true;
2562
2504
  }, 3000);
2563
-
2505
+
2564
2506
  nextTick(() => {
2565
2507
  nextTick(() => {
2566
2508
  setTimeout(() => {
@@ -2571,7 +2513,7 @@ onMounted(() => {
2571
2513
  initialAutoFitScale.value = autoScale;
2572
2514
  }
2573
2515
  } catch (error) {
2574
- console.warn("计算自适应宽度失败:", error);
2516
+ console.warn('计算自适应宽度失败:', error);
2575
2517
  } finally {
2576
2518
  // 清除超时保护
2577
2519
  clearTimeout(timeoutId);
@@ -2592,7 +2534,7 @@ onMounted(() => {
2592
2534
 
2593
2535
  // 监听容器尺寸变化(用于响应外部收起/展开操作)
2594
2536
  nextTick(() => {
2595
- if (containerRef.value && typeof ResizeObserver !== "undefined") {
2537
+ if (containerRef.value && typeof ResizeObserver !== 'undefined') {
2596
2538
  resizeObserver = new ResizeObserver((entries) => {
2597
2539
  // 使用防抖,避免频繁触发
2598
2540
  if (resizeDebounceTimer) {
@@ -2609,7 +2551,7 @@ onMounted(() => {
2609
2551
  resizeObserver.observe(containerRef.value);
2610
2552
  } else {
2611
2553
  // 降级方案:监听窗口大小变化
2612
- window.addEventListener("resize", handleContainerResize);
2554
+ window.addEventListener('resize', handleContainerResize);
2613
2555
  }
2614
2556
  });
2615
2557
  });
@@ -2639,7 +2581,7 @@ onBeforeUnmount(() => {
2639
2581
  resizeObserver = null;
2640
2582
  }
2641
2583
  // 移除窗口 resize 监听器(降级方案)
2642
- window.removeEventListener("resize", handleContainerResize);
2584
+ window.removeEventListener('resize', handleContainerResize);
2643
2585
  });
2644
2586
 
2645
2587
  defineExpose({