@koi-br/ocr-web-sdk 1.0.52 → 1.0.53
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-C0NlHof9.mjs → index-BniO2aM1.mjs} +5857 -5754
- package/dist/{index-zJuTnvPr.js → index-CAoaF0us.js} +72 -72
- package/dist/index.cjs.js +2 -2
- package/dist/index.esm.js +2 -2
- package/dist/preview/ImagePreview.vue.d.ts +4 -0
- package/dist/style.css +1 -1
- package/dist/{tiff.min-BrosVXyT.js → tiff.min-Djly3w3y.js} +1 -1
- package/dist/{tiff.min-CFrZ2Wj3.mjs → tiff.min-DqwrYsQT.mjs} +1 -1
- package/package.json +1 -1
- package/preview/ImagePreview.vue +279 -112
- package/preview/index.vue +4 -0
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>
|
|
@@ -194,15 +192,48 @@
|
|
|
194
192
|
@mouseenter="cancelHideAnnotationButton"
|
|
195
193
|
>
|
|
196
194
|
<div class="annotation-input-header">
|
|
197
|
-
<
|
|
195
|
+
<div class="annotation-input-header-title">
|
|
196
|
+
{{ currentAnnotationBlock?.content }}
|
|
197
|
+
</div>
|
|
198
198
|
<button class="annotation-close-btn" @click="closeAnnotationInput">
|
|
199
199
|
<X :size="14" />
|
|
200
200
|
</button>
|
|
201
201
|
</div>
|
|
202
202
|
<div class="annotation-input-content">
|
|
203
|
+
<!-- 插槽:允许父组件自定义批注头部显示(包括用户名和头像) -->
|
|
204
|
+
<slot
|
|
205
|
+
name="annotation-header"
|
|
206
|
+
:annotation="currentEditingAnnotation"
|
|
207
|
+
:is-editing="!!currentEditingAnnotation"
|
|
208
|
+
>
|
|
209
|
+
<!-- 默认显示:如果没有使用插槽,且有用户信息,则显示用户名和头像 -->
|
|
210
|
+
<div
|
|
211
|
+
v-if="
|
|
212
|
+
currentEditingAnnotation?.username ||
|
|
213
|
+
currentEditingAnnotation?.avatar
|
|
214
|
+
"
|
|
215
|
+
class="annotation-header-user-info"
|
|
216
|
+
>
|
|
217
|
+
<img
|
|
218
|
+
v-if="currentEditingAnnotation?.avatar"
|
|
219
|
+
:src="currentEditingAnnotation.avatar"
|
|
220
|
+
:alt="currentEditingAnnotation.username || '用户'"
|
|
221
|
+
class="annotation-user-avatar"
|
|
222
|
+
/>
|
|
223
|
+
<span
|
|
224
|
+
v-if="currentEditingAnnotation?.username"
|
|
225
|
+
class="annotation-username"
|
|
226
|
+
>
|
|
227
|
+
{{ currentEditingAnnotation.username }}
|
|
228
|
+
</span>
|
|
229
|
+
</div>
|
|
230
|
+
<span v-else class="annotation-input-title">
|
|
231
|
+
{{ currentEditingAnnotation ? "编辑批注" : "添加批注" }}
|
|
232
|
+
</span>
|
|
233
|
+
</slot>
|
|
203
234
|
<Textarea
|
|
204
235
|
v-model="annotationInput"
|
|
205
|
-
:auto-size="{ minRows:
|
|
236
|
+
:auto-size="{ minRows: 1, maxRows: 3}"
|
|
206
237
|
placeholder="请输入批注内容..."
|
|
207
238
|
class="annotation-textarea"
|
|
208
239
|
@keydown.ctrl.enter="saveAnnotation"
|
|
@@ -213,17 +244,18 @@
|
|
|
213
244
|
@mousedown.stop
|
|
214
245
|
/>
|
|
215
246
|
</div>
|
|
216
|
-
<div class="annotation-input-footer">
|
|
217
|
-
<AButton size="small" type="outline" @click="closeAnnotationInput">
|
|
247
|
+
<div class="annotation-input-footer" v-if="annotationInput">
|
|
248
|
+
<AButton size="small" type="outline" style="border:1px solid rgb(208, 211, 214);border-radius: 4px;" @click="closeAnnotationInput">
|
|
218
249
|
取消
|
|
219
250
|
</AButton>
|
|
220
251
|
<AButton
|
|
221
252
|
size="small"
|
|
222
253
|
type="primary"
|
|
254
|
+
style="border-radius: 4px;"
|
|
223
255
|
@click="saveAnnotation"
|
|
224
256
|
:disabled="!annotationInput.trim()"
|
|
225
257
|
>
|
|
226
|
-
|
|
258
|
+
发送
|
|
227
259
|
</AButton>
|
|
228
260
|
</div>
|
|
229
261
|
</div>
|
|
@@ -281,6 +313,10 @@ interface AnnotationInfo {
|
|
|
281
313
|
blockPage?: number; // 所属页码
|
|
282
314
|
content: string; // 批注内容
|
|
283
315
|
createTime: number; // 创建时间戳
|
|
316
|
+
// 可选的用户信息(业务层可以自行决定是否传递)
|
|
317
|
+
username?: string; // 用户名
|
|
318
|
+
avatar?: string; // 头像URL
|
|
319
|
+
userId?: string | number; // 用户ID
|
|
284
320
|
}
|
|
285
321
|
|
|
286
322
|
/**
|
|
@@ -380,7 +416,8 @@ const props = defineProps({
|
|
|
380
416
|
hoverStyle: {
|
|
381
417
|
type: Object as () => HoverStyle,
|
|
382
418
|
default: () => ({
|
|
383
|
-
backgroundColor:
|
|
419
|
+
backgroundColor:
|
|
420
|
+
"var(--s-color-brand-primary-transparent-3, rgba(0, 102, 255, .15))",
|
|
384
421
|
boxShadow: "0 0 0 2px rgba(30, 144, 255, 0.6)",
|
|
385
422
|
borderRadius: "2px",
|
|
386
423
|
padding: "1px 3px",
|
|
@@ -395,7 +432,8 @@ const props = defineProps({
|
|
|
395
432
|
border: "2px solid rgba(30, 144, 255, 0.8)",
|
|
396
433
|
borderRadius: "3px",
|
|
397
434
|
padding: "1px 3px",
|
|
398
|
-
boxShadow:
|
|
435
|
+
boxShadow:
|
|
436
|
+
"0 0 0 2px rgba(30, 144, 255, 0.6), 0 1px 2px rgba(255, 193, 7, 0.25)",
|
|
399
437
|
}),
|
|
400
438
|
},
|
|
401
439
|
// hover 时批注按钮的隐藏延迟(毫秒)
|
|
@@ -456,22 +494,22 @@ const isImageReady = ref(false); // 标记图片是否已准备好显示(自
|
|
|
456
494
|
watch(
|
|
457
495
|
() => imageUrls.value,
|
|
458
496
|
(newUrls, oldUrls) => {
|
|
459
|
-
console.log(
|
|
497
|
+
console.log("[ImagePreview] imageUrls changed:", {
|
|
460
498
|
newUrls: newUrls?.length,
|
|
461
499
|
oldUrls: oldUrls?.length,
|
|
462
500
|
autoFitWidth: props.autoFitWidth,
|
|
463
501
|
isImageReady: isImageReady.value,
|
|
464
502
|
isCalculatingAutoFit: isCalculatingAutoFit.value,
|
|
465
503
|
});
|
|
466
|
-
|
|
504
|
+
|
|
467
505
|
// 如果有新的图片URL,且启用自适应宽度,立即隐藏图片
|
|
468
506
|
if (newUrls && newUrls.length > 0 && props.autoFitWidth) {
|
|
469
|
-
console.log(
|
|
507
|
+
console.log("[ImagePreview] 设置图片隐藏,等待自适应宽度计算");
|
|
470
508
|
isImageReady.value = false;
|
|
471
509
|
isCalculatingAutoFit.value = true;
|
|
472
510
|
} else if (!props.autoFitWidth) {
|
|
473
511
|
// 如果没有启用自适应宽度,立即显示
|
|
474
|
-
console.log(
|
|
512
|
+
console.log("[ImagePreview] 未启用自适应宽度,立即显示图片");
|
|
475
513
|
isImageReady.value = true;
|
|
476
514
|
isCalculatingAutoFit.value = false;
|
|
477
515
|
}
|
|
@@ -537,10 +575,10 @@ const imageSize = computed({
|
|
|
537
575
|
// 在自适应宽度模式下,使用第一页的尺寸;否则使用当前页的尺寸
|
|
538
576
|
const scaledImageSize = computed(() => {
|
|
539
577
|
// 如果启用自适应宽度,使用第一页的尺寸作为基准
|
|
540
|
-
const baseSize = props.autoFitWidth
|
|
541
|
-
?
|
|
578
|
+
const baseSize = props.autoFitWidth
|
|
579
|
+
? imageSizes.get(1) || { width: 0, height: 0 }
|
|
542
580
|
: imageSize.value;
|
|
543
|
-
|
|
581
|
+
|
|
544
582
|
if (baseSize.width === 0 || baseSize.height === 0) {
|
|
545
583
|
return { width: 0, height: 0 };
|
|
546
584
|
}
|
|
@@ -587,6 +625,7 @@ const currentAnnotationBlock = ref<{
|
|
|
587
625
|
bbox: [number, number, number, number];
|
|
588
626
|
content: string;
|
|
589
627
|
} | null>(null); // 当前正在添加批注的文本块
|
|
628
|
+
const currentEditingAnnotation = ref<AnnotationInfo | null>(null); // 当前正在编辑的批注信息(如果有)
|
|
590
629
|
const annotationPopupRef = ref<HTMLElement>(); // 批注弹窗引用
|
|
591
630
|
|
|
592
631
|
// 文本选择相关状态(保留用于兼容)
|
|
@@ -635,7 +674,7 @@ const getPageScaledSize = (pageNo: number) => {
|
|
|
635
674
|
const getPageContainerStyle = (pageNo: number) => {
|
|
636
675
|
const scaledSize = getPageScaledSize(pageNo);
|
|
637
676
|
return {
|
|
638
|
-
height: scaledSize.height > 0 ? `${scaledSize.height}px` :
|
|
677
|
+
height: scaledSize.height > 0 ? `${scaledSize.height}px` : "auto",
|
|
639
678
|
};
|
|
640
679
|
};
|
|
641
680
|
|
|
@@ -939,7 +978,7 @@ const switchToPage = (page: number) => {
|
|
|
939
978
|
) as HTMLElement;
|
|
940
979
|
if (pageElement) {
|
|
941
980
|
// 标记这是翻页滚动,不应该被同步滚动干扰
|
|
942
|
-
containerRef.value.dataset.pageScrolling =
|
|
981
|
+
containerRef.value.dataset.pageScrolling = "true";
|
|
943
982
|
pageElement.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
944
983
|
// 更新 lastScrollTop,确保滚动方向判断准确
|
|
945
984
|
nextTick(() => {
|
|
@@ -975,12 +1014,12 @@ const reset = () => {
|
|
|
975
1014
|
const hadHighlight = isHighlighted.value && activeBlockDiv.value !== null;
|
|
976
1015
|
activeBlockDiv.value = null;
|
|
977
1016
|
isHighlighted.value = false;
|
|
978
|
-
|
|
1017
|
+
|
|
979
1018
|
// 如果之前有高亮,触发高亮清除事件
|
|
980
1019
|
if (hadHighlight) {
|
|
981
1020
|
emit("highlight-clear");
|
|
982
1021
|
}
|
|
983
|
-
|
|
1022
|
+
|
|
984
1023
|
// 清除已渲染页面集合
|
|
985
1024
|
renderedPages.value.clear();
|
|
986
1025
|
};
|
|
@@ -991,7 +1030,7 @@ const original = () => {
|
|
|
991
1030
|
|
|
992
1031
|
// 计算自适应宽度的缩放比例
|
|
993
1032
|
const calculateAutoFitScale = () => {
|
|
994
|
-
console.log(
|
|
1033
|
+
console.log("[ImagePreview] calculateAutoFitScale 开始:", {
|
|
995
1034
|
autoFitWidth: props.autoFitWidth,
|
|
996
1035
|
hasContainerRef: !!containerRef.value,
|
|
997
1036
|
containerRect: containerRef.value?.getBoundingClientRect(),
|
|
@@ -1000,16 +1039,19 @@ const calculateAutoFitScale = () => {
|
|
|
1000
1039
|
minScale: props.minScale,
|
|
1001
1040
|
maxScale: props.maxScale,
|
|
1002
1041
|
});
|
|
1003
|
-
|
|
1042
|
+
|
|
1004
1043
|
if (!props.autoFitWidth || !containerRef.value) {
|
|
1005
|
-
console.log(
|
|
1044
|
+
console.log("[ImagePreview] calculateAutoFitScale 返回 1 (条件不满足)");
|
|
1006
1045
|
return 1;
|
|
1007
1046
|
}
|
|
1008
1047
|
|
|
1009
1048
|
// 使用第一页的图片尺寸作为基准(所有页面使用相同的缩放比例)
|
|
1010
1049
|
const firstPageSize = imageSizes.get(1);
|
|
1011
1050
|
if (!firstPageSize || firstPageSize.width === 0) {
|
|
1012
|
-
console.log(
|
|
1051
|
+
console.log(
|
|
1052
|
+
"[ImagePreview] calculateAutoFitScale 返回 1 (第一页尺寸无效)",
|
|
1053
|
+
firstPageSize
|
|
1054
|
+
);
|
|
1013
1055
|
return 1;
|
|
1014
1056
|
}
|
|
1015
1057
|
|
|
@@ -1019,7 +1061,10 @@ const calculateAutoFitScale = () => {
|
|
|
1019
1061
|
const containerWidth = containerRect.width - 4;
|
|
1020
1062
|
|
|
1021
1063
|
if (containerWidth <= 0) {
|
|
1022
|
-
console.log(
|
|
1064
|
+
console.log(
|
|
1065
|
+
"[ImagePreview] calculateAutoFitScale 返回 1 (容器宽度无效)",
|
|
1066
|
+
containerWidth
|
|
1067
|
+
);
|
|
1023
1068
|
return 1;
|
|
1024
1069
|
}
|
|
1025
1070
|
|
|
@@ -1030,15 +1075,21 @@ const calculateAutoFitScale = () => {
|
|
|
1030
1075
|
const imageWidth = isRotated ? firstPageSize.height : firstPageSize.width;
|
|
1031
1076
|
|
|
1032
1077
|
if (imageWidth <= 0) {
|
|
1033
|
-
console.log(
|
|
1078
|
+
console.log(
|
|
1079
|
+
"[ImagePreview] calculateAutoFitScale 返回 1 (图片宽度无效)",
|
|
1080
|
+
imageWidth
|
|
1081
|
+
);
|
|
1034
1082
|
return 1;
|
|
1035
1083
|
}
|
|
1036
1084
|
|
|
1037
1085
|
// 计算缩放比例,使图片宽度完全适应容器宽度
|
|
1038
1086
|
const calculatedScale = containerWidth / imageWidth;
|
|
1039
|
-
const finalScale = Math.max(
|
|
1040
|
-
|
|
1041
|
-
|
|
1087
|
+
const finalScale = Math.max(
|
|
1088
|
+
props.minScale,
|
|
1089
|
+
Math.min(props.maxScale, calculatedScale)
|
|
1090
|
+
);
|
|
1091
|
+
|
|
1092
|
+
console.log("[ImagePreview] calculateAutoFitScale 计算结果:", {
|
|
1042
1093
|
containerWidth,
|
|
1043
1094
|
imageWidth,
|
|
1044
1095
|
calculatedScale,
|
|
@@ -1052,8 +1103,8 @@ const calculateAutoFitScale = () => {
|
|
|
1052
1103
|
// 图片加载完成处理
|
|
1053
1104
|
const onImageLoad = (event: Event, pageNum: number) => {
|
|
1054
1105
|
const img = event.target as HTMLImageElement;
|
|
1055
|
-
|
|
1056
|
-
console.log(
|
|
1106
|
+
|
|
1107
|
+
console.log("[ImagePreview] 图片加载完成:", {
|
|
1057
1108
|
pageNum,
|
|
1058
1109
|
naturalWidth: img.naturalWidth,
|
|
1059
1110
|
naturalHeight: img.naturalHeight,
|
|
@@ -1061,7 +1112,7 @@ const onImageLoad = (event: Event, pageNum: number) => {
|
|
|
1061
1112
|
isImageReady: isImageReady.value,
|
|
1062
1113
|
isCalculatingAutoFit: isCalculatingAutoFit.value,
|
|
1063
1114
|
});
|
|
1064
|
-
|
|
1115
|
+
|
|
1065
1116
|
// 存储该页的图片尺寸
|
|
1066
1117
|
imageSizes.set(pageNum, {
|
|
1067
1118
|
width: img.naturalWidth,
|
|
@@ -1070,10 +1121,10 @@ const onImageLoad = (event: Event, pageNum: number) => {
|
|
|
1070
1121
|
|
|
1071
1122
|
// 如果是第一页且启用自适应宽度,计算并设置初始缩放比例
|
|
1072
1123
|
if (pageNum === 1 && props.autoFitWidth) {
|
|
1073
|
-
console.log(
|
|
1124
|
+
console.log("[ImagePreview] 第一页加载完成,开始计算自适应宽度");
|
|
1074
1125
|
// 重置用户缩放标记
|
|
1075
1126
|
isUserZooming.value = false;
|
|
1076
|
-
|
|
1127
|
+
|
|
1077
1128
|
// 确保图片是隐藏的(watch 已经设置了,这里再次确认)
|
|
1078
1129
|
if (!isImageReady.value) {
|
|
1079
1130
|
isCalculatingAutoFit.value = true;
|
|
@@ -1081,7 +1132,7 @@ const onImageLoad = (event: Event, pageNum: number) => {
|
|
|
1081
1132
|
|
|
1082
1133
|
// 设置超时保护,防止一直显示 loading(最多等待 3 秒)
|
|
1083
1134
|
const timeoutId = setTimeout(() => {
|
|
1084
|
-
console.warn(
|
|
1135
|
+
console.warn("自适应宽度计算超时,强制显示图片");
|
|
1085
1136
|
isCalculatingAutoFit.value = false;
|
|
1086
1137
|
isImageReady.value = true;
|
|
1087
1138
|
}, 3000);
|
|
@@ -1092,38 +1143,51 @@ const onImageLoad = (event: Event, pageNum: number) => {
|
|
|
1092
1143
|
// 添加小延迟确保容器完全渲染
|
|
1093
1144
|
setTimeout(() => {
|
|
1094
1145
|
try {
|
|
1095
|
-
console.log(
|
|
1146
|
+
console.log("[ImagePreview] onImageLoad: 开始计算自适应宽度...");
|
|
1096
1147
|
const autoScale = calculateAutoFitScale();
|
|
1097
|
-
console.log(
|
|
1148
|
+
console.log("[ImagePreview] onImageLoad: 自适应宽度计算结果:", {
|
|
1098
1149
|
autoScale,
|
|
1099
1150
|
containerRef: !!containerRef.value,
|
|
1100
|
-
containerWidth:
|
|
1151
|
+
containerWidth:
|
|
1152
|
+
containerRef.value?.getBoundingClientRect()?.width,
|
|
1101
1153
|
firstPageSize: imageSizes.get(1),
|
|
1102
1154
|
});
|
|
1103
|
-
|
|
1155
|
+
|
|
1104
1156
|
if (autoScale > 0) {
|
|
1105
1157
|
scale.value = autoScale;
|
|
1106
1158
|
initialAutoFitScale.value = autoScale; // 记录初始自适应缩放比例
|
|
1107
1159
|
// 记录当前容器宽度,用于后续 resize 检查
|
|
1108
1160
|
if (containerRef.value) {
|
|
1109
|
-
lastContainerWidth =
|
|
1161
|
+
lastContainerWidth =
|
|
1162
|
+
containerRef.value.getBoundingClientRect().width;
|
|
1110
1163
|
}
|
|
1111
|
-
console.log(
|
|
1164
|
+
console.log(
|
|
1165
|
+
"[ImagePreview] onImageLoad: 缩放比例已设置:",
|
|
1166
|
+
autoScale
|
|
1167
|
+
);
|
|
1112
1168
|
} else {
|
|
1113
|
-
console.warn(
|
|
1169
|
+
console.warn(
|
|
1170
|
+
"[ImagePreview] onImageLoad: 计算出的缩放比例无效:",
|
|
1171
|
+
autoScale
|
|
1172
|
+
);
|
|
1114
1173
|
}
|
|
1115
1174
|
} catch (error) {
|
|
1116
|
-
console.error(
|
|
1175
|
+
console.error(
|
|
1176
|
+
"[ImagePreview] onImageLoad: 计算自适应宽度失败:",
|
|
1177
|
+
error
|
|
1178
|
+
);
|
|
1117
1179
|
} finally {
|
|
1118
1180
|
// 清除超时保护
|
|
1119
1181
|
clearTimeout(timeoutId);
|
|
1120
|
-
console.log(
|
|
1182
|
+
console.log(
|
|
1183
|
+
"[ImagePreview] onImageLoad: 更新状态: isCalculatingAutoFit = false, isImageReady = true"
|
|
1184
|
+
);
|
|
1121
1185
|
// 无论计算结果如何,都要更新状态,避免一直显示 loading
|
|
1122
1186
|
isCalculatingAutoFit.value = false;
|
|
1123
1187
|
// 使用 requestAnimationFrame 确保在下一帧显示,避免闪烁
|
|
1124
1188
|
requestAnimationFrame(() => {
|
|
1125
1189
|
isImageReady.value = true;
|
|
1126
|
-
console.log(
|
|
1190
|
+
console.log("[ImagePreview] onImageLoad: 图片已准备好显示");
|
|
1127
1191
|
});
|
|
1128
1192
|
}
|
|
1129
1193
|
}, 100); // 增加延迟,确保所有图片都已加载
|
|
@@ -1134,7 +1198,7 @@ const onImageLoad = (event: Event, pageNum: number) => {
|
|
|
1134
1198
|
isImageReady.value = true;
|
|
1135
1199
|
isCalculatingAutoFit.value = false;
|
|
1136
1200
|
}
|
|
1137
|
-
|
|
1201
|
+
|
|
1138
1202
|
// 如果第一页已经加载完成,且当前页不是第一页,也应用自适应宽度
|
|
1139
1203
|
if (pageNum > 1 && props.autoFitWidth && initialAutoFitScale.value !== null) {
|
|
1140
1204
|
// 确保后续页面也使用相同的缩放比例
|
|
@@ -1352,7 +1416,7 @@ const renderTextLayer = (pageNum?: number) => {
|
|
|
1352
1416
|
// 清除当前页面所有文本块的高亮样式(除了当前文本块)
|
|
1353
1417
|
// 这样可以防止多个文本块同时高亮的竞态条件
|
|
1354
1418
|
clearAllHighlights(blockDiv);
|
|
1355
|
-
|
|
1419
|
+
|
|
1356
1420
|
// 清除跳转高亮标志(如果之前是通过跳转高亮的)
|
|
1357
1421
|
if (isHighlighted.value) {
|
|
1358
1422
|
isHighlighted.value = false;
|
|
@@ -1436,7 +1500,7 @@ const showAnnotationButtonForBlock = (
|
|
|
1436
1500
|
|
|
1437
1501
|
try {
|
|
1438
1502
|
const bbox = JSON.parse(bboxStr) as [number, number, number, number];
|
|
1439
|
-
|
|
1503
|
+
|
|
1440
1504
|
// 从 blocksData 中查找对应的 content
|
|
1441
1505
|
const pageBlocksData = getPageBlocksData(currentPage.value);
|
|
1442
1506
|
const blockData = pageBlocksData.find((block) => {
|
|
@@ -1449,7 +1513,7 @@ const showAnnotationButtonForBlock = (
|
|
|
1449
1513
|
Math.abs(y2 - bbox[3]) < tolerance
|
|
1450
1514
|
);
|
|
1451
1515
|
});
|
|
1452
|
-
|
|
1516
|
+
|
|
1453
1517
|
if (!blockData) return;
|
|
1454
1518
|
|
|
1455
1519
|
const rect = blockDiv.getBoundingClientRect();
|
|
@@ -1537,7 +1601,10 @@ const showAnnotationButtonForBlock = (
|
|
|
1537
1601
|
* @param excludeBlockDiv 要排除的文本块(不清除它的样式)
|
|
1538
1602
|
* @param pageNum 要清除的页码,如果不提供则从 excludeBlockDiv 中获取
|
|
1539
1603
|
*/
|
|
1540
|
-
const clearAllHighlights = (
|
|
1604
|
+
const clearAllHighlights = (
|
|
1605
|
+
excludeBlockDiv?: HTMLElement,
|
|
1606
|
+
pageNum?: number
|
|
1607
|
+
) => {
|
|
1541
1608
|
// 确定要清除的页码
|
|
1542
1609
|
let targetPage = pageNum;
|
|
1543
1610
|
if (!targetPage && excludeBlockDiv) {
|
|
@@ -1571,11 +1638,23 @@ const clearAllHighlights = (excludeBlockDiv?: HTMLElement, pageNum?: number) =>
|
|
|
1571
1638
|
|
|
1572
1639
|
if (existingAnnotation) {
|
|
1573
1640
|
// 如果有批注,恢复批注样式(不使用 hover 样式)
|
|
1574
|
-
el.style.setProperty(
|
|
1575
|
-
|
|
1641
|
+
el.style.setProperty(
|
|
1642
|
+
"background-color",
|
|
1643
|
+
"rgba(255, 243, 205, 0.5)",
|
|
1644
|
+
"important"
|
|
1645
|
+
);
|
|
1646
|
+
el.style.setProperty(
|
|
1647
|
+
"border",
|
|
1648
|
+
"1px solid rgba(255, 193, 7, 0.7)",
|
|
1649
|
+
"important"
|
|
1650
|
+
);
|
|
1576
1651
|
el.style.setProperty("border-radius", "3px", "important");
|
|
1577
1652
|
el.style.setProperty("padding", "1px 3px", "important");
|
|
1578
|
-
el.style.setProperty(
|
|
1653
|
+
el.style.setProperty(
|
|
1654
|
+
"box-shadow",
|
|
1655
|
+
"0 1px 2px rgba(255, 193, 7, 0.25)",
|
|
1656
|
+
"important"
|
|
1657
|
+
);
|
|
1579
1658
|
} else {
|
|
1580
1659
|
// 如果没有批注,清除所有高亮样式
|
|
1581
1660
|
el.style.backgroundColor = "transparent";
|
|
@@ -1639,17 +1718,29 @@ const restoreBlockStyle = (blockDiv: HTMLElement) => {
|
|
|
1639
1718
|
|
|
1640
1719
|
if (existingAnnotation) {
|
|
1641
1720
|
// 如果有批注,保持批注样式(使用 !important 确保优先级)
|
|
1642
|
-
blockDiv.style.setProperty(
|
|
1643
|
-
|
|
1721
|
+
blockDiv.style.setProperty(
|
|
1722
|
+
"background-color",
|
|
1723
|
+
"rgba(255, 243, 205, 0.5)",
|
|
1724
|
+
"important"
|
|
1725
|
+
);
|
|
1726
|
+
blockDiv.style.setProperty(
|
|
1727
|
+
"border",
|
|
1728
|
+
"1px solid rgba(255, 193, 7, 0.7)",
|
|
1729
|
+
"important"
|
|
1730
|
+
);
|
|
1644
1731
|
blockDiv.style.setProperty("border-radius", "3px", "important");
|
|
1645
1732
|
blockDiv.style.setProperty("padding", "1px 3px", "important");
|
|
1646
|
-
blockDiv.style.setProperty(
|
|
1647
|
-
|
|
1733
|
+
blockDiv.style.setProperty(
|
|
1734
|
+
"box-shadow",
|
|
1735
|
+
"0 1px 2px rgba(255, 193, 7, 0.25)",
|
|
1736
|
+
"important"
|
|
1737
|
+
);
|
|
1738
|
+
|
|
1648
1739
|
// 确保元素可见
|
|
1649
1740
|
blockDiv.style.setProperty("display", "block", "important");
|
|
1650
1741
|
blockDiv.style.setProperty("visibility", "visible", "important");
|
|
1651
1742
|
blockDiv.style.setProperty("opacity", "1", "important");
|
|
1652
|
-
|
|
1743
|
+
|
|
1653
1744
|
console.log("restoreBlockStyle: 已设置批注样式", {
|
|
1654
1745
|
backgroundColor: blockDiv.style.backgroundColor,
|
|
1655
1746
|
border: blockDiv.style.border,
|
|
@@ -1690,13 +1781,21 @@ const applyHoverStyle = (blockDiv: HTMLElement, hasAnnotation: boolean) => {
|
|
|
1690
1781
|
// 有批注时使用 annotationHoverStyle
|
|
1691
1782
|
const style = props.annotationHoverStyle;
|
|
1692
1783
|
if (style.backgroundColor) {
|
|
1693
|
-
blockDiv.style.setProperty(
|
|
1784
|
+
blockDiv.style.setProperty(
|
|
1785
|
+
"background-color",
|
|
1786
|
+
style.backgroundColor,
|
|
1787
|
+
"important"
|
|
1788
|
+
);
|
|
1694
1789
|
}
|
|
1695
1790
|
if (style.border) {
|
|
1696
1791
|
blockDiv.style.setProperty("border", style.border, "important");
|
|
1697
1792
|
}
|
|
1698
1793
|
if (style.borderRadius) {
|
|
1699
|
-
blockDiv.style.setProperty(
|
|
1794
|
+
blockDiv.style.setProperty(
|
|
1795
|
+
"border-radius",
|
|
1796
|
+
style.borderRadius,
|
|
1797
|
+
"important"
|
|
1798
|
+
);
|
|
1700
1799
|
}
|
|
1701
1800
|
if (style.padding) {
|
|
1702
1801
|
blockDiv.style.setProperty("padding", style.padding, "important");
|
|
@@ -1747,12 +1846,7 @@ const hideAnnotationButton = () => {
|
|
|
1747
1846
|
const bboxStr = activeBlockDiv.value.dataset.bbox;
|
|
1748
1847
|
if (bboxStr) {
|
|
1749
1848
|
try {
|
|
1750
|
-
const bbox = JSON.parse(bboxStr) as [
|
|
1751
|
-
number,
|
|
1752
|
-
number,
|
|
1753
|
-
number,
|
|
1754
|
-
number
|
|
1755
|
-
];
|
|
1849
|
+
const bbox = JSON.parse(bboxStr) as [number, number, number, number];
|
|
1756
1850
|
const existingAnnotation = getAnnotationForBlock(bbox);
|
|
1757
1851
|
if (existingAnnotation) {
|
|
1758
1852
|
// 如果有批注,恢复批注样式
|
|
@@ -1888,8 +1982,10 @@ const openAnnotationInput = (e?: Event) => {
|
|
|
1888
1982
|
const existingAnnotation = getAnnotationForBlock(bbox);
|
|
1889
1983
|
if (existingAnnotation) {
|
|
1890
1984
|
annotationInput.value = existingAnnotation.content;
|
|
1985
|
+
currentEditingAnnotation.value = existingAnnotation; // 保存当前编辑的批注信息
|
|
1891
1986
|
} else {
|
|
1892
1987
|
annotationInput.value = "";
|
|
1988
|
+
currentEditingAnnotation.value = null; // 新批注,清空编辑信息
|
|
1893
1989
|
}
|
|
1894
1990
|
|
|
1895
1991
|
currentAnnotationBlock.value = {
|
|
@@ -2002,9 +2098,10 @@ const adjustAnnotationPopupPosition = () => {
|
|
|
2002
2098
|
*/
|
|
2003
2099
|
const closeAnnotationInput = () => {
|
|
2004
2100
|
currentAnnotationBlock.value = null;
|
|
2101
|
+
currentEditingAnnotation.value = null; // 清空当前编辑的批注信息
|
|
2005
2102
|
annotationInput.value = "";
|
|
2006
2103
|
showAnnotationPopup.value = false;
|
|
2007
|
-
|
|
2104
|
+
|
|
2008
2105
|
// 关闭批注输入弹窗后,恢复文本块的样式
|
|
2009
2106
|
if (activeBlockDiv.value && !isHighlighted.value) {
|
|
2010
2107
|
restoreBlockStyle(activeBlockDiv.value);
|
|
@@ -2043,13 +2140,16 @@ const saveAnnotation = () => {
|
|
|
2043
2140
|
const existingAnnotation = getAnnotationForBlock(bbox);
|
|
2044
2141
|
|
|
2045
2142
|
const annotation: AnnotationInfo = {
|
|
2046
|
-
id:
|
|
2047
|
-
existingAnnotation?.id || "",
|
|
2143
|
+
id: existingAnnotation?.id || "",
|
|
2048
2144
|
blockBbox: bbox,
|
|
2049
2145
|
blockContent: content,
|
|
2050
2146
|
blockPage: currentPage.value,
|
|
2051
2147
|
content: annotationContent,
|
|
2052
2148
|
createTime: existingAnnotation?.createTime || Date.now(),
|
|
2149
|
+
// 保留用户信息(如果存在)
|
|
2150
|
+
username: existingAnnotation?.username,
|
|
2151
|
+
avatar: existingAnnotation?.avatar,
|
|
2152
|
+
userId: existingAnnotation?.userId,
|
|
2053
2153
|
};
|
|
2054
2154
|
|
|
2055
2155
|
if (existingAnnotation) {
|
|
@@ -2075,9 +2175,9 @@ const saveAnnotation = () => {
|
|
|
2075
2175
|
*/
|
|
2076
2176
|
const handleScroll = (e: Event) => {
|
|
2077
2177
|
const container = e.target as HTMLElement;
|
|
2078
|
-
|
|
2178
|
+
|
|
2079
2179
|
// 检查是否是同步滚动触发的
|
|
2080
|
-
const isSyncing = container?.dataset?.syncingScroll ===
|
|
2180
|
+
const isSyncing = container?.dataset?.syncingScroll === "true";
|
|
2081
2181
|
if (isSyncing) {
|
|
2082
2182
|
// 即使是被同步滚动触发的,也应该立即更新页码(但不触发翻页动画)
|
|
2083
2183
|
// 使用 requestAnimationFrame 确保在浏览器渲染后立即更新
|
|
@@ -2096,7 +2196,7 @@ const handleScroll = (e: Event) => {
|
|
|
2096
2196
|
// 如果没有启用滚动翻页,立即清除标记
|
|
2097
2197
|
delete container.dataset.syncingScroll;
|
|
2098
2198
|
}
|
|
2099
|
-
|
|
2199
|
+
|
|
2100
2200
|
// 同步滚动时,不执行其他逻辑(如隐藏批注按钮等)
|
|
2101
2201
|
return;
|
|
2102
2202
|
}
|
|
@@ -2137,7 +2237,6 @@ const handleScroll = (e: Event) => {
|
|
|
2137
2237
|
}
|
|
2138
2238
|
};
|
|
2139
2239
|
|
|
2140
|
-
|
|
2141
2240
|
// 记录上次滚动位置,用于判断滚动方向
|
|
2142
2241
|
let lastScrollTop = 0;
|
|
2143
2242
|
|
|
@@ -2260,7 +2359,7 @@ const highlightPosition = (
|
|
|
2260
2359
|
activeBlockDiv.value = null;
|
|
2261
2360
|
}
|
|
2262
2361
|
isHighlighted.value = false;
|
|
2263
|
-
|
|
2362
|
+
|
|
2264
2363
|
// 如果之前有高亮,触发高亮清除事件
|
|
2265
2364
|
if (hadHighlight) {
|
|
2266
2365
|
emit("highlight-clear");
|
|
@@ -2351,13 +2450,13 @@ const highlightPosition = (
|
|
|
2351
2450
|
// 保存原来的 transition 和 transform,临时禁用以避免与动画冲突
|
|
2352
2451
|
const originalTransition = elementRef.style.transition || "";
|
|
2353
2452
|
const originalTransform = elementRef.style.transform || "";
|
|
2354
|
-
|
|
2453
|
+
|
|
2355
2454
|
// 先移除可能存在的动画类,确保可以重新触发
|
|
2356
2455
|
elementRef.classList.remove("highlight-animated");
|
|
2357
|
-
|
|
2456
|
+
|
|
2358
2457
|
// 强制浏览器重新计算样式(触发重排),确保动画可以重新开始
|
|
2359
2458
|
void elementRef.offsetHeight;
|
|
2360
|
-
|
|
2459
|
+
|
|
2361
2460
|
// 使用 requestAnimationFrame 确保动画正确触发
|
|
2362
2461
|
requestAnimationFrame(() => {
|
|
2363
2462
|
// 临时禁用 transition,避免与动画的 transform 冲突
|
|
@@ -2365,11 +2464,12 @@ const highlightPosition = (
|
|
|
2365
2464
|
// 清除内联 transform,让动画的 transform 生效
|
|
2366
2465
|
elementRef.style.transform = "";
|
|
2367
2466
|
elementRef.style.transformOrigin = "center center";
|
|
2368
|
-
|
|
2467
|
+
|
|
2369
2468
|
// 使用内联样式直接设置动画(优先级最高)
|
|
2370
|
-
elementRef.style.animation =
|
|
2469
|
+
elementRef.style.animation =
|
|
2470
|
+
"highlightPulse 0.8s cubic-bezier(0.34, 1.56, 0.64, 1) 2";
|
|
2371
2471
|
elementRef.style.animationFillMode = "forwards";
|
|
2372
|
-
|
|
2472
|
+
|
|
2373
2473
|
// 调试:检查动画是否应用
|
|
2374
2474
|
const computedStyle = window.getComputedStyle(elementRef);
|
|
2375
2475
|
console.log("跳转高亮动画已应用:", {
|
|
@@ -2379,14 +2479,14 @@ const highlightPosition = (
|
|
|
2379
2479
|
transform: computedStyle.transform,
|
|
2380
2480
|
transformOrigin: computedStyle.transformOrigin,
|
|
2381
2481
|
});
|
|
2382
|
-
|
|
2482
|
+
|
|
2383
2483
|
// 监听动画开始事件(用于调试)
|
|
2384
2484
|
const handleAnimationStart = (e: AnimationEvent) => {
|
|
2385
2485
|
console.log("动画开始", e);
|
|
2386
2486
|
elementRef.removeEventListener("animationstart", handleAnimationStart);
|
|
2387
2487
|
};
|
|
2388
2488
|
elementRef.addEventListener("animationstart", handleAnimationStart);
|
|
2389
|
-
|
|
2489
|
+
|
|
2390
2490
|
// 监听动画结束事件,移除动画并恢复样式(确保动画可以重复触发)
|
|
2391
2491
|
const handleAnimationEnd = (e: AnimationEvent) => {
|
|
2392
2492
|
console.log("动画结束", e);
|
|
@@ -2411,7 +2511,7 @@ const highlightPosition = (
|
|
|
2411
2511
|
const isVisible = isElementVisible(elementRef, containerRef.value);
|
|
2412
2512
|
if (!isVisible) {
|
|
2413
2513
|
// 标记这是定位滚动,不应该被同步滚动干扰
|
|
2414
|
-
containerRef.value.dataset.pageScrolling =
|
|
2514
|
+
containerRef.value.dataset.pageScrolling = "true";
|
|
2415
2515
|
elementRef.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
2416
2516
|
// 延迟清除标记,确保滚动完成
|
|
2417
2517
|
setTimeout(() => {
|
|
@@ -2480,7 +2580,9 @@ const jumpToPosition = (
|
|
|
2480
2580
|
retryCount++;
|
|
2481
2581
|
setTimeout(tryHighlight, retryDelay);
|
|
2482
2582
|
} else {
|
|
2483
|
-
console.warn(
|
|
2583
|
+
console.warn(
|
|
2584
|
+
`无法找到并高亮指定位置: 页码 ${pageNum}, bbox: [${bbox.join(", ")}]`
|
|
2585
|
+
);
|
|
2484
2586
|
}
|
|
2485
2587
|
};
|
|
2486
2588
|
|
|
@@ -2590,7 +2692,7 @@ const handleContainerResize = () => {
|
|
|
2590
2692
|
lastContainerWidth = currentWidth;
|
|
2591
2693
|
}
|
|
2592
2694
|
|
|
2593
|
-
console.log(
|
|
2695
|
+
console.log("[ImagePreview] handleContainerResize 被调用:", {
|
|
2594
2696
|
autoFitWidth: props.autoFitWidth,
|
|
2595
2697
|
isUserZooming: isUserZooming.value,
|
|
2596
2698
|
isImageReady: isImageReady.value,
|
|
@@ -2609,23 +2711,25 @@ const handleContainerResize = () => {
|
|
|
2609
2711
|
}
|
|
2610
2712
|
|
|
2611
2713
|
// 宽度变化时不显示 loading,只更新缩放比例(避免看起来像重新加载)
|
|
2612
|
-
console.log(
|
|
2714
|
+
console.log("[ImagePreview] handleContainerResize: 开始重新计算");
|
|
2613
2715
|
|
|
2614
2716
|
// 立即计算并应用新的缩放比例,避免过渡期间露出底色
|
|
2615
2717
|
// 使用 requestAnimationFrame 确保在浏览器重绘前更新
|
|
2616
2718
|
requestAnimationFrame(() => {
|
|
2617
2719
|
try {
|
|
2618
|
-
console.log(
|
|
2720
|
+
console.log(
|
|
2721
|
+
"[ImagePreview] handleContainerResize: 开始计算自适应宽度..."
|
|
2722
|
+
);
|
|
2619
2723
|
const newScale = calculateAutoFitScale();
|
|
2620
|
-
console.log(
|
|
2621
|
-
|
|
2724
|
+
console.log("[ImagePreview] handleContainerResize: 计算结果:", newScale);
|
|
2725
|
+
|
|
2622
2726
|
if (newScale > 0) {
|
|
2623
2727
|
// 即使变化很小也立即更新,确保过渡期间图片始终填满容器
|
|
2624
2728
|
scale.value = newScale;
|
|
2625
2729
|
initialAutoFitScale.value = newScale;
|
|
2626
2730
|
}
|
|
2627
2731
|
} catch (error) {
|
|
2628
|
-
console.error(
|
|
2732
|
+
console.error("[ImagePreview] handleContainerResize: 计算失败:", error);
|
|
2629
2733
|
}
|
|
2630
2734
|
|
|
2631
2735
|
// 在过渡动画完成后再次检查,确保最终状态正确(处理过渡动画期间的连续变化)
|
|
@@ -2637,10 +2741,13 @@ const handleContainerResize = () => {
|
|
|
2637
2741
|
initialAutoFitScale.value = finalScale;
|
|
2638
2742
|
}
|
|
2639
2743
|
} catch (error) {
|
|
2640
|
-
console.error(
|
|
2744
|
+
console.error(
|
|
2745
|
+
"[ImagePreview] handleContainerResize: 最终计算失败:",
|
|
2746
|
+
error
|
|
2747
|
+
);
|
|
2641
2748
|
} finally {
|
|
2642
2749
|
// 计算完成,重置标记(不改变图片显示状态,因为宽度变化时不应该显示loading)
|
|
2643
|
-
console.log(
|
|
2750
|
+
console.log("[ImagePreview] handleContainerResize: 更新状态完成");
|
|
2644
2751
|
isResizing = false; // 重置标记
|
|
2645
2752
|
}
|
|
2646
2753
|
}, 350); // 350ms 延迟,略大于过渡动画时间(300ms),确保过渡完成后稳定
|
|
@@ -2659,14 +2766,14 @@ onMounted(() => {
|
|
|
2659
2766
|
// 隐藏图片,显示 loading
|
|
2660
2767
|
isImageReady.value = false;
|
|
2661
2768
|
isCalculatingAutoFit.value = true;
|
|
2662
|
-
|
|
2769
|
+
|
|
2663
2770
|
// 设置超时保护,防止一直显示 loading(最多等待 3 秒)
|
|
2664
2771
|
const timeoutId = setTimeout(() => {
|
|
2665
|
-
console.warn(
|
|
2772
|
+
console.warn("自适应宽度计算超时,强制显示图片");
|
|
2666
2773
|
isCalculatingAutoFit.value = false;
|
|
2667
2774
|
isImageReady.value = true;
|
|
2668
2775
|
}, 3000);
|
|
2669
|
-
|
|
2776
|
+
|
|
2670
2777
|
nextTick(() => {
|
|
2671
2778
|
nextTick(() => {
|
|
2672
2779
|
setTimeout(() => {
|
|
@@ -2677,7 +2784,7 @@ onMounted(() => {
|
|
|
2677
2784
|
initialAutoFitScale.value = autoScale;
|
|
2678
2785
|
}
|
|
2679
2786
|
} catch (error) {
|
|
2680
|
-
console.warn(
|
|
2787
|
+
console.warn("计算自适应宽度失败:", error);
|
|
2681
2788
|
} finally {
|
|
2682
2789
|
// 清除超时保护
|
|
2683
2790
|
clearTimeout(timeoutId);
|
|
@@ -2698,7 +2805,7 @@ onMounted(() => {
|
|
|
2698
2805
|
|
|
2699
2806
|
// 监听容器尺寸变化(用于响应外部收起/展开操作)
|
|
2700
2807
|
nextTick(() => {
|
|
2701
|
-
if (containerRef.value && typeof ResizeObserver !==
|
|
2808
|
+
if (containerRef.value && typeof ResizeObserver !== "undefined") {
|
|
2702
2809
|
resizeObserver = new ResizeObserver((entries) => {
|
|
2703
2810
|
// 使用防抖,避免频繁触发
|
|
2704
2811
|
if (resizeDebounceTimer) {
|
|
@@ -2715,7 +2822,7 @@ onMounted(() => {
|
|
|
2715
2822
|
resizeObserver.observe(containerRef.value);
|
|
2716
2823
|
} else {
|
|
2717
2824
|
// 降级方案:监听窗口大小变化
|
|
2718
|
-
window.addEventListener(
|
|
2825
|
+
window.addEventListener("resize", handleContainerResize);
|
|
2719
2826
|
}
|
|
2720
2827
|
});
|
|
2721
2828
|
});
|
|
@@ -2745,7 +2852,7 @@ onBeforeUnmount(() => {
|
|
|
2745
2852
|
resizeObserver = null;
|
|
2746
2853
|
}
|
|
2747
2854
|
// 移除窗口 resize 监听器(降级方案)
|
|
2748
|
-
window.removeEventListener(
|
|
2855
|
+
window.removeEventListener("resize", handleContainerResize);
|
|
2749
2856
|
});
|
|
2750
2857
|
|
|
2751
2858
|
// 获取当前高亮位置信息(用于连接线功能)
|
|
@@ -3006,7 +3113,7 @@ defineExpose({
|
|
|
3006
3113
|
width: 320px;
|
|
3007
3114
|
pointer-events: auto;
|
|
3008
3115
|
background-color: #fff;
|
|
3009
|
-
border-radius:
|
|
3116
|
+
border-radius: 6px;
|
|
3010
3117
|
box-shadow: 0 4px 16px rgb(0 0 0 / 20%);
|
|
3011
3118
|
animation: fade-in 0.2s ease;
|
|
3012
3119
|
overflow: hidden;
|
|
@@ -3016,7 +3123,33 @@ defineExpose({
|
|
|
3016
3123
|
align-items: center;
|
|
3017
3124
|
justify-content: space-between;
|
|
3018
3125
|
padding: 12px 16px;
|
|
3019
|
-
|
|
3126
|
+
padding-bottom: 8px;
|
|
3127
|
+
|
|
3128
|
+
.annotation-input-header-title {
|
|
3129
|
+
position: relative;
|
|
3130
|
+
max-height: 21px;
|
|
3131
|
+
padding-left: 12px; // 增加左侧内边距,为竖线留出空间
|
|
3132
|
+
padding-right: 6px;
|
|
3133
|
+
color: #8f959e;
|
|
3134
|
+
font-size: 12px;
|
|
3135
|
+
line-height: 20px;
|
|
3136
|
+
overflow: hidden;
|
|
3137
|
+
white-space: nowrap;
|
|
3138
|
+
-o-text-overflow: ellipsis;
|
|
3139
|
+
text-overflow: ellipsis;
|
|
3140
|
+
|
|
3141
|
+
// 左侧竖线,表示引用
|
|
3142
|
+
&::before {
|
|
3143
|
+
content: "";
|
|
3144
|
+
position: absolute;
|
|
3145
|
+
left: 0;
|
|
3146
|
+
top: 2px;
|
|
3147
|
+
width: 2px;
|
|
3148
|
+
height: 16px;
|
|
3149
|
+
background-color: #bbbfc4 !important;
|
|
3150
|
+
border-radius: 1px;
|
|
3151
|
+
}
|
|
3152
|
+
}
|
|
3020
3153
|
|
|
3021
3154
|
.annotation-input-title {
|
|
3022
3155
|
font-size: 14px;
|
|
@@ -3024,6 +3157,27 @@ defineExpose({
|
|
|
3024
3157
|
color: #1d2129;
|
|
3025
3158
|
}
|
|
3026
3159
|
|
|
3160
|
+
.annotation-header-user-info {
|
|
3161
|
+
display: flex;
|
|
3162
|
+
align-items: center;
|
|
3163
|
+
gap: 8px;
|
|
3164
|
+
flex: 1;
|
|
3165
|
+
}
|
|
3166
|
+
|
|
3167
|
+
.annotation-user-avatar {
|
|
3168
|
+
width: 24px;
|
|
3169
|
+
height: 24px;
|
|
3170
|
+
border-radius: 50%;
|
|
3171
|
+
object-fit: cover;
|
|
3172
|
+
background-color: #f0f0f0;
|
|
3173
|
+
}
|
|
3174
|
+
|
|
3175
|
+
.annotation-username {
|
|
3176
|
+
font-size: 14px;
|
|
3177
|
+
font-weight: 500;
|
|
3178
|
+
color: #1d2129;
|
|
3179
|
+
}
|
|
3180
|
+
|
|
3027
3181
|
.annotation-close-btn {
|
|
3028
3182
|
display: flex;
|
|
3029
3183
|
align-items: center;
|
|
@@ -3037,6 +3191,7 @@ defineExpose({
|
|
|
3037
3191
|
border-radius: 4px;
|
|
3038
3192
|
color: #86909c;
|
|
3039
3193
|
transition: all 0.2s;
|
|
3194
|
+
margin-left: 10px;
|
|
3040
3195
|
|
|
3041
3196
|
&:hover {
|
|
3042
3197
|
background-color: #f0f0f0;
|
|
@@ -3046,10 +3201,12 @@ defineExpose({
|
|
|
3046
3201
|
}
|
|
3047
3202
|
|
|
3048
3203
|
.annotation-input-content {
|
|
3049
|
-
padding:
|
|
3204
|
+
padding: 0px 16px;
|
|
3050
3205
|
|
|
3051
3206
|
.annotation-textarea {
|
|
3052
3207
|
width: 100%;
|
|
3208
|
+
margin: 12px 0px;
|
|
3209
|
+
margin-bottom: 16px;
|
|
3053
3210
|
}
|
|
3054
3211
|
}
|
|
3055
3212
|
|
|
@@ -3059,10 +3216,20 @@ defineExpose({
|
|
|
3059
3216
|
justify-content: flex-end;
|
|
3060
3217
|
gap: 8px;
|
|
3061
3218
|
padding: 12px 16px;
|
|
3062
|
-
border-top: 1px solid #e5e7eb;
|
|
3063
3219
|
}
|
|
3064
3220
|
}
|
|
3065
3221
|
|
|
3222
|
+
.annotation-input-popup:before {
|
|
3223
|
+
content: "";
|
|
3224
|
+
position: absolute;
|
|
3225
|
+
top: -2px;
|
|
3226
|
+
left: -1px;
|
|
3227
|
+
right: -1px;
|
|
3228
|
+
border-top: 8px solid rgb(255, 198, 10);
|
|
3229
|
+
border-top-left-radius: 6px;
|
|
3230
|
+
border-top-right-radius: 6px;
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3066
3233
|
// 有批注的文本块样式
|
|
3067
3234
|
.text-block.has-annotation {
|
|
3068
3235
|
position: relative;
|