@koi-br/ocr-web-sdk 1.0.44 → 1.0.45
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/dist/{index-DwGxSvlf.mjs → index-Dr29vR47.mjs} +1553 -1484
- package/dist/{index-Ba59hLsq.js → index-oglARFSv.js} +37 -37
- package/dist/index.cjs.js +2 -2
- package/dist/index.esm.js +2 -2
- package/dist/{tiff.min-DGhJVJ1H.js → tiff.min-BZS1xd8w.js} +1 -1
- package/dist/{tiff.min-ds7NqR52.mjs → tiff.min-FlWYar5H.mjs} +1 -1
- package/package.json +1 -1
- package/preview/ImagePreview.vue +169 -104
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { g as getAugmentedNamespace, a as getDefaultExportFromCjs } from "./index-
|
|
1
|
+
import { g as getAugmentedNamespace, a as getDefaultExportFromCjs } from "./index-Dr29vR47.mjs";
|
|
2
2
|
function _mergeNamespaces(U, W) {
|
|
3
3
|
for (var Z = 0; Z < W.length; Z++) {
|
|
4
4
|
const s0 = W[Z];
|
package/package.json
CHANGED
package/preview/ImagePreview.vue
CHANGED
|
@@ -12,12 +12,7 @@
|
|
|
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
|
|
16
|
-
v-if="showResetButton"
|
|
17
|
-
mini
|
|
18
|
-
position="bottom"
|
|
19
|
-
content="重置"
|
|
20
|
-
>
|
|
15
|
+
<ATooltip v-if="showResetButton" mini position="bottom" content="重置">
|
|
21
16
|
<AButton size="small" type="outline" @click="reset">
|
|
22
17
|
<RefreshCcw :size="16" />
|
|
23
18
|
</AButton>
|
|
@@ -96,7 +91,7 @@
|
|
|
96
91
|
<AButton
|
|
97
92
|
size="small"
|
|
98
93
|
type="outline"
|
|
99
|
-
style="padding-right: 0px
|
|
94
|
+
style="padding-right: 0px"
|
|
100
95
|
:disabled="currentPage >= totalPages"
|
|
101
96
|
@click="goToNextPage"
|
|
102
97
|
>
|
|
@@ -121,9 +116,9 @@
|
|
|
121
116
|
<div class="loading-spinner"></div>
|
|
122
117
|
<div class="loading-text">加载中...</div>
|
|
123
118
|
</div>
|
|
124
|
-
|
|
125
|
-
<div
|
|
126
|
-
class="image-wrapper-container"
|
|
119
|
+
|
|
120
|
+
<div
|
|
121
|
+
class="image-wrapper-container"
|
|
127
122
|
:style="containerStyle"
|
|
128
123
|
:class="{ 'image-hidden': !isImageReady && autoFitWidth }"
|
|
129
124
|
>
|
|
@@ -162,7 +157,10 @@
|
|
|
162
157
|
|
|
163
158
|
<!-- 文本图层(用于文本块选择和定位) -->
|
|
164
159
|
<div
|
|
165
|
-
v-if="
|
|
160
|
+
v-if="
|
|
161
|
+
getPageBlocksData(pageIndex + 1) &&
|
|
162
|
+
getPageBlocksData(pageIndex + 1).length > 0
|
|
163
|
+
"
|
|
166
164
|
:ref="(el) => setTextLayerRef(el, pageIndex + 1)"
|
|
167
165
|
class="text-layer"
|
|
168
166
|
></div>
|
|
@@ -407,22 +405,22 @@ const isImageReady = ref(false); // 标记图片是否已准备好显示(自
|
|
|
407
405
|
watch(
|
|
408
406
|
() => imageUrls.value,
|
|
409
407
|
(newUrls, oldUrls) => {
|
|
410
|
-
console.log(
|
|
408
|
+
console.log("[ImagePreview] imageUrls changed:", {
|
|
411
409
|
newUrls: newUrls?.length,
|
|
412
410
|
oldUrls: oldUrls?.length,
|
|
413
411
|
autoFitWidth: props.autoFitWidth,
|
|
414
412
|
isImageReady: isImageReady.value,
|
|
415
413
|
isCalculatingAutoFit: isCalculatingAutoFit.value,
|
|
416
414
|
});
|
|
417
|
-
|
|
415
|
+
|
|
418
416
|
// 如果有新的图片URL,且启用自适应宽度,立即隐藏图片
|
|
419
417
|
if (newUrls && newUrls.length > 0 && props.autoFitWidth) {
|
|
420
|
-
console.log(
|
|
418
|
+
console.log("[ImagePreview] 设置图片隐藏,等待自适应宽度计算");
|
|
421
419
|
isImageReady.value = false;
|
|
422
420
|
isCalculatingAutoFit.value = true;
|
|
423
421
|
} else if (!props.autoFitWidth) {
|
|
424
422
|
// 如果没有启用自适应宽度,立即显示
|
|
425
|
-
console.log(
|
|
423
|
+
console.log("[ImagePreview] 未启用自适应宽度,立即显示图片");
|
|
426
424
|
isImageReady.value = true;
|
|
427
425
|
isCalculatingAutoFit.value = false;
|
|
428
426
|
}
|
|
@@ -488,10 +486,10 @@ const imageSize = computed({
|
|
|
488
486
|
// 在自适应宽度模式下,使用第一页的尺寸;否则使用当前页的尺寸
|
|
489
487
|
const scaledImageSize = computed(() => {
|
|
490
488
|
// 如果启用自适应宽度,使用第一页的尺寸作为基准
|
|
491
|
-
const baseSize = props.autoFitWidth
|
|
492
|
-
?
|
|
489
|
+
const baseSize = props.autoFitWidth
|
|
490
|
+
? imageSizes.get(1) || { width: 0, height: 0 }
|
|
493
491
|
: imageSize.value;
|
|
494
|
-
|
|
492
|
+
|
|
495
493
|
if (baseSize.width === 0 || baseSize.height === 0) {
|
|
496
494
|
return { width: 0, height: 0 };
|
|
497
495
|
}
|
|
@@ -537,6 +535,7 @@ const annotationInput = ref(""); // 批注输入内容
|
|
|
537
535
|
const currentAnnotationBlock = ref<{
|
|
538
536
|
bbox: [number, number, number, number];
|
|
539
537
|
content: string;
|
|
538
|
+
annotationId?: string; // 已有批注的ID(如果存在)
|
|
540
539
|
} | null>(null); // 当前正在添加批注的文本块
|
|
541
540
|
const annotationPopupRef = ref<HTMLElement>(); // 批注弹窗引用
|
|
542
541
|
|
|
@@ -586,7 +585,7 @@ const getPageScaledSize = (pageNo: number) => {
|
|
|
586
585
|
const getPageContainerStyle = (pageNo: number) => {
|
|
587
586
|
const scaledSize = getPageScaledSize(pageNo);
|
|
588
587
|
return {
|
|
589
|
-
height: scaledSize.height > 0 ? `${scaledSize.height}px` :
|
|
588
|
+
height: scaledSize.height > 0 ? `${scaledSize.height}px` : "auto",
|
|
590
589
|
};
|
|
591
590
|
};
|
|
592
591
|
|
|
@@ -884,7 +883,7 @@ const switchToPage = (page: number) => {
|
|
|
884
883
|
) as HTMLElement;
|
|
885
884
|
if (pageElement) {
|
|
886
885
|
// 标记这是翻页滚动,不应该被同步滚动干扰
|
|
887
|
-
containerRef.value.dataset.pageScrolling =
|
|
886
|
+
containerRef.value.dataset.pageScrolling = "true";
|
|
888
887
|
pageElement.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
889
888
|
// 更新 lastScrollTop,确保滚动方向判断准确
|
|
890
889
|
nextTick(() => {
|
|
@@ -919,7 +918,7 @@ const reset = () => {
|
|
|
919
918
|
annotationInput.value = "";
|
|
920
919
|
activeBlockDiv.value = null;
|
|
921
920
|
isHighlighted.value = false;
|
|
922
|
-
|
|
921
|
+
|
|
923
922
|
// 清除已渲染页面集合
|
|
924
923
|
renderedPages.value.clear();
|
|
925
924
|
};
|
|
@@ -930,7 +929,7 @@ const original = () => {
|
|
|
930
929
|
|
|
931
930
|
// 计算自适应宽度的缩放比例
|
|
932
931
|
const calculateAutoFitScale = () => {
|
|
933
|
-
console.log(
|
|
932
|
+
console.log("[ImagePreview] calculateAutoFitScale 开始:", {
|
|
934
933
|
autoFitWidth: props.autoFitWidth,
|
|
935
934
|
hasContainerRef: !!containerRef.value,
|
|
936
935
|
containerRect: containerRef.value?.getBoundingClientRect(),
|
|
@@ -939,16 +938,19 @@ const calculateAutoFitScale = () => {
|
|
|
939
938
|
minScale: props.minScale,
|
|
940
939
|
maxScale: props.maxScale,
|
|
941
940
|
});
|
|
942
|
-
|
|
941
|
+
|
|
943
942
|
if (!props.autoFitWidth || !containerRef.value) {
|
|
944
|
-
console.log(
|
|
943
|
+
console.log("[ImagePreview] calculateAutoFitScale 返回 1 (条件不满足)");
|
|
945
944
|
return 1;
|
|
946
945
|
}
|
|
947
946
|
|
|
948
947
|
// 使用第一页的图片尺寸作为基准(所有页面使用相同的缩放比例)
|
|
949
948
|
const firstPageSize = imageSizes.get(1);
|
|
950
949
|
if (!firstPageSize || firstPageSize.width === 0) {
|
|
951
|
-
console.log(
|
|
950
|
+
console.log(
|
|
951
|
+
"[ImagePreview] calculateAutoFitScale 返回 1 (第一页尺寸无效)",
|
|
952
|
+
firstPageSize
|
|
953
|
+
);
|
|
952
954
|
return 1;
|
|
953
955
|
}
|
|
954
956
|
|
|
@@ -958,7 +960,10 @@ const calculateAutoFitScale = () => {
|
|
|
958
960
|
const containerWidth = containerRect.width - 4;
|
|
959
961
|
|
|
960
962
|
if (containerWidth <= 0) {
|
|
961
|
-
console.log(
|
|
963
|
+
console.log(
|
|
964
|
+
"[ImagePreview] calculateAutoFitScale 返回 1 (容器宽度无效)",
|
|
965
|
+
containerWidth
|
|
966
|
+
);
|
|
962
967
|
return 1;
|
|
963
968
|
}
|
|
964
969
|
|
|
@@ -969,15 +974,21 @@ const calculateAutoFitScale = () => {
|
|
|
969
974
|
const imageWidth = isRotated ? firstPageSize.height : firstPageSize.width;
|
|
970
975
|
|
|
971
976
|
if (imageWidth <= 0) {
|
|
972
|
-
console.log(
|
|
977
|
+
console.log(
|
|
978
|
+
"[ImagePreview] calculateAutoFitScale 返回 1 (图片宽度无效)",
|
|
979
|
+
imageWidth
|
|
980
|
+
);
|
|
973
981
|
return 1;
|
|
974
982
|
}
|
|
975
983
|
|
|
976
984
|
// 计算缩放比例,使图片宽度完全适应容器宽度
|
|
977
985
|
const calculatedScale = containerWidth / imageWidth;
|
|
978
|
-
const finalScale = Math.max(
|
|
979
|
-
|
|
980
|
-
|
|
986
|
+
const finalScale = Math.max(
|
|
987
|
+
props.minScale,
|
|
988
|
+
Math.min(props.maxScale, calculatedScale)
|
|
989
|
+
);
|
|
990
|
+
|
|
991
|
+
console.log("[ImagePreview] calculateAutoFitScale 计算结果:", {
|
|
981
992
|
containerWidth,
|
|
982
993
|
imageWidth,
|
|
983
994
|
calculatedScale,
|
|
@@ -991,8 +1002,8 @@ const calculateAutoFitScale = () => {
|
|
|
991
1002
|
// 图片加载完成处理
|
|
992
1003
|
const onImageLoad = (event: Event, pageNum: number) => {
|
|
993
1004
|
const img = event.target as HTMLImageElement;
|
|
994
|
-
|
|
995
|
-
console.log(
|
|
1005
|
+
|
|
1006
|
+
console.log("[ImagePreview] 图片加载完成:", {
|
|
996
1007
|
pageNum,
|
|
997
1008
|
naturalWidth: img.naturalWidth,
|
|
998
1009
|
naturalHeight: img.naturalHeight,
|
|
@@ -1000,7 +1011,7 @@ const onImageLoad = (event: Event, pageNum: number) => {
|
|
|
1000
1011
|
isImageReady: isImageReady.value,
|
|
1001
1012
|
isCalculatingAutoFit: isCalculatingAutoFit.value,
|
|
1002
1013
|
});
|
|
1003
|
-
|
|
1014
|
+
|
|
1004
1015
|
// 存储该页的图片尺寸
|
|
1005
1016
|
imageSizes.set(pageNum, {
|
|
1006
1017
|
width: img.naturalWidth,
|
|
@@ -1009,10 +1020,10 @@ const onImageLoad = (event: Event, pageNum: number) => {
|
|
|
1009
1020
|
|
|
1010
1021
|
// 如果是第一页且启用自适应宽度,计算并设置初始缩放比例
|
|
1011
1022
|
if (pageNum === 1 && props.autoFitWidth) {
|
|
1012
|
-
console.log(
|
|
1023
|
+
console.log("[ImagePreview] 第一页加载完成,开始计算自适应宽度");
|
|
1013
1024
|
// 重置用户缩放标记
|
|
1014
1025
|
isUserZooming.value = false;
|
|
1015
|
-
|
|
1026
|
+
|
|
1016
1027
|
// 确保图片是隐藏的(watch 已经设置了,这里再次确认)
|
|
1017
1028
|
if (!isImageReady.value) {
|
|
1018
1029
|
isCalculatingAutoFit.value = true;
|
|
@@ -1020,7 +1031,7 @@ const onImageLoad = (event: Event, pageNum: number) => {
|
|
|
1020
1031
|
|
|
1021
1032
|
// 设置超时保护,防止一直显示 loading(最多等待 3 秒)
|
|
1022
1033
|
const timeoutId = setTimeout(() => {
|
|
1023
|
-
console.warn(
|
|
1034
|
+
console.warn("自适应宽度计算超时,强制显示图片");
|
|
1024
1035
|
isCalculatingAutoFit.value = false;
|
|
1025
1036
|
isImageReady.value = true;
|
|
1026
1037
|
}, 3000);
|
|
@@ -1031,38 +1042,51 @@ const onImageLoad = (event: Event, pageNum: number) => {
|
|
|
1031
1042
|
// 添加小延迟确保容器完全渲染
|
|
1032
1043
|
setTimeout(() => {
|
|
1033
1044
|
try {
|
|
1034
|
-
console.log(
|
|
1045
|
+
console.log("[ImagePreview] onImageLoad: 开始计算自适应宽度...");
|
|
1035
1046
|
const autoScale = calculateAutoFitScale();
|
|
1036
|
-
console.log(
|
|
1047
|
+
console.log("[ImagePreview] onImageLoad: 自适应宽度计算结果:", {
|
|
1037
1048
|
autoScale,
|
|
1038
1049
|
containerRef: !!containerRef.value,
|
|
1039
|
-
containerWidth:
|
|
1050
|
+
containerWidth:
|
|
1051
|
+
containerRef.value?.getBoundingClientRect()?.width,
|
|
1040
1052
|
firstPageSize: imageSizes.get(1),
|
|
1041
1053
|
});
|
|
1042
|
-
|
|
1054
|
+
|
|
1043
1055
|
if (autoScale > 0) {
|
|
1044
1056
|
scale.value = autoScale;
|
|
1045
1057
|
initialAutoFitScale.value = autoScale; // 记录初始自适应缩放比例
|
|
1046
1058
|
// 记录当前容器宽度,用于后续 resize 检查
|
|
1047
1059
|
if (containerRef.value) {
|
|
1048
|
-
lastContainerWidth =
|
|
1060
|
+
lastContainerWidth =
|
|
1061
|
+
containerRef.value.getBoundingClientRect().width;
|
|
1049
1062
|
}
|
|
1050
|
-
console.log(
|
|
1063
|
+
console.log(
|
|
1064
|
+
"[ImagePreview] onImageLoad: 缩放比例已设置:",
|
|
1065
|
+
autoScale
|
|
1066
|
+
);
|
|
1051
1067
|
} else {
|
|
1052
|
-
console.warn(
|
|
1068
|
+
console.warn(
|
|
1069
|
+
"[ImagePreview] onImageLoad: 计算出的缩放比例无效:",
|
|
1070
|
+
autoScale
|
|
1071
|
+
);
|
|
1053
1072
|
}
|
|
1054
1073
|
} catch (error) {
|
|
1055
|
-
console.error(
|
|
1074
|
+
console.error(
|
|
1075
|
+
"[ImagePreview] onImageLoad: 计算自适应宽度失败:",
|
|
1076
|
+
error
|
|
1077
|
+
);
|
|
1056
1078
|
} finally {
|
|
1057
1079
|
// 清除超时保护
|
|
1058
1080
|
clearTimeout(timeoutId);
|
|
1059
|
-
console.log(
|
|
1081
|
+
console.log(
|
|
1082
|
+
"[ImagePreview] onImageLoad: 更新状态: isCalculatingAutoFit = false, isImageReady = true"
|
|
1083
|
+
);
|
|
1060
1084
|
// 无论计算结果如何,都要更新状态,避免一直显示 loading
|
|
1061
1085
|
isCalculatingAutoFit.value = false;
|
|
1062
1086
|
// 使用 requestAnimationFrame 确保在下一帧显示,避免闪烁
|
|
1063
1087
|
requestAnimationFrame(() => {
|
|
1064
1088
|
isImageReady.value = true;
|
|
1065
|
-
console.log(
|
|
1089
|
+
console.log("[ImagePreview] onImageLoad: 图片已准备好显示");
|
|
1066
1090
|
});
|
|
1067
1091
|
}
|
|
1068
1092
|
}, 100); // 增加延迟,确保所有图片都已加载
|
|
@@ -1073,7 +1097,7 @@ const onImageLoad = (event: Event, pageNum: number) => {
|
|
|
1073
1097
|
isImageReady.value = true;
|
|
1074
1098
|
isCalculatingAutoFit.value = false;
|
|
1075
1099
|
}
|
|
1076
|
-
|
|
1100
|
+
|
|
1077
1101
|
// 如果第一页已经加载完成,且当前页不是第一页,也应用自适应宽度
|
|
1078
1102
|
if (pageNum > 1 && props.autoFitWidth && initialAutoFitScale.value !== null) {
|
|
1079
1103
|
// 确保后续页面也使用相同的缩放比例
|
|
@@ -1286,7 +1310,7 @@ const renderTextLayer = (pageNum?: number) => {
|
|
|
1286
1310
|
// 清除当前页面所有文本块的高亮样式(除了当前文本块)
|
|
1287
1311
|
// 这样可以防止多个文本块同时高亮的竞态条件
|
|
1288
1312
|
clearAllHighlights(blockDiv);
|
|
1289
|
-
|
|
1313
|
+
|
|
1290
1314
|
// 清除跳转高亮标志(如果之前是通过跳转高亮的)
|
|
1291
1315
|
if (isHighlighted.value) {
|
|
1292
1316
|
isHighlighted.value = false;
|
|
@@ -1299,11 +1323,23 @@ const renderTextLayer = (pageNum?: number) => {
|
|
|
1299
1323
|
const existingAnnotation = getAnnotationForBlock(bbox, targetPage);
|
|
1300
1324
|
if (existingAnnotation) {
|
|
1301
1325
|
// 如果有批注,保持批注背景色,但添加蓝色边框表示 hover
|
|
1302
|
-
blockDiv.style.setProperty(
|
|
1303
|
-
|
|
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
|
+
);
|
|
1304
1336
|
blockDiv.style.setProperty("border-radius", "3px", "important");
|
|
1305
1337
|
blockDiv.style.setProperty("padding", "1px 3px", "important");
|
|
1306
|
-
blockDiv.style.setProperty(
|
|
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
|
+
);
|
|
1307
1343
|
} else {
|
|
1308
1344
|
// 如果没有批注,先清除可能残留的样式,再设置 hover 样式(与 PdfPreview 保持一致)
|
|
1309
1345
|
blockDiv.style.removeProperty("background-color");
|
|
@@ -1376,7 +1412,7 @@ const showAnnotationButtonForBlock = (
|
|
|
1376
1412
|
|
|
1377
1413
|
try {
|
|
1378
1414
|
const bbox = JSON.parse(bboxStr) as [number, number, number, number];
|
|
1379
|
-
|
|
1415
|
+
|
|
1380
1416
|
// 从 blocksData 中查找对应的 content
|
|
1381
1417
|
const pageBlocksData = getPageBlocksData(currentPage.value);
|
|
1382
1418
|
const blockData = pageBlocksData.find((block) => {
|
|
@@ -1389,7 +1425,7 @@ const showAnnotationButtonForBlock = (
|
|
|
1389
1425
|
Math.abs(y2 - bbox[3]) < tolerance
|
|
1390
1426
|
);
|
|
1391
1427
|
});
|
|
1392
|
-
|
|
1428
|
+
|
|
1393
1429
|
if (!blockData) return;
|
|
1394
1430
|
|
|
1395
1431
|
const rect = blockDiv.getBoundingClientRect();
|
|
@@ -1477,7 +1513,10 @@ const showAnnotationButtonForBlock = (
|
|
|
1477
1513
|
* @param excludeBlockDiv 要排除的文本块(不清除它的样式)
|
|
1478
1514
|
* @param pageNum 要清除的页码,如果不提供则从 excludeBlockDiv 中获取
|
|
1479
1515
|
*/
|
|
1480
|
-
const clearAllHighlights = (
|
|
1516
|
+
const clearAllHighlights = (
|
|
1517
|
+
excludeBlockDiv?: HTMLElement,
|
|
1518
|
+
pageNum?: number
|
|
1519
|
+
) => {
|
|
1481
1520
|
// 确定要清除的页码
|
|
1482
1521
|
let targetPage = pageNum;
|
|
1483
1522
|
if (!targetPage && excludeBlockDiv) {
|
|
@@ -1511,11 +1550,23 @@ const clearAllHighlights = (excludeBlockDiv?: HTMLElement, pageNum?: number) =>
|
|
|
1511
1550
|
|
|
1512
1551
|
if (existingAnnotation) {
|
|
1513
1552
|
// 如果有批注,恢复批注样式(不使用 hover 样式)
|
|
1514
|
-
el.style.setProperty(
|
|
1515
|
-
|
|
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
|
+
);
|
|
1516
1563
|
el.style.setProperty("border-radius", "3px", "important");
|
|
1517
1564
|
el.style.setProperty("padding", "1px 3px", "important");
|
|
1518
|
-
el.style.setProperty(
|
|
1565
|
+
el.style.setProperty(
|
|
1566
|
+
"box-shadow",
|
|
1567
|
+
"0 1px 2px rgba(255, 193, 7, 0.25)",
|
|
1568
|
+
"important"
|
|
1569
|
+
);
|
|
1519
1570
|
} else {
|
|
1520
1571
|
// 如果没有批注,清除所有高亮样式
|
|
1521
1572
|
el.style.backgroundColor = "transparent";
|
|
@@ -1579,17 +1630,29 @@ const restoreBlockStyle = (blockDiv: HTMLElement) => {
|
|
|
1579
1630
|
|
|
1580
1631
|
if (existingAnnotation) {
|
|
1581
1632
|
// 如果有批注,保持批注样式(使用 !important 确保优先级)
|
|
1582
|
-
blockDiv.style.setProperty(
|
|
1583
|
-
|
|
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
|
+
);
|
|
1584
1643
|
blockDiv.style.setProperty("border-radius", "3px", "important");
|
|
1585
1644
|
blockDiv.style.setProperty("padding", "1px 3px", "important");
|
|
1586
|
-
blockDiv.style.setProperty(
|
|
1587
|
-
|
|
1645
|
+
blockDiv.style.setProperty(
|
|
1646
|
+
"box-shadow",
|
|
1647
|
+
"0 1px 2px rgba(255, 193, 7, 0.25)",
|
|
1648
|
+
"important"
|
|
1649
|
+
);
|
|
1650
|
+
|
|
1588
1651
|
// 确保元素可见
|
|
1589
1652
|
blockDiv.style.setProperty("display", "block", "important");
|
|
1590
1653
|
blockDiv.style.setProperty("visibility", "visible", "important");
|
|
1591
1654
|
blockDiv.style.setProperty("opacity", "1", "important");
|
|
1592
|
-
|
|
1655
|
+
|
|
1593
1656
|
console.log("restoreBlockStyle: 已设置批注样式", {
|
|
1594
1657
|
backgroundColor: blockDiv.style.backgroundColor,
|
|
1595
1658
|
border: blockDiv.style.border,
|
|
@@ -1639,12 +1702,7 @@ const hideAnnotationButton = () => {
|
|
|
1639
1702
|
const bboxStr = activeBlockDiv.value.dataset.bbox;
|
|
1640
1703
|
if (bboxStr) {
|
|
1641
1704
|
try {
|
|
1642
|
-
const bbox = JSON.parse(bboxStr) as [
|
|
1643
|
-
number,
|
|
1644
|
-
number,
|
|
1645
|
-
number,
|
|
1646
|
-
number
|
|
1647
|
-
];
|
|
1705
|
+
const bbox = JSON.parse(bboxStr) as [number, number, number, number];
|
|
1648
1706
|
const existingAnnotation = getAnnotationForBlock(bbox);
|
|
1649
1707
|
if (existingAnnotation) {
|
|
1650
1708
|
// 如果有批注,恢复批注样式
|
|
@@ -1787,6 +1845,7 @@ const openAnnotationInput = (e?: Event) => {
|
|
|
1787
1845
|
currentAnnotationBlock.value = {
|
|
1788
1846
|
bbox,
|
|
1789
1847
|
content: selectedText,
|
|
1848
|
+
annotationId: existingAnnotation?.id, // 保存已有批注的ID
|
|
1790
1849
|
};
|
|
1791
1850
|
|
|
1792
1851
|
// 确保弹窗显示
|
|
@@ -1896,7 +1955,7 @@ const closeAnnotationInput = () => {
|
|
|
1896
1955
|
currentAnnotationBlock.value = null;
|
|
1897
1956
|
annotationInput.value = "";
|
|
1898
1957
|
showAnnotationPopup.value = false;
|
|
1899
|
-
|
|
1958
|
+
|
|
1900
1959
|
// 关闭批注输入弹窗后,恢复文本块的样式
|
|
1901
1960
|
if (activeBlockDiv.value && !isHighlighted.value) {
|
|
1902
1961
|
restoreBlockStyle(activeBlockDiv.value);
|
|
@@ -1928,16 +1987,22 @@ const saveAnnotation = () => {
|
|
|
1928
1987
|
return;
|
|
1929
1988
|
}
|
|
1930
1989
|
|
|
1931
|
-
const { bbox, content } = currentAnnotationBlock.value;
|
|
1990
|
+
const { bbox, content, annotationId } = currentAnnotationBlock.value;
|
|
1932
1991
|
const annotationContent = annotationInput.value.trim();
|
|
1933
1992
|
|
|
1934
|
-
//
|
|
1935
|
-
|
|
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
|
+
}
|
|
1936
2003
|
|
|
1937
2004
|
const annotation: AnnotationInfo = {
|
|
1938
|
-
id:
|
|
1939
|
-
existingAnnotation?.id ||
|
|
1940
|
-
`annotation_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`,
|
|
2005
|
+
id: existingAnnotation?.id || "",
|
|
1941
2006
|
blockBbox: bbox,
|
|
1942
2007
|
blockContent: content,
|
|
1943
2008
|
blockPage: currentPage.value,
|
|
@@ -1969,9 +2034,9 @@ const saveAnnotation = () => {
|
|
|
1969
2034
|
*/
|
|
1970
2035
|
const handleScroll = (e: Event) => {
|
|
1971
2036
|
const container = e.target as HTMLElement;
|
|
1972
|
-
|
|
2037
|
+
|
|
1973
2038
|
// 检查是否是同步滚动触发的
|
|
1974
|
-
const isSyncing = container?.dataset?.syncingScroll ===
|
|
2039
|
+
const isSyncing = container?.dataset?.syncingScroll === "true";
|
|
1975
2040
|
if (isSyncing) {
|
|
1976
2041
|
// 即使是被同步滚动触发的,也应该立即更新页码(但不触发翻页动画)
|
|
1977
2042
|
// 使用 requestAnimationFrame 确保在浏览器渲染后立即更新
|
|
@@ -1990,7 +2055,7 @@ const handleScroll = (e: Event) => {
|
|
|
1990
2055
|
// 如果没有启用滚动翻页,立即清除标记
|
|
1991
2056
|
delete container.dataset.syncingScroll;
|
|
1992
2057
|
}
|
|
1993
|
-
|
|
2058
|
+
|
|
1994
2059
|
// 同步滚动时,不执行其他逻辑(如隐藏批注按钮等)
|
|
1995
2060
|
return;
|
|
1996
2061
|
}
|
|
@@ -2031,7 +2096,6 @@ const handleScroll = (e: Event) => {
|
|
|
2031
2096
|
}
|
|
2032
2097
|
};
|
|
2033
2098
|
|
|
2034
|
-
|
|
2035
2099
|
// 记录上次滚动位置,用于判断滚动方向
|
|
2036
2100
|
let lastScrollTop = 0;
|
|
2037
2101
|
|
|
@@ -2238,7 +2302,7 @@ const highlightPosition = (
|
|
|
2238
2302
|
const isVisible = isElementVisible(elementRef, containerRef.value);
|
|
2239
2303
|
if (!isVisible) {
|
|
2240
2304
|
// 标记这是定位滚动,不应该被同步滚动干扰
|
|
2241
|
-
containerRef.value.dataset.pageScrolling =
|
|
2305
|
+
containerRef.value.dataset.pageScrolling = "true";
|
|
2242
2306
|
elementRef.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
2243
2307
|
// 延迟清除标记,确保滚动完成
|
|
2244
2308
|
setTimeout(() => {
|
|
@@ -2303,7 +2367,9 @@ const jumpToPosition = (
|
|
|
2303
2367
|
retryCount++;
|
|
2304
2368
|
setTimeout(tryHighlight, retryDelay);
|
|
2305
2369
|
} else {
|
|
2306
|
-
console.warn(
|
|
2370
|
+
console.warn(
|
|
2371
|
+
`无法找到并高亮指定位置: 页码 ${pageNum}, bbox: [${bbox.join(", ")}]`
|
|
2372
|
+
);
|
|
2307
2373
|
}
|
|
2308
2374
|
};
|
|
2309
2375
|
|
|
@@ -2413,7 +2479,7 @@ const handleContainerResize = () => {
|
|
|
2413
2479
|
lastContainerWidth = currentWidth;
|
|
2414
2480
|
}
|
|
2415
2481
|
|
|
2416
|
-
console.log(
|
|
2482
|
+
console.log("[ImagePreview] handleContainerResize 被调用:", {
|
|
2417
2483
|
autoFitWidth: props.autoFitWidth,
|
|
2418
2484
|
isUserZooming: isUserZooming.value,
|
|
2419
2485
|
isImageReady: isImageReady.value,
|
|
@@ -2431,26 +2497,26 @@ const handleContainerResize = () => {
|
|
|
2431
2497
|
resizeTimer = null;
|
|
2432
2498
|
}
|
|
2433
2499
|
|
|
2434
|
-
//
|
|
2435
|
-
console.log(
|
|
2436
|
-
isImageReady.value = false;
|
|
2437
|
-
isCalculatingAutoFit.value = true;
|
|
2500
|
+
// 宽度变化时不显示 loading,只更新缩放比例(避免看起来像重新加载)
|
|
2501
|
+
console.log("[ImagePreview] handleContainerResize: 开始重新计算");
|
|
2438
2502
|
|
|
2439
2503
|
// 立即计算并应用新的缩放比例,避免过渡期间露出底色
|
|
2440
2504
|
// 使用 requestAnimationFrame 确保在浏览器重绘前更新
|
|
2441
2505
|
requestAnimationFrame(() => {
|
|
2442
2506
|
try {
|
|
2443
|
-
console.log(
|
|
2507
|
+
console.log(
|
|
2508
|
+
"[ImagePreview] handleContainerResize: 开始计算自适应宽度..."
|
|
2509
|
+
);
|
|
2444
2510
|
const newScale = calculateAutoFitScale();
|
|
2445
|
-
console.log(
|
|
2446
|
-
|
|
2511
|
+
console.log("[ImagePreview] handleContainerResize: 计算结果:", newScale);
|
|
2512
|
+
|
|
2447
2513
|
if (newScale > 0) {
|
|
2448
2514
|
// 即使变化很小也立即更新,确保过渡期间图片始终填满容器
|
|
2449
2515
|
scale.value = newScale;
|
|
2450
2516
|
initialAutoFitScale.value = newScale;
|
|
2451
2517
|
}
|
|
2452
2518
|
} catch (error) {
|
|
2453
|
-
console.error(
|
|
2519
|
+
console.error("[ImagePreview] handleContainerResize: 计算失败:", error);
|
|
2454
2520
|
}
|
|
2455
2521
|
|
|
2456
2522
|
// 在过渡动画完成后再次检查,确保最终状态正确(处理过渡动画期间的连续变化)
|
|
@@ -2462,15 +2528,14 @@ const handleContainerResize = () => {
|
|
|
2462
2528
|
initialAutoFitScale.value = finalScale;
|
|
2463
2529
|
}
|
|
2464
2530
|
} catch (error) {
|
|
2465
|
-
console.error(
|
|
2531
|
+
console.error(
|
|
2532
|
+
"[ImagePreview] handleContainerResize: 最终计算失败:",
|
|
2533
|
+
error
|
|
2534
|
+
);
|
|
2466
2535
|
} finally {
|
|
2467
|
-
//
|
|
2468
|
-
console.log(
|
|
2469
|
-
isCalculatingAutoFit.value = false;
|
|
2536
|
+
// 计算完成,重置标记(不改变图片显示状态,因为宽度变化时不应该显示loading)
|
|
2537
|
+
console.log("[ImagePreview] handleContainerResize: 更新状态完成");
|
|
2470
2538
|
isResizing = false; // 重置标记
|
|
2471
|
-
requestAnimationFrame(() => {
|
|
2472
|
-
isImageReady.value = true;
|
|
2473
|
-
});
|
|
2474
2539
|
}
|
|
2475
2540
|
}, 350); // 350ms 延迟,略大于过渡动画时间(300ms),确保过渡完成后稳定
|
|
2476
2541
|
});
|
|
@@ -2488,14 +2553,14 @@ onMounted(() => {
|
|
|
2488
2553
|
// 隐藏图片,显示 loading
|
|
2489
2554
|
isImageReady.value = false;
|
|
2490
2555
|
isCalculatingAutoFit.value = true;
|
|
2491
|
-
|
|
2556
|
+
|
|
2492
2557
|
// 设置超时保护,防止一直显示 loading(最多等待 3 秒)
|
|
2493
2558
|
const timeoutId = setTimeout(() => {
|
|
2494
|
-
console.warn(
|
|
2559
|
+
console.warn("自适应宽度计算超时,强制显示图片");
|
|
2495
2560
|
isCalculatingAutoFit.value = false;
|
|
2496
2561
|
isImageReady.value = true;
|
|
2497
2562
|
}, 3000);
|
|
2498
|
-
|
|
2563
|
+
|
|
2499
2564
|
nextTick(() => {
|
|
2500
2565
|
nextTick(() => {
|
|
2501
2566
|
setTimeout(() => {
|
|
@@ -2506,7 +2571,7 @@ onMounted(() => {
|
|
|
2506
2571
|
initialAutoFitScale.value = autoScale;
|
|
2507
2572
|
}
|
|
2508
2573
|
} catch (error) {
|
|
2509
|
-
console.warn(
|
|
2574
|
+
console.warn("计算自适应宽度失败:", error);
|
|
2510
2575
|
} finally {
|
|
2511
2576
|
// 清除超时保护
|
|
2512
2577
|
clearTimeout(timeoutId);
|
|
@@ -2527,7 +2592,7 @@ onMounted(() => {
|
|
|
2527
2592
|
|
|
2528
2593
|
// 监听容器尺寸变化(用于响应外部收起/展开操作)
|
|
2529
2594
|
nextTick(() => {
|
|
2530
|
-
if (containerRef.value && typeof ResizeObserver !==
|
|
2595
|
+
if (containerRef.value && typeof ResizeObserver !== "undefined") {
|
|
2531
2596
|
resizeObserver = new ResizeObserver((entries) => {
|
|
2532
2597
|
// 使用防抖,避免频繁触发
|
|
2533
2598
|
if (resizeDebounceTimer) {
|
|
@@ -2544,7 +2609,7 @@ onMounted(() => {
|
|
|
2544
2609
|
resizeObserver.observe(containerRef.value);
|
|
2545
2610
|
} else {
|
|
2546
2611
|
// 降级方案:监听窗口大小变化
|
|
2547
|
-
window.addEventListener(
|
|
2612
|
+
window.addEventListener("resize", handleContainerResize);
|
|
2548
2613
|
}
|
|
2549
2614
|
});
|
|
2550
2615
|
});
|
|
@@ -2574,7 +2639,7 @@ onBeforeUnmount(() => {
|
|
|
2574
2639
|
resizeObserver = null;
|
|
2575
2640
|
}
|
|
2576
2641
|
// 移除窗口 resize 监听器(降级方案)
|
|
2577
|
-
window.removeEventListener(
|
|
2642
|
+
window.removeEventListener("resize", handleContainerResize);
|
|
2578
2643
|
});
|
|
2579
2644
|
|
|
2580
2645
|
defineExpose({
|