更新ios
This commit is contained in:
@@ -16,6 +16,7 @@ import {
|
|||||||
MinuteChart,
|
MinuteChart,
|
||||||
KlineChart,
|
KlineChart,
|
||||||
OrderBook,
|
OrderBook,
|
||||||
|
StockInfoPanel,
|
||||||
RelatedInfoTabs,
|
RelatedInfoTabs,
|
||||||
EventsPanel,
|
EventsPanel,
|
||||||
ConceptsPanel,
|
ConceptsPanel,
|
||||||
@@ -264,7 +265,7 @@ const StockDetailScreen = () => {
|
|||||||
return (
|
return (
|
||||||
<Box flex={1} bg="#0A0A0F">
|
<Box flex={1} bg="#0A0A0F">
|
||||||
<SafeAreaView style={styles.container} edges={['top']}>
|
<SafeAreaView style={styles.container} edges={['top']}>
|
||||||
{/* 价格头部 - Wind 风格 */}
|
{/* 价格头部 - Wind 风格(固定在顶部) */}
|
||||||
<PriceHeader
|
<PriceHeader
|
||||||
stock={{ stock_code: stockCode, stock_name: displayStockName }}
|
stock={{ stock_code: stockCode, stock_name: displayStockName }}
|
||||||
quote={quote}
|
quote={quote}
|
||||||
@@ -273,6 +274,12 @@ const StockDetailScreen = () => {
|
|||||||
onBack={handleBack}
|
onBack={handleBack}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* 可滚动内容区域 */}
|
||||||
|
<ScrollView
|
||||||
|
flex={1}
|
||||||
|
showsVerticalScrollIndicator={false}
|
||||||
|
contentContainerStyle={styles.scrollContent}
|
||||||
|
>
|
||||||
{/* 图表类型切换 */}
|
{/* 图表类型切换 */}
|
||||||
<ChartTypeTabs
|
<ChartTypeTabs
|
||||||
activeType={chartType}
|
activeType={chartType}
|
||||||
@@ -311,13 +318,20 @@ const StockDetailScreen = () => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
{/* 股票详情信息面板 */}
|
||||||
|
<StockInfoPanel
|
||||||
|
data={currentStock}
|
||||||
|
loading={loading.detail}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* 相关信息 Tab */}
|
{/* 相关信息 Tab */}
|
||||||
<RelatedInfoTabs activeTab={infoTab} onChange={setInfoTab} />
|
<RelatedInfoTabs activeTab={infoTab} onChange={setInfoTab} />
|
||||||
|
|
||||||
{/* 相关信息内容 */}
|
{/* 相关信息内容 */}
|
||||||
<Box flex={1}>
|
<Box minH={300} pb={4}>
|
||||||
{renderInfoContent()}
|
{renderInfoContent()}
|
||||||
</Box>
|
</Box>
|
||||||
|
</ScrollView>
|
||||||
|
|
||||||
{/* 涨幅分析弹窗 */}
|
{/* 涨幅分析弹窗 */}
|
||||||
<RiseAnalysisModal
|
<RiseAnalysisModal
|
||||||
@@ -334,6 +348,9 @@ const styles = StyleSheet.create({
|
|||||||
container: {
|
container: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
},
|
},
|
||||||
|
scrollContent: {
|
||||||
|
flexGrow: 1,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
export default StockDetailScreen;
|
export default StockDetailScreen;
|
||||||
|
|||||||
159
MeAgent/src/screens/StockDetail/components/StockInfoPanel.js
Normal file
159
MeAgent/src/screens/StockDetail/components/StockInfoPanel.js
Normal 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;
|
||||||
@@ -14,3 +14,4 @@ export { default as ConceptsPanel } from './ConceptsPanel';
|
|||||||
export { default as AnnouncementsPanel } from './AnnouncementsPanel';
|
export { default as AnnouncementsPanel } from './AnnouncementsPanel';
|
||||||
export { default as RiseAnalysisModal } from './RiseAnalysisModal';
|
export { default as RiseAnalysisModal } from './RiseAnalysisModal';
|
||||||
export { default as RiseAnalysisPanel } from './RiseAnalysisPanel';
|
export { default as RiseAnalysisPanel } from './RiseAnalysisPanel';
|
||||||
|
export { default as StockInfoPanel } from './StockInfoPanel';
|
||||||
|
|||||||
@@ -125,15 +125,24 @@ export const stockDetailService = {
|
|||||||
high: quote.today_high || 0,
|
high: quote.today_high || 0,
|
||||||
low: quote.today_low || 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,
|
change_percent: quote.change_percent || 0,
|
||||||
// 成交量/成交额(从 market/trade 获取)
|
// 成交量/成交额(优先用 API 返回的,否则用 market/trade)
|
||||||
volume: latestTrade?.volume || 0,
|
volume: quote.volume || latestTrade?.volume || 0,
|
||||||
amount: latestTrade?.amount || 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,
|
turnover_rate: quote.turnover_rate || latestTrade?.turnover_rate || 0,
|
||||||
pe_ratio: quote.pe || 0,
|
pe_ratio: quote.pe || 0,
|
||||||
|
pe: quote.pe || 0,
|
||||||
market_cap: quote.market_cap || quote.circ_mv || 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周高低
|
// 52周高低
|
||||||
week52_high: quote.week52_high || 0,
|
week52_high: quote.week52_high || 0,
|
||||||
week52_low: quote.week52_low || 0,
|
week52_low: quote.week52_low || 0,
|
||||||
|
|||||||
37
app.py
37
app.py
@@ -9929,18 +9929,28 @@ def get_stock_quote_detail(stock_code):
|
|||||||
# 价格信息
|
# 价格信息
|
||||||
'current_price': None,
|
'current_price': None,
|
||||||
'change_percent': None,
|
'change_percent': None,
|
||||||
|
'change_amount': None, # 涨跌额(元)
|
||||||
'today_open': None,
|
'today_open': None,
|
||||||
'yesterday_close': None,
|
'yesterday_close': None,
|
||||||
'today_high': None,
|
'today_high': None,
|
||||||
'today_low': None,
|
'today_low': None,
|
||||||
|
|
||||||
|
# 成交信息
|
||||||
|
'volume': None, # 成交量(股)
|
||||||
|
'amount': None, # 成交额(元)
|
||||||
|
'total_trades': None, # 总笔数
|
||||||
|
'amplitude': None, # 振幅(%)
|
||||||
|
|
||||||
# 关键指标
|
# 关键指标
|
||||||
'pe': None,
|
'pe': None,
|
||||||
'pb': None,
|
'pb': None,
|
||||||
'eps': None,
|
'eps': None,
|
||||||
'market_cap': None,
|
'market_cap': None,
|
||||||
'circ_mv': None,
|
'circ_mv': None,
|
||||||
|
'total_mv': None, # 总市值(亿元)
|
||||||
'turnover_rate': None,
|
'turnover_rate': None,
|
||||||
|
'total_shares': None, # 总股本(股)
|
||||||
|
'float_shares': None, # 流通股本(股)
|
||||||
'week52_high': None,
|
'week52_high': None,
|
||||||
'week52_low': None,
|
'week52_low': None,
|
||||||
|
|
||||||
@@ -9953,21 +9963,25 @@ def get_stock_quote_detail(stock_code):
|
|||||||
}
|
}
|
||||||
|
|
||||||
with engine.connect() as conn:
|
with engine.connect() as conn:
|
||||||
# 1. 获取最新交易数据(来自 ea_trade)
|
# 1. 获取最新交易数据(来自 ea_trade,包含完整字段)
|
||||||
trade_query = text("""
|
trade_query = text("""
|
||||||
SELECT
|
SELECT
|
||||||
t.SECCODE,
|
t.SECCODE,
|
||||||
t.SECNAME,
|
t.SECNAME,
|
||||||
t.TRADEDATE,
|
t.TRADEDATE,
|
||||||
|
t.F001V as exchange,
|
||||||
t.F002N as pre_close,
|
t.F002N as pre_close,
|
||||||
t.F003N as open_price,
|
t.F003N as open_price,
|
||||||
t.F004N as volume,
|
t.F004N as volume,
|
||||||
t.F005N as high,
|
t.F005N as high,
|
||||||
t.F006N as low,
|
t.F006N as low,
|
||||||
t.F007N as close_price,
|
t.F007N as close_price,
|
||||||
|
t.F008N as total_trades,
|
||||||
|
t.F009N as change_amount,
|
||||||
t.F010N as change_pct,
|
t.F010N as change_pct,
|
||||||
t.F011N as amount,
|
t.F011N as amount,
|
||||||
t.F012N as turnover_rate,
|
t.F012N as turnover_rate,
|
||||||
|
t.F013N as amplitude,
|
||||||
t.F020N as total_shares,
|
t.F020N as total_shares,
|
||||||
t.F021N as float_shares,
|
t.F021N as float_shares,
|
||||||
t.F026N as pe_ratio,
|
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['name'] = row.get('SECNAME') or ''
|
||||||
result_data['current_price'] = float(row.get('close_price') or 0)
|
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_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['today_open'] = float(row.get('open_price') or 0)
|
||||||
result_data['yesterday_close'] = float(row.get('pre_close') 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_high'] = float(row.get('high') or 0)
|
||||||
result_data['today_low'] = float(row.get('low') 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['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['turnover_rate'] = float(row.get('turnover_rate') or 0)
|
||||||
result_data['sw_industry_l1'] = row.get('sw_industry_l1') or ''
|
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_l1'] = row.get('industry_l1') or ''
|
||||||
result_data['industry'] = row.get('sw_industry_l2') or row.get('sw_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)
|
float_shares = float(row.get('float_shares') or 0)
|
||||||
close_price = float(row.get('close_price') 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:
|
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['circ_mv'] = round(circ_mv, 2)
|
||||||
result_data['market_cap'] = f"{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')
|
trade_date = row.get('TRADEDATE')
|
||||||
if trade_date:
|
if trade_date:
|
||||||
|
|||||||
Reference in New Issue
Block a user