update pay function
This commit is contained in:
270
src/components/ImagePreviewModal/index.js
Normal file
270
src/components/ImagePreviewModal/index.js
Normal file
@@ -0,0 +1,270 @@
|
||||
/**
|
||||
* 图片预览弹窗组件
|
||||
* 支持多张图片左右切换、缩放、下载
|
||||
*/
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
Image,
|
||||
IconButton,
|
||||
HStack,
|
||||
Text,
|
||||
Box,
|
||||
} from '@chakra-ui/react';
|
||||
import { ChevronLeft, ChevronRight, Download, ZoomIn, ZoomOut } from 'lucide-react';
|
||||
import { motion, AnimatePresence } from 'framer-motion';
|
||||
|
||||
const MotionBox = motion(Box);
|
||||
|
||||
const ImagePreviewModal = ({ isOpen, onClose, images = [], initialIndex = 0 }) => {
|
||||
const [currentIndex, setCurrentIndex] = useState(initialIndex);
|
||||
const [scale, setScale] = useState(1);
|
||||
|
||||
// 切换到上一张
|
||||
const handlePrevious = () => {
|
||||
setCurrentIndex((prev) => (prev - 1 + images.length) % images.length);
|
||||
setScale(1); // 重置缩放
|
||||
};
|
||||
|
||||
// 切换到下一张
|
||||
const handleNext = () => {
|
||||
setCurrentIndex((prev) => (prev + 1) % images.length);
|
||||
setScale(1); // 重置缩放
|
||||
};
|
||||
|
||||
// 放大
|
||||
const handleZoomIn = () => {
|
||||
setScale((prev) => Math.min(prev + 0.25, 3));
|
||||
};
|
||||
|
||||
// 缩小
|
||||
const handleZoomOut = () => {
|
||||
setScale((prev) => Math.max(prev - 0.25, 0.5));
|
||||
};
|
||||
|
||||
// 下载图片
|
||||
const handleDownload = () => {
|
||||
const link = document.createElement('a');
|
||||
link.href = images[currentIndex];
|
||||
link.download = `image-${currentIndex + 1}.jpg`;
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
};
|
||||
|
||||
// 键盘快捷键
|
||||
React.useEffect(() => {
|
||||
const handleKeyDown = (e) => {
|
||||
if (!isOpen) return;
|
||||
|
||||
switch (e.key) {
|
||||
case 'ArrowLeft':
|
||||
handlePrevious();
|
||||
break;
|
||||
case 'ArrowRight':
|
||||
handleNext();
|
||||
break;
|
||||
case 'Escape':
|
||||
onClose();
|
||||
break;
|
||||
case '+':
|
||||
case '=':
|
||||
handleZoomIn();
|
||||
break;
|
||||
case '-':
|
||||
handleZoomOut();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
window.addEventListener('keydown', handleKeyDown);
|
||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||
}, [isOpen, currentIndex]);
|
||||
|
||||
// 关闭时重置状态
|
||||
const handleClose = () => {
|
||||
setScale(1);
|
||||
setCurrentIndex(initialIndex);
|
||||
onClose();
|
||||
};
|
||||
|
||||
if (!images || images.length === 0) return null;
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={handleClose} size="full" isCentered>
|
||||
<ModalOverlay bg="blackAlpha.900" backdropFilter="blur(10px)" />
|
||||
<ModalContent bg="transparent" boxShadow="none" m="0">
|
||||
<ModalCloseButton
|
||||
size="lg"
|
||||
color="white"
|
||||
bg="blackAlpha.600"
|
||||
_hover={{ bg: 'blackAlpha.800' }}
|
||||
zIndex="2"
|
||||
top="20px"
|
||||
right="20px"
|
||||
/>
|
||||
|
||||
<ModalBody
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
position="relative"
|
||||
p="0"
|
||||
>
|
||||
{/* 图片显示区域 */}
|
||||
<AnimatePresence mode="wait">
|
||||
<MotionBox
|
||||
key={currentIndex}
|
||||
initial={{ opacity: 0, scale: 0.9 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
exit={{ opacity: 0, scale: 0.9 }}
|
||||
transition={{ duration: 0.3 }}
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
justifyContent="center"
|
||||
maxH="90vh"
|
||||
maxW="90vw"
|
||||
>
|
||||
<Image
|
||||
src={images[currentIndex]}
|
||||
alt={`图片 ${currentIndex + 1}`}
|
||||
maxH="90vh"
|
||||
maxW="90vw"
|
||||
objectFit="contain"
|
||||
transform={`scale(${scale})`}
|
||||
transition="transform 0.3s"
|
||||
cursor={scale > 1 ? 'grab' : 'default'}
|
||||
userSelect="none"
|
||||
/>
|
||||
</MotionBox>
|
||||
</AnimatePresence>
|
||||
|
||||
{/* 左右切换按钮(仅多张图片时显示) */}
|
||||
{images.length > 1 && (
|
||||
<>
|
||||
<IconButton
|
||||
icon={<ChevronLeft size={32} />}
|
||||
position="absolute"
|
||||
left="20px"
|
||||
top="50%"
|
||||
transform="translateY(-50%)"
|
||||
onClick={handlePrevious}
|
||||
size="lg"
|
||||
borderRadius="full"
|
||||
bg="blackAlpha.600"
|
||||
color="white"
|
||||
_hover={{ bg: 'blackAlpha.800', transform: 'translateY(-50%) scale(1.1)' }}
|
||||
_active={{ transform: 'translateY(-50%) scale(0.95)' }}
|
||||
aria-label="上一张"
|
||||
zIndex="2"
|
||||
/>
|
||||
|
||||
<IconButton
|
||||
icon={<ChevronRight size={32} />}
|
||||
position="absolute"
|
||||
right="20px"
|
||||
top="50%"
|
||||
transform="translateY(-50%)"
|
||||
onClick={handleNext}
|
||||
size="lg"
|
||||
borderRadius="full"
|
||||
bg="blackAlpha.600"
|
||||
color="white"
|
||||
_hover={{ bg: 'blackAlpha.800', transform: 'translateY(-50%) scale(1.1)' }}
|
||||
_active={{ transform: 'translateY(-50%) scale(0.95)' }}
|
||||
aria-label="下一张"
|
||||
zIndex="2"
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 底部工具栏 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
bottom="30px"
|
||||
left="50%"
|
||||
transform="translateX(-50%)"
|
||||
bg="blackAlpha.700"
|
||||
borderRadius="full"
|
||||
px="6"
|
||||
py="3"
|
||||
backdropFilter="blur(10px)"
|
||||
zIndex="2"
|
||||
>
|
||||
<HStack spacing="4">
|
||||
{/* 缩放控制 */}
|
||||
<HStack spacing="2">
|
||||
<IconButton
|
||||
icon={<ZoomOut size={18} />}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
color="white"
|
||||
onClick={handleZoomOut}
|
||||
isDisabled={scale <= 0.5}
|
||||
_hover={{ bg: 'whiteAlpha.200' }}
|
||||
aria-label="缩小"
|
||||
/>
|
||||
<Text color="white" fontSize="sm" fontWeight="500" minW="60px" textAlign="center">
|
||||
{Math.round(scale * 100)}%
|
||||
</Text>
|
||||
<IconButton
|
||||
icon={<ZoomIn size={18} />}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
color="white"
|
||||
onClick={handleZoomIn}
|
||||
isDisabled={scale >= 3}
|
||||
_hover={{ bg: 'whiteAlpha.200' }}
|
||||
aria-label="放大"
|
||||
/>
|
||||
</HStack>
|
||||
|
||||
{/* 下载按钮 */}
|
||||
<IconButton
|
||||
icon={<Download size={18} />}
|
||||
size="sm"
|
||||
variant="ghost"
|
||||
color="white"
|
||||
onClick={handleDownload}
|
||||
_hover={{ bg: 'whiteAlpha.200' }}
|
||||
aria-label="下载图片"
|
||||
/>
|
||||
|
||||
{/* 图片计数(仅多张图片时显示) */}
|
||||
{images.length > 1 && (
|
||||
<Text color="white" fontSize="sm" fontWeight="500">
|
||||
{currentIndex + 1} / {images.length}
|
||||
</Text>
|
||||
)}
|
||||
</HStack>
|
||||
</Box>
|
||||
|
||||
{/* 快捷键提示 */}
|
||||
<Box
|
||||
position="absolute"
|
||||
top="80px"
|
||||
left="20px"
|
||||
bg="blackAlpha.600"
|
||||
borderRadius="md"
|
||||
px="4"
|
||||
py="2"
|
||||
backdropFilter="blur(10px)"
|
||||
>
|
||||
<Text color="whiteAlpha.800" fontSize="xs">
|
||||
快捷键: ← → 切换 | + - 缩放 | ESC 关闭
|
||||
</Text>
|
||||
</Box>
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ImagePreviewModal;
|
||||
Reference in New Issue
Block a user