feat(ui): 新增支持缩放、拖拽及键盘控制的图片预览组件并优化样式,更新安装程序支持 VC++ 运行库自动安装,多账号支持列表选择。

This commit is contained in:
Forrest
2026-01-16 22:16:55 +08:00
parent b7eb19aad6
commit 6eabd707f8
8 changed files with 456 additions and 44 deletions

View File

@@ -0,0 +1,101 @@
import React, { useState, useRef, useCallback, useEffect } from 'react'
import { X } from 'lucide-react'
import { createPortal } from 'react-dom'
import './ImagePreview.scss'
interface ImagePreviewProps {
src: string
onClose: () => void
}
export const ImagePreview: React.FC<ImagePreviewProps> = ({ src, onClose }) => {
const [scale, setScale] = useState(1)
const [position, setPosition] = useState({ x: 0, y: 0 })
const [isDragging, setIsDragging] = useState(false)
const dragStart = useRef({ x: 0, y: 0 })
const positionStart = useRef({ x: 0, y: 0 })
const containerRef = useRef<HTMLDivElement>(null)
// 滚轮缩放
const handleWheel = useCallback((e: React.WheelEvent) => {
e.preventDefault()
const delta = e.deltaY > 0 ? 0.9 : 1.1
setScale(prev => Math.min(Math.max(prev * delta, 0.5), 5))
}, [])
// 开始拖动
const handleMouseDown = useCallback((e: React.MouseEvent) => {
if (scale <= 1) return
e.preventDefault()
setIsDragging(true)
dragStart.current = { x: e.clientX, y: e.clientY }
positionStart.current = { ...position }
}, [scale, position])
// 拖动中
const handleMouseMove = useCallback((e: React.MouseEvent) => {
if (!isDragging) return
const dx = e.clientX - dragStart.current.x
const dy = e.clientY - dragStart.current.y
setPosition({
x: positionStart.current.x + dx,
y: positionStart.current.y + dy
})
}, [isDragging])
// 结束拖动
const handleMouseUp = useCallback(() => {
setIsDragging(false)
}, [])
// 双击重置
const handleDoubleClick = useCallback(() => {
setScale(1)
setPosition({ x: 0, y: 0 })
}, [])
// 点击背景关闭
const handleOverlayClick = useCallback((e: React.MouseEvent) => {
if (e.target === containerRef.current) {
onClose()
}
}, [onClose])
// ESC 关闭
useEffect(() => {
const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') onClose()
}
window.addEventListener('keydown', handleKeyDown)
return () => window.removeEventListener('keydown', handleKeyDown)
}, [onClose])
return createPortal(
<div
ref={containerRef}
className="image-preview-overlay"
onClick={handleOverlayClick}
onMouseMove={handleMouseMove}
onMouseUp={handleMouseUp}
onMouseLeave={handleMouseUp}
>
<img
src={src}
alt="图片预览"
className={`preview-image ${isDragging ? 'dragging' : ''}`}
style={{
transform: `translate(${position.x}px, ${position.y}px) scale(${scale})`,
cursor: scale > 1 ? (isDragging ? 'grabbing' : 'grab') : 'default'
}}
onWheel={handleWheel}
onMouseDown={handleMouseDown}
onDoubleClick={handleDoubleClick}
draggable={false}
/>
<button className="image-preview-close" onClick={onClose}>
<X size={20} />
</button>
</div>,
document.body
)
}