update pay function

This commit is contained in:
2025-11-23 22:27:57 +08:00
parent 76bf560b36
commit d9a169d2e0
2 changed files with 290 additions and 0 deletions

View 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;

View File

@@ -40,6 +40,7 @@ import {
} from '@services/elasticsearchService';
import EventTimeline from './components/EventTimeline';
import CommentSection from './components/CommentSection';
import ImagePreviewModal from '@components/ImagePreviewModal';
const MotionBox = motion(Box);
@@ -53,6 +54,10 @@ const PostDetail = () => {
const [isLiked, setIsLiked] = useState(false);
const [likes, setLikes] = useState(0);
// 图片预览相关状态
const [isImagePreviewOpen, setIsImagePreviewOpen] = useState(false);
const [previewImageIndex, setPreviewImageIndex] = useState(0);
// 加载帖子数据
useEffect(() => {
const loadPostData = async () => {
@@ -91,6 +96,12 @@ const PostDetail = () => {
}
};
// 打开图片预览
const handleImageClick = (index) => {
setPreviewImageIndex(index);
setIsImagePreviewOpen(true);
};
// 格式化时间
const formatTime = (dateString) => {
const date = new Date(dateString);
@@ -272,6 +283,7 @@ const PostDetail = () => {
border="1px solid"
borderColor={forumColors.border.default}
cursor="pointer"
onClick={() => handleImageClick(index)}
_hover={{
transform: 'scale(1.05)',
boxShadow: forumColors.shadows.gold,
@@ -363,6 +375,14 @@ const PostDetail = () => {
</Box>
</SimpleGrid>
</Container>
{/* 图片预览弹窗 */}
<ImagePreviewModal
isOpen={isImagePreviewOpen}
onClose={() => setIsImagePreviewOpen(false)}
images={post?.images || []}
initialIndex={previewImageIndex}
/>
</Box>
);
};