212 lines
8.5 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useState } from 'react';
import { Search, Loader2, FileText, TrendingUp, Layers, Zap } from 'lucide-react';
import { useNavigate } from 'react-router-dom';
import { Logo } from '../components/Logo';
// 评级模版数据
const RATING_TEMPLATES = [
{
id: 'default',
name: '默认评级模版',
description: '包含估值、资金、宏观、基本面四大核心维度',
icon: <FileText size={24} />,
bgColor: 'bg-blue-50',
iconColor: 'text-blue-600',
borderColor: 'border-blue-200',
tags: ['通用', '全面']
},
{
id: 'consumer',
name: '消费股模版',
description: '聚焦消费品估值体系、品牌溢价、渠道分析',
icon: <TrendingUp size={24} />,
bgColor: 'bg-purple-50',
iconColor: 'text-purple-600',
borderColor: 'border-purple-200',
tags: ['消费', '品牌']
},
{
id: 'tech',
name: '科技股模版',
description: '关注研发投入、技术壁垒、市场份额等科技指标',
icon: <Layers size={24} />,
bgColor: 'bg-teal-50',
iconColor: 'text-teal-600',
borderColor: 'border-teal-200',
tags: ['科技', '成长']
},
{
id: 'quick',
name: '快速诊断模版',
description: '精简版模板,聚焦PE/PB band与短期资金流向',
icon: <Zap size={24} />,
bgColor: 'bg-amber-50',
iconColor: 'text-amber-600',
borderColor: 'border-amber-200',
tags: ['快速', '简洁']
},
];
export const SearchPage: React.FC = () => {
const navigate = useNavigate();
const [searchTerm, setSearchTerm] = useState('');
const [selectedTemplate, setSelectedTemplate] = useState<string>('default');
const [isSearching, setIsSearching] = useState(false);
const handleSearch = (e: React.FormEvent) => {
e.preventDefault();
if (searchTerm.trim() && selectedTemplate) {
setIsSearching(true);
// Simulate loading
setTimeout(() => navigate(`/dashboard?template=${selectedTemplate}`), 800);
}
};
const handleTemplateClick = (templateId: string) => {
setSelectedTemplate(templateId);
// 如果已经输入了股票名称,自动跳转
if (searchTerm.trim()) {
setIsSearching(true);
setTimeout(() => navigate(`/dashboard?template=${templateId}`), 800);
}
};
// 当输入框失去焦点时,如果有选中的模版,也可以直接跳转
const handleInputBlur = () => {
// 延迟判断,避免点击搜索下拉项时触发
setTimeout(() => {
if (searchTerm.trim() && selectedTemplate && !isSearching) {
// 这里不自动跳转,让用户明确选择模版
}
}, 200);
};
return (
<div className="min-h-screen bg-slate-50 flex flex-col">
{/* Header */}
<header className="h-16 bg-white border-b border-slate-200 px-6 flex items-center justify-between sticky top-0 z-10">
<Logo />
</header>
{/* Main Content */}
<main className="flex-1 max-w-6xl mx-auto w-full px-6 py-12 flex flex-col items-center">
{/* Search Section */}
<div className="w-full max-w-2xl mb-8 relative z-20">
<form onSubmit={handleSearch} className="relative group">
<div className="absolute inset-y-0 left-4 flex items-center pointer-events-none">
<Search className="text-slate-400" size={20} />
</div>
<input
type="text"
value={searchTerm}
onChange={(e) => setSearchTerm(e.target.value)}
onBlur={handleInputBlur}
className="w-full pl-12 pr-4 py-4 bg-white border border-slate-200 rounded-2xl shadow-sm text-lg focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all placeholder:text-slate-400"
placeholder="搜索标的..."
/>
{searchTerm && (
<div className="absolute top-full left-0 right-0 mt-2 bg-white rounded-xl shadow-xl border border-slate-100 overflow-hidden animate-in fade-in slide-in-from-top-2 z-30">
<div
onClick={handleSearch}
className="px-4 py-3 hover:bg-slate-50 cursor-pointer flex items-center gap-3 border-b border-slate-50 last:border-0"
>
<div className="w-10 h-10 rounded bg-slate-100 flex items-center justify-center text-xs font-bold text-slate-600">
SH
</div>
<div>
<p className="font-bold text-slate-800"></p>
<p className="text-xs text-slate-500">600519.SH · Stock</p>
</div>
</div>
</div>
)}
</form>
{/* 提示文字 */}
<div className="text-center mt-4">
{!searchTerm ? (
<p className="text-sm text-slate-500">
</p>
) : !selectedTemplate ? (
<p className="text-sm text-amber-600 font-medium">
</p>
) : (
<p className="text-sm text-green-600 font-medium">
</p>
)}
</div>
</div>
{/* 模版标题 */}
<div className="w-full mb-6">
<h2 className="text-xl font-bold text-slate-900 text-center"></h2>
<p className="text-sm text-slate-500 text-center mt-2"></p>
</div>
{/* Templates Grid */}
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 w-full">
{RATING_TEMPLATES.map((template) => (
<div
key={template.id}
onClick={() => handleTemplateClick(template.id)}
className={`bg-white p-6 rounded-2xl border-2 shadow-sm hover:shadow-md transition-all cursor-pointer group relative ${
selectedTemplate === template.id
? `${template.borderColor} ring-2 ring-offset-2 ${template.iconColor.replace('text-', 'ring-')}`
: 'border-slate-200 hover:border-slate-300'
}`}
>
{/* 选中标识 */}
{selectedTemplate === template.id && (
<div className={`absolute -top-2 -right-2 w-6 h-6 rounded-full ${template.iconColor.replace('text-', 'bg-')} text-white flex items-center justify-center`}>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={3} d="M5 13l4 4L19 7" />
</svg>
</div>
)}
<div className="flex flex-col items-center text-center">
{/* Icon */}
<div className={`w-16 h-16 rounded-2xl ${template.bgColor} flex items-center justify-center mb-4 ${template.iconColor} group-hover:scale-110 transition-transform`}>
{template.icon}
</div>
{/* Template Name */}
<h3 className="font-bold text-slate-900 mb-2 text-lg">
{template.name}
</h3>
{/* Description */}
<p className="text-xs text-slate-600 leading-relaxed mb-3">
{template.description}
</p>
{/* Tags */}
<div className="flex gap-2 flex-wrap justify-center">
{template.tags.map(tag => (
<span key={tag} className="px-2 py-1 text-xs rounded-full bg-slate-100 text-slate-600">
{tag}
</span>
))}
</div>
</div>
</div>
))}
</div>
{/* Loading indicator */}
{isSearching && (
<div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50">
<div className="bg-white rounded-2xl p-8 shadow-2xl flex flex-col items-center gap-4">
<Loader2 className="animate-spin text-blue-600" size={40} />
<p className="text-slate-700 font-medium">...</p>
</div>
</div>
)}
</main>
</div>
);
};