From 40106095aa00a5aa57cdd452476055eb96520745 Mon Sep 17 00:00:00 2001 From: shiyu Date: Fri, 6 Jun 2025 14:33:06 +0800 Subject: [PATCH] feat(imageGrid): enhance layout and responsiveness of image grid and cards --- Web/src/components/image/ImageGrid.css | 156 ++++++++++++++++--------- Web/src/components/image/ImageGrid.tsx | 45 +++++-- Web/src/routes/index.tsx | 6 +- 3 files changed, 141 insertions(+), 66 deletions(-) diff --git a/Web/src/components/image/ImageGrid.css b/Web/src/components/image/ImageGrid.css index 7794351..e33822d 100644 --- a/Web/src/components/image/ImageGrid.css +++ b/Web/src/components/image/ImageGrid.css @@ -1,27 +1,36 @@ .image-grid { margin-bottom: 40px; - display: grid; - grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); - grid-gap: 24px; + display: flex; + flex-wrap: wrap; + gap: 3px; + justify-content: space-between; + /* 改为space-between让每行铺满 */ + align-items: flex-start; +} + +.image-grid::after { + content: ''; + flex: auto; } -/* 现代化卡片样式 */ .custom-card { position: relative; - border-radius: 12px; overflow: hidden; - box-shadow: 0 8px 20px rgba(0,0,0,0.08); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08); transition: all 0.35s cubic-bezier(0.23, 1, 0.32, 1); - height: 100%; background: #ffffff; transform: translateY(0); cursor: pointer; - aspect-ratio: 1 / 1; + height: 250px; + flex-grow: 1; + flex-shrink: 0; + min-width: 180px; + max-width: 400px; } .custom-card:hover { - transform: translateY(-5px); - box-shadow: 0 14px 28px rgba(0,0,0,0.15); + transform: translateY(-3px); + box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15); } /* 图片占满卡片 */ @@ -51,13 +60,13 @@ left: 0; right: 0; bottom: 0; - background: linear-gradient(to top, rgba(0,0,0,0.85) 0%, rgba(0,0,0,0) 50%); + background: linear-gradient(to top, rgba(0, 0, 0, 0.85) 0%, rgba(0, 0, 0, 0) 50%); opacity: 0; transition: opacity 0.35s ease; display: flex; flex-direction: column; justify-content: flex-end; - padding: 20px; + padding: 16px; color: white; } @@ -78,44 +87,46 @@ } .custom-card-title { - font-size: 18px; + font-size: 14px; font-weight: 600; color: #ffffff; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - margin-bottom: 8px; - text-shadow: 0 1px 3px rgba(0,0,0,0.3); + margin-bottom: 6px; + text-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); } .custom-card-tags-container { - margin-top: 6px; - margin-bottom: 10px; + margin-top: 4px; + margin-bottom: 8px; max-width: 100%; overflow: hidden; + display: flex; + flex-wrap: wrap; + gap: 4px; } .image-tag { - margin-right: 6px; - font-size: 11px !important; - background: rgba(255,255,255,0.2); - padding: 3px 8px; - border-radius: 4px; + font-size: 10px !important; + background: rgba(255, 255, 255, 0.2); + padding: 2px 6px; + border-radius: 3px; color: #ffffff; display: inline-block; - margin-bottom: 4px; backdrop-filter: blur(4px); + white-space: nowrap; } /* 权限和元数据指示器 */ .custom-card-indicators { position: absolute; - top: 12px; + top: 8px; left: 0; right: 0; display: flex; justify-content: space-between; - padding: 0 12px; + padding: 0 8px; opacity: 0; transition: opacity 0.35s ease; z-index: 2; @@ -128,25 +139,25 @@ .custom-card-permission { background-color: rgba(0, 0, 0, 0.6); color: white; - border-radius: 20px; - padding: 5px 10px; - font-size: 12px; + border-radius: 12px; + padding: 3px 8px; + font-size: 10px; font-weight: 500; display: flex; align-items: center; - gap: 4px; + gap: 3px; backdrop-filter: blur(4px); - box-shadow: 0 2px 8px rgba(0,0,0,0.2); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); } .custom-card-metadata { background-color: rgba(0, 0, 0, 0.6); color: white; - border-radius: 20px; - padding: 5px 10px; - font-size: 12px; + border-radius: 12px; + padding: 3px 8px; + font-size: 10px; font-weight: 500; - box-shadow: 0 2px 8px rgba(0,0,0,0.2); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); backdrop-filter: blur(4px); } @@ -154,20 +165,22 @@ .custom-card-actions { display: flex; justify-content: space-between; - margin-top: 12px; + margin-top: 8px; + gap: 6px; } .custom-card-action-item { background-color: rgba(255, 255, 255, 0.15); border-radius: 50%; - width: 36px; - height: 36px; + width: 28px; + height: 28px; display: flex; align-items: center; justify-content: center; cursor: pointer; transition: all 0.3s; backdrop-filter: blur(4px); + flex-shrink: 0; } .custom-card-action-item:hover { @@ -179,7 +192,7 @@ .context-menu { background-color: white; border-radius: 8px; - box-shadow: 0 4px 20px rgba(0,0,0,0.2); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); padding: 8px 0; min-width: 160px; z-index: 1000; @@ -200,38 +213,38 @@ /* 选中状态样式 */ .custom-card-selected { - box-shadow: 0 0 0 3px #1890ff, 0 14px 28px rgba(0,0,0,0.15) !important; + box-shadow: 0 0 0 3px #1890ff, 0 8px 20px rgba(0, 0, 0, 0.15) !important; } .custom-card-selected::before { content: ''; position: absolute; - top: 10px; - right: 10px; - width: 22px; - height: 22px; + top: 8px; + right: 8px; + width: 20px; + height: 20px; border-radius: 50%; background-color: #1890ff; z-index: 5; display: flex; align-items: center; justify-content: center; - box-shadow: 0 2px 8px rgba(0,0,0,0.2); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); } .custom-card-selected::after { content: '✓'; position: absolute; - top: 10px; - right: 10px; - width: 22px; - height: 22px; + top: 8px; + right: 8px; + width: 20px; + height: 20px; z-index: 6; color: white; display: flex; align-items: center; justify-content: center; - font-size: 14px; + font-size: 12px; } .image-grid-pagination { @@ -252,19 +265,54 @@ left: 0; width: 100%; height: 100%; - background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent); + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); animation: loading 1.5s infinite; } @keyframes loading { - 0% { transform: translateX(-100%); } - 100% { transform: translateX(100%); } + 0% { + transform: translateX(-100%); + } + + 100% { + transform: translateX(100%); + } } /* 响应式调整 */ @media (max-width: 768px) { .image-grid { - grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); - grid-gap: 16px; + gap: 8px; + } + + .custom-card { + height: 150px; + } + + .custom-card-overlay { + padding: 12px; + } + + .custom-card-title { + font-size: 12px; + } + + .custom-card-actions { + gap: 4px; + } + + .custom-card-action-item { + width: 24px; + height: 24px; } } + +@media (max-width: 480px) { + .custom-card { + height: 120px; + } + + .custom-card-overlay { + padding: 8px; + } +} \ No newline at end of file diff --git a/Web/src/components/image/ImageGrid.tsx b/Web/src/components/image/ImageGrid.tsx index bccdb7c..02c3d87 100644 --- a/Web/src/components/image/ImageGrid.tsx +++ b/Web/src/components/image/ImageGrid.tsx @@ -439,6 +439,21 @@ const ImageGrid: React.FC = ({ return !!user && !!image.userId && user.id === image.userId; }; + // 计算图片宽度的函数 - 修改为计算最小宽度 + const calculateImageMinWidth = (image: PictureResponse): number => { + const fixedHeight = 200; // 固定高度 + const defaultMinWidth = 180; // 默认最小宽度 + + if (image.exifInfo?.width && image.exifInfo?.height) { + const aspectRatio = image.exifInfo.width / image.exifInfo.height; + const calculatedWidth = Math.round(fixedHeight * aspectRatio); + // 确保最小宽度不小于180px,最大不超过400px + return Math.max(180, Math.min(400, calculatedWidth)); + } + + return defaultMinWidth; + }; + // 优化渲染内容函数 const renderContent = () => { // 渲染加载状态 @@ -446,7 +461,11 @@ const ImageGrid: React.FC = ({ return (
{Array.from({ length: pageSize }).map((_, index) => ( -
+
{/* 简单的加载状态 */}
@@ -472,11 +491,16 @@ const ImageGrid: React.FC = ({
{images.map((image, index) => { const isOwner = canEditImage(image); + const imageMinWidth = calculateImageMinWidth(image); return (
handleImageClick(image, index)} onContextMenu={(e) => handleContextMenu(e, image)} > @@ -519,11 +543,14 @@ const ImageGrid: React.FC = ({
{image.name}
- {image.tags && ( + {image.tags && image.tags.length > 0 && (
- {image.tags.map(tag => ( - #{tag} + {image.tags.slice(0, 3).map((tag, tagIndex) => ( + #{tag} ))} + {image.tags.length > 3 && ( + +{image.tags.length - 3} + )}
)} @@ -536,9 +563,9 @@ const ImageGrid: React.FC = ({ }} > {image.isFavorited ? ( - + ) : ( - + )}
@@ -554,7 +581,7 @@ const ImageGrid: React.FC = ({ } }} > - +
)} @@ -565,7 +592,7 @@ const ImageGrid: React.FC = ({ onShare?.(image); }} > - +
= ({ onDownload?.(image); }} > - +
diff --git a/Web/src/routes/index.tsx b/Web/src/routes/index.tsx index 802d30c..0805af6 100644 --- a/Web/src/routes/index.tsx +++ b/Web/src/routes/index.tsx @@ -8,7 +8,7 @@ import { CompassOutlined, DashboardOutlined, UserOutlined, - LogoutOutlined + FileTextOutlined } from '@ant-design/icons'; import AllImages from '../pages/allImages/Index'; @@ -171,10 +171,10 @@ const routes: RouteConfig[] = [ title: '图片管理' } }, - { + { path: 'log', key: 'admin-log', - icon: , + icon: , label: '日志中心', element: , area: 'admin',