@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koi-br/ocr-web-sdk",
3
- "version": "1.0.29",
3
+ "version": "1.0.30",
4
4
  "description": "一个支持多种Office文件格式预览的Vue3组件SDK,包括PDF、Word、Excel、图片、OFD、TIF等格式",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.esm.js",
@@ -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
- * 渲染文本图层(使用 blocksData 数据,支持增量渲染)
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
- const pageBlocksData = getPageBlocksData(targetPage);
995
- console.log("renderTextLayer", targetPage, pageBlocksData);
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
- const existingBlocks = new Map<string, HTMLElement>();
1010
- textLayer.querySelectorAll(".text-block").forEach((el) => {
1011
- const bboxStr = (el as HTMLElement).dataset.bbox;
1012
- if (bboxStr) {
1013
- existingBlocks.set(bboxStr, el as HTMLElement);
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
- // 创建新的文本块 Map,用于跟踪需要保留的块
1018
- const newBlocksMap = new Map<string, boolean>();
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
- // 使用指定页码的 blocksData 创建或更新可交互的块
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 = "text"; // 改为文本选择光标
1046
- // 设置文本内容(使用 textContent 而不是 innerHTML,避免 XSS 风险)
1047
- blockDiv.textContent = content;
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
- // 检查是否已存在相同的文本块(通过 bbox 匹配)
1112
- const bboxKey = JSON.stringify(bbox);
1113
- const existingBlock = existingBlocks.get(bboxKey);
1114
-
1115
- if (existingBlock) {
1116
- // 如果已存在,更新内容(增量更新,避免闪烁)
1117
- existingBlock.textContent = content;
1118
- existingBlock.dataset.text = content;
1119
- // 更新批注状态
1120
- const existingAnnotation = getAnnotationForBlock(bbox);
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
- if (!existingBlock.classList.contains("has-annotation")) {
1123
- existingBlock.classList.add("has-annotation");
1124
- existingBlock.title = `已有批注: ${existingAnnotation.content}`;
1125
- existingBlock.style.backgroundColor = "rgba(255, 243, 205, 0.5)";
1126
- existingBlock.style.border = "1px solid rgba(255, 193, 7, 0.7)";
1127
- existingBlock.style.borderRadius = "3px";
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
- if (existingBlock.classList.contains("has-annotation")) {
1156
- existingBlock.classList.remove("has-annotation");
1157
- existingBlock.title = "";
1158
- existingBlock.style.backgroundColor = "transparent";
1159
- existingBlock.style.border = "none";
1160
- existingBlock.style.padding = "0";
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
- existingBlocks.forEach((block, bboxKey) => {
1178
- if (!newBlocksMap.has(bboxKey)) {
1179
- block.remove();
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 === currentPage.value)
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 (!text || !bboxStr) return;
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
- if (activeBlockDiv.value && !isHighlighted.value) {
1320
- activeBlockDiv.value.style.backgroundColor = "transparent";
1321
- activeBlockDiv.value.style.boxShadow = "none";
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
- selectedText = activeBlockDiv.value.dataset.text || "";
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
  /**