@koi-br/ocr-web-sdk 1.0.29 → 1.0.30
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-DG14_3TA.js → index-BQyoWLBi.js} +84 -84
- package/dist/{index-QOLKOKLO.mjs → index-CPXnfqe3.mjs} +8674 -8644
- package/dist/index.cjs.js +2 -2
- package/dist/index.esm.js +2 -2
- package/dist/{tiff.min-D0JMbw7k.mjs → tiff.min-CDFWZFIu.mjs} +1 -1
- package/dist/{tiff.min-Com-G-pb.js → tiff.min-DXit1zYs.js} +1 -1
- package/package.json +1 -1
- package/preview/ImagePreview.vue +253 -132
package/package.json
CHANGED
package/preview/ImagePreview.vue
CHANGED
|
@@ -395,6 +395,9 @@ const annotationButtonRef = ref<HTMLElement>();
|
|
|
395
395
|
// 图片尺寸(存储每页的尺寸)
|
|
396
396
|
const imageSizes = new Map<number, { width: number; height: number }>();
|
|
397
397
|
|
|
398
|
+
// 已渲染的页面集合(用于跟踪哪些页面已渲染)
|
|
399
|
+
const renderedPages = ref<Set<number>>(new Set());
|
|
400
|
+
|
|
398
401
|
// 设置图片引用
|
|
399
402
|
const setImageRef = (el: any, pageNum: number) => {
|
|
400
403
|
if (el) {
|
|
@@ -823,6 +826,9 @@ const reset = () => {
|
|
|
823
826
|
annotationInput.value = "";
|
|
824
827
|
activeBlockDiv.value = null;
|
|
825
828
|
isHighlighted.value = false;
|
|
829
|
+
|
|
830
|
+
// 清除已渲染页面集合
|
|
831
|
+
renderedPages.value.clear();
|
|
826
832
|
};
|
|
827
833
|
|
|
828
834
|
const original = () => {
|
|
@@ -975,7 +981,7 @@ const calculateFontSize = (
|
|
|
975
981
|
};
|
|
976
982
|
|
|
977
983
|
/**
|
|
978
|
-
*
|
|
984
|
+
* 渲染文本图层
|
|
979
985
|
*/
|
|
980
986
|
const renderTextLayer = (pageNum?: number) => {
|
|
981
987
|
const targetPage = pageNum || currentPage.value;
|
|
@@ -991,33 +997,51 @@ const renderTextLayer = (pageNum?: number) => {
|
|
|
991
997
|
return;
|
|
992
998
|
}
|
|
993
999
|
|
|
994
|
-
|
|
995
|
-
|
|
1000
|
+
// 如果没有提供分块数据,跳过渲染(不标记为已渲染)
|
|
1001
|
+
if (!props.blocksData || props.blocksData.length === 0) {
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
996
1004
|
|
|
997
|
-
|
|
998
|
-
textLayer.style.width = `${image.naturalWidth}px`;
|
|
999
|
-
textLayer.style.height = `${image.naturalHeight}px`;
|
|
1005
|
+
const pageBlocksData = getPageBlocksData(targetPage);
|
|
1000
1006
|
|
|
1001
|
-
//
|
|
1007
|
+
// 如果当前页面没有数据,跳过渲染(不标记为已渲染)
|
|
1002
1008
|
if (!pageBlocksData || pageBlocksData.length === 0) {
|
|
1003
|
-
textLayer.innerHTML = "";
|
|
1004
1009
|
return;
|
|
1005
1010
|
}
|
|
1006
1011
|
|
|
1012
|
+
// 如果页面已渲染且用户正在交互,跳过渲染(避免打断用户)
|
|
1013
|
+
if (renderedPages.value.has(targetPage) && showAnnotationPopup.value) {
|
|
1014
|
+
return;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
// 如果页面已渲染,但当前有数据,需要检查是否真的渲染过
|
|
1018
|
+
// 如果textLayer是空的,说明之前没有真正渲染过,应该重新渲染
|
|
1019
|
+
if (renderedPages.value.has(targetPage) && textLayer.children.length === 0) {
|
|
1020
|
+
// 从已渲染集合中移除,允许重新渲染
|
|
1021
|
+
renderedPages.value.delete(targetPage);
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1007
1024
|
try {
|
|
1008
|
-
//
|
|
1009
|
-
|
|
1010
|
-
textLayer.
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
}
|
|
1015
|
-
});
|
|
1025
|
+
// 设置文本图层的尺寸与图片尺寸一致
|
|
1026
|
+
textLayer.style.width = `${image.naturalWidth}px`;
|
|
1027
|
+
textLayer.style.height = `${image.naturalHeight}px`;
|
|
1028
|
+
|
|
1029
|
+
// 清空文本图层
|
|
1030
|
+
textLayer.innerHTML = "";
|
|
1016
1031
|
|
|
1017
|
-
//
|
|
1018
|
-
|
|
1032
|
+
// 如果清空前有激活的文本块,且该文本块属于当前页面,则清除引用
|
|
1033
|
+
if (activeBlockDiv.value) {
|
|
1034
|
+
const activePageElement = activeBlockDiv.value.closest(
|
|
1035
|
+
".image-page-container"
|
|
1036
|
+
);
|
|
1037
|
+
const currentPageElement = textLayer.closest(".image-page-container");
|
|
1038
|
+
if (activePageElement === currentPageElement) {
|
|
1039
|
+
activeBlockDiv.value = null;
|
|
1040
|
+
showAnnotationPopup.value = false;
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1019
1043
|
|
|
1020
|
-
//
|
|
1044
|
+
// 使用 blocksData 数据创建可交互的块
|
|
1021
1045
|
pageBlocksData.forEach((block, index) => {
|
|
1022
1046
|
const { content, bbox } = block;
|
|
1023
1047
|
|
|
@@ -1026,14 +1050,11 @@ const renderTextLayer = (pageNum?: number) => {
|
|
|
1026
1050
|
const width = x2 - x1;
|
|
1027
1051
|
const height = y2 - y1;
|
|
1028
1052
|
|
|
1029
|
-
//
|
|
1030
|
-
const calculatedFontSize = calculateFontSize(content, width, height);
|
|
1031
|
-
|
|
1032
|
-
// 创建文本块
|
|
1053
|
+
// 创建文本块(不显示内容,只用于 hover 交互和批注功能)
|
|
1033
1054
|
const blockDiv = document.createElement("div");
|
|
1034
1055
|
blockDiv.className = "text-block";
|
|
1035
|
-
blockDiv.dataset.text = content;
|
|
1036
1056
|
blockDiv.dataset.bbox = JSON.stringify(bbox);
|
|
1057
|
+
blockDiv.dataset.page = String(targetPage);
|
|
1037
1058
|
|
|
1038
1059
|
// 设置基础样式
|
|
1039
1060
|
blockDiv.style.position = "absolute";
|
|
@@ -1041,38 +1062,16 @@ const renderTextLayer = (pageNum?: number) => {
|
|
|
1041
1062
|
blockDiv.style.top = `${y1}px`;
|
|
1042
1063
|
blockDiv.style.width = `${width}px`;
|
|
1043
1064
|
blockDiv.style.height = `${height}px`;
|
|
1044
|
-
blockDiv.style.zIndex = "20";
|
|
1045
|
-
blockDiv.style.cursor = "
|
|
1046
|
-
|
|
1047
|
-
blockDiv.
|
|
1048
|
-
|
|
1049
|
-
blockDiv.style.color = "red"; // 红色文字
|
|
1050
|
-
blockDiv.style.fontSize = `${calculatedFontSize}px`; // 使用计算出的字体大小
|
|
1051
|
-
blockDiv.style.fontFamily = "Arial, sans-serif"; // 设置明确的字体
|
|
1052
|
-
blockDiv.style.lineHeight = "1.2"; // 设置合适的行高
|
|
1053
|
-
blockDiv.style.whiteSpace = "pre-wrap"; // 保留换行和空格
|
|
1054
|
-
blockDiv.style.overflow = "visible"; // 确保文字不被裁剪
|
|
1055
|
-
blockDiv.style.display = "block"; // 确保是块级元素
|
|
1056
|
-
blockDiv.style.visibility = "visible"; // 确保可见
|
|
1057
|
-
// 允许文本选择
|
|
1058
|
-
blockDiv.style.userSelect = "text";
|
|
1059
|
-
blockDiv.style.webkitUserSelect = "text";
|
|
1060
|
-
blockDiv.style.mozUserSelect = "text";
|
|
1061
|
-
blockDiv.style.msUserSelect = "text";
|
|
1062
|
-
// 允许指针事件,但不阻止文本选择
|
|
1065
|
+
blockDiv.style.zIndex = "20";
|
|
1066
|
+
blockDiv.style.cursor = "pointer";
|
|
1067
|
+
blockDiv.style.borderRadius = "2px";
|
|
1068
|
+
blockDiv.style.transition = "all 0.2s ease";
|
|
1069
|
+
blockDiv.style.backgroundColor = "transparent";
|
|
1063
1070
|
blockDiv.style.pointerEvents = "auto";
|
|
1064
|
-
|
|
1065
|
-
// 右键菜单:显示批注按钮
|
|
1066
|
-
blockDiv.addEventListener("contextmenu", (e) => {
|
|
1067
|
-
e.preventDefault();
|
|
1068
|
-
// 设置当前文本块为激活状态
|
|
1069
|
-
activeBlockDiv.value = blockDiv;
|
|
1070
|
-
// 显示批注按钮
|
|
1071
|
-
showAnnotationButtonForBlock(e, blockDiv);
|
|
1072
|
-
});
|
|
1071
|
+
blockDiv.style.userSelect = "none";
|
|
1073
1072
|
|
|
1074
1073
|
// 检查是否有已有批注,如果有则显示批注标记
|
|
1075
|
-
const existingAnnotation = getAnnotationForBlock(bbox);
|
|
1074
|
+
const existingAnnotation = getAnnotationForBlock(bbox, targetPage);
|
|
1076
1075
|
if (existingAnnotation) {
|
|
1077
1076
|
// 添加批注标记样式类
|
|
1078
1077
|
blockDiv.classList.add("has-annotation");
|
|
@@ -1103,82 +1102,57 @@ const renderTextLayer = (pageNum?: number) => {
|
|
|
1103
1102
|
annotationMarker.style.boxShadow = "0 1px 3px rgba(0, 0, 0, 0.25)";
|
|
1104
1103
|
annotationMarker.style.lineHeight = "1";
|
|
1105
1104
|
blockDiv.appendChild(annotationMarker);
|
|
1106
|
-
} else {
|
|
1107
|
-
// 没有批注时设置为透明背景
|
|
1108
|
-
blockDiv.style.backgroundColor = "transparent";
|
|
1109
1105
|
}
|
|
1110
1106
|
|
|
1111
|
-
//
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
//
|
|
1120
|
-
|
|
1107
|
+
// Hover 和点击事件
|
|
1108
|
+
blockDiv.addEventListener("mouseenter", (e) => {
|
|
1109
|
+
// 取消之前的隐藏定时器
|
|
1110
|
+
if (hideTimer) {
|
|
1111
|
+
clearTimeout(hideTimer);
|
|
1112
|
+
hideTimer = null;
|
|
1113
|
+
}
|
|
1114
|
+
|
|
1115
|
+
// 如果有之前激活的文本块,先恢复其样式
|
|
1116
|
+
if (activeBlockDiv.value && activeBlockDiv.value !== blockDiv) {
|
|
1117
|
+
restoreBlockStyle(activeBlockDiv.value);
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
// 设置当前文本块为激活状态
|
|
1121
|
+
activeBlockDiv.value = blockDiv;
|
|
1122
|
+
|
|
1123
|
+
// 检查是否有批注,如果有批注,hover 时在批注样式基础上添加蓝色边框
|
|
1124
|
+
const existingAnnotation = getAnnotationForBlock(bbox, targetPage);
|
|
1121
1125
|
if (existingAnnotation) {
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
existingBlock.style.padding = "1px 3px";
|
|
1129
|
-
existingBlock.style.boxShadow = "0 1px 2px rgba(255, 193, 7, 0.25)";
|
|
1130
|
-
|
|
1131
|
-
// 添加批注标记(如果还没有)
|
|
1132
|
-
if (!existingBlock.querySelector(".annotation-marker")) {
|
|
1133
|
-
const annotationMarker = document.createElement("span");
|
|
1134
|
-
annotationMarker.className = "annotation-marker";
|
|
1135
|
-
annotationMarker.textContent = "📝";
|
|
1136
|
-
annotationMarker.style.position = "absolute";
|
|
1137
|
-
annotationMarker.style.top = "-6px";
|
|
1138
|
-
annotationMarker.style.right = "-6px";
|
|
1139
|
-
annotationMarker.style.fontSize = "11px";
|
|
1140
|
-
annotationMarker.style.backgroundColor = "rgba(255, 193, 7, 0.95)";
|
|
1141
|
-
annotationMarker.style.borderRadius = "50%";
|
|
1142
|
-
annotationMarker.style.width = "16px";
|
|
1143
|
-
annotationMarker.style.height = "16px";
|
|
1144
|
-
annotationMarker.style.display = "flex";
|
|
1145
|
-
annotationMarker.style.alignItems = "center";
|
|
1146
|
-
annotationMarker.style.justifyContent = "center";
|
|
1147
|
-
annotationMarker.style.zIndex = "30";
|
|
1148
|
-
annotationMarker.style.boxShadow = "0 1px 3px rgba(0, 0, 0, 0.25)";
|
|
1149
|
-
annotationMarker.style.lineHeight = "1";
|
|
1150
|
-
existingBlock.appendChild(annotationMarker);
|
|
1151
|
-
}
|
|
1152
|
-
}
|
|
1126
|
+
// 如果有批注,保持批注背景色,但添加蓝色边框表示 hover
|
|
1127
|
+
blockDiv.style.setProperty("background-color", "rgba(255, 243, 205, 0.5)", "important");
|
|
1128
|
+
blockDiv.style.setProperty("border", "2px solid rgba(30, 144, 255, 0.8)", "important");
|
|
1129
|
+
blockDiv.style.setProperty("border-radius", "3px", "important");
|
|
1130
|
+
blockDiv.style.setProperty("padding", "1px 3px", "important");
|
|
1131
|
+
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");
|
|
1153
1132
|
} else {
|
|
1154
|
-
//
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
existingBlock.style.boxShadow = "none";
|
|
1162
|
-
const marker = existingBlock.querySelector(".annotation-marker");
|
|
1163
|
-
if (marker) {
|
|
1164
|
-
marker.remove();
|
|
1165
|
-
}
|
|
1166
|
-
}
|
|
1133
|
+
// 如果没有批注,使用 hover 样式(与 PdfPreview 保持一致)
|
|
1134
|
+
blockDiv.style.backgroundColor =
|
|
1135
|
+
"var(--s-color-brand-primary-transparent-3, rgba(0, 102, 255, .15))";
|
|
1136
|
+
blockDiv.style.boxShadow = "0 0 0 2px rgba(30, 144, 255, 0.6)";
|
|
1137
|
+
blockDiv.style.borderRadius = "2px";
|
|
1138
|
+
blockDiv.style.padding = "1px 3px";
|
|
1139
|
+
blockDiv.style.border = "none";
|
|
1167
1140
|
}
|
|
1168
|
-
newBlocksMap.set(bboxKey, true);
|
|
1169
|
-
} else {
|
|
1170
|
-
// 如果不存在,创建新的文本块
|
|
1171
|
-
textLayer.appendChild(blockDiv);
|
|
1172
|
-
newBlocksMap.set(bboxKey, true);
|
|
1173
|
-
}
|
|
1174
|
-
});
|
|
1175
1141
|
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1142
|
+
// 显示批注按钮浮层
|
|
1143
|
+
showAnnotationButtonForBlock(e, blockDiv);
|
|
1144
|
+
});
|
|
1145
|
+
|
|
1146
|
+
blockDiv.addEventListener("mouseleave", () => {
|
|
1147
|
+
// 延迟隐藏,给用户时间移动到批注按钮
|
|
1148
|
+
hideAnnotationButton();
|
|
1149
|
+
});
|
|
1150
|
+
|
|
1151
|
+
textLayer.appendChild(blockDiv);
|
|
1181
1152
|
});
|
|
1153
|
+
|
|
1154
|
+
// 标记该页面已渲染
|
|
1155
|
+
renderedPages.value.add(targetPage);
|
|
1182
1156
|
} catch (error) {
|
|
1183
1157
|
console.error("❌ 文本图层渲染失败:", error);
|
|
1184
1158
|
}
|
|
@@ -1188,12 +1162,14 @@ const renderTextLayer = (pageNum?: number) => {
|
|
|
1188
1162
|
* 获取文本块对应的批注
|
|
1189
1163
|
*/
|
|
1190
1164
|
const getAnnotationForBlock = (
|
|
1191
|
-
bbox: [number, number, number, number]
|
|
1165
|
+
bbox: [number, number, number, number],
|
|
1166
|
+
pageNum?: number
|
|
1192
1167
|
): AnnotationInfo | null => {
|
|
1193
1168
|
if (!props.annotations || props.annotations.length === 0) {
|
|
1194
1169
|
return null;
|
|
1195
1170
|
}
|
|
1196
1171
|
|
|
1172
|
+
const targetPage = pageNum !== undefined ? pageNum : currentPage.value;
|
|
1197
1173
|
const tolerance = 2; // 容差
|
|
1198
1174
|
return (
|
|
1199
1175
|
props.annotations.find((annotation) => {
|
|
@@ -1204,7 +1180,7 @@ const getAnnotationForBlock = (
|
|
|
1204
1180
|
Math.abs(x2 - bbox[2]) < tolerance &&
|
|
1205
1181
|
Math.abs(y2 - bbox[3]) < tolerance &&
|
|
1206
1182
|
(annotation.blockPage === undefined ||
|
|
1207
|
-
annotation.blockPage ===
|
|
1183
|
+
annotation.blockPage === targetPage)
|
|
1208
1184
|
);
|
|
1209
1185
|
}) || null
|
|
1210
1186
|
);
|
|
@@ -1217,12 +1193,26 @@ const showAnnotationButtonForBlock = (
|
|
|
1217
1193
|
event: MouseEvent,
|
|
1218
1194
|
blockDiv: HTMLElement
|
|
1219
1195
|
) => {
|
|
1220
|
-
const text = blockDiv.dataset.text || "";
|
|
1221
1196
|
const bboxStr = blockDiv.dataset.bbox || "";
|
|
1222
|
-
if (!
|
|
1197
|
+
if (!bboxStr) return;
|
|
1223
1198
|
|
|
1224
1199
|
try {
|
|
1225
1200
|
const bbox = JSON.parse(bboxStr) as [number, number, number, number];
|
|
1201
|
+
|
|
1202
|
+
// 从 blocksData 中查找对应的 content
|
|
1203
|
+
const pageBlocksData = getPageBlocksData(currentPage.value);
|
|
1204
|
+
const blockData = pageBlocksData.find((block) => {
|
|
1205
|
+
const [x1, y1, x2, y2] = block.bbox;
|
|
1206
|
+
const tolerance = 2;
|
|
1207
|
+
return (
|
|
1208
|
+
Math.abs(x1 - bbox[0]) < tolerance &&
|
|
1209
|
+
Math.abs(y1 - bbox[1]) < tolerance &&
|
|
1210
|
+
Math.abs(x2 - bbox[2]) < tolerance &&
|
|
1211
|
+
Math.abs(y2 - bbox[3]) < tolerance
|
|
1212
|
+
);
|
|
1213
|
+
});
|
|
1214
|
+
|
|
1215
|
+
if (!blockData) return;
|
|
1226
1216
|
|
|
1227
1217
|
const rect = blockDiv.getBoundingClientRect();
|
|
1228
1218
|
const containerRect = containerRef.value?.getBoundingClientRect();
|
|
@@ -1305,7 +1295,78 @@ const showAnnotationButtonForBlock = (
|
|
|
1305
1295
|
};
|
|
1306
1296
|
|
|
1307
1297
|
/**
|
|
1308
|
-
*
|
|
1298
|
+
* 恢复文本块样式(根据是否有批注)
|
|
1299
|
+
*/
|
|
1300
|
+
const restoreBlockStyle = (blockDiv: HTMLElement) => {
|
|
1301
|
+
const bboxStr = blockDiv.dataset.bbox;
|
|
1302
|
+
if (!bboxStr) {
|
|
1303
|
+
console.log("restoreBlockStyle: 没有 bbox");
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
try {
|
|
1308
|
+
const bbox = JSON.parse(bboxStr) as [number, number, number, number];
|
|
1309
|
+
const pageStr = blockDiv.dataset.page;
|
|
1310
|
+
const pageNum = pageStr ? parseInt(pageStr, 10) : undefined;
|
|
1311
|
+
const existingAnnotation = getAnnotationForBlock(bbox, pageNum);
|
|
1312
|
+
|
|
1313
|
+
console.log("restoreBlockStyle:", {
|
|
1314
|
+
bbox,
|
|
1315
|
+
pageNum,
|
|
1316
|
+
existingAnnotation,
|
|
1317
|
+
blockDiv: blockDiv,
|
|
1318
|
+
width: blockDiv.style.width,
|
|
1319
|
+
height: blockDiv.style.height,
|
|
1320
|
+
computedStyle: {
|
|
1321
|
+
backgroundColor: window.getComputedStyle(blockDiv).backgroundColor,
|
|
1322
|
+
width: window.getComputedStyle(blockDiv).width,
|
|
1323
|
+
height: window.getComputedStyle(blockDiv).height,
|
|
1324
|
+
display: window.getComputedStyle(blockDiv).display,
|
|
1325
|
+
visibility: window.getComputedStyle(blockDiv).visibility,
|
|
1326
|
+
},
|
|
1327
|
+
inlineStyle: {
|
|
1328
|
+
backgroundColor: blockDiv.style.backgroundColor,
|
|
1329
|
+
border: blockDiv.style.border,
|
|
1330
|
+
},
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
if (existingAnnotation) {
|
|
1334
|
+
// 如果有批注,保持批注样式(使用 !important 确保优先级)
|
|
1335
|
+
blockDiv.style.setProperty("background-color", "rgba(255, 243, 205, 0.5)", "important");
|
|
1336
|
+
blockDiv.style.setProperty("border", "1px solid rgba(255, 193, 7, 0.7)", "important");
|
|
1337
|
+
blockDiv.style.setProperty("border-radius", "3px", "important");
|
|
1338
|
+
blockDiv.style.setProperty("padding", "1px 3px", "important");
|
|
1339
|
+
blockDiv.style.setProperty("box-shadow", "0 1px 2px rgba(255, 193, 7, 0.25)", "important");
|
|
1340
|
+
|
|
1341
|
+
// 确保元素可见
|
|
1342
|
+
blockDiv.style.setProperty("display", "block", "important");
|
|
1343
|
+
blockDiv.style.setProperty("visibility", "visible", "important");
|
|
1344
|
+
blockDiv.style.setProperty("opacity", "1", "important");
|
|
1345
|
+
|
|
1346
|
+
console.log("restoreBlockStyle: 已设置批注样式", {
|
|
1347
|
+
backgroundColor: blockDiv.style.backgroundColor,
|
|
1348
|
+
border: blockDiv.style.border,
|
|
1349
|
+
computedAfter: window.getComputedStyle(blockDiv).backgroundColor,
|
|
1350
|
+
});
|
|
1351
|
+
} else {
|
|
1352
|
+
// 如果没有批注,恢复透明背景
|
|
1353
|
+
blockDiv.style.backgroundColor = "transparent";
|
|
1354
|
+
blockDiv.style.border = "none";
|
|
1355
|
+
blockDiv.style.padding = "0";
|
|
1356
|
+
blockDiv.style.boxShadow = "none";
|
|
1357
|
+
}
|
|
1358
|
+
} catch (error) {
|
|
1359
|
+
console.error("restoreBlockStyle 错误:", error);
|
|
1360
|
+
// 如果解析失败,恢复透明背景
|
|
1361
|
+
blockDiv.style.backgroundColor = "transparent";
|
|
1362
|
+
blockDiv.style.border = "none";
|
|
1363
|
+
blockDiv.style.padding = "0";
|
|
1364
|
+
blockDiv.style.boxShadow = "none";
|
|
1365
|
+
}
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* 隐藏批注按钮和高亮
|
|
1309
1370
|
*/
|
|
1310
1371
|
const hideAnnotationButton = () => {
|
|
1311
1372
|
// 如果正在输入批注,不自动隐藏
|
|
@@ -1316,9 +1377,41 @@ const hideAnnotationButton = () => {
|
|
|
1316
1377
|
hideTimer = setTimeout(() => {
|
|
1317
1378
|
showAnnotationPopup.value = false;
|
|
1318
1379
|
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
activeBlockDiv.value.
|
|
1380
|
+
// 恢复文本块的样式(如果有批注则保持批注样式)
|
|
1381
|
+
if (activeBlockDiv.value) {
|
|
1382
|
+
const bboxStr = activeBlockDiv.value.dataset.bbox;
|
|
1383
|
+
if (bboxStr) {
|
|
1384
|
+
try {
|
|
1385
|
+
const bbox = JSON.parse(bboxStr) as [
|
|
1386
|
+
number,
|
|
1387
|
+
number,
|
|
1388
|
+
number,
|
|
1389
|
+
number
|
|
1390
|
+
];
|
|
1391
|
+
const existingAnnotation = getAnnotationForBlock(bbox);
|
|
1392
|
+
if (existingAnnotation) {
|
|
1393
|
+
// 如果有批注,恢复批注样式
|
|
1394
|
+
activeBlockDiv.value.style.backgroundColor =
|
|
1395
|
+
"rgba(255, 243, 205, 0.5)";
|
|
1396
|
+
activeBlockDiv.value.style.border =
|
|
1397
|
+
"1px solid rgba(255, 193, 7, 0.7)";
|
|
1398
|
+
activeBlockDiv.value.style.padding = "1px 3px";
|
|
1399
|
+
activeBlockDiv.value.style.boxShadow =
|
|
1400
|
+
"0 1px 2px rgba(255, 193, 7, 0.25)";
|
|
1401
|
+
} else {
|
|
1402
|
+
// 如果没有批注,恢复透明背景
|
|
1403
|
+
activeBlockDiv.value.style.backgroundColor = "transparent";
|
|
1404
|
+
activeBlockDiv.value.style.border = "none";
|
|
1405
|
+
activeBlockDiv.value.style.padding = "0";
|
|
1406
|
+
activeBlockDiv.value.style.boxShadow = "none";
|
|
1407
|
+
}
|
|
1408
|
+
} catch (error) {
|
|
1409
|
+
activeBlockDiv.value.style.backgroundColor = "transparent";
|
|
1410
|
+
activeBlockDiv.value.style.border = "none";
|
|
1411
|
+
activeBlockDiv.value.style.padding = "0";
|
|
1412
|
+
activeBlockDiv.value.style.boxShadow = "none";
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1322
1415
|
activeBlockDiv.value = null;
|
|
1323
1416
|
}
|
|
1324
1417
|
}, 300);
|
|
@@ -1343,14 +1436,36 @@ const openAnnotationInput = (e?: Event) => {
|
|
|
1343
1436
|
e.stopPropagation();
|
|
1344
1437
|
}
|
|
1345
1438
|
|
|
1346
|
-
//
|
|
1439
|
+
// 获取选中的文本或从文本块中获取
|
|
1347
1440
|
const selection = window.getSelection();
|
|
1348
1441
|
let selectedText = "";
|
|
1349
1442
|
|
|
1350
1443
|
if (selection && selection.toString().trim().length > 0) {
|
|
1351
1444
|
selectedText = selection.toString().trim();
|
|
1352
1445
|
} else if (activeBlockDiv.value) {
|
|
1353
|
-
|
|
1446
|
+
// 从 blocksData 中查找对应的 content
|
|
1447
|
+
const bboxStr = activeBlockDiv.value.dataset.bbox || "";
|
|
1448
|
+
if (bboxStr) {
|
|
1449
|
+
try {
|
|
1450
|
+
const bbox = JSON.parse(bboxStr) as [number, number, number, number];
|
|
1451
|
+
const pageBlocksData = getPageBlocksData(currentPage.value);
|
|
1452
|
+
const blockData = pageBlocksData.find((block) => {
|
|
1453
|
+
const [x1, y1, x2, y2] = block.bbox;
|
|
1454
|
+
const tolerance = 2;
|
|
1455
|
+
return (
|
|
1456
|
+
Math.abs(x1 - bbox[0]) < tolerance &&
|
|
1457
|
+
Math.abs(y1 - bbox[1]) < tolerance &&
|
|
1458
|
+
Math.abs(x2 - bbox[2]) < tolerance &&
|
|
1459
|
+
Math.abs(y2 - bbox[3]) < tolerance
|
|
1460
|
+
);
|
|
1461
|
+
});
|
|
1462
|
+
if (blockData) {
|
|
1463
|
+
selectedText = blockData.content || "";
|
|
1464
|
+
}
|
|
1465
|
+
} catch (error) {
|
|
1466
|
+
console.error("解析 bbox 失败:", error);
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1354
1469
|
}
|
|
1355
1470
|
|
|
1356
1471
|
if (!selectedText && !activeBlockDiv.value) return;
|
|
@@ -1511,6 +1626,12 @@ const closeAnnotationInput = () => {
|
|
|
1511
1626
|
currentAnnotationBlock.value = null;
|
|
1512
1627
|
annotationInput.value = "";
|
|
1513
1628
|
showAnnotationPopup.value = false;
|
|
1629
|
+
|
|
1630
|
+
// 关闭批注输入弹窗后,恢复文本块的样式
|
|
1631
|
+
if (activeBlockDiv.value && !isHighlighted.value) {
|
|
1632
|
+
restoreBlockStyle(activeBlockDiv.value);
|
|
1633
|
+
activeBlockDiv.value = null;
|
|
1634
|
+
}
|
|
1514
1635
|
};
|
|
1515
1636
|
|
|
1516
1637
|
/**
|