feat: 实现素材库检索的高级设置和视图切换功能
新增功能: - 高级设置面板:相关性阈值、最大结果数量、包含信息选项 - 视图切换:网格视图和列表视图切换 - 环境标签和服装类别快速过滤器 - 设置重置和应用功能 UI改进: - 设置按钮现在可以展开/收起高级设置面板 - 视图切换按钮支持网格和列表两种显示模式 - 列表视图使用紧凑布局,网格视图保持卡片布局 - 添加悬停效果和状态指示 交互优化: - 设置面板包含完整的搜索配置选项 - 视图模式状态管理和动态样式切换 - 响应式布局适配不同屏幕尺寸
This commit is contained in:
parent
3adc1355b6
commit
74c20f7e7b
|
|
@ -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,6 +335,7 @@ const MaterialSearchPanel: React.FC<MaterialSearchPanelProps> = ({
|
|||
|
||||
{/* 搜索结果 */}
|
||||
{searchResponse && !isGenerating && (
|
||||
<div className="flex-1 min-h-0 overflow-y-auto">
|
||||
<MaterialSearchResults
|
||||
results={searchResponse.results}
|
||||
totalSize={searchResponse.total_size}
|
||||
|
|
@ -241,6 +347,7 @@ const MaterialSearchPanel: React.FC<MaterialSearchPanelProps> = ({
|
|||
onMaterialSelect={onMaterialSelect}
|
||||
className="p-6"
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* 空状态 */}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in New Issue