Merge branch 'feature_2025/251117_pref' into feature_2025/251121_h5UI

* feature_2025/251117_pref:
  update pay function
  update pay function
  update pay function
  update pay function
  update pay function
  update pay function
  update pay function
This commit is contained in:
zdl
2025-11-21 18:30:51 +08:00
12 changed files with 9746 additions and 511 deletions

Binary file not shown.

367
app_vx.py
View File

@@ -2463,16 +2463,16 @@ def api_get_events():
order_func = desc if order.lower() == 'desc' else asc
if sort_by == 'hot':
query = query.order_by(order_func(Event.hot_score))
query = query.order_by(order_func(Event.hot_score), desc(Event.created_at))
elif sort_by == 'new':
query = query.order_by(order_func(Event.created_at))
elif sort_by == 'returns':
if return_type == 'avg':
query = query.order_by(order_func(Event.related_avg_chg))
query = query.order_by(order_func(Event.related_avg_chg), desc(Event.created_at))
elif return_type == 'max':
query = query.order_by(order_func(Event.related_max_chg))
query = query.order_by(order_func(Event.related_max_chg), desc(Event.created_at))
elif return_type == 'week':
query = query.order_by(order_func(Event.related_week_chg))
query = query.order_by(order_func(Event.related_week_chg), desc(Event.created_at))
elif sort_by == 'importance':
importance_order = case(
(Event.importance == 'S', 1),
@@ -2482,287 +2482,29 @@ def api_get_events():
else_=5
)
if order.lower() == 'desc':
query = query.order_by(importance_order)
query = query.order_by(importance_order, desc(Event.created_at))
else:
query = query.order_by(desc(importance_order))
query = query.order_by(desc(importance_order), desc(Event.created_at))
elif sort_by == 'view_count':
query = query.order_by(order_func(Event.view_count))
query = query.order_by(order_func(Event.view_count), desc(Event.created_at))
elif sort_by == 'follow' and hasattr(request, 'user') and request.user.is_authenticated:
# 关注的事件排序
query = query.join(EventFollow).filter(
EventFollow.user_id == request.user.id
).order_by(order_func(Event.created_at))
else:
# 兜底排序:始终按时间倒序
query = query.order_by(desc(Event.created_at))
# ==================== 分页查询 ====================
paginated = query.paginate(page=page, per_page=per_page, error_out=False)
# ==================== 批量获取股票行情数据(优化版) ====================
# 1. 收集当前页所有事件的ID
event_ids = [event.id for event in paginated.items]
# 2. 获取所有相关股票
all_related_stocks = {}
if event_ids:
related_stocks = RelatedStock.query.filter(
RelatedStock.event_id.in_(event_ids)
).all()
# 按事件ID分组
for stock in related_stocks:
if stock.event_id not in all_related_stocks:
all_related_stocks[stock.event_id] = []
all_related_stocks[stock.event_id].append(stock)
# 3. 收集所有股票代码
all_stock_codes = []
stock_code_mapping = {} # 清理后的代码 -> 原始代码的映射
for stocks in all_related_stocks.values():
for stock in stocks:
clean_code = stock.stock_code.replace('.SH', '').replace('.SZ', '').replace('.BJ', '')
all_stock_codes.append(clean_code)
stock_code_mapping[clean_code] = stock.stock_code
# 去重
all_stock_codes = list(set(all_stock_codes))
# 4. 批量查询最近7个交易日的数据用于计算日涨跌和周涨跌
stock_price_data = {}
if all_stock_codes:
# 构建SQL查询 - 获取最近7个交易日的数据
codes_str = "'" + "', '".join(all_stock_codes) + "'"
# 获取最近7个交易日的数据
recent_trades_sql = f"""
SELECT
SECCODE,
SECNAME,
F007N as close_price,
F010N as daily_change,
TRADEDATE,
ROW_NUMBER() OVER (PARTITION BY SECCODE ORDER BY TRADEDATE DESC) as rn
FROM ea_trade
WHERE SECCODE IN ({codes_str})
AND F007N IS NOT NULL
AND TRADEDATE >= DATE_SUB(CURDATE(), INTERVAL 10 DAY)
ORDER BY SECCODE, TRADEDATE DESC
"""
result = db.session.execute(text(recent_trades_sql))
# 整理数据
for row in result.fetchall():
sec_code = row[0]
if sec_code not in stock_price_data:
stock_price_data[sec_code] = {
'stock_name': row[1],
'prices': []
}
stock_price_data[sec_code]['prices'].append({
'close_price': float(row[2]) if row[2] else 0,
'daily_change': float(row[3]) if row[3] else 0,
'trade_date': row[4],
'rank': row[5]
})
# 5. 计算日涨跌和周涨跌
stock_changes = {}
for sec_code, data in stock_price_data.items():
prices = data['prices']
# 最新日涨跌第1条记录
daily_change = 0
if prices and prices[0]['rank'] == 1:
daily_change = prices[0]['daily_change']
# 计算周涨跌(最新价 vs 5个交易日前的价格
week_change = 0
if len(prices) >= 2:
latest_price = prices[0]['close_price']
# 找到第5个交易日的数据如果有
week_ago_price = None
for price_data in prices:
if price_data['rank'] >= 5:
week_ago_price = price_data['close_price']
break
# 如果没有第5天的数据使用最早的数据
if week_ago_price is None and len(prices) > 1:
week_ago_price = prices[-1]['close_price']
if week_ago_price and week_ago_price > 0:
week_change = (latest_price - week_ago_price) / week_ago_price * 100
stock_changes[sec_code] = {
'stock_name': data['stock_name'],
'daily_change': daily_change,
'week_change': week_change
}
# ==================== 获取整体统计信息(应用所有筛选条件) ====================
overall_distribution = {
'limit_down': 0,
'down_over_5': 0,
'down_5_to_1': 0,
'down_within_1': 0,
'flat': 0,
'up_within_1': 0,
'up_1_to_5': 0,
'up_over_5': 0,
'limit_up': 0
}
# 使用当前筛选条件的query但不应用分页限制获取所有符合条件的事件
# 这样统计数据会跟随用户的筛选条件变化
all_filtered_events = query.limit(1000).all() # 限制最多1000个事件避免查询过慢
week_event_ids = [e.id for e in all_filtered_events]
if week_event_ids:
# 获取这些事件的所有关联股票
week_related_stocks = RelatedStock.query.filter(
RelatedStock.event_id.in_(week_event_ids)
).all()
# 按事件ID分组
week_stocks_by_event = {}
for stock in week_related_stocks:
if stock.event_id not in week_stocks_by_event:
week_stocks_by_event[stock.event_id] = []
week_stocks_by_event[stock.event_id].append(stock)
# 收集所有股票代码(用于批量查询行情)
week_stock_codes = []
week_code_mapping = {}
for stocks in week_stocks_by_event.values():
for stock in stocks:
clean_code = stock.stock_code.replace('.SH', '').replace('.SZ', '').replace('.BJ', '')
week_stock_codes.append(clean_code)
week_code_mapping[clean_code] = stock.stock_code
week_stock_codes = list(set(week_stock_codes))
# 批量查询这些股票的最新行情数据
week_stock_changes = {}
if week_stock_codes:
codes_str = "'" + "', '".join(week_stock_codes) + "'"
recent_trades_sql = f"""
SELECT
SECCODE,
SECNAME,
F010N as daily_change,
ROW_NUMBER() OVER (PARTITION BY SECCODE ORDER BY TRADEDATE DESC) as rn
FROM ea_trade
WHERE SECCODE IN ({codes_str})
AND F010N IS NOT NULL
AND TRADEDATE >= DATE_SUB(CURDATE(), INTERVAL 3 DAY)
ORDER BY SECCODE, TRADEDATE DESC
"""
result = db.session.execute(text(recent_trades_sql))
for row in result.fetchall():
sec_code = row[0]
if row[3] == 1: # 只取最新的数据rn=1
week_stock_changes[sec_code] = {
'stock_name': row[1],
'daily_change': float(row[2]) if row[2] else 0
}
# 按事件统计平均涨跌幅分布
event_avg_changes = {}
for event in all_filtered_events:
event_stocks = week_stocks_by_event.get(event.id, [])
if not event_stocks:
continue
total_change = 0
valid_count = 0
for stock in event_stocks:
clean_code = stock.stock_code.replace('.SH', '').replace('.SZ', '').replace('.BJ', '')
if clean_code in week_stock_changes:
daily_change = week_stock_changes[clean_code]['daily_change']
total_change += daily_change
valid_count += 1
if valid_count > 0:
avg_change = total_change / valid_count
event_avg_changes[event.id] = avg_change
# 统计事件平均涨跌幅的分布
for event_id, avg_change in event_avg_changes.items():
# 对于事件平均涨幅,不使用涨跌停分类,使用通用分类
if avg_change <= -10:
overall_distribution['limit_down'] += 1
elif avg_change >= 10:
overall_distribution['limit_up'] += 1
elif avg_change > 5:
overall_distribution['up_over_5'] += 1
elif avg_change > 1:
overall_distribution['up_1_to_5'] += 1
elif avg_change > 0.1:
overall_distribution['up_within_1'] += 1
elif avg_change >= -0.1:
overall_distribution['flat'] += 1
elif avg_change > -1:
overall_distribution['down_within_1'] += 1
elif avg_change > -5:
overall_distribution['down_5_to_1'] += 1
else:
overall_distribution['down_over_5'] += 1
# ==================== 构建响应数据 ====================
events_data = []
for event in paginated.items:
event_stocks = all_related_stocks.get(event.id, [])
stocks_data = []
total_daily_change = 0
max_daily_change = -999
total_week_change = 0
max_week_change = -999
valid_stocks_count = 0
# 处理每个股票的数据
for stock in event_stocks:
clean_code = stock.stock_code.replace('.SH', '').replace('.SZ', '').replace('.BJ', '')
stock_info = stock_changes.get(clean_code, {})
daily_change = stock_info.get('daily_change', 0)
week_change = stock_info.get('week_change', 0)
if stock_info:
total_daily_change += daily_change
max_daily_change = max(max_daily_change, daily_change)
total_week_change += week_change
max_week_change = max(max_week_change, week_change)
valid_stocks_count += 1
stocks_data.append({
"stock_code": stock.stock_code,
"stock_name": stock.stock_name,
"sector": stock.sector,
"week_change": round(week_change, 2),
"daily_change": round(daily_change, 2)
})
avg_daily_change = total_daily_change / valid_stocks_count if valid_stocks_count > 0 else 0
avg_week_change = total_week_change / valid_stocks_count if valid_stocks_count > 0 else 0
if max_daily_change == -999:
max_daily_change = 0
if max_week_change == -999:
max_week_change = 0
# 构建事件数据
# 构建事件数据(保持原有结构,个股信息和统计置空)
event_dict = {
'id': event.id,
'title': event.title,
@@ -2774,20 +2516,21 @@ def api_get_events():
'updated_at': event.updated_at.isoformat() if event.updated_at else None,
'start_time': event.start_time.isoformat() if event.start_time else None,
'end_time': event.end_time.isoformat() if event.end_time else None,
'related_stocks': stocks_data,
# 个股信息(置空)
'related_stocks': [],
# 股票统计(置空或使用数据库字段)
'stocks_stats': {
'stocks_count': len(event_stocks),
'valid_stocks_count': valid_stocks_count,
# 周涨跌统计
'avg_week_change': round(avg_week_change, 2),
'max_week_change': round(max_week_change, 2),
# 日涨跌统计
'avg_daily_change': round(avg_daily_change, 2),
'max_daily_change': round(max_daily_change, 2)
'stocks_count': 10,
'valid_stocks_count': 0,
# 使用数据库字段的涨跌幅
'avg_week_change': round(event.related_week_chg, 2) if event.related_week_chg else 0,
'max_week_change': round(event.related_max_chg, 2) if event.related_max_chg else 0,
'avg_daily_change': round(event.related_avg_chg, 2) if event.related_avg_chg else 0,
'max_daily_change': round(event.related_max_chg, 2) if event.related_max_chg else 0
}
}
# 统计信息
# 统计信息(可选)
if include_stats:
event_dict.update({
'hot_score': event.hot_score,
@@ -2801,7 +2544,7 @@ def api_get_events():
'trending_score': event.trending_score,
})
# 创建者信息
# 创建者信息(可选)
if include_creator:
event_dict['creator'] = {
'id': event.creator.id if event.creator else None,
@@ -2815,24 +2558,18 @@ def api_get_events():
event_dict['keywords'] = event.keywords if isinstance(event.keywords, list) else []
event_dict['related_industries'] = event.related_industries
# 包含统计信息
# 包含统计信息(可选,置空)
if include_stats:
event_dict['stats'] = {
'related_stocks_count': len(event_stocks),
'historical_events_count': 0, # 需要额外查询
'related_data_count': 0, # 需要额外查询
'related_concepts_count': 0 # 需要额外查询
'related_stocks_count': 10,
'historical_events_count': 0,
'related_data_count': 0,
'related_concepts_count': 0
}
# 包含关联数据
# 包含关联数据(可选,已置空)
if include_related_data:
event_dict['related_stocks'] = [{
'id': stock.id,
'stock_code': stock.stock_code,
'stock_name': stock.stock_name,
'sector': stock.sector,
'correlation': float(stock.correlation) if stock.correlation else 0
} for stock in event_stocks[:5]] # 限制返回5个
event_dict['related_stocks'] = []
events_data.append(event_dict)
@@ -2860,7 +2597,7 @@ def api_get_events():
applied_filters['search_query'] = search_query
applied_filters['search_type'] = search_type
# ==================== 返回结果(保持完全兼容) ====================
# ==================== 返回结果(保持完全兼容,统计数据置空 ====================
return jsonify({
'success': True,
@@ -2885,12 +2622,30 @@ def api_get_events():
'order': order
}
},
# 整体股票涨跌幅分布统计
# 整体股票涨跌幅分布统计(置空)
'overall_stats': {
'total_stocks': len(all_stocks_for_stats) if 'all_stocks_for_stats' in locals() else 0,
'change_distribution': overall_distribution,
'total_stocks': 0,
'change_distribution': {
'limit_down': 0,
'down_over_5': 0,
'down_5_to_1': 0,
'down_within_1': 0,
'flat': 0,
'up_within_1': 0,
'up_1_to_5': 0,
'up_over_5': 0,
'limit_up': 0
},
'change_distribution_percentages': {
k: v for k, v in overall_distribution.items()
'limit_down': 0,
'down_over_5': 0,
'down_5_to_1': 0,
'down_within_1': 0,
'flat': 0,
'up_within_1': 0,
'up_1_to_5': 0,
'up_over_5': 0,
'limit_up': 0
}
}
}
@@ -2964,13 +2719,12 @@ def get_calendar_event_counts():
# 修改查询以仅统计type为event的事件数量
query = """
SELECT DATE (calendar_time) as date, COUNT (*) as count
FROM future_events
WHERE calendar_time BETWEEN :start_date \
AND :end_date
AND type = 'event'
GROUP BY DATE (calendar_time) \
"""
SELECT DATE(calendar_time) as date, COUNT(*) as count
FROM future_events
WHERE calendar_time BETWEEN :start_date AND :end_date
AND type = 'event'
GROUP BY DATE(calendar_time)
"""
result = db.session.execute(text(query), {
'start_date': start_date,
@@ -2987,7 +2741,8 @@ def get_calendar_event_counts():
return jsonify(events)
except Exception as e:
return jsonify({'error': str(e)}), 500
app.logger.error(f"获取日历事件统计出错: {str(e)}", exc_info=True)
return jsonify({'error': str(e), 'error_type': type(e).__name__}), 500
def get_full_avatar_url(avatar_url):

6352
app_vx_copy1.py Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -55,19 +55,40 @@ HTTP_CLIENT = httpx.AsyncClient(timeout=60.0)
# ==================== Agent系统配置 ====================
# Kimi 配置 - 用于计划制定和深度推理
KIMI_CONFIG = {
"api_key": "sk-TzB4VYJfCoXGcGrGMiewukVRzjuDsbVCkaZXi2LvkS8s60E5",
"base_url": "https://api.moonshot.cn/v1",
"model": "kimi-k2-thinking", # 思考模型
# ==================== 多模型配置 ====================
# 模型配置字典(支持动态切换)
MODEL_CONFIGS = {
"kimi-k2": {
"api_key": "sk-TzB4VYJfCoXGcGrGMiewukVRzjuDsbVCkaZXi2LvkS8s60E5",
"base_url": "https://api.moonshot.cn/v1",
"model": "moonshot-v1-8k", # 快速模型
},
"kimi-k2-thinking": {
"api_key": "sk-TzB4VYJfCoXGcGrGMiewukVRzjuDsbVCkaZXi2LvkS8s60E5",
"base_url": "https://api.moonshot.cn/v1",
"model": "kimi-k2-thinking", # 深度思考模型
},
"glm-4.6": {
"api_key": "", # 需要配置智谱AI密钥
"base_url": "https://open.bigmodel.cn/api/paas/v4",
"model": "glm-4",
},
"deepmoney": {
"api_key": "", # 空值
"base_url": "http://111.62.35.50:8000/v1",
"model": "deepmoney",
},
"gemini-3": {
"api_key": "", # 需要配置Google API密钥
"base_url": "https://generativelanguage.googleapis.com/v1",
"model": "gemini-pro",
},
}
# DeepMoney 配置 - 用于新闻总结
DEEPMONEY_CONFIG = {
"api_key": "", # 空值
"base_url": "http://111.62.35.50:8000/v1",
"model": "deepmoney",
}
# 保持向后兼容的配置(默认使用 kimi-k2-thinking
KIMI_CONFIG = MODEL_CONFIGS["kimi-k2-thinking"]
DEEPMONEY_CONFIG = MODEL_CONFIGS["deepmoney"]
# ==================== MCP协议数据模型 ====================
@@ -143,6 +164,8 @@ class AgentChatRequest(BaseModel):
user_avatar: Optional[str] = None # 用户头像URL
subscription_type: Optional[str] = None # 用户订阅类型free/pro/max
session_id: Optional[str] = None # 会话ID如果为空则创建新会话
model: Optional[str] = "kimi-k2-thinking" # 选择的模型kimi-k2, kimi-k2-thinking, glm-4.6, deepmoney, gemini-3
tools: Optional[List[str]] = None # 选择的工具列表工具名称数组如果为None则使用全部工具
# ==================== MCP工具定义 ====================
@@ -1579,6 +1602,7 @@ class MCPAgentIntegrated:
user_nickname: str = None,
user_avatar: str = None,
cookies: dict = None,
model_config: dict = None, # 新增:动态模型配置
) -> AsyncGenerator[str, None]:
"""主流程(流式输出)- 逐步返回执行结果"""
logger.info(f"[Agent Stream] 处理查询: {user_query}")
@@ -1586,11 +1610,24 @@ class MCPAgentIntegrated:
# 将 cookies 存储为实例属性,供工具调用时使用
self.cookies = cookies or {}
# 如果传入了自定义模型配置,使用自定义配置,否则使用默认的 Kimi
if model_config:
planning_client = OpenAI(
api_key=model_config["api_key"],
base_url=model_config["base_url"],
)
planning_model = model_config["model"]
logger.info(f"[Agent Stream] 使用自定义模型: {planning_model}")
else:
planning_client = self.kimi_client
planning_model = self.kimi_model
logger.info(f"[Agent Stream] 使用默认模型: {planning_model}")
try:
# 发送开始事件
yield self._format_sse("status", {"stage": "start", "message": "开始处理查询"})
# 阶段1: Kimi 制定计划(流式,带 DeepMoney 备选)
# 阶段1: 使用选中的模型制定计划(流式,带 DeepMoney 备选)
yield self._format_sse("status", {"stage": "planning", "message": "正在制定执行计划..."})
messages = [
@@ -1603,9 +1640,9 @@ class MCPAgentIntegrated:
use_fallback = False
try:
# 尝试使用 Kimi 流式 API
stream = self.kimi_client.chat.completions.create(
model=self.kimi_model,
# 尝试使用选中的模型流式 API
stream = planning_client.chat.completions.create(
model=planning_model,
messages=messages,
temperature=1.0,
max_tokens=16000,
@@ -2165,11 +2202,12 @@ async def agent_chat_stream(chat_request: AgentChatRequest, request: Request):
except Exception as e:
logger.error(f"[ES] 保存用户消息失败: {e}")
# 获取工具列表
tools = [tool.dict() for tool in TOOLS]
# ==================== 动态工具过滤 ====================
# 获取所有可用工具
all_tools = [tool.dict() for tool in TOOLS]
# 添加特殊工具summarize_news
tools.append({
all_tools.append({
"name": "summarize_news",
"description": "使用 DeepMoney 模型总结新闻数据,提取关键信息",
"parameters": {
@@ -2188,6 +2226,21 @@ async def agent_chat_stream(chat_request: AgentChatRequest, request: Request):
}
})
# 如果用户指定了工具列表,则进行过滤
if chat_request.tools is not None and len(chat_request.tools) > 0:
selected_tool_names = set(chat_request.tools)
tools = [tool for tool in all_tools if tool["name"] in selected_tool_names]
logger.info(f"[工具过滤] 用户选择了 {len(tools)}/{len(all_tools)} 个工具: {selected_tool_names}")
else:
# 默认使用全部工具
tools = all_tools
logger.info(f"[工具过滤] 使用全部 {len(tools)} 个工具")
# ==================== 动态模型选择 ====================
selected_model = chat_request.model or "kimi-k2-thinking"
model_config = MODEL_CONFIGS.get(selected_model, MODEL_CONFIGS["kimi-k2-thinking"])
logger.info(f"[模型选择] 使用模型: {selected_model} ({model_config['model']})")
# 返回流式响应
return StreamingResponse(
agent.process_query_stream(
@@ -2199,6 +2252,7 @@ async def agent_chat_stream(chat_request: AgentChatRequest, request: Request):
user_nickname=chat_request.user_nickname,
user_avatar=chat_request.user_avatar,
cookies=cookies, # 传递 cookies 用于认证 API 调用
model_config=model_config, # 传递选中的模型配置
),
media_type="text/event-stream",
headers={

View File

@@ -23,8 +23,8 @@ import { FiTarget, FiCheckCircle, FiXCircle, FiClock, FiTool } from 'react-icons
* 执行计划卡片组件
*/
export const PlanCard = ({ plan, stepResults }) => {
const cardBg = useColorModeValue('blue.50', 'blue.900');
const borderColor = useColorModeValue('blue.200', 'blue.700');
const cardBg = useColorModeValue('blue.50', 'rgba(40, 45, 50, 0.8)');
const borderColor = useColorModeValue('blue.200', 'rgba(255, 215, 0, 0.3)');
const successColor = useColorModeValue('green.500', 'green.300');
const errorColor = useColorModeValue('red.500', 'red.300');
const pendingColor = useColorModeValue('gray.400', 'gray.500');
@@ -73,7 +73,7 @@ export const PlanCard = ({ plan, stepResults }) => {
<Icon as={FiTarget} color="blue.500" boxSize={5} />
<Text fontWeight="bold" fontSize="md">执行目标</Text>
</HStack>
<Text fontSize="sm" color="gray.600" pl={7}>
<Text fontSize="sm" color={useColorModeValue('gray.600', '#9BA1A6')} pl={7}>
{plan.goal}
</Text>
@@ -83,7 +83,7 @@ export const PlanCard = ({ plan, stepResults }) => {
{plan.reasoning && (
<>
<Text fontSize="sm" fontWeight="bold">规划思路</Text>
<Text fontSize="sm" color="gray.600">
<Text fontSize="sm" color={useColorModeValue('gray.600', '#9BA1A6')}>
{plan.reasoning}
</Text>
<Divider />
@@ -106,7 +106,7 @@ export const PlanCard = ({ plan, stepResults }) => {
<HStack
key={index}
p={2}
bg={useColorModeValue('white', 'gray.700')}
bg={useColorModeValue('white', 'rgba(50, 55, 60, 0.6)')}
borderRadius="md"
borderWidth="1px"
borderColor={stepColor}
@@ -129,7 +129,7 @@ export const PlanCard = ({ plan, stepResults }) => {
status === 'failed' ? '✗ 失败' : '⏳ 等待'}
</Badge>
</HStack>
<Text fontSize="xs" color="gray.600">
<Text fontSize="xs" color={useColorModeValue('gray.600', '#9BA1A6')}>
{step.reason}
</Text>
</VStack>

View File

@@ -23,8 +23,8 @@ import { FiChevronDown, FiChevronUp, FiCheckCircle, FiXCircle, FiClock, FiDataba
export const StepResultCard = ({ stepResult }) => {
const [isExpanded, setIsExpanded] = useState(false);
const cardBg = useColorModeValue('white', 'gray.700');
const borderColor = useColorModeValue('gray.200', 'gray.600');
const cardBg = useColorModeValue('white', 'rgba(40, 45, 50, 0.8)');
const borderColor = useColorModeValue('gray.200', 'rgba(255, 215, 0, 0.2)');
const successColor = useColorModeValue('green.500', 'green.300');
const errorColor = useColorModeValue('red.500', 'red.300');
@@ -80,7 +80,7 @@ export const StepResultCard = ({ stepResult }) => {
justify="space-between"
cursor="pointer"
onClick={() => setIsExpanded(!isExpanded)}
_hover={{ bg: useColorModeValue('gray.50', 'gray.600') }}
_hover={{ bg: useColorModeValue('gray.50', 'rgba(50, 55, 60, 0.7)') }}
>
<HStack flex={1}>
<Icon as={StatusIcon} color={`${statusColorScheme}.500`} boxSize={5} />
@@ -94,7 +94,7 @@ export const StepResultCard = ({ stepResult }) => {
stepResult.status === 'failed' ? '失败' : '执行中'}
</Badge>
</HStack>
<Text fontSize="xs" color="gray.500">
<Text fontSize="xs" color={useColorModeValue('gray.500', '#9BA1A6')}>
耗时: {stepResult.execution_time?.toFixed(2)}s
</Text>
</VStack>
@@ -140,7 +140,7 @@ export const StepResultCard = ({ stepResult }) => {
maxH="300px"
overflowY="auto"
p={2}
bg={useColorModeValue('gray.50', 'gray.800')}
bg={useColorModeValue('gray.50', 'rgba(25, 28, 32, 0.6)')}
borderRadius="md"
fontSize="xs"
>
@@ -155,7 +155,7 @@ export const StepResultCard = ({ stepResult }) => {
</Code>
))}
{stepResult.result.length > 3 && (
<Text fontSize="xs" color="gray.500">
<Text fontSize="xs" color={useColorModeValue('gray.500', '#9BA1A6')}>
...还有 {stepResult.result.length - 3} 条记录
</Text>
)}
@@ -172,7 +172,7 @@ export const StepResultCard = ({ stepResult }) => {
{stepResult.status === 'failed' && stepResult.error && (
<VStack align="stretch" spacing={2}>
<Text fontSize="xs" fontWeight="bold" color="red.500">错误信息:</Text>
<Text fontSize="xs" color="red.600" p={2} bg="red.50" borderRadius="md">
<Text fontSize="xs" color={useColorModeValue('red.600', 'red.300')} p={2} bg={useColorModeValue('red.50', 'rgba(220, 38, 38, 0.15)')} borderRadius="md">
{stepResult.error}
</Text>
</VStack>

View File

@@ -27,6 +27,11 @@ export const secondaryNavConfig = {
path: '/concepts',
label: '概念中心',
badges: [{ text: 'NEW', colorScheme: 'red' }]
},
{
path: '/data-browser',
label: '数据浏览器',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},
@@ -45,6 +50,34 @@ export const secondaryNavConfig = {
path: '/concepts',
label: '概念中心',
badges: [{ text: 'NEW', colorScheme: 'red' }]
},
{
path: '/data-browser',
label: '数据浏览器',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},
'/data-browser': {
title: '高频跟踪',
items: [
{
path: '/community',
label: '事件中心',
badges: [
{ text: 'HOT', colorScheme: 'green' },
{ text: 'NEW', colorScheme: 'red' }
]
},
{
path: '/concepts',
label: '概念中心',
badges: [{ text: 'NEW', colorScheme: 'red' }]
},
{
path: '/data-browser',
label: '数据浏览器',
badges: [{ text: 'NEW', colorScheme: 'red' }]
}
]
},

View File

@@ -102,17 +102,6 @@ export const homeRoutes = [
}
},
// 数据浏览器 - /home/data-browser
{
path: 'data-browser',
component: lazyComponents.DataBrowser,
protection: PROTECTION_MODES.MODAL,
meta: {
title: '数据浏览器',
description: '化工商品数据分类树浏览器'
}
},
// 回退路由 - 匹配任何未定义的 /home/* 路径
{
path: '*',

View File

@@ -53,6 +53,16 @@ export const routeConfig = [
description: '热门概念追踪'
}
},
{
path: 'data-browser',
component: lazyComponents.DataBrowser,
protection: PROTECTION_MODES.MODAL,
layout: 'main',
meta: {
title: '数据浏览器',
description: '化工商品数据分类树浏览器'
}
},
{
path: 'stocks',
component: lazyComponents.StockOverview,

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff