community增加事件详情
This commit is contained in:
@@ -3,6 +3,8 @@
|
|||||||
// 点击日期弹出详情弹窗(TAB切换历史涨停/未来事件)
|
// 点击日期弹出详情弹窗(TAB切换历史涨停/未来事件)
|
||||||
|
|
||||||
import React, { useEffect, useState, useCallback, useMemo, memo } from 'react';
|
import React, { useEffect, useState, useCallback, useMemo, memo } from 'react';
|
||||||
|
import { useSelector, useDispatch } from 'react-redux';
|
||||||
|
import { loadWatchlist, toggleWatchlist } from '@store/slices/stockSlice';
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
Card,
|
Card,
|
||||||
@@ -34,7 +36,7 @@ import {
|
|||||||
DrawerBody,
|
DrawerBody,
|
||||||
DrawerCloseButton,
|
DrawerCloseButton,
|
||||||
} from '@chakra-ui/react';
|
} 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 {
|
import {
|
||||||
CalendarOutlined,
|
CalendarOutlined,
|
||||||
StarFilled,
|
StarFilled,
|
||||||
@@ -44,6 +46,8 @@ import {
|
|||||||
ClockCircleOutlined,
|
ClockCircleOutlined,
|
||||||
RobotOutlined,
|
RobotOutlined,
|
||||||
FireOutlined,
|
FireOutlined,
|
||||||
|
LineChartOutlined,
|
||||||
|
StarOutlined,
|
||||||
} from '@ant-design/icons';
|
} from '@ant-design/icons';
|
||||||
import { AlertCircle, Clock, Info, Calendar, ChevronLeft, ChevronRight, Flame, TrendingUp, TrendingDown, FileText, Star } from 'lucide-react';
|
import { AlertCircle, Clock, Info, Calendar, ChevronLeft, ChevronRight, Flame, TrendingUp, TrendingDown, FileText, Star } from 'lucide-react';
|
||||||
import { GLASS_BLUR } from '@/constants/glassConfig';
|
import { GLASS_BLUR } from '@/constants/glassConfig';
|
||||||
@@ -51,6 +55,7 @@ import { eventService } from '@services/eventService';
|
|||||||
import { getApiBase } from '@utils/apiConfig';
|
import { getApiBase } from '@utils/apiConfig';
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import KLineChartModal from '@components/StockChart/KLineChartModal';
|
||||||
|
|
||||||
const { TabPane } = Tabs;
|
const { TabPane } = Tabs;
|
||||||
const { Text: AntText } = Typography;
|
const { Text: AntText } = Typography;
|
||||||
@@ -407,6 +412,9 @@ CalendarCell.displayName = 'CalendarCell';
|
|||||||
* 详情弹窗组件 - 完整展示涨停分析和事件详情
|
* 详情弹窗组件 - 完整展示涨停分析和事件详情
|
||||||
*/
|
*/
|
||||||
const DetailModal = ({ isOpen, onClose, selectedDate, ztDetail, events, loading }) => {
|
const DetailModal = ({ isOpen, onClose, selectedDate, ztDetail, events, loading }) => {
|
||||||
|
const dispatch = useDispatch();
|
||||||
|
const reduxWatchlist = useSelector(state => state.stock.watchlist);
|
||||||
|
|
||||||
const [detailDrawerVisible, setDetailDrawerVisible] = useState(false);
|
const [detailDrawerVisible, setDetailDrawerVisible] = useState(false);
|
||||||
const [selectedContent, setSelectedContent] = useState(null);
|
const [selectedContent, setSelectedContent] = useState(null);
|
||||||
const [ztViewMode, setZtViewMode] = useState('sector'); // 'sector' | 'stock'
|
const [ztViewMode, setZtViewMode] = useState('sector'); // 'sector' | 'stock'
|
||||||
@@ -417,6 +425,8 @@ const DetailModal = ({ isOpen, onClose, selectedDate, ztDetail, events, loading
|
|||||||
const [stockQuotes, setStockQuotes] = useState({});
|
const [stockQuotes, setStockQuotes] = useState({});
|
||||||
const [stockQuotesLoading, setStockQuotesLoading] = useState(false);
|
const [stockQuotesLoading, setStockQuotesLoading] = useState(false);
|
||||||
const [expandedReasons, setExpandedReasons] = useState({});
|
const [expandedReasons, setExpandedReasons] = useState({});
|
||||||
|
const [klineModalVisible, setKlineModalVisible] = useState(false);
|
||||||
|
const [selectedKlineStock, setSelectedKlineStock] = useState(null);
|
||||||
|
|
||||||
// 板块数据处理 - 必须在条件返回之前调用所有hooks
|
// 板块数据处理 - 必须在条件返回之前调用所有hooks
|
||||||
const sectorList = useMemo(() => {
|
const sectorList = useMemo(() => {
|
||||||
@@ -549,6 +559,64 @@ const DetailModal = ({ isOpen, onClose, selectedDate, ztDetail, events, loading
|
|||||||
loadStockQuotes(sortedStocks);
|
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 = [
|
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>
|
</ModalBody>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
||||||
|
{/* K线图弹窗 */}
|
||||||
|
{selectedKlineStock && (
|
||||||
|
<KLineChartModal
|
||||||
|
isOpen={klineModalVisible}
|
||||||
|
onClose={() => {
|
||||||
|
setKlineModalVisible(false);
|
||||||
|
setSelectedKlineStock(null);
|
||||||
|
}}
|
||||||
|
stock={selectedKlineStock}
|
||||||
|
eventTime={selectedEventTime}
|
||||||
|
size="5xl"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user