212 lines
8.5 KiB
TypeScript
212 lines
8.5 KiB
TypeScript
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>
|
||
);
|
||
}; |