diff --git a/MeAgent/src/screens/StockDetail/StockDetailScreen.js b/MeAgent/src/screens/StockDetail/StockDetailScreen.js
index 40954836..1df9d18e 100644
--- a/MeAgent/src/screens/StockDetail/StockDetailScreen.js
+++ b/MeAgent/src/screens/StockDetail/StockDetailScreen.js
@@ -16,6 +16,7 @@ import {
MinuteChart,
KlineChart,
OrderBook,
+ StockInfoPanel,
RelatedInfoTabs,
EventsPanel,
ConceptsPanel,
@@ -264,7 +265,7 @@ const StockDetailScreen = () => {
return (
- {/* 价格头部 - Wind 风格 */}
+ {/* 价格头部 - Wind 风格(固定在顶部) */}
{
onBack={handleBack}
/>
- {/* 图表类型切换 */}
-
+ {/* 可滚动内容区域 */}
+
+ {/* 图表类型切换 */}
+
- {/* 图表区域 */}
-
- {chartType === 'minute' ? (
-
- ) : (
-
+ {chartType === 'minute' ? (
+
+ ) : (
+
+ )}
+
+
+ {/* 5档盘口 - 优先 WebSocket 实时数据,降级到 API 数据 */}
+ {chartType === 'minute' && (
+
)}
-
- {/* 5档盘口 - 优先 WebSocket 实时数据,降级到 API 数据 */}
- {chartType === 'minute' && (
-
- )}
- {/* 相关信息 Tab */}
-
+ {/* 相关信息 Tab */}
+
- {/* 相关信息内容 */}
-
- {renderInfoContent()}
-
+ {/* 相关信息内容 */}
+
+ {renderInfoContent()}
+
+
{/* 涨幅分析弹窗 */}
{
+ if (!num || num === 0) return '--';
+ if (num >= 100000000) {
+ return `${(num / 100000000).toFixed(2)}亿${unit}`;
+ }
+ if (num >= 10000) {
+ return `${(num / 10000).toFixed(2)}万${unit}`;
+ }
+ return `${num.toFixed(2)}${unit}`;
+};
+
+// 格式化百分比
+const formatPercent = (num) => {
+ if (num === null || num === undefined) return '--';
+ return `${Number(num).toFixed(2)}%`;
+};
+
+// 格式化价格
+const formatPrice = (num) => {
+ if (num === null || num === undefined || num === 0) return '--';
+ return Number(num).toFixed(2);
+};
+
+/**
+ * 单个信息项
+ */
+const InfoItem = memo(({ label, value, color = 'white' }) => (
+
+
+ {label}
+
+
+ {value}
+
+
+));
+
+/**
+ * 股票详情信息面板
+ */
+const StockInfoPanel = memo(({ data, loading }) => {
+ // 处理数据
+ const infoRows = useMemo(() => {
+ if (!data) return [];
+
+ return [
+ // 第一行:成交量、成交额、振幅
+ [
+ { label: '成交量', value: formatLargeNumber(data.volume, '股') },
+ { label: '成交额', value: formatLargeNumber(data.amount, '') },
+ { label: '振幅', value: formatPercent(data.amplitude) },
+ ],
+ // 第二行:换手率、市盈率、总市值
+ [
+ { label: '换手率', value: formatPercent(data.turnover_rate) },
+ { label: '市盈率', value: data.pe ? formatPrice(data.pe) : '--' },
+ { label: '总市值', value: data.total_mv ? `${data.total_mv}亿` : '--' },
+ ],
+ // 第三行:流通市值、总股本、流通股
+ [
+ { label: '流通市值', value: data.circ_mv ? `${data.circ_mv}亿` : '--' },
+ { label: '总股本', value: formatLargeNumber(data.total_shares, '股') },
+ { label: '流通股', value: formatLargeNumber(data.float_shares, '股') },
+ ],
+ // 第四行:涨跌额、总笔数、52周高/低
+ [
+ {
+ label: '涨跌额',
+ value: data.change_amount ? formatPrice(data.change_amount) : '--',
+ color: data.change_amount > 0 ? '#EF4444' : data.change_amount < 0 ? '#22C55E' : 'white',
+ },
+ { label: '总笔数', value: data.total_trades ? `${data.total_trades}` : '--' },
+ {
+ label: '52周高/低',
+ value: data.week52_high && data.week52_low
+ ? `${formatPrice(data.week52_high)}/${formatPrice(data.week52_low)}`
+ : '--',
+ },
+ ],
+ ];
+ }, [data]);
+
+ if (loading) {
+ return (
+
+ 加载中...
+
+ );
+ }
+
+ if (!data) {
+ return null;
+ }
+
+ return (
+
+ {/* 标题栏 */}
+
+
+ 行情数据
+
+ {data.update_time && (
+
+ 更新于 {data.update_time}
+
+ )}
+
+
+ {/* 数据行 */}
+
+ {infoRows.map((row, rowIndex) => (
+ 0 ? 1 : 0}
+ borderTopColor="rgba(255,255,255,0.06)"
+ >
+ {row.map((item, itemIndex) => (
+
+ ))}
+
+ ))}
+
+
+ );
+});
+
+StockInfoPanel.displayName = 'StockInfoPanel';
+
+export default StockInfoPanel;
diff --git a/MeAgent/src/screens/StockDetail/components/index.js b/MeAgent/src/screens/StockDetail/components/index.js
index 375d5206..5d2b637c 100644
--- a/MeAgent/src/screens/StockDetail/components/index.js
+++ b/MeAgent/src/screens/StockDetail/components/index.js
@@ -14,3 +14,4 @@ export { default as ConceptsPanel } from './ConceptsPanel';
export { default as AnnouncementsPanel } from './AnnouncementsPanel';
export { default as RiseAnalysisModal } from './RiseAnalysisModal';
export { default as RiseAnalysisPanel } from './RiseAnalysisPanel';
+export { default as StockInfoPanel } from './StockInfoPanel';
diff --git a/MeAgent/src/services/stockService.js b/MeAgent/src/services/stockService.js
index 0e69213b..b5dabea4 100644
--- a/MeAgent/src/services/stockService.js
+++ b/MeAgent/src/services/stockService.js
@@ -125,15 +125,24 @@ export const stockDetailService = {
high: quote.today_high || 0,
low: quote.today_low || 0,
// 涨跌
- change_amount: (quote.current_price - quote.yesterday_close) || 0,
+ change_amount: quote.change_amount || (quote.current_price - quote.yesterday_close) || 0,
change_percent: quote.change_percent || 0,
- // 成交量/成交额(从 market/trade 获取)
- volume: latestTrade?.volume || 0,
- amount: latestTrade?.amount || 0,
+ // 成交量/成交额(优先用 API 返回的,否则用 market/trade)
+ volume: quote.volume || latestTrade?.volume || 0,
+ amount: quote.amount || latestTrade?.amount || 0,
+ // 新增:总笔数、振幅
+ total_trades: quote.total_trades || 0,
+ amplitude: quote.amplitude || 0,
// 换手率/市盈率/市值
turnover_rate: quote.turnover_rate || latestTrade?.turnover_rate || 0,
pe_ratio: quote.pe || 0,
+ pe: quote.pe || 0,
market_cap: quote.market_cap || quote.circ_mv || 0,
+ circ_mv: quote.circ_mv || 0,
+ total_mv: quote.total_mv || 0,
+ // 股本信息
+ total_shares: quote.total_shares || 0,
+ float_shares: quote.float_shares || 0,
// 52周高低
week52_high: quote.week52_high || 0,
week52_low: quote.week52_low || 0,
diff --git a/app.py b/app.py
index 1c134fcc..af51e43b 100755
--- a/app.py
+++ b/app.py
@@ -9929,18 +9929,28 @@ def get_stock_quote_detail(stock_code):
# 价格信息
'current_price': None,
'change_percent': None,
+ 'change_amount': None, # 涨跌额(元)
'today_open': None,
'yesterday_close': None,
'today_high': None,
'today_low': None,
+ # 成交信息
+ 'volume': None, # 成交量(股)
+ 'amount': None, # 成交额(元)
+ 'total_trades': None, # 总笔数
+ 'amplitude': None, # 振幅(%)
+
# 关键指标
'pe': None,
'pb': None,
'eps': None,
'market_cap': None,
'circ_mv': None,
+ 'total_mv': None, # 总市值(亿元)
'turnover_rate': None,
+ 'total_shares': None, # 总股本(股)
+ 'float_shares': None, # 流通股本(股)
'week52_high': None,
'week52_low': None,
@@ -9953,21 +9963,25 @@ def get_stock_quote_detail(stock_code):
}
with engine.connect() as conn:
- # 1. 获取最新交易数据(来自 ea_trade)
+ # 1. 获取最新交易数据(来自 ea_trade,包含完整字段)
trade_query = text("""
SELECT
t.SECCODE,
t.SECNAME,
t.TRADEDATE,
+ t.F001V as exchange,
t.F002N as pre_close,
t.F003N as open_price,
t.F004N as volume,
t.F005N as high,
t.F006N as low,
t.F007N as close_price,
+ t.F008N as total_trades,
+ t.F009N as change_amount,
t.F010N as change_pct,
t.F011N as amount,
t.F012N as turnover_rate,
+ t.F013N as amplitude,
t.F020N as total_shares,
t.F021N as float_shares,
t.F026N as pe_ratio,
@@ -9988,10 +10002,19 @@ def get_stock_quote_detail(stock_code):
result_data['name'] = row.get('SECNAME') or ''
result_data['current_price'] = float(row.get('close_price') or 0)
result_data['change_percent'] = float(row.get('change_pct') or 0)
+ result_data['change_amount'] = float(row.get('change_amount') or 0)
result_data['today_open'] = float(row.get('open_price') or 0)
result_data['yesterday_close'] = float(row.get('pre_close') or 0)
result_data['today_high'] = float(row.get('high') or 0)
result_data['today_low'] = float(row.get('low') or 0)
+
+ # 成交信息
+ result_data['volume'] = float(row.get('volume') or 0)
+ result_data['amount'] = float(row.get('amount') or 0)
+ result_data['total_trades'] = int(row.get('total_trades') or 0) if row.get('total_trades') else None
+ result_data['amplitude'] = float(row.get('amplitude') or 0)
+
+ # 关键指标
result_data['pe'] = float(row.get('pe_ratio') or 0) if row.get('pe_ratio') else None
result_data['turnover_rate'] = float(row.get('turnover_rate') or 0)
result_data['sw_industry_l1'] = row.get('sw_industry_l1') or ''
@@ -9999,13 +10022,21 @@ def get_stock_quote_detail(stock_code):
result_data['industry_l1'] = row.get('industry_l1') or ''
result_data['industry'] = row.get('sw_industry_l2') or row.get('sw_industry_l1') or ''
- # 计算流通市值(亿元)
+ # 股本信息
+ total_shares = float(row.get('total_shares') or 0)
float_shares = float(row.get('float_shares') or 0)
close_price = float(row.get('close_price') or 0)
+ result_data['total_shares'] = total_shares
+ result_data['float_shares'] = float_shares
+
+ # 计算市值(亿元)
if float_shares > 0 and close_price > 0:
- circ_mv = (float_shares * close_price) / 100000000 # 转为亿
+ circ_mv = (float_shares * close_price) / 100000000 # 流通市值(亿)
result_data['circ_mv'] = round(circ_mv, 2)
result_data['market_cap'] = f"{round(circ_mv, 2)}亿"
+ if total_shares > 0 and close_price > 0:
+ total_mv = (total_shares * close_price) / 100000000 # 总市值(亿)
+ result_data['total_mv'] = round(total_mv, 2)
trade_date = row.get('TRADEDATE')
if trade_date: