feat: 实现素材库检索的高级设置和视图切换功能

新增功能:
- 高级设置面板:相关性阈值、最大结果数量、包含信息选项
- 视图切换:网格视图和列表视图切换
- 环境标签和服装类别快速过滤器
- 设置重置和应用功能

 UI改进:
- 设置按钮现在可以展开/收起高级设置面板
- 视图切换按钮支持网格和列表两种显示模式
- 列表视图使用紧凑布局,网格视图保持卡片布局
- 添加悬停效果和状态指示

 交互优化:
- 设置面板包含完整的搜索配置选项
- 视图模式状态管理和动态样式切换
- 响应式布局适配不同屏幕尺寸
This commit is contained in:
imeepos 2025-07-25 15:45:08 +08:00
parent 3adc1355b6
commit 74c20f7e7b
2 changed files with 154 additions and 22 deletions

View File

@ -134,7 +134,7 @@ const MaterialSearchPanel: React.FC<MaterialSearchPanelProps> = ({
return (
<div className={`fixed inset-0 z-50 bg-black/50 backdrop-blur-sm animate-fade-in ${className}`}>
<div className="flex items-center justify-center min-h-screen p-4">
<div className="bg-white rounded-xl shadow-2xl max-w-6xl w-full max-h-[90vh] overflow-hidden animate-fade-in-up">
<div className="bg-white rounded-xl shadow-2xl max-w-6xl w-full max-h-[90vh] flex flex-col animate-fade-in-up">
{/* 头部 */}
<div className="flex items-center justify-between p-6 border-b border-gray-200 bg-gradient-to-r from-primary-50 to-blue-50">
<div className="flex items-center gap-3">
@ -193,8 +193,113 @@ const MaterialSearchPanel: React.FC<MaterialSearchPanelProps> = ({
</div>
)}
{/* 高级设置面板 */}
{showAdvanced && (
<div className="px-6 py-4 bg-gray-50 border-b border-gray-200">
<div className="space-y-4">
<h4 className="text-sm font-semibold text-gray-900 flex items-center gap-2">
<Settings className="w-4 h-4" />
</h4>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
{/* 相关性阈值 */}
<div>
<label className="block text-xs font-medium text-gray-700 mb-2">
</label>
<select className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-transparent">
<option value="HIGH"> ()</option>
<option value="MEDIUM"></option>
<option value="LOW"></option>
<option value="LOWEST"></option>
</select>
</div>
{/* 最大结果数量 */}
<div>
<label className="block text-xs font-medium text-gray-700 mb-2">
</label>
<select className="w-full px-3 py-2 border border-gray-300 rounded-lg text-sm focus:ring-2 focus:ring-primary-500 focus:border-transparent">
<option value="20">20 </option>
<option value="50">50 ()</option>
<option value="100">100 </option>
</select>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
{/* 生成选项 */}
<div>
<label className="block text-xs font-medium text-gray-700 mb-2">
</label>
<div className="space-y-2">
<label className="flex items-center gap-2">
<input type="checkbox" defaultChecked className="rounded border-gray-300 text-primary-600 focus:ring-primary-500" />
<span className="text-xs text-gray-600"></span>
</label>
<label className="flex items-center gap-2">
<input type="checkbox" defaultChecked className="rounded border-gray-300 text-primary-600 focus:ring-primary-500" />
<span className="text-xs text-gray-600"></span>
</label>
<label className="flex items-center gap-2">
<input type="checkbox" defaultChecked className="rounded border-gray-300 text-primary-600 focus:ring-primary-500" />
<span className="text-xs text-gray-600"></span>
</label>
</div>
</div>
{/* 环境过滤 */}
<div>
<label className="block text-xs font-medium text-gray-700 mb-2">
</label>
<div className="flex flex-wrap gap-1">
{['室内', '户外', '办公室', '聚会', '约会'].map((tag) => (
<button
key={tag}
className="px-2 py-1 text-xs bg-gray-100 hover:bg-gray-200 text-gray-700 rounded transition-colors duration-200"
>
{tag}
</button>
))}
</div>
</div>
{/* 类别过滤 */}
<div>
<label className="block text-xs font-medium text-gray-700 mb-2">
</label>
<div className="flex flex-wrap gap-1">
{['上衣', '下装', '外套', '鞋子', '配饰'].map((category) => (
<button
key={category}
className="px-2 py-1 text-xs bg-gray-100 hover:bg-gray-200 text-gray-700 rounded transition-colors duration-200"
>
{category}
</button>
))}
</div>
</div>
</div>
<div className="flex items-center justify-between pt-3 border-t border-gray-200">
<button className="text-xs text-gray-500 hover:text-gray-700">
</button>
<button className="px-3 py-1 bg-primary-500 text-white rounded text-xs hover:bg-primary-600 transition-colors duration-200">
</button>
</div>
</div>
</div>
)}
{/* 主要内容 */}
<div className="flex-1 overflow-auto">
<div className="flex-1 overflow-y-auto min-h-0">
{/* 错误状态 */}
{error && (
<div className="p-6">
@ -230,17 +335,19 @@ const MaterialSearchPanel: React.FC<MaterialSearchPanelProps> = ({
{/* 搜索结果 */}
{searchResponse && !isGenerating && (
<MaterialSearchResults
results={searchResponse.results}
totalSize={searchResponse.total_size}
currentPage={searchResponse.current_page}
pageSize={searchResponse.page_size}
isLoading={isSearching}
error={error || undefined}
onPageChange={handlePageChange}
onMaterialSelect={onMaterialSelect}
className="p-6"
/>
<div className="flex-1 min-h-0 overflow-y-auto">
<MaterialSearchResults
results={searchResponse.results}
totalSize={searchResponse.total_size}
currentPage={searchResponse.current_page}
pageSize={searchResponse.page_size}
isLoading={isSearching}
error={error || undefined}
onPageChange={handlePageChange}
onMaterialSelect={onMaterialSelect}
className="p-6"
/>
</div>
)}
{/* 空状态 */}

View File

@ -1,4 +1,4 @@
import React, { useCallback } from 'react';
import React, { useCallback, useState } from 'react';
import {
Grid,
List,
@ -26,6 +26,9 @@ const MaterialSearchResults: React.FC<MaterialSearchResultsProps> = ({
onMaterialSelect,
className = '',
}) => {
// 视图模式状态
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
// 计算分页信息
const totalPages = MaterialSearchService.getTotalPages(totalSize, pageSize);
const pageInfo = MaterialSearchService.getPageRangeInfo(currentPage, pageSize, totalSize);
@ -140,7 +143,7 @@ const MaterialSearchResults: React.FC<MaterialSearchResultsProps> = ({
// 正常状态 - 显示结果列表
return (
<div className={`space-y-6 ${className}`}>
<div className={`space-y-6 max-h-full overflow-y-auto ${className}`}>
{/* 列表标题和统计信息 */}
<div className="flex items-center justify-between">
<div className="flex items-center gap-3">
@ -157,27 +160,49 @@ const MaterialSearchResults: React.FC<MaterialSearchResultsProps> = ({
</div>
</div>
{/* 视图切换(预留) */}
{/* 视图切换 */}
<div className="flex items-center gap-2">
<button className="p-2 text-primary-600 bg-primary-50 rounded-lg">
<button
onClick={() => setViewMode('grid')}
className={`p-2 rounded-lg transition-colors duration-200 ${
viewMode === 'grid'
? 'text-primary-600 bg-primary-50'
: 'text-gray-400 hover:text-gray-600 hover:bg-gray-100'
}`}
title="网格视图"
>
<Grid className="w-4 h-4" />
</button>
<button className="p-2 text-gray-400 hover:text-gray-600 rounded-lg">
<button
onClick={() => setViewMode('list')}
className={`p-2 rounded-lg transition-colors duration-200 ${
viewMode === 'list'
? 'text-primary-600 bg-primary-50'
: 'text-gray-400 hover:text-gray-600 hover:bg-gray-100'
}`}
title="列表视图"
>
<List className="w-4 h-4" />
</button>
</div>
</div>
{/* 素材卡片网格 */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{/* 素材卡片展示 */}
<div className={
viewMode === 'grid'
? "grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6"
: "space-y-4"
}>
{results.map((material, index) => (
<MaterialCard
key={material.id || index}
material={material}
onSelect={handleMaterialSelect}
showScore={true}
compact={false}
className={`animate-fade-in-up ${isLoading ? 'opacity-50 pointer-events-none' : ''}`}
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' : ''
}`}
/>
))}
</div>