From 3b5a1eea26da0dc1c97cf22bdbaf092e0382d55a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=8B=A4=E6=B0=91?= Date: Tue, 11 Nov 2025 16:44:22 +0800 Subject: [PATCH] init --- src/renderer/components/OCRPage.vue | 137 ++++++++++++++++++++++------ 1 file changed, 111 insertions(+), 26 deletions(-) diff --git a/src/renderer/components/OCRPage.vue b/src/renderer/components/OCRPage.vue index bb54cea..4e560de 100644 --- a/src/renderer/components/OCRPage.vue +++ b/src/renderer/components/OCRPage.vue @@ -80,20 +80,20 @@ @wheel="handleWheel">
- - - +
+ +
{{ getFileTypeIcon(currentFile) }} @@ -279,6 +279,8 @@ const isDragging = ref(false) const lastDragPos = ref({ x: 0, y: 0 }) const resultContainer = ref(null) const previewContainer = ref(null) +const previewImage = ref(null) +const imageWrapper = ref(null) const textareas = ref([]) // 计算属性 @@ -340,16 +342,38 @@ const selectFile = async (file: FileRecord): Promise => { // 图片加载完成后的处理 const handleImageLoad = (): void => { // 确保图片在可视区域内 - resetZoom() + nextTick(() => { + resetZoom() + centerImage() + }) +} + +// 居中图片 +const centerImage = (): void => { + if (!previewImage.value || !imageWrapper.value) return + + const container = imageWrapper.value + const img = previewImage.value + + // 计算居中偏移 + const containerRect = container.getBoundingClientRect() + const imgRect = img.getBoundingClientRect() + + // 如果图片比容器小,居中显示 + if (imgRect.width < containerRect.width && imgRect.height < containerRect.height) { + dragOffset.value = { x: 0, y: 0 } + } } // 缩放功能 const zoomIn = (): void => { zoomLevel.value = Math.min(zoomLevel.value + 0.1, 3) + constrainDragOffset() // 限制拖拽范围 } const zoomOut = (): void => { zoomLevel.value = Math.max(zoomLevel.value - 0.1, 0.5) + constrainDragOffset() // 限制拖拽范围 } const resetZoom = (): void => { @@ -357,6 +381,28 @@ const resetZoom = (): void => { dragOffset.value = { x: 0, y: 0 } } +// 限制拖拽范围,防止图片被拖出可视区域 +const constrainDragOffset = (): void => { + if (!previewImage.value || !imageWrapper.value) return + + const container = imageWrapper.value + const img = previewImage.value + + const containerRect = container.getBoundingClientRect() + const imgRect = { + width: img.naturalWidth * zoomLevel.value, + height: img.naturalHeight * zoomLevel.value + } + + // 计算最大允许的拖拽偏移 + const maxX = Math.max(0, (imgRect.width - containerRect.width) / 2) + const maxY = Math.max(0, (imgRect.height - containerRect.height) / 2) + + // 限制拖拽范围 + dragOffset.value.x = Math.max(-maxX, Math.min(maxX, dragOffset.value.x)) + dragOffset.value.y = Math.max(-maxY, Math.min(maxY, dragOffset.value.y)) +} + // 拖拽功能 const startDrag = (event: MouseEvent): void => { if (!currentFile.value || !isImage(currentFile.value)) return @@ -375,6 +421,9 @@ const doDrag = (event: MouseEvent): void => { dragOffset.value.x += deltaX dragOffset.value.y += deltaY + // 限制拖拽范围 + constrainDragOffset() + lastDragPos.value = { x: event.clientX, y: event.clientY } } @@ -425,11 +474,6 @@ const autoResizeTextarea = (event: { target: EventTarget | null }): void => { // 设置新高度,但限制最大高度 const newHeight = Math.min(textarea.scrollHeight, 300) // 最大300px textarea.style.height = `${newHeight}px` - - // 确保结果容器可以滚动 - if (resultContainer.value) { - resultContainer.value.style.overflowY = 'auto' - } } const saveTextEdit = async (): Promise => { @@ -526,6 +570,9 @@ const startOcr = async (): Promise => { // 识别成功后重置图片位置,确保在可视区域内 resetZoom() + nextTick(() => { + centerImage() + }) } else { throw new Error('OCR识别失败') } @@ -794,6 +841,7 @@ onUnmounted(() => { gap: 1px; background: #e9ecef; overflow: hidden; + min-height: 0; /* 重要:允许内容缩小 */ } .file-panel, @@ -802,6 +850,7 @@ onUnmounted(() => { background: white; display: flex; flex-direction: column; + min-height: 0; /* 重要:允许内容缩小 */ } .panel-header { @@ -860,6 +909,7 @@ onUnmounted(() => { overflow: hidden; /* 防止内部溢出 */ display: flex; flex-direction: column; + min-height: 0; /* 重要:允许内容缩小 */ } .file-preview { @@ -879,6 +929,25 @@ onUnmounted(() => { cursor: grabbing; } +.preview-content { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + position: relative; +} + +.image-wrapper { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + position: relative; +} + .preview-image { max-width: 100%; max-height: 100%; @@ -887,6 +956,7 @@ onUnmounted(() => { border-radius: 4px; transition: transform 0.1s ease; transform-origin: center center; + position: relative; } .file-list { @@ -986,10 +1056,19 @@ onUnmounted(() => { color: #6c757d; } +/* 修复识别结果滚动问题 */ .ocr-result { flex: 1; - overflow-y: auto; - padding: 1rem; + display: flex; + flex-direction: column; + min-height: 0; /* 重要:允许内容缩小 */ + overflow: hidden; /* 外层隐藏滚动 */ +} + +.result-content { + flex: 1; + overflow-y: auto; /* 内容区域滚动 */ + padding: 0 1rem 1rem 1rem; min-height: 0; /* 重要:允许内容缩小 */ } @@ -997,6 +1076,11 @@ onUnmounted(() => { text-align: center; padding: 2rem; color: #6c757d; + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; } .spinner { @@ -1019,10 +1103,6 @@ onUnmounted(() => { margin-top: 0.5rem; } -.result-content { - line-height: 1.6; -} - .text-block { margin-bottom: 1rem; padding: 0.5rem; @@ -1181,6 +1261,11 @@ onUnmounted(() => { text-align: center; padding: 2rem; color: #6c757d; + flex: 1; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; } .result-summary {