2025-12-26 15:12:49 +08:00
|
|
|
// src/pages/gallery\components\GridView.tsx
|
2025-08-04 16:56:39 +08:00
|
|
|
import { ImageItem } from '@/pages/gallery/typings';
|
2026-02-25 15:02:05 +08:00
|
|
|
import { Spin } from 'antd';
|
2026-02-24 10:33:10 +08:00
|
|
|
import React, { FC, useCallback, useEffect, useState } from 'react';
|
|
|
|
|
import { useIsMobile } from '@/hooks/useIsMobile';
|
2026-02-25 15:02:05 +08:00
|
|
|
import PhotoCard from './PhotoCard';
|
2025-08-04 16:56:39 +08:00
|
|
|
import '../index.css';
|
|
|
|
|
|
|
|
|
|
interface GridViewProps {
|
|
|
|
|
imageList: ImageItem[];
|
|
|
|
|
viewMode: 'small' | 'large' | 'list' | 'table';
|
|
|
|
|
batchMode: boolean;
|
|
|
|
|
selectedRowKeys: string[];
|
|
|
|
|
onPreview: (index: number) => void;
|
|
|
|
|
onSelect: (instanceId: string, checked: boolean) => void;
|
|
|
|
|
onDownload: (instanceId: string, imageName: string) => void;
|
|
|
|
|
onDelete: (instanceId: string, imageName: string) => void;
|
|
|
|
|
loadingMore?: boolean;
|
|
|
|
|
onScroll: (e: React.UIEvent<HTMLDivElement>) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const GridView: FC<GridViewProps> = ({
|
|
|
|
|
imageList,
|
|
|
|
|
viewMode,
|
|
|
|
|
batchMode,
|
|
|
|
|
selectedRowKeys,
|
|
|
|
|
onPreview,
|
|
|
|
|
onSelect,
|
|
|
|
|
onDownload,
|
|
|
|
|
onDelete,
|
|
|
|
|
loadingMore,
|
|
|
|
|
onScroll,
|
|
|
|
|
}) => {
|
2026-02-24 10:33:10 +08:00
|
|
|
const isMobile = useIsMobile();
|
|
|
|
|
const [windowWidth, setWindowWidth] = useState(window.innerWidth);
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
const handleResize = () => setWindowWidth(window.innerWidth);
|
|
|
|
|
window.addEventListener('resize', handleResize);
|
|
|
|
|
return () => window.removeEventListener('resize', handleResize);
|
|
|
|
|
}, []);
|
|
|
|
|
|
2025-08-04 16:56:39 +08:00
|
|
|
const getImageSize = useCallback(() => {
|
2026-02-24 10:33:10 +08:00
|
|
|
if (isMobile) {
|
|
|
|
|
// Mobile: 3 columns for small, 2 columns for large
|
|
|
|
|
const padding = 16; // page padding
|
|
|
|
|
const gap = 8; // grid gap
|
|
|
|
|
if (viewMode === 'large') {
|
|
|
|
|
const size = (windowWidth - padding * 2 - gap) / 2;
|
|
|
|
|
return { width: size, height: size };
|
|
|
|
|
}
|
|
|
|
|
// default small
|
|
|
|
|
const size = (windowWidth - padding * 2 - gap * 2) / 3;
|
|
|
|
|
return { width: size, height: size };
|
|
|
|
|
}
|
|
|
|
|
|
2025-08-04 16:56:39 +08:00
|
|
|
switch (viewMode) {
|
|
|
|
|
case 'small':
|
|
|
|
|
return { width: 150, height: 150 };
|
|
|
|
|
case 'large':
|
|
|
|
|
return { width: 300, height: 300 };
|
|
|
|
|
default:
|
|
|
|
|
return { width: 150, height: 150 };
|
|
|
|
|
}
|
2026-02-24 10:33:10 +08:00
|
|
|
}, [viewMode, isMobile, windowWidth]);
|
2025-08-04 16:56:39 +08:00
|
|
|
|
|
|
|
|
const imageSize = getImageSize();
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={viewMode === 'small' ? 'small-grid-view' : 'large-grid-view'}
|
|
|
|
|
onScroll={onScroll}
|
2026-02-24 10:33:10 +08:00
|
|
|
style={isMobile ? {
|
|
|
|
|
display: 'grid',
|
|
|
|
|
gridTemplateColumns: viewMode === 'large' ? 'repeat(2, 1fr)' : 'repeat(3, 1fr)',
|
|
|
|
|
gap: '8px',
|
|
|
|
|
paddingBottom: '20px'
|
|
|
|
|
} : {}}
|
2025-08-04 16:56:39 +08:00
|
|
|
>
|
|
|
|
|
{imageList.map((item: ImageItem, index: number) => (
|
2026-02-25 15:02:05 +08:00
|
|
|
<PhotoCard
|
|
|
|
|
key={item.instanceId}
|
|
|
|
|
item={item}
|
|
|
|
|
index={index}
|
|
|
|
|
viewMode={viewMode}
|
|
|
|
|
batchMode={batchMode}
|
|
|
|
|
isSelected={selectedRowKeys.includes(item.instanceId)}
|
|
|
|
|
imageSize={imageSize}
|
|
|
|
|
isMobile={isMobile}
|
|
|
|
|
onPreview={onPreview}
|
|
|
|
|
onSelect={onSelect}
|
|
|
|
|
onDownload={onDownload}
|
|
|
|
|
onDelete={onDelete}
|
|
|
|
|
/>
|
2025-08-04 16:56:39 +08:00
|
|
|
))}
|
|
|
|
|
{loadingMore && (
|
|
|
|
|
<div style={{ gridColumn: '1 / -1', textAlign: 'center', padding: '20px' }}>
|
|
|
|
|
<Spin />
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2026-02-12 16:55:05 +08:00
|
|
|
export default GridView;
|