更新ios

This commit is contained in:
2026-01-18 18:43:48 +08:00
parent bf0ec415d5
commit 2f2f0c6b63
5 changed files with 263 additions and 46 deletions

View File

@@ -16,6 +16,7 @@ import {
MinuteChart,
KlineChart,
OrderBook,
StockInfoPanel,
RelatedInfoTabs,
EventsPanel,
ConceptsPanel,
@@ -264,7 +265,7 @@ const StockDetailScreen = () => {
return (
<Box flex={1} bg="#0A0A0F">
<SafeAreaView style={styles.container} edges={['top']}>
{/* 价格头部 - Wind 风格 */}
{/* 价格头部 - Wind 风格(固定在顶部) */}
<PriceHeader
stock={{ stock_code: stockCode, stock_name: displayStockName }}
quote={quote}
@@ -273,6 +274,12 @@ const StockDetailScreen = () => {
onBack={handleBack}
/>
{/* 可滚动内容区域 */}
<ScrollView
flex={1}
showsVerticalScrollIndicator={false}
contentContainerStyle={styles.scrollContent}
>
{/* 图表类型切换 */}
<ChartTypeTabs
activeType={chartType}
@@ -311,13 +318,20 @@ const StockDetailScreen = () => {
/>
)}
{/* 股票详情信息面板 */}
<StockInfoPanel
data={currentStock}
loading={loading.detail}
/>
{/* 相关信息 Tab */}
<RelatedInfoTabs activeTab={infoTab} onChange={setInfoTab} />
{/* 相关信息内容 */}
<Box flex={1}>
<Box minH={300} pb={4}>
{renderInfoContent()}
</Box>
</ScrollView>
{/* 涨幅分析弹窗 */}
<RiseAnalysisModal
@@ -334,6 +348,9 @@ const styles = StyleSheet.create({
container: {
flex: 1,
},
scrollContent: {
flexGrow: 1,
},
});
export default StockDetailScreen;

View File

@@ -0,0 +1,159 @@
/**
* 股票详情信息面板
* 展示成交量、成交额、振幅、换手率、市盈率、市值等详细数据
*/
import React, { memo, useMemo } from 'react';
import { Box, HStack, VStack, Text } from 'native-base';
// 格式化大数字(带单位)
const formatLargeNumber = (num, unit = '') => {
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' }) => (
<VStack alignItems="center" flex={1} py={2}>
<Text color="gray.500" fontSize={10} mb={1}>
{label}
</Text>
<Text color={color} fontSize={12} fontWeight="medium">
{value}
</Text>
</VStack>
));
/**
* 股票详情信息面板
*/
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 (
<Box
mx={4}
mt={3}
p={4}
bg="rgba(30, 41, 59, 0.6)"
borderRadius={16}
borderWidth={1}
borderColor="rgba(255,255,255,0.08)"
>
<Text color="gray.500" textAlign="center">加载中...</Text>
</Box>
);
}
if (!data) {
return null;
}
return (
<Box
mx={4}
mt={3}
bg="rgba(30, 41, 59, 0.6)"
borderRadius={16}
borderWidth={1}
borderColor="rgba(255,255,255,0.08)"
overflow="hidden"
>
{/* 标题栏 */}
<HStack px={4} py={2.5} alignItems="center" justifyContent="space-between">
<Text color="white" fontSize={14} fontWeight="bold">
行情数据
</Text>
{data.update_time && (
<Text color="gray.600" fontSize={10}>
更新于 {data.update_time}
</Text>
)}
</HStack>
{/* 数据行 */}
<Box px={2} pb={2}>
{infoRows.map((row, rowIndex) => (
<HStack
key={rowIndex}
borderTopWidth={rowIndex > 0 ? 1 : 0}
borderTopColor="rgba(255,255,255,0.06)"
>
{row.map((item, itemIndex) => (
<InfoItem
key={itemIndex}
label={item.label}
value={item.value}
color={item.color || 'white'}
/>
))}
</HStack>
))}
</Box>
</Box>
);
});
StockInfoPanel.displayName = 'StockInfoPanel';
export default StockInfoPanel;

View File

@@ -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';

View File

@@ -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,

37
app.py
View File

@@ -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: