fix: 首页自选增加详情和精简模式

master
Lxy 4 months ago
parent d05b1ede63
commit fd99430c6f

11
.env

@ -1,8 +1,13 @@
# API配置
OPENAI_API_KEY=
DEEPSEEK_API_KEY=
# DeepSeek API 配置
DEEPSEEK_API_KEY=sk-49dccf9e8a754d3abb36ce396cb8f189
DEEPSEEK_API_URL=https://api.deepseek.com/v1/chat/completions
# OpenAI API 配置
OPENAI_API_KEY=your_openai_api_key
# Gemini API 配置
GEMINI_API_KEY=your_gemini_api_key
# 天勤TQSDK配置
# 填写你的TQSDK账号密码以使用真实数据
TQSDK_USERNAME=windsdreamer

@ -7,16 +7,51 @@ from qihuo_analyzer.core.models import AnalysisResult
class DeepseekAgent:
"""DeepSeek AI 研判代理"""
"""AI 研判代理,支持多种模型"""
def __init__(self):
self.api_key = config_manager.deepseek_api_key
self.api_url = config_manager.deepseek_api_url
self.headers = {
def __init__(self, model_name='deepseek'):
"""初始化AI代理
Args:
model_name: 模型名称支持 'deepseek', 'gpt', 'gemini'
"""
self.model_name = model_name
# 安全获取API密钥避免访问不存在的属性
gemini_api_key = getattr(config_manager, 'gemini_api_key', '')
self.api_configs = {
'deepseek': {
'api_key': config_manager.deepseek_api_key,
'api_url': config_manager.deepseek_api_url,
'headers': {
'Content-Type': 'application/json',
'Authorization': f'Bearer {config_manager.deepseek_api_key}'
}
},
'gpt': {
'api_key': config_manager.openai_api_key,
'api_url': 'https://api.openai.com/v1/chat/completions',
'headers': {
'Content-Type': 'application/json',
'Authorization': f'Bearer {config_manager.openai_api_key}'
}
},
'gemini': {
'api_key': gemini_api_key,
'api_url': 'https://generativelanguage.googleapis.com/v1/models/gemini-pro:generateContent',
'headers': {
'Content-Type': 'application/json',
'Authorization': f'Bearer {self.api_key}'
'Authorization': f'Bearer {gemini_api_key}'
}
}
}
# 获取当前模型的配置
self.current_config = self.api_configs.get(model_name, self.api_configs['deepseek'])
self.api_key = self.current_config['api_key']
self.api_url = self.current_config['api_url']
self.headers = self.current_config['headers']
def analyze_market(self, market_data: Dict, technical_indicators: Dict,
trend_analysis: Dict, risk_metrics: Dict) -> Dict:
"""分析市场"""
@ -24,7 +59,7 @@ class DeepseekAgent:
prompt = self._build_analysis_prompt(market_data, technical_indicators, trend_analysis, risk_metrics)
# 调用API
response = self._call_deepseek_api(prompt)
response = self._call_ai_api(prompt)
# 解析结果
analysis_result = self._parse_analysis_result(response)
@ -37,7 +72,7 @@ class DeepseekAgent:
prompt = self._build_recommendation_prompt(analysis_result)
# 调用API
response = self._call_deepseek_api(prompt)
response = self._call_ai_api(prompt)
# 解析结果
recommendation = self._parse_recommendation_result(response)
@ -126,12 +161,73 @@ class DeepseekAgent:
"""
return prompt
def _call_deepseek_api(self, prompt: str) -> str:
"""调用DeepSeek API"""
# 如果没有API密钥返回模拟结果
def _call_ai_api(self, prompt: str) -> str:
"""调用AI API"""
# 如果没有API密钥返回错误提示
if not self.api_key:
return self._get_mock_response(prompt)
return json.dumps({'error': 'API密钥未配置'})
# 根据模型类型构建不同的payload
if self.model_name == 'deepseek':
payload = {
'model': 'deepseek-chat',
'messages': [
{
'role': 'system',
'content': '你是一位专业的期货市场分析师,精通技术分析和基本面分析,能够基于多维度数据提供准确的市场研判和交易建议。'
},
{
'role': 'user',
'content': prompt
}
],
'temperature': 0.3,
'max_tokens': 2000,
'top_p': 0.9
}
elif self.model_name == 'gpt':
payload = {
'model': 'gpt-3.5-turbo',
'messages': [
{
'role': 'system',
'content': '你是一位专业的期货市场分析师,精通技术分析和基本面分析,能够基于多维度数据提供准确的市场研判和交易建议。'
},
{
'role': 'user',
'content': prompt
}
],
'temperature': 0.3,
'max_tokens': 2000,
'top_p': 0.9
}
elif self.model_name == 'gemini':
payload = {
'contents': [
{
'parts': [
{
'text': '你是一位专业的期货市场分析师,精通技术分析和基本面分析,能够基于多维度数据提供准确的市场研判和交易建议。'
}
]
},
{
'parts': [
{
'text': prompt
}
]
}
],
'generationConfig': {
'temperature': 0.3,
'maxOutputTokens': 2000,
'topP': 0.9
}
}
else:
# 默认使用deepseek格式
payload = {
'model': 'deepseek-chat',
'messages': [
@ -159,10 +255,17 @@ class DeepseekAgent:
response.raise_for_status()
result = response.json()
# 根据模型类型解析不同的响应格式
if self.model_name == 'deepseek' or self.model_name == 'gpt':
return result['choices'][0]['message']['content']
elif self.model_name == 'gemini':
return result['candidates'][0]['content']['parts'][0]['text']
else:
return result['choices'][0]['message']['content']
except Exception as e:
print(f"API调用失败{e}")
return self._get_mock_response(prompt)
return json.dumps({'error': 'API调用失败'})
def _get_mock_response(self, prompt: str) -> str:
"""获取模拟响应"""

@ -254,12 +254,28 @@ def index():
except (ValueError, TypeError):
safe_adx = 0
# 计算胜率(模拟数据,实际项目中应该使用真实的胜率计算)
win_rate = round(win_rate, 1) if win_rate is not None else 0
# 计算换月预警(模拟数据,实际项目中应该使用真实的距离交割天数)
rollover_warning = _get_rollover_warning(rollover_analysis)
# 计算主力资金流向(模拟数据,实际项目中应该使用真实的资金流向数据)
fund_flow = fund_flow_to_cn(fund_flow_analysis.get('fund_signal', 'neutral'))
# 计算周期(模拟数据,实际项目中应该使用真实的周期判断)
cycle = cycle_to_cn(cycle)
symbol_data = {
'symbol': symbol,
'name': f"{product_name_cn}({symbol})".upper(),
'current_price': safe_current_price,
'direction': trend_analysis.get('overall_trend', 'sideways'),
'trend_strength': _get_trend_strength_display(safe_adx)
'win_rate': win_rate,
'trend_strength': _get_trend_strength_display(safe_adx),
'cycle': cycle,
'rollover_warning': rollover_warning,
'fund_flow': fund_flow
}
selected_symbols_data.append(symbol_data)
except Exception as e:
@ -288,6 +304,9 @@ def index():
def symbol_detail(symbol):
"""品种详情页"""
try:
# 获取模型选择参数默认为deepseek
model_name = request.args.get('model', 'deepseek')
# 辅助函数:安全地四舍五入数字
def safe_round(value, decimals=2, context=''):
try:
@ -429,9 +448,10 @@ def symbol_detail(symbol):
'risk_ratio': safe_round(position_info.get('actual_risk_percent', 0) * 100, 2, context='risk_metrics_risk_ratio')
}
# AI 分析
ai_analysis = deepseek_agent.analyze_market(market_data, technical_indicators, trend_data, risk_metrics)
recommendation = deepseek_agent.generate_trade_recommendation(ai_analysis)
# AI 分析 - 使用指定的模型
ai_agent = DeepseekAgent(model_name=model_name)
ai_analysis = ai_agent.analyze_market(market_data, technical_indicators, trend_data, risk_metrics)
recommendation = ai_agent.generate_trade_recommendation(ai_analysis)
# 构建模板数据
# 获取中文名称
@ -512,7 +532,9 @@ def symbol_detail(symbol):
'ai_analysis': ai_analysis,
'recommendation': recommendation,
'kline_data': _get_kline_data_for_chart(kline_data),
'is_favorite': symbol in selected_symbols
'is_favorite': symbol in selected_symbols,
'model_name': model_name,
'available_models': ['deepseek', 'gpt', 'gemini']
}
print("[DEBUG] context built successfully")

@ -291,13 +291,23 @@
<div class="section">
<div class="section-title">
自选关注
<div style="display: flex; align-items: center; gap: 16px;">
<span style="font-size: 14px; font-weight: 400; color: #666;">{{ selected_symbols_data | length }} 个品种</span>
<div style="display: flex; align-items: center;">
<span style="font-size: 14px; color: #666; margin-right: 8px;">显示模式:</span>
<div style="display: flex; border: 1px solid #d9d9d9; border-radius: 4px; overflow: hidden;">
<button id="simpleModeBtn" onclick="switchDisplayMode('simple')" style="padding: 4px 12px; background: #f0f0f0; border: none; font-size: 14px; cursor: pointer; outline: none;">简约</button>
<button id="detailModeBtn" onclick="switchDisplayMode('detail')" style="padding: 4px 12px; background: white; border: none; font-size: 14px; cursor: pointer; outline: none;">详细</button>
</div>
</div>
</div>
</div>
{% if selected_symbols_data %}
<div class="panel-grid">
{% for symbol_data in selected_symbols_data %}
<div class="symbol-card" onclick="window.location.href='/symbol/{{ symbol_data.symbol }}'">
<!-- 简约模式卡片 -->
<div class="symbol-card simple-mode" onclick="window.location.href='/symbol/{{ symbol_data.symbol }}'" style="display: block;">
<div class="favorite-btn favorite" onclick="event.stopPropagation(); removeFromFavorite('{{ symbol_data.symbol }}');"></div>
<div class="symbol-header">
<div>
@ -328,6 +338,50 @@
</div>
</div>
</div>
<!-- 详细模式卡片 -->
<div class="symbol-card detail-mode" onclick="window.location.href='/symbol/{{ symbol_data.symbol }}'" style="display: none;">
<div class="favorite-btn favorite" onclick="event.stopPropagation(); removeFromFavorite('{{ symbol_data.symbol }}');"></div>
<div class="symbol-header">
<div>
<div class="symbol-name">{{ symbol_data.name }}</div>
<div class="symbol-code">{{ symbol_data.symbol }}</div>
</div>
<div class="price-info">
<div class="current-price">{{ symbol_data.current_price }}</div>
<div class="direction-indicator
{% if symbol_data.direction == 'bullish' or symbol_data.direction == 'strong_bullish' or symbol_data.direction == 'weak_bullish' %}direction-up{% elif symbol_data.direction == 'bearish' or symbol_data.direction == 'strong_bearish' or symbol_data.direction == 'weak_bearish' %}direction-down{% else %}direction-sideways{% endif %}">
{% if symbol_data.direction == 'strong_bullish' or symbol_data.direction == 'weak_bullish' or symbol_data.direction == 'bullish' %}🟢 做多
{% elif symbol_data.direction == 'strong_bearish' or symbol_data.direction == 'weak_bearish' or symbol_data.direction == 'bearish' %}🔴 做空
{% else %}⚪ 观望
{% endif %}
</div>
</div>
</div>
<div class="analysis-grid">
<div class="analysis-item">
<div class="analysis-label">胜率</div>
<div class="analysis-value">{{ symbol_data.win_rate }}%</div>
</div>
<div class="analysis-item">
<div class="analysis-label">趋势强度</div>
<div class="analysis-value">{{ symbol_data.trend_strength }}</div>
</div>
<div class="analysis-item">
<div class="analysis-label">周期</div>
<div class="analysis-value">{{ symbol_data.cycle }}</div>
</div>
<div class="analysis-item">
<div class="analysis-label">换月预警</div>
<div class="analysis-value warning">{{ symbol_data.rollover_warning }}</div>
</div>
<div class="analysis-item">
<div class="analysis-label">主力资金</div>
<div class="analysis-value fund-flow">{{ symbol_data.fund_flow }}</div>
</div>
</div>
</div>
{% endfor %}
</div>
{% else %}
@ -542,6 +596,40 @@
});
}
// 切换显示模式
function switchDisplayMode(mode) {
const simpleCards = document.querySelectorAll('.simple-mode');
const detailCards = document.querySelectorAll('.detail-mode');
const simpleBtn = document.getElementById('simpleModeBtn');
const detailBtn = document.getElementById('detailModeBtn');
if (mode === 'simple') {
// 显示简约模式卡片,隐藏详细模式卡片
simpleCards.forEach(card => {
card.style.display = 'block';
});
detailCards.forEach(card => {
card.style.display = 'none';
});
// 更新按钮样式
simpleBtn.style.background = '#f0f0f0';
detailBtn.style.background = 'white';
} else if (mode === 'detail') {
// 显示详细模式卡片,隐藏简约模式卡片
simpleCards.forEach(card => {
card.style.display = 'none';
});
detailCards.forEach(card => {
card.style.display = 'block';
});
// 更新按钮样式
simpleBtn.style.background = 'white';
detailBtn.style.background = '#f0f0f0';
}
}
// 搜索功能
document.addEventListener('DOMContentLoaded', function() {
const searchInput = document.getElementById('symbolSearch');

@ -468,98 +468,233 @@
<div class="ai-title">
<div class="ai-icon">AI</div>
AI智能分析
<div style="margin-left: auto; display: flex; align-items: center;">
<span style="font-size: 14px; margin-right: 8px; color: #666;">模型选择:</span>
<form action="/symbol/{{ symbol }}" method="get" style="display: flex; align-items: center;">
<select name="model" onchange="this.form.submit()" style="padding: 4px 8px; border: 1px solid #d9d9d9; border-radius: 4px; font-size: 14px; outline: none;">
{% for model in available_models %}
<option value="{{ model }}" {% if model_name == model %}selected{% endif %}>
{% if model == 'deepseek' %}DeepSeek
{% elif model == 'gpt' %}GPT
{% elif model == 'gemini' %}Gemini
{% else %}{{ model }}
{% endif %}
</option>
{% endfor %}
</select>
</form>
</div>
</div>
{% if ai_analysis.error %}
<!-- 模型不可用时的友好提示 -->
<div style="text-align: center; padding: 60px; background: #f9fafb; border-radius: 8px;">
<div style="font-size: 48px; margin-bottom: 20px;">⚠️</div>
<h3 style="font-size: 18px; font-weight: 600; color: #333; margin-bottom: 12px;">AI分析不可用</h3>
<p style="font-size: 14px; color: #666; margin-bottom: 24px;">
{% if ai_analysis.error == 'API密钥未配置' %}
当前模型的API密钥未配置请在.env文件中添加相应的API密钥
{% elif ai_analysis.error == 'API调用失败' %}
API调用失败请检查网络连接或API密钥是否正确
{% else %}
{{ ai_analysis.error }}
{% endif %}
</p>
<div style="display: flex; justify-content: center; gap: 16px;">
<a href="/symbol/{{ symbol }}?model=deepseek" style="padding: 8px 16px; background: #f0f0f0; color: #333; text-decoration: none; border-radius: 4px; font-size: 14px;">切换模型</a>
</div>
</div>
{% else %}
<!-- AI分析内容 -->
<div class="ai-content">
<div class="analysis-grid">
<!-- 趋势分析 -->
<div class="analysis-card">
<div class="card-title">趋势判断</div>
<div class="analysis-item">
<div class="analysis-label">趋势方向</div>
<!-- 趋势分析卡片 -->
<div class="analysis-card" style="margin-bottom: 20px;">
<div class="card-title">
<div style="display: flex; align-items: center;">
<div style="width: 8px; height: 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 4px; margin-right: 8px;"></div>
趋势判断
</div>
</div>
<div class="analysis-grid" style="gap: 16px;">
<div class="analysis-item" style="flex: 1; padding: 12px; background: #f9fafb; border-radius: 6px;">
<div class="analysis-label" style="margin-bottom: 4px;">趋势方向</div>
<div class="analysis-value
{% if ai_analysis.trend_judgment and ('多' in ai_analysis.trend_judgment) %}direction-up{% elif ai_analysis.trend_judgment and ('空' in ai_analysis.trend_judgment) %}direction-down{% endif %}">
{% if ai_analysis.trend_judgment and ('多' in ai_analysis.trend_judgment) %}direction-up{% elif ai_analysis.trend_judgment and ('空' in ai_analysis.trend_judgment) %}direction-down{% endif %}"
style="font-size: 16px; font-weight: 600;">
{{ ai_analysis.trend_judgment | default('未知') }}
</div>
</div>
<div class="analysis-item">
<div class="analysis-label">胜率评估</div>
<div class="analysis-value">{{ ai_analysis.win_rate_assessment | default('未知') }}</div>
</div>
<div class="analysis-item">
<div class="analysis-label">风险预警</div>
<div class="analysis-value warning">{{ ai_analysis.risk_warning | default('无') }}</div>
<div class="analysis-item" style="flex: 1; padding: 12px; background: #f9fafb; border-radius: 6px;">
<div class="analysis-label" style="margin-bottom: 4px;">胜率评估</div>
<div class="analysis-value" style="font-size: 16px; font-weight: 600;">{{ ai_analysis.win_rate_assessment | default('未知') }}</div>
</div>
<div class="analysis-item">
<div class="analysis-label">交易建议</div>
<div class="analysis-item" style="flex: 1; padding: 12px; background: #f9fafb; border-radius: 6px;">
<div class="analysis-label" style="margin-bottom: 4px;">交易建议</div>
<div class="analysis-value
{% if ai_analysis.trade_recommendation and ('多' in ai_analysis.trade_recommendation) %}direction-up{% elif ai_analysis.trade_recommendation and ('空' in ai_analysis.trade_recommendation) %}direction-down{% endif %}">
{% if ai_analysis.trade_recommendation and ('多' in ai_analysis.trade_recommendation) %}direction-up{% elif ai_analysis.trade_recommendation and ('空' in ai_analysis.trade_recommendation) %}direction-down{% endif %}"
style="font-size: 16px; font-weight: 600;">
{{ ai_analysis.trade_recommendation | default('未知') }}
</div>
</div>
</div>
<!-- 分析逻辑 -->
<div class="analysis-card">
<div class="card-title">分析逻辑</div>
<div style="padding: 8px 0;">
{{ ai_analysis.analysis_logic | default('无详细分析') }}
<!-- 风险预警 -->
{% if ai_analysis.risk_warning and ai_analysis.risk_warning != '无' %}
<div style="margin-top: 16px; padding: 12px; background: #fff2f0; border: 1px solid #ffccc7; border-radius: 6px;">
<div style="font-size: 14px; font-weight: 600; color: #ff4d4f; margin-bottom: 4px;">⚠️ 风险预警</div>
<div style="font-size: 14px; color: #ff4d4f;">{{ ai_analysis.risk_warning }}</div>
</div>
{% endif %}
</div>
<!-- 分析逻辑 -->
{% if ai_analysis.analysis_logic %}
<div class="analysis-card" style="margin-bottom: 20px;">
<div class="card-title">
<div style="display: flex; align-items: center;">
<div style="width: 8px; height: 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 4px; margin-right: 8px;"></div>
分析逻辑
</div>
</div>
<div style="padding: 12px; background: #f9fafb; border-radius: 6px;">
{% if ai_analysis.analysis_logic is string %}
<!-- 处理字符串形式的分析逻辑 -->
{% if '1.' in ai_analysis.analysis_logic or '2.' in ai_analysis.analysis_logic or '3.' in ai_analysis.analysis_logic %}
<!-- 处理列表形式的分析逻辑 -->
<ul style="list-style: none; padding: 0; margin: 0;">
{% for item in ai_analysis.analysis_logic.split('\n') %}
{% if item.strip() %}
<li style="padding: 8px 0; border-bottom: 1px solid #f0f0f0;">
<div style="display: flex; align-items: flex-start;">
<div style="width: 20px; height: 20px; background: #667eea; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; margin-right: 12px; flex-shrink: 0;">
{% if loop.index <= 9 %}{{ loop.index }}{% else %}{{ loop.index }}{% endif %}
</div>
<div style="flex: 1;">{{ item.strip() }}</div>
</div>
</li>
{% endif %}
{% endfor %}
</ul>
{% else %}
<!-- 处理普通文本形式的分析逻辑 -->
<p style="margin: 0;">{{ ai_analysis.analysis_logic }}</p>
{% endif %}
{% else %}
<!-- 处理其他形式的分析逻辑 -->
<p style="margin: 0;">{{ ai_analysis.analysis_logic | default('无详细分析') }}</p>
{% endif %}
</div>
</div>
{% endif %}
</div>
<!-- 交易建议 -->
<div class="recommendation" style="margin-top: 20px;">
<div class="recommendation-title">详细交易建议</div>
<div class="analysis-grid" style="margin-top: 16px;">
<div class="recommendation-title">
<div style="display: flex; align-items: center;">
<div style="width: 8px; height: 16px; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 4px; margin-right: 8px;"></div>
详细交易建议
</div>
</div>
<div class="analysis-grid" style="margin-top: 16px; gap: 20px;">
<!-- 交易参数 -->
<div class="analysis-card">
<div class="analysis-card" style="flex: 1;">
<div class="card-title">交易参数</div>
<div class="analysis-item">
<div class="analysis-label">交易方向</div>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px;">
<div class="analysis-item" style="padding: 12px; background: #f9fafb; border-radius: 6px;">
<div class="analysis-label" style="margin-bottom: 4px;">交易方向</div>
<div class="analysis-value
{% if recommendation.direction == 'long' %}direction-up{% elif recommendation.direction == 'short' %}direction-down{% endif %}">
{% if recommendation.direction == 'long' %}direction-up{% elif recommendation.direction == 'short' %}direction-down{% endif %}"
style="font-size: 16px; font-weight: 600;">
{% if recommendation.direction == 'long' %}多头
{% elif recommendation.direction == 'short' %}空头
{% else %}{{ recommendation.direction | default('未知') }}
{% endif %}
</div>
</div>
<div class="analysis-item">
<div class="analysis-label">入场价格</div>
<div class="analysis-value">{{ recommendation.entry_price | default('未知') }}</div>
<div class="analysis-item" style="padding: 12px; background: #f9fafb; border-radius: 6px;">
<div class="analysis-label" style="margin-bottom: 4px;">入场价格</div>
<div class="analysis-value" style="font-size: 16px; font-weight: 600;">{{ recommendation.entry_price | default('未知') }}</div>
</div>
<div class="analysis-item">
<div class="analysis-label">止损价格</div>
<div class="analysis-value warning">{{ recommendation.stop_loss | default('未知') }}</div>
<div class="analysis-item" style="padding: 12px; background: #fff2f0; border-radius: 6px;">
<div class="analysis-label" style="margin-bottom: 4px;">止损价格</div>
<div class="analysis-value warning" style="font-size: 16px; font-weight: 600;">{{ recommendation.stop_loss | default('未知') }}</div>
</div>
<div class="analysis-item">
<div class="analysis-label">目标价格</div>
<div class="analysis-value direction-up">{{ recommendation.target_price | default('未知') }}</div>
<div class="analysis-item" style="padding: 12px; background: #f0f5ff; border-radius: 6px;">
<div class="analysis-label" style="margin-bottom: 4px;">目标价格</div>
<div class="analysis-value direction-up" style="font-size: 16px; font-weight: 600;">{{ recommendation.target_price | default('未知') }}</div>
</div>
<div class="analysis-item" style="padding: 12px; background: #f9fafb; border-radius: 6px;">
<div class="analysis-label" style="margin-bottom: 4px;">仓位大小</div>
<div class="analysis-value" style="font-size: 16px; font-weight: 600;">{{ recommendation.position_size | default('未知') }}手</div>
</div>
<div class="analysis-item">
<div class="analysis-label">仓位大小</div>
<div class="analysis-value">{{ recommendation.position_size | default('未知') }}手</div>
</div>
</div>
<!-- 执行计划 -->
<div class="analysis-card">
<div class="analysis-card" style="flex: 1;">
<div class="card-title">执行计划</div>
<div style="padding: 8px 0;">
{{ recommendation.execution_plan | default('无详细计划') }}
<div style="padding: 12px; background: #f9fafb; border-radius: 6px;">
{% if recommendation.execution_plan %}
{% if '1.' in recommendation.execution_plan or '2.' in recommendation.execution_plan or '3.' in recommendation.execution_plan %}
<!-- 处理列表形式的执行计划 -->
<ul style="list-style: none; padding: 0; margin: 0;">
{% for item in recommendation.execution_plan.split('\n') %}
{% if item.strip() %}
<li style="padding: 8px 0; border-bottom: 1px solid #f0f0f0;">
<div style="display: flex; align-items: flex-start;">
<div style="width: 20px; height: 20px; background: #764ba2; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; margin-right: 12px; flex-shrink: 0;">
{% if loop.index <= 9 %}{{ loop.index }}{% else %}{{ loop.index }}{% endif %}
</div>
<div style="flex: 1;">{{ item.strip() }}</div>
</div>
</li>
{% endif %}
{% endfor %}
</ul>
{% else %}
<!-- 处理普通文本形式的执行计划 -->
<p style="margin: 0;">{{ recommendation.execution_plan }}</p>
{% endif %}
{% else %}
<p style="margin: 0; color: #999;">无详细计划</p>
{% endif %}
</div>
</div>
<!-- 风险提示 -->
<div class="analysis-card">
<div class="analysis-card" style="flex: 1;">
<div class="card-title">风险提示</div>
<div style="padding: 8px 0; color: #ff4d4f;">
{{ recommendation.risk_tips | default('无风险提示') }}
<div style="padding: 12px; background: #fff2f0; border: 1px solid #ffccc7; border-radius: 6px;">
{% if recommendation.risk_tips %}
{% if '1.' in recommendation.risk_tips or '2.' in recommendation.risk_tips or '3.' in recommendation.risk_tips %}
<!-- 处理列表形式的风险提示 -->
<ul style="list-style: none; padding: 0; margin: 0;">
{% for item in recommendation.risk_tips.split('\n') %}
{% if item.strip() %}
<li style="padding: 8px 0; border-bottom: 1px solid #ffccc7;">
<div style="display: flex; align-items: flex-start;">
<div style="width: 20px; height: 20px; background: #ff4d4f; color: white; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 600; margin-right: 12px; flex-shrink: 0;">
⚠️
</div>
<div style="flex: 1; color: #ff4d4f;">{{ item.strip() }}</div>
</div>
</li>
{% endif %}
{% endfor %}
</ul>
{% else %}
<!-- 处理普通文本形式的风险提示 -->
<p style="margin: 0; color: #ff4d4f;">{{ recommendation.risk_tips }}</p>
{% endif %}
{% else %}
<p style="margin: 0; color: #ff4d4f;">无风险提示</p>
{% endif %}
</div>
</div>
</div>
</div>
{% endif %}
</div>
<!-- 底部 -->

Loading…
Cancel
Save