diff --git a/ui/src/components/ai-chat/index.vue b/ui/src/components/ai-chat/index.vue index 02a18e4ec..b402cc497 100644 --- a/ui/src/components/ai-chat/index.vue +++ b/ui/src/components/ai-chat/index.vue @@ -134,6 +134,7 @@ import UserForm from '@/components/ai-chat/component/user-form/index.vue' import Control from '@/components/ai-chat/component/control/index.vue' import { t } from '@/locales' import bus from '@/bus' +import { throttle } from 'lodash-es' provide('upload', (file: any, loading?: Ref) => { return props.type === 'debug-ai-chat' ? applicationApi.postUploadFile(file, 'TEMPORARY_120_MINUTE', 'TEMPORARY_120_MINUTE', loading) @@ -658,6 +659,29 @@ onBeforeMount(() => { return Promise.resolve(null) } }) + +function parseTransform(transformStr: string) { + const result = { scale: 1, translateX: 0, translateY: 0, translateZ: 0 } + + if (!transformStr || transformStr === 'none') return result + + // 使用正则表达式匹配 scale 和 translate3d 的值 + const scaleMatch = transformStr.match(/scale\(([^)]+)\)/) + const translateMatch = transformStr.match(/translate3d\(([^)]+)\)/) + + if (scaleMatch) { + // scale可能是一个值,也可能是两个值(scaleX, scaleY) + const scaleValues = scaleMatch[1].split(',').map((v) => parseFloat(v.trim())) + result.scale = scaleValues[0] + } + + if (translateMatch) { + const translateValues = translateMatch[1].split(',').map((v) => parseFloat(v.trim())) + ;[result.translateX, result.translateY, result.translateZ] = translateValues + } + + return result +} onMounted(() => { if (isUserInput.value && localStorage.getItem(`${accessToken}userForm`)) { const userFormData = JSON.parse(localStorage.getItem(`${accessToken}userForm`) || '{}') @@ -667,6 +691,47 @@ onMounted(() => { window.speechSynthesis.cancel() } + const handleZoom = throttle((event: WheelEvent, target: HTMLElement) => { + // 2. 解析当前变换状态 + const currentTransform = target.style.transform + const transformValues = parseTransform(currentTransform) + let { scale, translateX, translateY } = transformValues + // 确保scale是数值类型 + const currentScale = Array.isArray(scale) ? scale[0] : scale + + // 3. 计算缩放方向和新的缩放比例 + const zoomIntensity = 0.05 // 每次滚轮的缩放步长 + const zoomFactor = event.deltaY < 0 ? 1 + zoomIntensity : 1 - zoomIntensity + const newScale = Math.max(0.1, currentScale * zoomFactor) // 设置最小缩放限制 + // 4. 计算新的平移值 + const newTranslateX = (translateX * currentScale) / newScale + const newTranslateY = (translateY * currentScale) / newScale + // 5. 应用新的变换 + target.style.transform = `scale(${newScale}) translate3d(${newTranslateX}px, ${newTranslateY}px, 0px)` + }, 50) // 50ms 内只执行一次 + + document.body.addEventListener( + 'wheel', + (event) => { + // 1. 定位目标元素 + if (event.target) { + const target = event.target as HTMLElement + // 假设打开状态的图片具有特定类名 + if (target.className && target.className.includes('medium-zoom-overlay')) { + event.preventDefault() + event.stopPropagation() + } + if (target.className && target.className.includes('medium-zoom-image--opened')) { + event.preventDefault() + event.stopPropagation() + + handleZoom(event, target) + } + } + }, + { passive: false }, + ) + window.sendMessage = sendMessage bus.on('on:transcribing', (status: boolean) => { transcribing.value = status