Merge branch 'feature_2025/251117_pref' of https://git.valuefrontier.cn/vf/vf_react into feature_2025/251117_pref

* 'feature_2025/251117_pref' of https://git.valuefrontier.cn/vf/vf_react:
  update pay function
  update pay function
  update pay function
This commit is contained in:
zdl
2025-11-24 20:33:30 +08:00
4 changed files with 189 additions and 12 deletions

4
app.py
View File

@@ -6904,12 +6904,12 @@ def get_daily_kline(stock_code, event_datetime, stock_name):
stock_code = stock_code.split('.')[0]
with engine.connect() as conn:
# 获取事件日期前后的数据(前730天/2后30天
# 获取事件日期前后的数据(前365天/1后30天
kline_sql = """
WITH date_range AS (SELECT TRADEDATE \
FROM ea_trade \
WHERE SECCODE = :stock_code \
AND TRADEDATE BETWEEN DATE_SUB(:trade_date, INTERVAL 730 DAY) \
AND TRADEDATE BETWEEN DATE_SUB(:trade_date, INTERVAL 365 DAY) \
AND DATE_ADD(:trade_date, INTERVAL 30 DAY) \
GROUP BY TRADEDATE \
ORDER BY TRADEDATE)

View File

@@ -170,6 +170,21 @@ const KLineChartModal: React.FC<KLineChartModalProps> = ({
d.close >= d.open ? '#ef5350' : '#26a69a'
);
// 提取事件发生日期YYYY-MM-DD格式
let eventDateStr: string | null = null;
if (eventTime) {
try {
const eventDate = new Date(eventTime);
const year = eventDate.getFullYear();
const month = (eventDate.getMonth() + 1).toString().padStart(2, '0');
const day = eventDate.getDate().toString().padStart(2, '0');
eventDateStr = `${year}-${month}-${day}`;
console.log('[KLineChartModal] 事件发生日期:', eventDateStr);
} catch (e) {
console.error('[KLineChartModal] 解析事件日期失败:', e);
}
}
// 图表配置
const option: echarts.EChartsOption = {
backgroundColor: '#1a1a1a',
@@ -346,6 +361,34 @@ const KLineChartModal: React.FC<KLineChartModalProps> = ({
borderColor: '#ef5350',
borderColor0: '#26a69a',
},
markLine: eventDateStr ? {
silent: false,
symbol: 'none',
label: {
show: true,
position: 'insideEndTop',
formatter: '事件发生',
color: '#ffd700',
fontSize: 12,
fontWeight: 'bold',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: [4, 8],
borderRadius: 4,
},
lineStyle: {
color: '#ffd700',
width: 2,
type: 'solid',
},
data: [
{
xAxis: eventDateStr,
label: {
formatter: '⚡ 事件发生',
},
},
],
} : undefined,
},
{
name: '成交量',
@@ -420,7 +463,7 @@ const KLineChartModal: React.FC<KLineChartModalProps> = ({
</Text>
{data.length > 0 && (
<Text fontSize="xs" color="#666" fontStyle="italic">
{data.length}2
{data.length}1
</Text>
)}
</HStack>

View File

@@ -172,6 +172,20 @@ const TimelineChartModal: React.FC<TimelineChartModalProps> = ({
d.price >= basePrice ? '#ef5350' : '#26a69a'
);
// 提取事件发生时间HH:MM格式
let eventTimeStr: string | null = null;
if (eventTime) {
try {
const eventDate = new Date(eventTime);
const hours = eventDate.getHours().toString().padStart(2, '0');
const minutes = eventDate.getMinutes().toString().padStart(2, '0');
eventTimeStr = `${hours}:${minutes}`;
console.log('[TimelineChartModal] 事件发生时间:', eventTimeStr);
} catch (e) {
console.error('[TimelineChartModal] 解析事件时间失败:', e);
}
}
// 图表配置
const option: echarts.EChartsOption = {
backgroundColor: '#1a1a1a',
@@ -341,6 +355,34 @@ const TimelineChartModal: React.FC<TimelineChartModalProps> = ({
{ offset: 1, color: 'rgba(33, 150, 243, 0.05)' },
]),
},
markLine: eventTimeStr ? {
silent: false,
symbol: 'none',
label: {
show: true,
position: 'insideEndTop',
formatter: '事件发生',
color: '#ffd700',
fontSize: 12,
fontWeight: 'bold',
backgroundColor: 'rgba(0, 0, 0, 0.7)',
padding: [4, 8],
borderRadius: 4,
},
lineStyle: {
color: '#ffd700',
width: 2,
type: 'solid',
},
data: [
{
xAxis: eventTimeStr,
label: {
formatter: '⚡ 事件发生',
},
},
],
} : undefined,
},
{
name: '均价',

View File

@@ -56,6 +56,8 @@ import dayjs from 'dayjs';
import 'dayjs/locale/zh-cn';
import { logger } from '../../../utils/logger';
import { getApiBase } from '../../../utils/apiConfig';
import TimelineChartModal from '../../../components/StockChart/TimelineChartModal';
import KLineChartModal from '../../../components/StockChart/KLineChartModal';
import './InvestmentCalendar.css';
dayjs.locale('zh-cn');
@@ -63,6 +65,8 @@ dayjs.locale('zh-cn');
export default function InvestmentCalendarChakra() {
const { isOpen, onOpen, onClose } = useDisclosure();
const { isOpen: isAddOpen, onOpen: onAddOpen, onClose: onAddClose } = useDisclosure();
const { isOpen: isTimelineModalOpen, onOpen: onTimelineModalOpen, onClose: onTimelineModalClose } = useDisclosure();
const { isOpen: isKLineModalOpen, onOpen: onKLineModalOpen, onClose: onKLineModalClose } = useDisclosure();
const toast = useToast();
// 颜色主题
@@ -74,6 +78,7 @@ export default function InvestmentCalendarChakra() {
const [events, setEvents] = useState([]);
const [selectedDate, setSelectedDate] = useState(null);
const [selectedDateEvents, setSelectedDateEvents] = useState([]);
const [selectedStock, setSelectedStock] = useState(null);
const [loading, setLoading] = useState(false);
const [newEvent, setNewEvent] = useState({
title: '',
@@ -262,6 +267,35 @@ export default function InvestmentCalendarChakra() {
}
};
// 处理股票点击 - 打开图表弹窗
const handleStockClick = (stockCodeOrName, eventDate) => {
// 解析股票代码(可能是 "600000" 或 "600000 平安银行" 格式)
let stockCode = stockCodeOrName;
let stockName = '';
if (typeof stockCodeOrName === 'string') {
const parts = stockCodeOrName.trim().split(/\s+/);
stockCode = parts[0];
stockName = parts.slice(1).join(' ');
}
// 添加交易所后缀(如果没有)
if (!stockCode.includes('.')) {
if (stockCode.startsWith('6')) {
stockCode = `${stockCode}.SH`;
} else if (stockCode.startsWith('0') || stockCode.startsWith('3')) {
stockCode = `${stockCode}.SZ`;
} else if (stockCode.startsWith('688')) {
stockCode = `${stockCode}.SH`;
}
}
setSelectedStock({
stock_code: stockCode,
stock_name: stockName || stockCode,
});
};
return (
<Card bg={bgColor} shadow="md">
<CardHeader pb={4}>
@@ -386,15 +420,47 @@ export default function InvestmentCalendarChakra() {
)}
{event.extendedProps?.stocks && event.extendedProps.stocks.length > 0 && (
<HStack spacing={2} flexWrap="wrap">
<Text fontSize="sm" color={secondaryText}>相关股票:</Text>
{event.extendedProps.stocks.map((stock, i) => (
<Tag key={i} size="sm" colorScheme="blue">
<TagLeftIcon as={FiTrendingUp} />
<TagLabel>{stock}</TagLabel>
</Tag>
))}
</HStack>
<VStack align="stretch" spacing={2}>
<HStack spacing={2} flexWrap="wrap">
<Text fontSize="sm" color={secondaryText}>相关股票:</Text>
{event.extendedProps.stocks.map((stock, i) => (
<Tag
key={i}
size="sm"
colorScheme="blue"
cursor="pointer"
onClick={() => handleStockClick(stock, event.start)}
_hover={{ transform: 'scale(1.05)', shadow: 'md' }}
transition="all 0.2s"
>
<TagLeftIcon as={FiTrendingUp} />
<TagLabel>{stock}</TagLabel>
</Tag>
))}
</HStack>
{selectedStock && (
<HStack spacing={2}>
<Button
size="xs"
colorScheme="blue"
variant="outline"
leftIcon={<FiClock />}
onClick={onTimelineModalOpen}
>
分时图
</Button>
<Button
size="xs"
colorScheme="purple"
variant="outline"
leftIcon={<FiTrendingUp />}
onClick={onKLineModalOpen}
>
日K线
</Button>
</HStack>
)}
</VStack>
)}
</Box>
))}
@@ -489,6 +555,32 @@ export default function InvestmentCalendarChakra() {
</ModalContent>
</Modal>
)}
{/* 分时图弹窗 */}
{selectedStock && (
<TimelineChartModal
isOpen={isTimelineModalOpen}
onClose={() => {
onTimelineModalClose();
setSelectedStock(null);
}}
stock={selectedStock}
eventTime={selectedDate?.toISOString()}
/>
)}
{/* K线图弹窗 */}
{selectedStock && (
<KLineChartModal
isOpen={isKLineModalOpen}
onClose={() => {
onKLineModalClose();
setSelectedStock(null);
}}
stock={selectedStock}
eventTime={selectedDate?.toISOString()}
/>
)}
</Card>
);
}