import React, { useState, useEffect } from 'react'; import { api } from '../services/api'; import './RecommendationsViewer.css'; function RecommendationsViewer() { const [recommendations, setRecommendations] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [directionFilter, setDirectionFilter] = useState(''); const [showDetails, setShowDetails] = useState({}); useEffect(() => { loadRecommendations(); // 每10秒静默更新价格(不触发loading状态) const interval = setInterval(async () => { try { const result = await api.getRecommendations({ type: 'realtime', direction: directionFilter, limit: 50, min_signal_strength: 5 }); const newData = result.data || []; // 使用setState直接更新,不触发loading状态 setRecommendations(prevRecommendations => { if (newData.length === 0) { return prevRecommendations; } // 实时推荐没有id,使用symbol作为key const newDataMap = new Map(newData.map(rec => [rec.symbol, rec])); const prevMap = new Map(prevRecommendations.map(rec => [rec.symbol || rec.id, rec])); // 合并数据:优先使用新数据(包含实时价格更新) const updated = prevRecommendations.map(prevRec => { const key = prevRec.symbol || prevRec.id; const newRec = newDataMap.get(key); if (newRec) { return newRec; } return prevRec; }); // 添加新出现的推荐 const newItems = newData.filter(newRec => !prevMap.has(newRec.symbol)); // 合并并去重(按symbol) const merged = [...updated, ...newItems]; const uniqueMap = new Map(); merged.forEach(rec => { const key = rec.symbol || rec.id; if (!uniqueMap.has(key)) { uniqueMap.set(key, rec); } }); return Array.from(uniqueMap.values()); }); } catch (err) { // 静默失败,不显示错误 console.debug('静默更新价格失败:', err); } }, 10000); // 每10秒刷新 return () => { clearInterval(interval); }; }, [directionFilter]); const loadRecommendations = async () => { try { setLoading(true); setError(null); const params = { type: 'realtime', limit: 50, min_signal_strength: 5 }; if (directionFilter) { params.direction = directionFilter; } const result = await api.getRecommendations(params); const data = result.data || []; setRecommendations(data); } catch (err) { setError(err.message); console.error('加载推荐失败:', err); } finally { setLoading(false); } }; const toggleDetails = (key) => { setShowDetails(prev => ({ ...prev, [key]: !prev[key] })); }; const formatTime = (timeStr) => { if (!timeStr) return '-'; try { const date = new Date(timeStr); if (isNaN(date.getTime())) return timeStr; // 使用北京时间格式化 return date.toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', timeZone: 'Asia/Shanghai' // 明确使用北京时间 }); } catch (e) { return timeStr; } }; const getSignalStrengthColor = (strength) => { if (strength >= 8) return 'signal-strong'; if (strength >= 6) return 'signal-medium'; return 'signal-weak'; }; return (

交易推荐

每10秒自动更新
{error && (
{error}
)} {loading ? (
加载中...
) : recommendations.length === 0 ? (

暂无推荐记录

) : (
{recommendations.map((rec) => { const key = rec.id || rec.symbol; return (
{rec.symbol} {rec.direction === 'BUY' ? '做多' : '做空'}
信号强度: {rec.signal_strength}/10 {rec.recommendation_time && ( {formatTime(rec.recommendation_time)} )}
{parseFloat(rec.current_price || 0).toFixed(4)} USDT {rec.price_updated && ( 🟢 )}
{rec.change_percent !== undefined && rec.change_percent !== null && (
= 0 ? 'positive' : 'negative'}> {rec.change_percent >= 0 ? '+' : ''}{parseFloat(rec.change_percent).toFixed(2)}%
)}
推荐原因:

{rec.recommendation_reason || '-'}

{rec.order_type === 'LIMIT' ? '限价单' : '市价单'}
{rec.order_type === 'LIMIT' && rec.suggested_limit_price && (
{parseFloat(rec.suggested_limit_price || 0).toFixed(4)} USDT {rec.current_price && ( ({rec.direction === 'BUY' ? '低于' : '高于'}当前价 {Math.abs(((rec.suggested_limit_price - rec.current_price) / rec.current_price) * 100).toFixed(2)}%) )}
)}
{parseFloat(rec.suggested_stop_loss || 0).toFixed(4)}
{parseFloat(rec.suggested_take_profit_1 || 0).toFixed(4)}
{parseFloat(rec.suggested_take_profit_2 || 0).toFixed(4)}
{(parseFloat(rec.suggested_position_percent || 0) * 100).toFixed(2)}%
{rec.suggested_leverage || 10}x
{showDetails[key] && (

技术指标

{rec.rsi && (
{parseFloat(rec.rsi).toFixed(2)}
)} {rec.macd_histogram !== null && rec.macd_histogram !== undefined && (
{parseFloat(rec.macd_histogram).toFixed(6)}
)} {rec.ema20 && (
{parseFloat(rec.ema20).toFixed(4)}
)} {rec.ema50 && (
{parseFloat(rec.ema50).toFixed(4)}
)} {rec.atr && (
{parseFloat(rec.atr).toFixed(4)}
)}
{rec.market_regime && (

市场状态

{rec.market_regime === 'trending' ? '趋势市场' : '震荡市场'}

)} {rec.trend_4h && (

4H趋势

{rec.trend_4h === 'up' ? '上涨' : rec.trend_4h === 'down' ? '下跌' : '中性'}

)} {rec.volume_24h && (

24小时成交量

{parseFloat(rec.volume_24h).toFixed(2)}

)} {rec.volatility && (

波动率

{parseFloat(rec.volatility).toFixed(4)}

)}
)}
); })}
)}
); } export default RecommendationsViewer;