Add File
This commit is contained in:
167
src/elderly/components/MediaLibraryGrid.tsx
Normal file
167
src/elderly/components/MediaLibraryGrid.tsx
Normal file
@@ -0,0 +1,167 @@
|
||||
import React, { useState, useMemo } from 'react';
|
||||
import { X } from 'lucide-react';
|
||||
|
||||
interface MediaItem {
|
||||
id: string;
|
||||
url: string;
|
||||
thumbnailUrl?: string;
|
||||
type: 'photo' | 'video';
|
||||
caption: string;
|
||||
tags?: string[];
|
||||
}
|
||||
|
||||
interface MediaLibraryGridProps {
|
||||
mediaList: MediaItem[];
|
||||
currentIndex: number;
|
||||
onSelect: (index: number) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* 媒体库网格视图
|
||||
* 类似手机相册的缩略图展示,支持标签筛选
|
||||
*/
|
||||
export const MediaLibraryGrid: React.FC<MediaLibraryGridProps> = ({
|
||||
mediaList,
|
||||
currentIndex,
|
||||
onSelect,
|
||||
onClose,
|
||||
}) => {
|
||||
const [selectedTag, setSelectedTag] = useState<string>('全部');
|
||||
|
||||
// 提取所有标签
|
||||
const allTags = useMemo(() => {
|
||||
const tagsSet = new Set<string>();
|
||||
mediaList.forEach(item => {
|
||||
item.tags?.forEach(tag => tagsSet.add(tag));
|
||||
});
|
||||
return ['全部', ...Array.from(tagsSet).sort()];
|
||||
}, [mediaList]);
|
||||
|
||||
// 根据选中的标签筛选媒体
|
||||
const filteredMedia = useMemo(() => {
|
||||
if (selectedTag === '全部') {
|
||||
return mediaList;
|
||||
}
|
||||
return mediaList.filter(item => item.tags?.includes(selectedTag));
|
||||
}, [mediaList, selectedTag]);
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/95 backdrop-blur-sm z-50 flex flex-col">
|
||||
{/* 顶部区域 - 标题和标签 */}
|
||||
<div className="bg-gradient-to-b from-black/60 to-transparent">
|
||||
{/* 标题栏 */}
|
||||
<div className="flex items-center justify-between p-4 pb-2">
|
||||
<h2 className="text-2xl font-bold text-white">回忆相册</h2>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="w-12 h-12 flex items-center justify-center bg-white/20 hover:bg-white/30 rounded-full transition-colors"
|
||||
aria-label="关闭"
|
||||
>
|
||||
<X size={28} className="text-white" strokeWidth={2.5} />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* 标签筛选栏 */}
|
||||
{allTags.length > 1 && (
|
||||
<div className="px-4 pb-4">
|
||||
<div className="flex gap-2 overflow-x-auto pb-2 scrollbar-hide">
|
||||
{allTags.map(tag => (
|
||||
<button
|
||||
key={tag}
|
||||
onClick={() => setSelectedTag(tag)}
|
||||
className={`
|
||||
px-5 py-2.5 rounded-full text-base font-medium whitespace-nowrap transition-all
|
||||
${selectedTag === tag
|
||||
? 'bg-primary-500 text-white shadow-lg scale-105'
|
||||
: 'bg-white/20 text-white hover:bg-white/30'
|
||||
}
|
||||
`}
|
||||
>
|
||||
{tag}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 网格视图 */}
|
||||
<div className="flex-1 overflow-y-auto p-4">
|
||||
{filteredMedia.length === 0 ? (
|
||||
<div className="text-center text-white py-12">
|
||||
<p className="text-xl">暂无"{selectedTag}"相关的照片或视频</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid grid-cols-2 gap-3 max-w-lg mx-auto">
|
||||
{filteredMedia.map((item) => {
|
||||
// 找到该项在原始列表中的索引
|
||||
const originalIndex = mediaList.findIndex(m => m.id === item.id);
|
||||
return (
|
||||
<button
|
||||
key={item.id}
|
||||
onClick={() => {
|
||||
onSelect(originalIndex);
|
||||
}}
|
||||
className={`
|
||||
relative aspect-square rounded-2xl overflow-hidden
|
||||
shadow-xl hover:scale-105 active:scale-95 transition-all
|
||||
${originalIndex === currentIndex ? 'ring-4 ring-primary-500' : ''}
|
||||
`}
|
||||
>
|
||||
{/* 缩略图 */}
|
||||
{item.type === 'photo' ? (
|
||||
<img
|
||||
src={item.url}
|
||||
alt={item.caption}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : item.thumbnailUrl ? (
|
||||
<img
|
||||
src={item.thumbnailUrl}
|
||||
alt={item.caption}
|
||||
className="w-full h-full object-cover"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full h-full bg-gray-800 flex items-center justify-center">
|
||||
<span className="text-4xl">🎬</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 当前选中标识 */}
|
||||
{originalIndex === currentIndex && (
|
||||
<div className="absolute top-2 right-2 w-8 h-8 bg-primary-500 rounded-full flex items-center justify-center">
|
||||
<span className="text-white text-lg font-bold">✓</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 标题遮罩 */}
|
||||
<div className="absolute bottom-0 left-0 right-0 bg-gradient-to-t from-black/80 to-transparent p-3">
|
||||
<p className="text-sm font-bold text-white line-clamp-2">
|
||||
{item.caption}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* 序号 */}
|
||||
<div className="absolute top-2 left-2 bg-black/60 backdrop-blur-sm text-white text-xs font-bold px-2 py-1 rounded-full">
|
||||
{originalIndex + 1}
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 底部信息 */}
|
||||
<div className="p-4 bg-gradient-to-t from-black/60 to-transparent">
|
||||
<p className="text-center text-white text-base">
|
||||
{selectedTag === '全部'
|
||||
? `共 ${mediaList.length} 张照片`
|
||||
: `${selectedTag}:${filteredMedia.length} 张照片`
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user