feat: 优化素材库检索列表视图显示效果

列表视图重新设计:
- 采用水平布局:左侧缩略图 + 中间内容 + 右侧操作
- 紧凑的20x20图片尺寸,保持清晰度
- 优化信息层次:标题、描述、标签分层显示
- 右侧操作区域:颜色指示器 + 查看按钮 + 时间

 交互优化:
- 悬停效果:图片缩放、背景渐变、阴影变化
- 评分标识移至图片右上角
- AI推荐标识在悬停时显示
- 标签数量限制和省略显示

 布局改进:
- 列表项间距调整为3px,更紧凑
- 移除不必要的flex样式冲突
- 保持网格视图原有的卡片布局
- 响应式设计适配不同屏幕尺寸

现在列表视图更加美观实用,信息密度合理!
This commit is contained in:
imeepos 2025-07-25 15:48:27 +08:00
parent 74c20f7e7b
commit 99763ebefb
2 changed files with 167 additions and 5 deletions

View File

@ -63,6 +63,170 @@ const MaterialCard: React.FC<MaterialCardProps> = ({
const primaryProduct = material.products[0];
const hasMultipleProducts = material.products.length > 1;
// 列表视图的特殊布局
if (compact) {
return (
<div
className={`
card card-interactive group cursor-pointer animate-fade-in-up
relative overflow-hidden bg-gradient-to-br from-white to-gray-50/50
hover:from-white hover:to-primary-50/30 transition-all duration-300
hover:shadow-md hover:shadow-primary-500/10
border border-gray-200 hover:border-primary-300
p-4 flex items-center gap-4
${className}
`}
onClick={handleCardClick}
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
>
{/* 装饰性背景 */}
<div className="absolute top-0 right-0 w-16 h-16 bg-gradient-to-br from-primary-100 to-primary-200 rounded-full -translate-y-8 translate-x-8 opacity-40 group-hover:opacity-60 transition-all duration-500"></div>
{/* 左侧图片 */}
<div className="relative flex-shrink-0">
<div className="w-20 h-20 rounded-lg overflow-hidden bg-gray-100">
{!imageError ? (
<img
src={material.image_url}
alt={material.style_description}
className={`w-full h-full object-cover transition-all duration-300 group-hover:scale-105 ${
imageLoaded ? 'opacity-100' : 'opacity-0'
}`}
onLoad={handleImageLoad}
onError={handleImageError}
/>
) : (
<div className="w-full h-full flex items-center justify-center bg-gray-100">
<Package className="w-6 h-6 text-gray-400" />
</div>
)}
{/* 加载状态 */}
{!imageLoaded && !imageError && (
<div className="absolute inset-0 flex items-center justify-center bg-gray-100">
<div className="w-4 h-4 border-2 border-primary-500 border-t-transparent rounded-full animate-spin"></div>
</div>
)}
</div>
{/* 评分标识 */}
{showScore && (
<div className="absolute -top-2 -right-2 flex items-center gap-1 px-2 py-1 bg-yellow-100 text-yellow-700 rounded-full text-xs font-medium">
<Star className="w-3 h-3 fill-current" />
{MaterialSearchService.formatRelevanceScore(material.relevance_score)}
</div>
)}
</div>
{/* 中间内容 */}
<div className="flex-1 min-w-0 space-y-2">
{/* 标题和描述 */}
<div>
<h3 className="text-sm font-semibold text-high-emphasis line-clamp-1 group-hover:text-primary-600 transition-colors duration-200">
{material.style_description || '时尚素材'}
</h3>
{primaryProduct && (
<p className="text-xs text-gray-600 line-clamp-1 mt-1">
{primaryProduct.description}
</p>
)}
</div>
{/* 标签行 */}
<div className="flex items-center gap-2 flex-wrap">
{/* 类别标签 */}
{primaryProduct && (
<div className="flex items-center gap-1">
<Tag className="w-3 h-3 text-gray-500" />
<span className="text-xs font-medium text-gray-700">{primaryProduct.category}</span>
{hasMultipleProducts && (
<span className="text-xs text-gray-500">+{material.products.length - 1}</span>
)}
</div>
)}
{/* 环境标签 */}
{material.environment_tags.slice(0, 2).map((tag, index) => (
<div
key={index}
className="px-2 py-0.5 bg-blue-100 text-blue-700 rounded text-xs"
>
{tag}
</div>
))}
{material.environment_tags.length > 2 && (
<div className="px-2 py-0.5 bg-gray-100 text-gray-500 rounded text-xs">
+{material.environment_tags.length - 2}
</div>
)}
</div>
{/* 设计风格 */}
{primaryProduct && primaryProduct.design_styles.length > 0 && (
<div className="flex flex-wrap gap-1">
{primaryProduct.design_styles.slice(0, 3).map((style, index) => (
<div
key={index}
className="px-2 py-0.5 bg-gray-100 text-gray-600 rounded text-xs"
>
{style}
</div>
))}
{primaryProduct.design_styles.length > 3 && (
<div className="px-2 py-0.5 bg-gray-100 text-gray-500 rounded text-xs">
+{primaryProduct.design_styles.length - 3}
</div>
)}
</div>
)}
</div>
{/* 右侧操作区域 */}
<div className="flex-shrink-0 flex flex-col items-end gap-2">
{/* 颜色信息 */}
{primaryProduct && (
<div className="flex items-center gap-2">
<Palette className="w-3 h-3 text-gray-500" />
<div
className="w-4 h-4 rounded-full border border-gray-300 shadow-sm"
style={{
backgroundColor: `hsl(${primaryProduct.color_pattern.hue * 360}, ${primaryProduct.color_pattern.saturation * 100}%, ${primaryProduct.color_pattern.value * 100}%)`
}}
title="主要颜色"
/>
</div>
)}
{/* 操作按钮 */}
<button
onClick={(e) => {
e.stopPropagation();
if (onSelect) onSelect(material);
}}
className="flex items-center gap-1 px-3 py-1 text-primary-600 hover:text-primary-700 hover:bg-primary-50 rounded transition-colors duration-200 text-xs font-medium"
>
<Eye className="w-3 h-3" />
<ChevronRight className="w-3 h-3" />
</button>
{/* 时间信息 */}
<div className="text-xs text-gray-500">
{new Date(material.created_at).toLocaleDateString()}
</div>
</div>
{/* AI推荐标识 */}
<div className="absolute bottom-2 left-4 flex items-center gap-1 px-2 py-1 bg-gradient-to-r from-purple-100 to-pink-100 text-purple-700 rounded-full text-xs font-medium opacity-0 group-hover:opacity-100 transition-opacity duration-300">
<Sparkles className="w-3 h-3" />
AI推荐
</div>
</div>
);
}
// 网格视图的原有布局
return (
<div
className={`
@ -72,7 +236,7 @@ const MaterialCard: React.FC<MaterialCardProps> = ({
hover:shadow-lg hover:shadow-primary-500/10 hover:-translate-y-1
border border-gray-200 hover:border-primary-300
transform-gpu will-change-transform
${compact ? 'p-4' : 'p-5'}
p-5
${className}
`}
onClick={handleCardClick}

View File

@ -191,7 +191,7 @@ const MaterialSearchResults: React.FC<MaterialSearchResultsProps> = ({
<div className={
viewMode === 'grid'
? "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"
: "space-y-4"
: "space-y-3"
}>
{results.map((material, index) => (
<MaterialCard
@ -200,9 +200,7 @@ const MaterialSearchResults: React.FC<MaterialSearchResultsProps> = ({
onSelect={handleMaterialSelect}
showScore={true}
compact={viewMode === 'list'}
className={`animate-fade-in-up ${isLoading ? 'opacity-50 pointer-events-none' : ''} ${
viewMode === 'list' ? 'flex flex-row items-center gap-4 p-4' : ''
}`}
className={`animate-fade-in-up ${isLoading ? 'opacity-50 pointer-events-none' : ''}`}
/>
))}
</div>