mirror of
https://github.com/DrizzleTime/Foxel.git
synced 2026-07-12 16:03:27 +08:00
feat(image-viewer): add loading spinner and improve image loading logic
This commit is contained in:
@@ -109,11 +109,6 @@
|
|||||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.viewer-img.thumbnail {
|
|
||||||
filter: blur(1px);
|
|
||||||
transform-origin: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zoom-controls {
|
.zoom-controls {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 100px;
|
bottom: 100px;
|
||||||
@@ -141,15 +136,6 @@
|
|||||||
background: rgba(255, 255, 255, 0.2) !important;
|
background: rgba(255, 255, 255, 0.2) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 添加原图模式指示样式 */
|
|
||||||
.zoom-controls .ant-btn-primary {
|
|
||||||
background-color: #1890ff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.zoom-controls .ant-btn-primary:hover {
|
|
||||||
background-color: #40a9ff !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-button {
|
.nav-button {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
@@ -351,3 +337,31 @@
|
|||||||
.exif-description .ant-descriptions-row > td {
|
.exif-description .ant-descriptions-row > td {
|
||||||
padding: 12px 16px;
|
padding: 12px 16px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 图片加载动画样式 */
|
||||||
|
.image-loading-spinner {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
z-index: 1004;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-text {
|
||||||
|
color: white;
|
||||||
|
font-size: 14px;
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spin组件自定义样式 */
|
||||||
|
.image-loading-spinner .ant-spin {
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-loading-spinner .ant-spin-dot-item {
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,21 +1,10 @@
|
|||||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||||
import { Button, Space, Dropdown, message } from 'antd';
|
import { Button, Space, Dropdown, message, Spin } from 'antd';
|
||||||
import {
|
import {
|
||||||
ZoomInOutlined,
|
ZoomInOutlined, ZoomOutOutlined, ExpandOutlined, InfoCircleOutlined,
|
||||||
ZoomOutOutlined,
|
CloseOutlined, LeftOutlined, RightOutlined, RotateLeftOutlined,
|
||||||
ExpandOutlined,
|
RotateRightOutlined, HeartOutlined, HeartFilled, DownloadOutlined,
|
||||||
InfoCircleOutlined,
|
ShareAltOutlined, FolderAddOutlined
|
||||||
CloseOutlined,
|
|
||||||
LeftOutlined,
|
|
||||||
RightOutlined,
|
|
||||||
RotateLeftOutlined,
|
|
||||||
RotateRightOutlined,
|
|
||||||
HeartOutlined,
|
|
||||||
HeartFilled,
|
|
||||||
DownloadOutlined,
|
|
||||||
ShareAltOutlined,
|
|
||||||
FolderAddOutlined,
|
|
||||||
EyeOutlined
|
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch';
|
import { TransformWrapper, TransformComponent } from 'react-zoom-pan-pinch';
|
||||||
import type { PictureResponse, AlbumResponse } from '../../api/types';
|
import type { PictureResponse, AlbumResponse } from '../../api/types';
|
||||||
@@ -24,7 +13,6 @@ import ImageInfo from './ImageInfo';
|
|||||||
import ShareImageDialog from './ShareImageDialog';
|
import ShareImageDialog from './ShareImageDialog';
|
||||||
import './ImageViewer.css';
|
import './ImageViewer.css';
|
||||||
|
|
||||||
|
|
||||||
interface ImageViewerProps {
|
interface ImageViewerProps {
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
@@ -45,7 +33,7 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
|
|||||||
onFavorite,
|
onFavorite,
|
||||||
onNext,
|
onNext,
|
||||||
onPrevious,
|
onPrevious,
|
||||||
showFavoriteCount = false, // 默认不显示收藏数量
|
showFavoriteCount = false,
|
||||||
onShare,
|
onShare,
|
||||||
}) => {
|
}) => {
|
||||||
const wasVisible = useRef(visible);
|
const wasVisible = useRef(visible);
|
||||||
@@ -56,80 +44,71 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
|
|||||||
const [loadingAlbums, setLoadingAlbums] = useState(false);
|
const [loadingAlbums, setLoadingAlbums] = useState(false);
|
||||||
const [localImages, setLocalImages] = useState<PictureResponse[]>(images);
|
const [localImages, setLocalImages] = useState<PictureResponse[]>(images);
|
||||||
const [shareDialogVisible, setShareDialogVisible] = useState(false);
|
const [shareDialogVisible, setShareDialogVisible] = useState(false);
|
||||||
|
|
||||||
// 保留加载状态跟踪,但不再用于显示缩略图
|
|
||||||
const [imageLoaded, setImageLoaded] = useState(false);
|
const [imageLoaded, setImageLoaded] = useState(false);
|
||||||
const [showOriginal, setShowOriginal] = useState(false); // 新增:控制是否显示原图
|
const preloadImageRef = useRef<HTMLImageElement | null>(null);
|
||||||
|
|
||||||
// 保留防缓存机制
|
|
||||||
const [cacheKey, setCacheKey] = useState<number>(Date.now());
|
const [cacheKey, setCacheKey] = useState<number>(Date.now());
|
||||||
|
|
||||||
// 当前显示的图片
|
|
||||||
const currentImage = localImages[currentIndex];
|
const currentImage = localImages[currentIndex];
|
||||||
|
|
||||||
// 重置查看器状态,包含图片加载状态
|
|
||||||
const resetViewerState = useCallback(() => {
|
const resetViewerState = useCallback(() => {
|
||||||
setRotation(0);
|
setRotation(0);
|
||||||
setIsInfoDrawerOpen(false);
|
setIsInfoDrawerOpen(false);
|
||||||
setImageLoaded(false); // 重置图片加载状态
|
setImageLoaded(false);
|
||||||
setShowOriginal(false); // 重置为显示缩略图
|
setCacheKey(Date.now());
|
||||||
setCacheKey(Date.now()); // 每次重置时更新缓存键
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setImageLoaded(false);
|
setImageLoaded(false);
|
||||||
setShowOriginal(false); // 切换图片时重置为缩略图模式
|
|
||||||
}, [currentIndex]);
|
|
||||||
|
|
||||||
// 监听visible变化的处理
|
|
||||||
useEffect(() => {
|
|
||||||
// 当查看器从不可见变为可见时
|
|
||||||
if (visible && !wasVisible.current) {
|
|
||||||
if (initialIndex >= 0 && initialIndex < images.length) {
|
|
||||||
setCurrentIndex(initialIndex);
|
|
||||||
}
|
|
||||||
resetViewerState();
|
|
||||||
}
|
|
||||||
// 当查看器从可见变为不可见时
|
|
||||||
else if (!visible && wasVisible.current) {
|
|
||||||
// 关闭后等待一小段时间再更新缓存键,确保下次打开时强制刷新图片
|
|
||||||
setTimeout(() => setCacheKey(Date.now()), 300);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 更新ref以跟踪visible的变化
|
|
||||||
wasVisible.current = visible;
|
|
||||||
}, [visible, initialIndex, images.length, resetViewerState]);
|
|
||||||
|
|
||||||
// 当currentIndex变化时,重置图片加载状态并更新缓存键
|
|
||||||
useEffect(() => {
|
|
||||||
setImageLoaded(false);
|
|
||||||
setShowOriginal(false); // 重置为缩略图模式
|
|
||||||
setCacheKey(Date.now());
|
setCacheKey(Date.now());
|
||||||
}, [currentIndex]);
|
}, [currentIndex]);
|
||||||
|
|
||||||
// 当外部传入的images发生变化时,更新本地缓存
|
useEffect(() => {
|
||||||
|
if (visible && !wasVisible.current) {
|
||||||
|
resetViewerState();
|
||||||
|
} else if (!visible && wasVisible.current) {
|
||||||
|
setTimeout(() => setCacheKey(Date.now()), 300);
|
||||||
|
}
|
||||||
|
wasVisible.current = visible;
|
||||||
|
}, [visible, resetViewerState]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (visible && initialIndex >= 0 && initialIndex < images.length) {
|
||||||
|
setCurrentIndex(initialIndex);
|
||||||
|
}
|
||||||
|
}, [visible, initialIndex, images.length]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (currentImage) {
|
||||||
|
const img = new Image();
|
||||||
|
img.onload = () => {
|
||||||
|
setImageLoaded(true);
|
||||||
|
preloadImageRef.current = img;
|
||||||
|
};
|
||||||
|
const cacheBuster = `${currentImage.path}${currentImage.path.includes('?') ? '&' : '?'}_cb=${cacheKey}`;
|
||||||
|
img.src = cacheBuster;
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
img.onload = null;
|
||||||
|
if (preloadImageRef.current === img) {
|
||||||
|
preloadImageRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, [currentImage, cacheKey]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLocalImages(images);
|
setLocalImages(images);
|
||||||
}, [images]);
|
}, [images]);
|
||||||
|
|
||||||
// 处理键盘事件
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyDown = (e: KeyboardEvent) => {
|
const handleKeyDown = (e: KeyboardEvent) => {
|
||||||
if (!visible) return;
|
if (!visible) return;
|
||||||
|
|
||||||
switch (e.key) {
|
switch (e.key) {
|
||||||
case 'ArrowLeft':
|
case 'ArrowLeft': handlePrevious(); break;
|
||||||
handlePrevious();
|
case 'ArrowRight': handleNext(); break;
|
||||||
break;
|
case 'Escape': onClose(); break;
|
||||||
case 'ArrowRight':
|
case 'i': setIsInfoDrawerOpen(prev => !prev); break;
|
||||||
handleNext();
|
|
||||||
break;
|
|
||||||
case 'Escape':
|
|
||||||
onClose();
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
setIsInfoDrawerOpen(prev => !prev);
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -137,81 +116,56 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
|
|||||||
return () => window.removeEventListener('keydown', handleKeyDown);
|
return () => window.removeEventListener('keydown', handleKeyDown);
|
||||||
}, [visible, currentIndex, images.length]);
|
}, [visible, currentIndex, images.length]);
|
||||||
|
|
||||||
// 处理上一张图片
|
|
||||||
const handlePrevious = useCallback(() => {
|
const handlePrevious = useCallback(() => {
|
||||||
if (currentIndex > 0) {
|
if (currentIndex > 0) {
|
||||||
setCurrentIndex(prevIndex => prevIndex - 1);
|
setCurrentIndex(prevIndex => prevIndex - 1);
|
||||||
if (onPrevious) onPrevious();
|
onPrevious?.();
|
||||||
}
|
}
|
||||||
}, [currentIndex, onPrevious]);
|
}, [currentIndex, onPrevious]);
|
||||||
|
|
||||||
// 处理下一张图片
|
|
||||||
const handleNext = useCallback(() => {
|
const handleNext = useCallback(() => {
|
||||||
if (currentIndex < images.length - 1) {
|
if (currentIndex < images.length - 1) {
|
||||||
setCurrentIndex(prevIndex => prevIndex + 1);
|
setCurrentIndex(prevIndex => prevIndex + 1);
|
||||||
if (onNext) onNext();
|
onNext?.();
|
||||||
}
|
}
|
||||||
}, [currentIndex, images.length, onNext]);
|
}, [currentIndex, images.length, onNext]);
|
||||||
|
|
||||||
// 处理收藏按钮点击
|
|
||||||
const handleFavoriteClick = useCallback(async () => {
|
const handleFavoriteClick = useCallback(async () => {
|
||||||
if (!currentImage) return;
|
if (!currentImage) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 如果提供了onFavorite回调,直接使用它,不再重复发送请求
|
|
||||||
if (onFavorite) {
|
if (onFavorite) {
|
||||||
onFavorite(currentImage);
|
onFavorite(currentImage);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 只有在没有提供onFavorite回调时才直接发送网络请求
|
const isFavorited = currentImage.isFavorited;
|
||||||
let result;
|
const result = isFavorited
|
||||||
if (currentImage.isFavorited) {
|
? await unfavoritePicture(currentImage.id)
|
||||||
result = await unfavoritePicture(currentImage.id);
|
: await favoritePicture(currentImage.id);
|
||||||
if (result.success) {
|
|
||||||
message.success('已取消收藏');
|
if (result.success) {
|
||||||
} else {
|
message.success(isFavorited ? '已取消收藏' : '已添加到收藏');
|
||||||
message.error(result.message || '取消收藏失败');
|
setLocalImages(prevImages =>
|
||||||
return; // 如果请求失败,不更新UI状态
|
prevImages.map(img =>
|
||||||
}
|
img.id === currentImage.id ? {
|
||||||
|
...img,
|
||||||
|
isFavorited: !isFavorited,
|
||||||
|
favoriteCount: isFavorited
|
||||||
|
? Math.max(0, (img.favoriteCount || 0) - 1)
|
||||||
|
: (img.favoriteCount || 0) + 1
|
||||||
|
} : img
|
||||||
|
)
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
result = await favoritePicture(currentImage.id);
|
message.error(result.message || (isFavorited ? '取消收藏失败' : '收藏失败'));
|
||||||
if (result.success) {
|
|
||||||
message.success('已添加到收藏');
|
|
||||||
} else {
|
|
||||||
message.error(result.message || '收藏失败');
|
|
||||||
return; // 如果请求失败,不更新UI状态
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 请求成功后,更新本地状态
|
|
||||||
setLocalImages(prevImages =>
|
|
||||||
prevImages.map(img =>
|
|
||||||
img.id === currentImage.id ? {
|
|
||||||
...img,
|
|
||||||
isFavorited: !img.isFavorited,
|
|
||||||
favoriteCount: img.isFavorited
|
|
||||||
? Math.max(0, (img.favoriteCount || 0) - 1)
|
|
||||||
: (img.favoriteCount || 0) + 1
|
|
||||||
} : img
|
|
||||||
)
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('收藏操作失败:', error);
|
console.error('收藏操作失败:', error);
|
||||||
message.error('操作失败,请重试');
|
message.error('操作失败,请重试');
|
||||||
}
|
}
|
||||||
}, [currentImage, onFavorite]);
|
}, [currentImage, onFavorite]);
|
||||||
|
|
||||||
// 处理旋转
|
|
||||||
const handleRotateLeft = () => {
|
|
||||||
setRotation(prev => prev - 90);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleRotateRight = () => {
|
|
||||||
setRotation(prev => prev + 90);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 加载相册
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (visible) {
|
if (visible) {
|
||||||
loadAlbums();
|
loadAlbums();
|
||||||
@@ -236,13 +190,8 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
|
|||||||
if (!currentImage) return;
|
if (!currentImage) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 使用新的批量添加方法,参数为数组
|
|
||||||
const result = await addPicturesToAlbum(albumId, [currentImage.id]);
|
const result = await addPicturesToAlbum(albumId, [currentImage.id]);
|
||||||
if (result.success) {
|
message.success(result.success ? '已添加到相册' : (result.message || '添加到相册失败'));
|
||||||
message.success('已添加到相册');
|
|
||||||
} else {
|
|
||||||
message.error(result.message || '添加到相册失败');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('添加到相册失败:', error);
|
console.error('添加到相册失败:', error);
|
||||||
message.error('添加到相册失败,请重试');
|
message.error('添加到相册失败,请重试');
|
||||||
@@ -255,44 +204,11 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
|
|||||||
onClick: () => handleAddToAlbum(album.id)
|
onClick: () => handleAddToAlbum(album.id)
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 处理切换到原图
|
|
||||||
const handleToggleOriginal = () => {
|
|
||||||
setShowOriginal(prev => !prev);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 处理图片加载完成事件
|
|
||||||
const handleImageLoaded = () => {
|
|
||||||
setImageLoaded(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 新增:检测图片是否已经加载
|
|
||||||
useEffect(() => {
|
|
||||||
// 如果当前图片已加载到缓存中
|
|
||||||
if (currentImage && !imageLoaded) {
|
|
||||||
const img = new Image();
|
|
||||||
img.onload = () => {
|
|
||||||
// 图片已在缓存中,立即设置为已加载
|
|
||||||
setImageLoaded(true);
|
|
||||||
};
|
|
||||||
|
|
||||||
// 添加缓存键参数强制刷新
|
|
||||||
const cacheBuster = `${currentImage.path}${currentImage.path.includes('?') ? '&' : '?'}_cb=${cacheKey}`;
|
|
||||||
img.src = cacheBuster;
|
|
||||||
}
|
|
||||||
}, [currentImage, imageLoaded, cacheKey]);
|
|
||||||
|
|
||||||
// 处理分享按钮点击
|
|
||||||
const handleShareClick = useCallback(() => {
|
const handleShareClick = useCallback(() => {
|
||||||
if (!currentImage) return;
|
if (!currentImage) return;
|
||||||
|
onShare ? onShare(currentImage) : setShareDialogVisible(true);
|
||||||
if (onShare) {
|
|
||||||
onShare(currentImage);
|
|
||||||
} else {
|
|
||||||
setShareDialogVisible(true);
|
|
||||||
}
|
|
||||||
}, [currentImage, onShare]);
|
}, [currentImage, onShare]);
|
||||||
|
|
||||||
// 当没有图片或当前图片不存在时,不渲染任何内容
|
|
||||||
if (images.length === 0 || !currentImage) {
|
if (images.length === 0 || !currentImage) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -325,43 +241,40 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
|
|||||||
wrapperClass="transform-wrapper"
|
wrapperClass="transform-wrapper"
|
||||||
contentClass="transform-content"
|
contentClass="transform-content"
|
||||||
>
|
>
|
||||||
<img
|
{currentImage && (
|
||||||
src={showOriginal
|
<img
|
||||||
? `${currentImage.path}${currentImage.path.includes('?') ? '&' : '?'}_cb=${cacheKey}`
|
src={`${currentImage.path}${currentImage.path.includes('?') ? '&' : '?'}_cb=${cacheKey}`}
|
||||||
: `${currentImage.thumbnailPath || currentImage.path}${(currentImage.thumbnailPath || currentImage.path).includes('?') ? '&' : '?'}_cb=${cacheKey}`
|
alt={currentImage.name}
|
||||||
}
|
style={{
|
||||||
alt={currentImage.name}
|
transform: `rotate(${rotation}deg)`,
|
||||||
style={{
|
opacity: imageLoaded ? 1 : 0.3,
|
||||||
transform: `rotate(${rotation}deg)`,
|
transition: 'opacity 0.3s ease'
|
||||||
opacity: imageLoaded ? 1 : 0.3,
|
}}
|
||||||
transition: 'opacity 0.3s ease'
|
className="viewer-img"
|
||||||
}}
|
loading="lazy"
|
||||||
className={`viewer-img ${showOriginal ? '' : 'thumbnail'}`}
|
/>
|
||||||
loading="lazy"
|
)}
|
||||||
onLoad={handleImageLoaded}
|
|
||||||
/>
|
|
||||||
</TransformComponent>
|
</TransformComponent>
|
||||||
|
|
||||||
|
{!imageLoaded && (
|
||||||
|
<div className="image-loading-spinner">
|
||||||
|
<Spin size="large" tip={<span className="loading-text">图片加载中...</span>} />
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="zoom-controls">
|
<div className="zoom-controls">
|
||||||
<Space>
|
<Space>
|
||||||
<Button icon={<ExpandOutlined />} onClick={() => resetTransform()} />
|
<Button icon={<ExpandOutlined />} onClick={(e) => resetTransform()} />
|
||||||
<Button icon={<ZoomOutOutlined />} onClick={() => zoomOut(0.5)} />
|
<Button icon={<ZoomOutOutlined />} onClick={() => zoomOut(0.5)} />
|
||||||
<Button icon={<ZoomInOutlined />} onClick={() => zoomIn(0.5)} />
|
<Button icon={<ZoomInOutlined />} onClick={() => zoomIn(0.5)} />
|
||||||
<Button icon={<RotateLeftOutlined />} onClick={handleRotateLeft} />
|
<Button icon={<RotateLeftOutlined />} onClick={() => setRotation(prev => prev - 90)} />
|
||||||
<Button icon={<RotateRightOutlined />} onClick={handleRotateRight} />
|
<Button icon={<RotateRightOutlined />} onClick={() => setRotation(prev => prev + 90)} />
|
||||||
<Button
|
|
||||||
icon={<EyeOutlined />}
|
|
||||||
onClick={handleToggleOriginal}
|
|
||||||
type={showOriginal ? "primary" : "default"}
|
|
||||||
title={showOriginal ? "正在查看原图" : "查看原图"}
|
|
||||||
/>
|
|
||||||
</Space>
|
</Space>
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</TransformWrapper>
|
</TransformWrapper>
|
||||||
|
|
||||||
{/* 图片导航按钮 */}
|
|
||||||
{currentIndex > 0 && (
|
{currentIndex > 0 && (
|
||||||
<Button
|
<Button
|
||||||
className="nav-button prev-button"
|
className="nav-button prev-button"
|
||||||
@@ -383,7 +296,6 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 顶部操作栏 */}
|
|
||||||
<div className="viewer-header">
|
<div className="viewer-header">
|
||||||
<div className="image-counter">
|
<div className="image-counter">
|
||||||
{currentIndex + 1} / {images.length}
|
{currentIndex + 1} / {images.length}
|
||||||
@@ -404,11 +316,8 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 底部操作栏 */}
|
|
||||||
<div className="viewer-footer">
|
<div className="viewer-footer">
|
||||||
<div className="image-name">
|
<div className="image-name">{currentImage.name}</div>
|
||||||
{currentImage.name}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="footer-actions">
|
<div className="footer-actions">
|
||||||
{onFavorite && (
|
{onFavorite && (
|
||||||
@@ -449,7 +358,6 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* 图片信息 */}
|
|
||||||
{currentImage && (
|
{currentImage && (
|
||||||
<ImageInfo
|
<ImageInfo
|
||||||
image={currentImage}
|
image={currentImage}
|
||||||
@@ -458,7 +366,6 @@ const ImageViewer: React.FC<ImageViewerProps> = ({
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* 添加分享对话框 */}
|
|
||||||
{!onShare && currentImage && (
|
{!onShare && currentImage && (
|
||||||
<ShareImageDialog
|
<ShareImageDialog
|
||||||
visible={shareDialogVisible}
|
visible={shareDialogVisible}
|
||||||
|
|||||||
Reference in New Issue
Block a user