community增加事件详情
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
// 点击日期弹出详情弹窗(TAB切换历史涨停/未来事件)
|
||||
|
||||
import React, { useEffect, useState, useCallback, useMemo, memo } from 'react';
|
||||
import { useSelector, useDispatch } from 'react-redux';
|
||||
import { loadWatchlist, toggleWatchlist } from '@store/slices/stockSlice';
|
||||
import {
|
||||
Box,
|
||||
Card,
|
||||
@@ -34,7 +36,7 @@ import {
|
||||
DrawerBody,
|
||||
DrawerCloseButton,
|
||||
} from '@chakra-ui/react';
|
||||
import { Table, Tabs, Tag, Space, Button, Spin, Typography } from 'antd';
|
||||
import { Table, Tabs, Tag, Space, Button, Spin, Typography, message } from 'antd';
|
||||
import {
|
||||
CalendarOutlined,
|
||||
StarFilled,
|
||||
@@ -44,6 +46,8 @@ import {
|
||||
ClockCircleOutlined,
|
||||
RobotOutlined,
|
||||
FireOutlined,
|
||||
LineChartOutlined,
|
||||
StarOutlined,
|
||||
} from '@ant-design/icons';
|
||||
import { AlertCircle, Clock, Info, Calendar, ChevronLeft, ChevronRight, Flame, TrendingUp, TrendingDown, FileText, Star } from 'lucide-react';
|
||||
import { GLASS_BLUR } from '@/constants/glassConfig';
|
||||
@@ -51,6 +55,7 @@ import { eventService } from '@services/eventService';
|
||||
import { getApiBase } from '@utils/apiConfig';
|
||||
import ReactMarkdown from 'react-markdown';
|
||||
import dayjs from 'dayjs';
|
||||
import KLineChartModal from '@components/StockChart/KLineChartModal';
|
||||
|
||||
const { TabPane } = Tabs;
|
||||
const { Text: AntText } = Typography;
|
||||
@@ -407,6 +412,9 @@ CalendarCell.displayName = 'CalendarCell';
|
||||
* 详情弹窗组件 - 完整展示涨停分析和事件详情
|
||||
*/
|
||||
const DetailModal = ({ isOpen, onClose, selectedDate, ztDetail, events, loading }) => {
|
||||
const dispatch = useDispatch();
|
||||
const reduxWatchlist = useSelector(state => state.stock.watchlist);
|
||||
|
||||
const [detailDrawerVisible, setDetailDrawerVisible] = useState(false);
|
||||
const [selectedContent, setSelectedContent] = useState(null);
|
||||
const [ztViewMode, setZtViewMode] = useState('sector'); // 'sector' | 'stock'
|
||||
@@ -417,6 +425,8 @@ const DetailModal = ({ isOpen, onClose, selectedDate, ztDetail, events, loading
|
||||
const [stockQuotes, setStockQuotes] = useState({});
|
||||
const [stockQuotesLoading, setStockQuotesLoading] = useState(false);
|
||||
const [expandedReasons, setExpandedReasons] = useState({});
|
||||
const [klineModalVisible, setKlineModalVisible] = useState(false);
|
||||
const [selectedKlineStock, setSelectedKlineStock] = useState(null);
|
||||
|
||||
// 板块数据处理 - 必须在条件返回之前调用所有hooks
|
||||
const sectorList = useMemo(() => {
|
||||
@@ -549,6 +559,64 @@ const DetailModal = ({ isOpen, onClose, selectedDate, ztDetail, events, loading
|
||||
loadStockQuotes(sortedStocks);
|
||||
};
|
||||
|
||||
// 添加交易所后缀
|
||||
const addExchangeSuffix = (code) => {
|
||||
const sixDigitCode = getSixDigitCode(code);
|
||||
if (code.includes('.')) return code;
|
||||
if (sixDigitCode.startsWith('6')) {
|
||||
return `${sixDigitCode}.SH`;
|
||||
} else if (sixDigitCode.startsWith('0') || sixDigitCode.startsWith('3')) {
|
||||
return `${sixDigitCode}.SZ`;
|
||||
}
|
||||
return sixDigitCode;
|
||||
};
|
||||
|
||||
// 检查股票是否已在自选中
|
||||
const isStockInWatchlist = useCallback((stockCode) => {
|
||||
const sixDigitCode = getSixDigitCode(stockCode);
|
||||
return reduxWatchlist?.some(item =>
|
||||
getSixDigitCode(item.stock_code) === sixDigitCode
|
||||
);
|
||||
}, [reduxWatchlist]);
|
||||
|
||||
// 显示K线图
|
||||
const showKline = (stock) => {
|
||||
const code = stock.code;
|
||||
const name = stock.name;
|
||||
const stockCode = addExchangeSuffix(code);
|
||||
|
||||
setSelectedKlineStock({
|
||||
stock_code: stockCode,
|
||||
stock_name: name,
|
||||
});
|
||||
setKlineModalVisible(true);
|
||||
};
|
||||
|
||||
// 添加单只股票到自选
|
||||
const addSingleToWatchlist = async (stock) => {
|
||||
const code = stock.code;
|
||||
const name = stock.name;
|
||||
const stockCode = getSixDigitCode(code);
|
||||
|
||||
if (isStockInWatchlist(code)) {
|
||||
message.info(`${name} 已在自选中`);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await dispatch(toggleWatchlist({
|
||||
stockCode,
|
||||
stockName: name,
|
||||
isInWatchlist: false
|
||||
})).unwrap();
|
||||
|
||||
message.success(`已将 ${name}(${stockCode}) 添加到自选`);
|
||||
} catch (error) {
|
||||
console.error('添加自选失败:', error);
|
||||
message.error('添加失败,请重试');
|
||||
}
|
||||
};
|
||||
|
||||
// 相关股票表格列定义(和投资日历保持一致)
|
||||
const stockColumns = [
|
||||
{
|
||||
@@ -702,6 +770,40 @@ const DetailModal = ({ isOpen, onClose, selectedDate, ztDetail, events, loading
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
title: 'K线图',
|
||||
key: 'kline',
|
||||
width: 80,
|
||||
render: (_, record) => (
|
||||
<Button
|
||||
type="primary"
|
||||
size="small"
|
||||
icon={<LineChartOutlined />}
|
||||
onClick={() => showKline(record)}
|
||||
>
|
||||
查看
|
||||
</Button>
|
||||
)
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
key: 'action',
|
||||
width: 90,
|
||||
render: (_, record) => {
|
||||
const inWatchlist = isStockInWatchlist(record.code);
|
||||
return (
|
||||
<Button
|
||||
type={inWatchlist ? 'primary' : 'default'}
|
||||
size="small"
|
||||
icon={inWatchlist ? <StarFilled /> : <StarOutlined />}
|
||||
onClick={() => addSingleToWatchlist(record)}
|
||||
disabled={inWatchlist}
|
||||
>
|
||||
{inWatchlist ? '已添加' : '加自选'}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
},
|
||||
];
|
||||
|
||||
// 涨停板块表格列
|
||||
@@ -1250,6 +1352,20 @@ const DetailModal = ({ isOpen, onClose, selectedDate, ztDetail, events, loading
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
|
||||
{/* K线图弹窗 */}
|
||||
{selectedKlineStock && (
|
||||
<KLineChartModal
|
||||
isOpen={klineModalVisible}
|
||||
onClose={() => {
|
||||
setKlineModalVisible(false);
|
||||
setSelectedKlineStock(null);
|
||||
}}
|
||||
stock={selectedKlineStock}
|
||||
eventTime={selectedEventTime}
|
||||
size="5xl"
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user