2025-08-04 16:56:39 +08:00
|
|
|
// src/pages/gallery/components/GridView.tsx
|
|
|
|
|
import { ImageItem } from '@/pages/gallery/typings';
|
|
|
|
|
import { DeleteOutlined, DownloadOutlined, EyeOutlined, MoreOutlined } from '@ant-design/icons';
|
|
|
|
|
import { Button, Checkbox, Dropdown, Menu, Spin } from 'antd';
|
|
|
|
|
import React, { FC, useCallback } from 'react';
|
|
|
|
|
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,
|
|
|
|
|
}) => {
|
|
|
|
|
const getImageSize = useCallback(() => {
|
|
|
|
|
switch (viewMode) {
|
|
|
|
|
case 'small':
|
|
|
|
|
return { width: 150, height: 150 };
|
|
|
|
|
case 'large':
|
|
|
|
|
return { width: 300, height: 300 };
|
|
|
|
|
default:
|
|
|
|
|
return { width: 150, height: 150 };
|
|
|
|
|
}
|
|
|
|
|
}, [viewMode]);
|
|
|
|
|
|
|
|
|
|
const imageSize = getImageSize();
|
|
|
|
|
|
|
|
|
|
const getImageMenu = useCallback(
|
|
|
|
|
(item: ImageItem) => (
|
|
|
|
|
<Menu>
|
|
|
|
|
<Menu.Item
|
|
|
|
|
key="preview"
|
|
|
|
|
icon={<EyeOutlined />}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
const index = imageList.findIndex((img) => img.instanceId === item.instanceId);
|
|
|
|
|
onPreview(index);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
预览
|
|
|
|
|
</Menu.Item>
|
|
|
|
|
<Menu.Item
|
|
|
|
|
key="download"
|
|
|
|
|
icon={<DownloadOutlined />}
|
|
|
|
|
onClick={() => onDownload(item.instanceId, item.imageName)}
|
|
|
|
|
>
|
|
|
|
|
下载
|
|
|
|
|
</Menu.Item>
|
|
|
|
|
<Menu.Divider />
|
|
|
|
|
<Menu.Item
|
|
|
|
|
key="delete"
|
|
|
|
|
icon={<DeleteOutlined />}
|
|
|
|
|
danger
|
|
|
|
|
onClick={() => onDelete(item.instanceId, item.imageName)}
|
|
|
|
|
>
|
|
|
|
|
删除
|
|
|
|
|
</Menu.Item>
|
|
|
|
|
</Menu>
|
|
|
|
|
),
|
|
|
|
|
[imageList, onPreview, onDownload, onDelete],
|
|
|
|
|
);
|
|
|
|
|
|
2025-08-06 18:41:32 +08:00
|
|
|
// 根据视图模式确定图像 URL
|
|
|
|
|
const getImageUrl = (instanceId: string) => {
|
|
|
|
|
// 小图模式使用低分辨率图像
|
|
|
|
|
if (viewMode === 'small') {
|
|
|
|
|
return `/file/image-low-res/${instanceId}`;
|
|
|
|
|
}
|
|
|
|
|
// 其他模式使用原图
|
|
|
|
|
return `/file/image/${instanceId}`;
|
|
|
|
|
};
|
|
|
|
|
|
2025-08-04 16:56:39 +08:00
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
className={viewMode === 'small' ? 'small-grid-view' : 'large-grid-view'}
|
|
|
|
|
onScroll={onScroll}
|
|
|
|
|
>
|
|
|
|
|
{imageList.map((item: ImageItem, index: number) => (
|
|
|
|
|
<div key={item.instanceId} className="image-card">
|
|
|
|
|
{batchMode && (
|
|
|
|
|
<Checkbox
|
|
|
|
|
className="image-checkbox"
|
|
|
|
|
checked={selectedRowKeys.includes(item.instanceId)}
|
|
|
|
|
onChange={(e) => onSelect(item.instanceId, e.target.checked)}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
<div
|
|
|
|
|
className="image-wrapper"
|
|
|
|
|
style={{
|
|
|
|
|
width: imageSize.width,
|
|
|
|
|
height: imageSize.height,
|
2025-08-06 18:41:32 +08:00
|
|
|
backgroundImage: `url(${getImageUrl(item.instanceId)})`,
|
2025-08-04 16:56:39 +08:00
|
|
|
backgroundSize: 'cover',
|
|
|
|
|
backgroundPosition: 'center',
|
|
|
|
|
backgroundRepeat: 'no-repeat',
|
|
|
|
|
}}
|
|
|
|
|
onClick={() => !batchMode && onPreview(index)}
|
|
|
|
|
/>
|
|
|
|
|
<div className="image-info">
|
|
|
|
|
<div className="image-title" title={item.imageName}>
|
|
|
|
|
{item.imageName}
|
|
|
|
|
</div>
|
|
|
|
|
<Dropdown overlay={getImageMenu(item)} trigger={['click']}>
|
|
|
|
|
<Button type="text" icon={<MoreOutlined />} />
|
|
|
|
|
</Dropdown>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
{loadingMore && (
|
|
|
|
|
<div style={{ gridColumn: '1 / -1', textAlign: 'center', padding: '20px' }}>
|
|
|
|
|
<Spin />
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
export default GridView;
|