forked from 77media/video-flow
统一创建入口:图片名称展示
This commit is contained in:
parent
1662ee026c
commit
90964aab74
@ -1,9 +1,10 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import { Image } from 'antd';
|
import { Image, Tooltip } from 'antd';
|
||||||
import NextImage from 'next/image';
|
import NextImage from 'next/image';
|
||||||
import { EyeOutlined, SwapOutlined, UserOutlined, CameraOutlined, BulbOutlined } from '@ant-design/icons';
|
import { EyeOutlined, SwapOutlined, UserOutlined, CameraOutlined, BulbOutlined } from '@ant-design/icons';
|
||||||
|
import { useDeviceType } from '@/hooks/useDeviceType';
|
||||||
import type { PhotoPreviewSectionProps, PhotoType } from './types';
|
import type { PhotoPreviewSectionProps, PhotoType } from './types';
|
||||||
import './styles.css';
|
import './styles.css';
|
||||||
|
|
||||||
@ -24,6 +25,7 @@ export default function PhotoPreviewSection({
|
|||||||
const [previewVisible, setPreviewVisible] = useState(false);
|
const [previewVisible, setPreviewVisible] = useState(false);
|
||||||
const [previewImage, setPreviewImage] = useState('');
|
const [previewImage, setPreviewImage] = useState('');
|
||||||
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
|
||||||
|
const { isMobileDevice } = useDeviceType();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get icon for photo type
|
* Get icon for photo type
|
||||||
@ -93,58 +95,90 @@ export default function PhotoPreviewSection({
|
|||||||
data-alt="photo-list-container"
|
data-alt="photo-list-container"
|
||||||
className="flex gap-2 overflow-x-auto overflow-y-hidden photo-list-scrollbar pb-1"
|
className="flex gap-2 overflow-x-auto overflow-y-hidden photo-list-scrollbar pb-1"
|
||||||
>
|
>
|
||||||
{photos.map((photo, index) => (
|
{photos.map((photo, index) => {
|
||||||
<div
|
const photoName = photo.name || `${getTypeLabel(photo.type)} ${index + 1}`;
|
||||||
key={photo.id || `photo-${index}`}
|
|
||||||
data-alt="photo-item"
|
return (
|
||||||
className="relative flex-shrink-0 w-16 h-16 rounded-[16px] overflow-hidden border border-white/10 hover:border-cyan-400/60 transition-all duration-200 group bg-black/20"
|
|
||||||
onMouseEnter={() => setHoveredIndex(index)}
|
|
||||||
onMouseLeave={() => setHoveredIndex(null)}
|
|
||||||
>
|
|
||||||
{/* Photo Image */}
|
|
||||||
<div className="w-full h-full relative">
|
|
||||||
<NextImage
|
|
||||||
src={photo.url}
|
|
||||||
alt={`${getTypeLabel(photo.type)} ${index + 1}`}
|
|
||||||
fill
|
|
||||||
className="object-cover"
|
|
||||||
unoptimized
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Hover Overlay with Preview and Replace Icons */}
|
|
||||||
<div
|
<div
|
||||||
data-alt="hover-overlay"
|
key={photo.id || `photo-${index}`}
|
||||||
className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex items-center justify-center gap-1 z-[5]"
|
data-alt="photo-item-wrapper"
|
||||||
|
className="flex-shrink-0 flex flex-col gap-1"
|
||||||
>
|
>
|
||||||
{/* Preview Button */}
|
{/* Photo Image Container */}
|
||||||
<button
|
<div
|
||||||
data-alt="preview-button"
|
data-alt="photo-item"
|
||||||
className="w-5 h-5 rounded-full bg-cyan-400/90 hover:bg-cyan-400 flex items-center justify-center transition-all duration-200 shadow-lg"
|
className="relative w-16 h-16 rounded-[16px] overflow-hidden border border-white/10 hover:border-cyan-400/60 transition-all duration-200 group bg-black/20"
|
||||||
onClick={(e) => handlePreviewClick(e, photo.url)}
|
onMouseEnter={() => setHoveredIndex(index)}
|
||||||
|
onMouseLeave={() => setHoveredIndex(null)}
|
||||||
>
|
>
|
||||||
<EyeOutlined className="text-xs text-black" />
|
{/* Photo Image */}
|
||||||
</button>
|
<div className="w-full h-full relative">
|
||||||
|
<NextImage
|
||||||
|
src={photo.url}
|
||||||
|
alt={photoName}
|
||||||
|
fill
|
||||||
|
className="object-cover"
|
||||||
|
unoptimized
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Replace Button */}
|
{/* Hover Overlay with Preview and Replace Icons */}
|
||||||
<button
|
<div
|
||||||
data-alt="replace-button"
|
data-alt="hover-overlay"
|
||||||
className="w-5 h-5 rounded-full bg-cyan-400/90 hover:bg-cyan-400 flex items-center justify-center transition-all duration-200 shadow-lg"
|
className="absolute inset-0 bg-black/40 opacity-0 group-hover:opacity-100 transition-opacity duration-200 flex items-center justify-center gap-1 z-[5]"
|
||||||
onClick={(e) => handleReplaceClick(e, index)}
|
>
|
||||||
>
|
{/* Preview Button */}
|
||||||
<SwapOutlined className="text-xs text-black" />
|
<button
|
||||||
</button>
|
data-alt="preview-button"
|
||||||
</div>
|
className="w-5 h-5 rounded-full bg-cyan-400/90 hover:bg-cyan-400 flex items-center justify-center transition-all duration-200 shadow-lg"
|
||||||
|
onClick={(e) => handlePreviewClick(e, photo.url)}
|
||||||
|
>
|
||||||
|
<EyeOutlined className="text-xs text-black" />
|
||||||
|
</button>
|
||||||
|
|
||||||
{/* Type Icon - Bottom Left Corner */}
|
{/* Replace Button */}
|
||||||
<div
|
<button
|
||||||
data-alt="type-icon"
|
data-alt="replace-button"
|
||||||
className="absolute bottom-1 left-1 w-4 h-4 bg-black/60 backdrop-blur-sm text-cyan-400 rounded-sm flex items-center justify-center z-[6]"
|
className="w-5 h-5 rounded-full bg-cyan-400/90 hover:bg-cyan-400 flex items-center justify-center transition-all duration-200 shadow-lg"
|
||||||
>
|
onClick={(e) => handleReplaceClick(e, index)}
|
||||||
{getTypeIcon(photo.type)}
|
>
|
||||||
|
<SwapOutlined className="text-xs text-black" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Type Icon - Bottom Left Corner */}
|
||||||
|
<div
|
||||||
|
data-alt="type-icon"
|
||||||
|
className="absolute bottom-1 left-1 w-4 h-4 bg-black/60 backdrop-blur-sm text-cyan-400 rounded-sm flex items-center justify-center z-[6]"
|
||||||
|
>
|
||||||
|
{getTypeIcon(photo.type)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Photo Name - Below Image */}
|
||||||
|
{isMobileDevice ? (
|
||||||
|
// H5: Direct display with line-clamp
|
||||||
|
<div
|
||||||
|
data-alt="photo-name-mobile"
|
||||||
|
className="w-16 text-white/70 text-[10px] leading-tight line-clamp-2 text-center px-0.5"
|
||||||
|
style={{ wordBreak: 'break-word' }}
|
||||||
|
>
|
||||||
|
{photoName}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
// PC: Tooltip on hover
|
||||||
|
<Tooltip title={photoName} placement="bottom">
|
||||||
|
<div
|
||||||
|
data-alt="photo-name-desktop"
|
||||||
|
className="w-16 text-white/70 text-xs text-center truncate px-0.5 cursor-default"
|
||||||
|
>
|
||||||
|
{photoName}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
);
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Preview Modal - Using absolute positioning to avoid layout space */}
|
{/* Preview Modal - Using absolute positioning to avoid layout space */}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user