update pay ui
This commit is contained in:
4
app.py
4
app.py
@@ -5601,6 +5601,8 @@ def get_historical_event_stocks(event_id):
|
|||||||
if event_trading_date:
|
if event_trading_date:
|
||||||
try:
|
try:
|
||||||
# 查询股票在事件对应交易日的数据
|
# 查询股票在事件对应交易日的数据
|
||||||
|
# ea_dailyline 表的 seccode 不带后缀,需要去掉 .SH/.SZ 后缀
|
||||||
|
base_stock_code = stock.stock_code.split('.')[0] if stock.stock_code else ''
|
||||||
with engine.connect() as conn:
|
with engine.connect() as conn:
|
||||||
query = text("""
|
query = text("""
|
||||||
SELECT close_price, change_pct
|
SELECT close_price, change_pct
|
||||||
@@ -5612,7 +5614,7 @@ def get_historical_event_stocks(event_id):
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
result = conn.execute(query, {
|
result = conn.execute(query, {
|
||||||
'stock_code': stock.stock_code,
|
'stock_code': base_stock_code,
|
||||||
'trading_date': event_trading_date
|
'trading_date': event_trading_date
|
||||||
}).fetchone()
|
}).fetchone()
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
import { createPortal } from 'react-dom';
|
import { createPortal } from 'react-dom';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import { stockService } from '@services/eventService';
|
import dayjs from 'dayjs';
|
||||||
|
import { klineDataCache, getCacheKey, fetchKlineData } from '@views/Community/components/StockDetailPanel/utils/klineDataCache';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 股票信息
|
* 股票信息
|
||||||
@@ -63,7 +64,7 @@ const KLineChartModal: React.FC<KLineChartModalProps> = ({
|
|||||||
error
|
error
|
||||||
});
|
});
|
||||||
|
|
||||||
// 加载K线数据
|
// 加载K线数据(优先使用缓存)
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
if (!stock?.stock_code) return;
|
if (!stock?.stock_code) return;
|
||||||
|
|
||||||
@@ -71,20 +72,30 @@ const KLineChartModal: React.FC<KLineChartModalProps> = ({
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await stockService.getKlineData(
|
// 标准化事件时间
|
||||||
stock.stock_code,
|
const stableEventTime = eventTime ? dayjs(eventTime).format('YYYY-MM-DD HH:mm') : '';
|
||||||
'daily',
|
|
||||||
eventTime || undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('[KLineChartModal] API响应:', response);
|
// 先检查缓存
|
||||||
|
const cacheKey = getCacheKey(stock.stock_code, stableEventTime, 'daily');
|
||||||
|
const cachedData = klineDataCache.get(cacheKey);
|
||||||
|
|
||||||
if (!response || !response.data || response.data.length === 0) {
|
if (cachedData && cachedData.length > 0) {
|
||||||
|
console.log('[KLineChartModal] 使用缓存数据, 数据条数:', cachedData.length);
|
||||||
|
setData(cachedData);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存没有则请求(会自动存入缓存)
|
||||||
|
console.log('[KLineChartModal] 缓存未命中,发起请求');
|
||||||
|
const result = await fetchKlineData(stock.stock_code, stableEventTime, 'daily');
|
||||||
|
|
||||||
|
if (!result || result.length === 0) {
|
||||||
throw new Error('暂无K线数据');
|
throw new Error('暂无K线数据');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[KLineChartModal] 数据条数:', response.data.length);
|
console.log('[KLineChartModal] 数据条数:', result.length);
|
||||||
setData(response.data);
|
setData(result);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const errorMsg = err instanceof Error ? err.message : '数据加载失败';
|
const errorMsg = err instanceof Error ? err.message : '数据加载失败';
|
||||||
setError(errorMsg);
|
setError(errorMsg);
|
||||||
|
|||||||
@@ -17,7 +17,8 @@ import {
|
|||||||
AlertIcon,
|
AlertIcon,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import * as echarts from 'echarts';
|
import * as echarts from 'echarts';
|
||||||
import { stockService } from '@services/eventService';
|
import dayjs from 'dayjs';
|
||||||
|
import { klineDataCache, getCacheKey, fetchKlineData } from '@views/Community/components/StockDetailPanel/utils/klineDataCache';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 股票信息
|
* 股票信息
|
||||||
@@ -67,7 +68,7 @@ const TimelineChartModal: React.FC<TimelineChartModalProps> = ({
|
|||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const [data, setData] = useState<TimelineDataPoint[]>([]);
|
const [data, setData] = useState<TimelineDataPoint[]>([]);
|
||||||
|
|
||||||
// 加载分时图数据
|
// 加载分时图数据(优先使用缓存)
|
||||||
const loadData = async () => {
|
const loadData = async () => {
|
||||||
if (!stock?.stock_code) return;
|
if (!stock?.stock_code) return;
|
||||||
|
|
||||||
@@ -75,20 +76,30 @@ const TimelineChartModal: React.FC<TimelineChartModalProps> = ({
|
|||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const response = await stockService.getKlineData(
|
// 标准化事件时间
|
||||||
stock.stock_code,
|
const stableEventTime = eventTime ? dayjs(eventTime).format('YYYY-MM-DD HH:mm') : '';
|
||||||
'timeline',
|
|
||||||
eventTime || undefined
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('[TimelineChartModal] API响应:', response);
|
// 先检查缓存
|
||||||
|
const cacheKey = getCacheKey(stock.stock_code, stableEventTime, 'timeline');
|
||||||
|
const cachedData = klineDataCache.get(cacheKey);
|
||||||
|
|
||||||
if (!response || !response.data || response.data.length === 0) {
|
if (cachedData && cachedData.length > 0) {
|
||||||
|
console.log('[TimelineChartModal] 使用缓存数据, 数据条数:', cachedData.length);
|
||||||
|
setData(cachedData);
|
||||||
|
setLoading(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缓存没有则请求(会自动存入缓存)
|
||||||
|
console.log('[TimelineChartModal] 缓存未命中,发起请求');
|
||||||
|
const result = await fetchKlineData(stock.stock_code, stableEventTime, 'timeline');
|
||||||
|
|
||||||
|
if (!result || result.length === 0) {
|
||||||
throw new Error('暂无分时数据');
|
throw new Error('暂无分时数据');
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('[TimelineChartModal] 数据条数:', response.data.length);
|
console.log('[TimelineChartModal] 数据条数:', result.length);
|
||||||
setData(response.data);
|
setData(result);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
const errorMsg = err instanceof Error ? err.message : '数据加载失败';
|
const errorMsg = err instanceof Error ? err.message : '数据加载失败';
|
||||||
setError(errorMsg);
|
setError(errorMsg);
|
||||||
|
|||||||
@@ -14,12 +14,9 @@ import {
|
|||||||
Center,
|
Center,
|
||||||
Wrap,
|
Wrap,
|
||||||
WrapItem,
|
WrapItem,
|
||||||
Icon,
|
|
||||||
Progress,
|
|
||||||
useColorModeValue,
|
useColorModeValue,
|
||||||
useToast,
|
useToast,
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
import { FaChartLine } from 'react-icons/fa';
|
|
||||||
import { getImportanceConfig } from '../../../../constants/importanceLevels';
|
import { getImportanceConfig } from '../../../../constants/importanceLevels';
|
||||||
import { eventService } from '../../../../services/eventService';
|
import { eventService } from '../../../../services/eventService';
|
||||||
import { useEventStocks } from '../StockDetailPanel/hooks/useEventStocks';
|
import { useEventStocks } from '../StockDetailPanel/hooks/useEventStocks';
|
||||||
@@ -338,76 +335,6 @@ const DynamicNewsDetailPanel = ({ event, showHeader = true }) => {
|
|||||||
{/* 事件描述 */}
|
{/* 事件描述 */}
|
||||||
<EventDescriptionSection description={event.description} />
|
<EventDescriptionSection description={event.description} />
|
||||||
|
|
||||||
{/* 超预期得分显示 - 参考历史事件对比的样式 */}
|
|
||||||
{expectationScore != null && expectationScore > 0 && (
|
|
||||||
<Box
|
|
||||||
p={3}
|
|
||||||
bg={useColorModeValue(
|
|
||||||
expectationScore >= 60 ? 'red.50' : expectationScore >= 40 ? 'orange.50' : 'yellow.50',
|
|
||||||
expectationScore >= 60 ? 'red.900' : expectationScore >= 40 ? 'orange.900' : 'yellow.900'
|
|
||||||
)}
|
|
||||||
borderColor={useColorModeValue(
|
|
||||||
expectationScore >= 60 ? 'red.200' : expectationScore >= 40 ? 'orange.200' : 'yellow.200',
|
|
||||||
expectationScore >= 60 ? 'red.700' : expectationScore >= 40 ? 'orange.700' : 'yellow.700'
|
|
||||||
)}
|
|
||||||
borderWidth="1px"
|
|
||||||
borderRadius="md"
|
|
||||||
>
|
|
||||||
<HStack spacing={3} justify="space-between">
|
|
||||||
<HStack spacing={2}>
|
|
||||||
<Icon
|
|
||||||
as={FaChartLine}
|
|
||||||
color={useColorModeValue(
|
|
||||||
expectationScore >= 60 ? 'red.600' : expectationScore >= 40 ? 'orange.600' : 'yellow.600',
|
|
||||||
expectationScore >= 60 ? 'red.300' : expectationScore >= 40 ? 'orange.300' : 'yellow.300'
|
|
||||||
)}
|
|
||||||
boxSize="18px"
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
fontSize="sm"
|
|
||||||
fontWeight="bold"
|
|
||||||
color={useColorModeValue(
|
|
||||||
expectationScore >= 60 ? 'red.700' : expectationScore >= 40 ? 'orange.700' : 'yellow.800',
|
|
||||||
expectationScore >= 60 ? 'red.200' : expectationScore >= 40 ? 'orange.200' : 'yellow.200'
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
超预期得分
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
<HStack spacing={3} flex={1} maxW="200px">
|
|
||||||
<Progress
|
|
||||||
value={expectationScore}
|
|
||||||
max={100}
|
|
||||||
size="sm"
|
|
||||||
flex={1}
|
|
||||||
colorScheme={expectationScore >= 60 ? 'red' : expectationScore >= 40 ? 'orange' : 'yellow'}
|
|
||||||
borderRadius="full"
|
|
||||||
bg={useColorModeValue('gray.200', 'gray.600')}
|
|
||||||
/>
|
|
||||||
<Text
|
|
||||||
fontSize="lg"
|
|
||||||
fontWeight="bold"
|
|
||||||
color={useColorModeValue(
|
|
||||||
expectationScore >= 60 ? 'red.600' : expectationScore >= 40 ? 'orange.600' : 'yellow.700',
|
|
||||||
expectationScore >= 60 ? 'red.300' : expectationScore >= 40 ? 'orange.300' : 'yellow.300'
|
|
||||||
)}
|
|
||||||
minW="40px"
|
|
||||||
textAlign="right"
|
|
||||||
>
|
|
||||||
{expectationScore}
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
</HStack>
|
|
||||||
<Text
|
|
||||||
fontSize="xs"
|
|
||||||
color={useColorModeValue('gray.600', 'gray.400')}
|
|
||||||
mt={1}
|
|
||||||
>
|
|
||||||
基于历史事件判断当前事件的超预期情况,满分100分
|
|
||||||
</Text>
|
|
||||||
</Box>
|
|
||||||
)}
|
|
||||||
|
|
||||||
{/* 相关股票(可折叠) - 懒加载 - 需要 PRO 权限 - 支持精简/详细模式 */}
|
{/* 相关股票(可折叠) - 懒加载 - 需要 PRO 权限 - 支持精简/详细模式 */}
|
||||||
<CollapsibleSection
|
<CollapsibleSection
|
||||||
title="相关股票"
|
title="相关股票"
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
// src/views/EventDetail/components/HistoricalEvents.js
|
// src/views/EventDetail/components/HistoricalEvents.js
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
VStack,
|
VStack,
|
||||||
@@ -21,7 +20,6 @@ import {
|
|||||||
ModalHeader,
|
ModalHeader,
|
||||||
ModalCloseButton,
|
ModalCloseButton,
|
||||||
ModalBody,
|
ModalBody,
|
||||||
Link,
|
|
||||||
Flex,
|
Flex,
|
||||||
Collapse
|
Collapse
|
||||||
} from '@chakra-ui/react';
|
} from '@chakra-ui/react';
|
||||||
@@ -42,8 +40,6 @@ const HistoricalEvents = ({
|
|||||||
loading = false,
|
loading = false,
|
||||||
error = null
|
error = null
|
||||||
}) => {
|
}) => {
|
||||||
const navigate = useNavigate();
|
|
||||||
|
|
||||||
// 状态管理
|
// 状态管理
|
||||||
const [selectedEventForStocks, setSelectedEventForStocks] = useState(null);
|
const [selectedEventForStocks, setSelectedEventForStocks] = useState(null);
|
||||||
const [stocksModalOpen, setStocksModalOpen] = useState(false);
|
const [stocksModalOpen, setStocksModalOpen] = useState(false);
|
||||||
@@ -117,10 +113,10 @@ const HistoricalEvents = ({
|
|||||||
setSelectedEventForStocks(null);
|
setSelectedEventForStocks(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 处理卡片点击跳转到事件详情页
|
// 历史事件卡片不需要点击跳转(历史事件ID与主事件不同,链接无效)
|
||||||
const handleCardClick = (event) => {
|
// const handleCardClick = (event) => {
|
||||||
navigate(`/event-detail/${event.id}`);
|
// navigate(`/event-detail/${event.id}`);
|
||||||
};
|
// };
|
||||||
|
|
||||||
// 获取重要性颜色
|
// 获取重要性颜色
|
||||||
const getImportanceColor = (importance) => {
|
const getImportanceColor = (importance) => {
|
||||||
@@ -250,8 +246,6 @@ const HistoricalEvents = ({
|
|||||||
borderRadius="lg"
|
borderRadius="lg"
|
||||||
position="relative"
|
position="relative"
|
||||||
overflow="visible"
|
overflow="visible"
|
||||||
cursor="pointer"
|
|
||||||
onClick={() => handleCardClick(event)}
|
|
||||||
_before={{
|
_before={{
|
||||||
content: '""',
|
content: '""',
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@@ -263,10 +257,6 @@ const HistoricalEvents = ({
|
|||||||
borderTopLeftRadius: 'lg',
|
borderTopLeftRadius: 'lg',
|
||||||
borderTopRightRadius: 'lg',
|
borderTopRightRadius: 'lg',
|
||||||
}}
|
}}
|
||||||
_hover={{
|
|
||||||
boxShadow: 'lg',
|
|
||||||
borderColor: 'blue.400',
|
|
||||||
}}
|
|
||||||
transition="all 0.2s"
|
transition="all 0.2s"
|
||||||
>
|
>
|
||||||
<VStack align="stretch" spacing={3} p={4}>
|
<VStack align="stretch" spacing={3} p={4}>
|
||||||
@@ -280,12 +270,6 @@ const HistoricalEvents = ({
|
|||||||
fontWeight="bold"
|
fontWeight="bold"
|
||||||
color={useColorModeValue('blue.500', 'blue.300')}
|
color={useColorModeValue('blue.500', 'blue.300')}
|
||||||
lineHeight="1.4"
|
lineHeight="1.4"
|
||||||
cursor="pointer"
|
|
||||||
onClick={(e) => {
|
|
||||||
e.stopPropagation();
|
|
||||||
handleCardClick(event);
|
|
||||||
}}
|
|
||||||
_hover={{ textDecoration: 'underline' }}
|
|
||||||
>
|
>
|
||||||
{event.title || '未命名事件'}
|
{event.title || '未命名事件'}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
Reference in New Issue
Block a user