255 lines
9.1 KiB
TypeScript
255 lines
9.1 KiB
TypeScript
import React, { useState, useMemo } from 'react';
|
||
import {
|
||
Wrench,
|
||
Search,
|
||
Grid,
|
||
List,
|
||
Star,
|
||
Sparkles
|
||
} from 'lucide-react';
|
||
import { useNavigate } from 'react-router-dom';
|
||
import { CardGrid } from '../components/CardGrid';
|
||
import ToolCard from '../components/ToolCard';
|
||
import { TOOLS_DATA, TOOL_CATEGORIES, searchTools } from '../data/tools';
|
||
import { Tool, ToolCategory } from '../types/tool';
|
||
|
||
/**
|
||
* 便捷小工具页面
|
||
* 遵循 Tauri 开发规范和 UI/UX 设计标准
|
||
* 使用卡片列表展示工具,点击进入详情页
|
||
*/
|
||
const Tools: React.FC = () => {
|
||
const navigate = useNavigate();
|
||
|
||
// 工具列表状态
|
||
const [searchQuery, setSearchQuery] = useState('');
|
||
const [selectedCategory, setSelectedCategory] = useState<ToolCategory | null>(null);
|
||
const [showNewOnly, setShowNewOnly] = useState(false);
|
||
const [showPopularOnly, setShowPopularOnly] = useState(false);
|
||
const [viewMode, setViewMode] = useState<'grid' | 'list'>('grid');
|
||
|
||
// 根据筛选条件过滤工具列表
|
||
const filteredTools = useMemo(() => {
|
||
let result = [...TOOLS_DATA];
|
||
|
||
// 搜索过滤
|
||
if (searchQuery) {
|
||
result = searchTools(searchQuery);
|
||
}
|
||
|
||
// 分类过滤
|
||
if (selectedCategory) {
|
||
result = result.filter(tool => tool.category === selectedCategory);
|
||
}
|
||
|
||
// 新功能过滤
|
||
if (showNewOnly) {
|
||
result = result.filter(tool => tool.isNew);
|
||
}
|
||
|
||
// 热门工具过滤
|
||
if (showPopularOnly) {
|
||
result = result.filter(tool => tool.isPopular);
|
||
}
|
||
|
||
return result;
|
||
}, [searchQuery, selectedCategory, showNewOnly, showPopularOnly]);
|
||
|
||
// 处理工具卡片点击
|
||
const handleToolClick = (tool: Tool) => {
|
||
navigate(tool.route);
|
||
};
|
||
|
||
// 清除所有筛选条件
|
||
const clearFilters = () => {
|
||
setSearchQuery('');
|
||
setSelectedCategory(null);
|
||
setShowNewOnly(false);
|
||
setShowPopularOnly(false);
|
||
};
|
||
|
||
return (
|
||
<div className="space-y-6">
|
||
{/* 页面标题 */}
|
||
<div className="page-header flex items-center justify-between">
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-12 h-12 bg-gradient-to-br from-purple-500 to-purple-600 rounded-xl flex items-center justify-center shadow-lg hover:shadow-xl transition-all duration-300">
|
||
<Wrench className="w-6 h-6 text-white" />
|
||
</div>
|
||
<div>
|
||
<h1 className="text-3xl font-bold bg-gradient-to-r from-gray-900 to-purple-600 bg-clip-text text-transparent">便捷小工具</h1>
|
||
<p className="text-gray-600 text-lg">专业的开发和数据处理工具集合</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 视图切换 */}
|
||
<div className="flex items-center gap-2">
|
||
<button
|
||
onClick={() => setViewMode('grid')}
|
||
className={`p-2 rounded-lg transition-colors ${
|
||
viewMode === 'grid'
|
||
? 'bg-primary-100 text-primary-600'
|
||
: 'text-gray-400 hover:text-gray-600'
|
||
}`}
|
||
>
|
||
<Grid className="w-5 h-5" />
|
||
</button>
|
||
<button
|
||
onClick={() => setViewMode('list')}
|
||
className={`p-2 rounded-lg transition-colors ${
|
||
viewMode === 'list'
|
||
? 'bg-primary-100 text-primary-600'
|
||
: 'text-gray-400 hover:text-gray-600'
|
||
}`}
|
||
>
|
||
<List className="w-5 h-5" />
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 搜索和筛选区域 */}
|
||
<div className="card p-6 animate-fade-in">
|
||
<div className="flex flex-col lg:flex-row gap-4">
|
||
{/* 搜索框 */}
|
||
<div className="flex-1">
|
||
<div className="relative">
|
||
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5" />
|
||
<input
|
||
type="text"
|
||
placeholder="搜索工具名称、描述或标签..."
|
||
value={searchQuery}
|
||
onChange={(e) => setSearchQuery(e.target.value)}
|
||
className="w-full pl-10 pr-4 py-3 border border-gray-300 rounded-xl focus:ring-2 focus:ring-primary-500 focus:border-primary-500 transition-all duration-200 bg-gray-50 hover:bg-white"
|
||
/>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 分类筛选 */}
|
||
<div className="flex flex-wrap gap-2">
|
||
<button
|
||
onClick={() => setSelectedCategory(null)}
|
||
className={`px-4 py-2 rounded-xl text-sm font-medium transition-all duration-200 ${
|
||
selectedCategory === null
|
||
? 'bg-primary-500 text-white shadow-md hover:bg-primary-600'
|
||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200 hover:shadow-sm'
|
||
}`}
|
||
>
|
||
全部
|
||
</button>
|
||
{TOOL_CATEGORIES.map((category) => (
|
||
<button
|
||
key={category.id}
|
||
onClick={() => setSelectedCategory(category.id)}
|
||
className={`px-4 py-2 rounded-xl text-sm font-medium transition-all duration-200 ${
|
||
selectedCategory === category.id
|
||
? 'bg-primary-500 text-white shadow-md hover:bg-primary-600'
|
||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200 hover:shadow-sm'
|
||
}`}
|
||
>
|
||
{category.name}
|
||
</button>
|
||
))}
|
||
</div>
|
||
|
||
{/* 快速筛选 */}
|
||
<div className="flex gap-2">
|
||
<button
|
||
onClick={() => setShowNewOnly(!showNewOnly)}
|
||
className={`flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-medium transition-all duration-200 ${
|
||
showNewOnly
|
||
? 'bg-green-500 text-white shadow-md hover:bg-green-600'
|
||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200 hover:shadow-sm'
|
||
}`}
|
||
>
|
||
<Sparkles className="w-4 h-4" />
|
||
新功能
|
||
</button>
|
||
<button
|
||
onClick={() => setShowPopularOnly(!showPopularOnly)}
|
||
className={`flex items-center gap-2 px-4 py-2 rounded-xl text-sm font-medium transition-all duration-200 ${
|
||
showPopularOnly
|
||
? 'bg-yellow-500 text-white shadow-md hover:bg-yellow-600'
|
||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200 hover:shadow-sm'
|
||
}`}
|
||
>
|
||
<Star className="w-4 h-4" />
|
||
热门
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* 筛选结果统计 */}
|
||
<div className="mt-6 pt-4 border-t border-gray-100 flex items-center justify-between">
|
||
<div className="flex items-center gap-2">
|
||
<span className="text-sm font-medium text-gray-900">找到 {filteredTools.length} 个工具</span>
|
||
{filteredTools.length !== TOOLS_DATA.length && (
|
||
<span className="text-xs text-gray-500">(共 {TOOLS_DATA.length} 个)</span>
|
||
)}
|
||
</div>
|
||
{(searchQuery || selectedCategory || showNewOnly || showPopularOnly) && (
|
||
<button
|
||
onClick={clearFilters}
|
||
className="px-3 py-1.5 text-sm font-medium text-primary-600 hover:text-primary-700 hover:bg-primary-50 rounded-lg transition-all duration-200"
|
||
>
|
||
清除筛选
|
||
</button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
|
||
{/* 工具卡片列表 */}
|
||
<div className="animate-fade-in">
|
||
<CardGrid
|
||
items={filteredTools}
|
||
renderCard={(tool) => (
|
||
<ToolCard
|
||
key={tool.id}
|
||
tool={tool}
|
||
onClick={handleToolClick}
|
||
config={{
|
||
showStatus: true,
|
||
showCategory: true,
|
||
showTags: false,
|
||
showVersion: true,
|
||
size: 'medium',
|
||
enableHover: true
|
||
}}
|
||
/>
|
||
)}
|
||
loading={false}
|
||
searchable={false}
|
||
sortable={false}
|
||
viewModes={[viewMode]}
|
||
defaultViewMode={viewMode}
|
||
gridCols={{
|
||
sm: 1,
|
||
md: 2,
|
||
lg: 3,
|
||
xl: 3,
|
||
'2xl': 4
|
||
}}
|
||
gap={6}
|
||
emptyText="没有找到匹配的工具"
|
||
emptyComponent={
|
||
<div className="text-center py-16">
|
||
<div className="w-24 h-24 bg-gradient-to-br from-gray-100 to-gray-200 rounded-full flex items-center justify-center mx-auto mb-6">
|
||
<Wrench className="w-12 h-12 text-gray-400" />
|
||
</div>
|
||
<h3 className="text-xl font-semibold text-gray-900 mb-3">没有找到匹配的工具</h3>
|
||
<p className="text-gray-600 mb-6 max-w-md mx-auto">尝试调整搜索条件或筛选器,或者浏览所有可用的工具</p>
|
||
<button
|
||
onClick={clearFilters}
|
||
className="px-6 py-3 bg-primary-600 text-white rounded-xl hover:bg-primary-700 transition-all duration-200 shadow-md hover:shadow-lg font-medium"
|
||
>
|
||
清除筛选条件
|
||
</button>
|
||
</div>
|
||
}
|
||
/>
|
||
</div>
|
||
</div>
|
||
);
|
||
};
|
||
|
||
export default Tools;
|