refactor: 提取 ConceptStocksModal 为通用组件,统一概念中心和个股中心弹窗
- 将 ConceptStocksModal 从 StockOverview/components 移到 components 目录 - 概念中心复用 ConceptStocksModal,删除冗余的 renderStockTable 函数(约100行) - 统一 H5 端弹窗体验:响应式尺寸、高度限制(70vh)、左右滑动、垂直居中 - 移除重复的底部关闭按钮,只保留右上角关闭按钮 - 添加"板块原因"列,表头改为中文 - 使用 @components 路径别名 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -86,6 +86,7 @@ import { BsGraphUp, BsLightningFill } from 'react-icons/bs';
|
||||
import { keyframes } from '@emotion/react';
|
||||
import ConceptTimelineModal from './ConceptTimelineModal';
|
||||
import ConceptStatsPanel from './components/ConceptStatsPanel';
|
||||
import ConceptStocksModal from '@components/ConceptStocksModal';
|
||||
// 导航栏已由 MainLayout 提供,无需在此导入
|
||||
// 导入订阅权限管理
|
||||
import { useSubscription } from '../../hooks/useSubscription';
|
||||
@@ -528,109 +529,6 @@ const ConceptCenter = () => {
|
||||
return `https://valuefrontier.cn/company?scode=${seccode}`;
|
||||
};
|
||||
|
||||
// 渲染动态表格列
|
||||
const renderStockTable = () => {
|
||||
if (!selectedConceptStocks || selectedConceptStocks.length === 0) {
|
||||
return <Text>暂无相关股票数据</Text>;
|
||||
}
|
||||
|
||||
const allFields = new Set();
|
||||
selectedConceptStocks.forEach(stock => {
|
||||
Object.keys(stock).forEach(key => allFields.add(key));
|
||||
});
|
||||
|
||||
// 定义固定的列顺序,包含新增的现价和涨跌幅列
|
||||
const orderedFields = ['stock_name', 'stock_code', 'current_price', 'change_percent'];
|
||||
allFields.forEach(field => {
|
||||
if (!orderedFields.includes(field)) {
|
||||
orderedFields.push(field);
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<Box>
|
||||
{loadingStockData && (
|
||||
<Box mb={4} textAlign="center">
|
||||
<HStack justify="center" spacing={2}>
|
||||
<Spinner size="sm" color="purple.500" />
|
||||
<Text fontSize="sm" color="gray.600">正在获取行情数据...</Text>
|
||||
</HStack>
|
||||
</Box>
|
||||
)}
|
||||
|
||||
<TableContainer maxH="60vh" overflowY="auto">
|
||||
<Table variant="simple" size="sm">
|
||||
<Thead position="sticky" top={0} bg="white" zIndex={1}>
|
||||
<Tr>
|
||||
{orderedFields.map(field => (
|
||||
<Th key={field}>
|
||||
{field === 'stock_name' ? '股票名称' :
|
||||
field === 'stock_code' ? '股票代码' :
|
||||
field === 'current_price' ? '现价' :
|
||||
field === 'change_percent' ? '当日涨跌幅' : field}
|
||||
</Th>
|
||||
))}
|
||||
</Tr>
|
||||
</Thead>
|
||||
<Tbody>
|
||||
{selectedConceptStocks.map((stock, idx) => {
|
||||
const marketData = stockMarketData[stock.stock_code];
|
||||
const companyLink = generateCompanyLink(stock.stock_code);
|
||||
|
||||
return (
|
||||
<Tr key={idx} _hover={{ bg: 'gray.50' }}>
|
||||
{orderedFields.map(field => {
|
||||
let cellContent = stock[field] || '-';
|
||||
let cellProps = {};
|
||||
|
||||
// 处理特殊字段
|
||||
if (field === 'current_price') {
|
||||
cellContent = marketData ? formatPrice(marketData.close) : (loadingStockData ? <Spinner size="xs" /> : '-');
|
||||
} else if (field === 'change_percent') {
|
||||
if (marketData) {
|
||||
cellContent = formatStockChangePercent(marketData.change_percent);
|
||||
cellProps.color = `${getStockChangeColor(marketData.change_percent)}.500`;
|
||||
cellProps.fontWeight = 'bold';
|
||||
} else {
|
||||
cellContent = loadingStockData ? <Spinner size="xs" /> : '-';
|
||||
}
|
||||
} else if (field === 'stock_name' || field === 'stock_code') {
|
||||
// 添加超链接
|
||||
cellContent = (
|
||||
<Text
|
||||
as="a"
|
||||
href={companyLink}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
color="blue.600"
|
||||
textDecoration="underline"
|
||||
_hover={{
|
||||
color: 'blue.800',
|
||||
textDecoration: 'underline'
|
||||
}}
|
||||
cursor="pointer"
|
||||
>
|
||||
{stock[field] || '-'}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Td key={field} {...cellProps}>
|
||||
{cellContent}
|
||||
</Td>
|
||||
);
|
||||
})}
|
||||
</Tr>
|
||||
);
|
||||
})}
|
||||
</Tbody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
// 格式化添加日期显示
|
||||
const formatAddedDate = (concept) => {
|
||||
// 优先使用 created_at 或 added_date 字段
|
||||
@@ -1763,32 +1661,15 @@ const ConceptCenter = () => {
|
||||
</Flex>
|
||||
</Container>
|
||||
|
||||
{/* 股票详情Modal */}
|
||||
<Modal
|
||||
{/* 股票详情Modal - 复用通用组件 */}
|
||||
<ConceptStocksModal
|
||||
isOpen={isStockModalOpen}
|
||||
onClose={() => setIsStockModalOpen(false)}
|
||||
size="6xl"
|
||||
scrollBehavior="inside"
|
||||
>
|
||||
<ModalOverlay />
|
||||
<ModalContent>
|
||||
<ModalHeader bg="purple.500" color="white">
|
||||
<HStack>
|
||||
<Icon as={FaTable} />
|
||||
<Text>{selectedConceptName} - 相关个股</Text>
|
||||
</HStack>
|
||||
</ModalHeader>
|
||||
<ModalCloseButton color="white" />
|
||||
<ModalBody py={6}>
|
||||
{renderStockTable()}
|
||||
</ModalBody>
|
||||
<ModalFooter>
|
||||
<Button colorScheme="purple" onClick={() => setIsStockModalOpen(false)}>
|
||||
关闭
|
||||
</Button>
|
||||
</ModalFooter>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
concept={{
|
||||
concept_name: selectedConceptName,
|
||||
stocks: selectedConceptStocks
|
||||
}}
|
||||
/>
|
||||
{/* 时间轴Modal */}
|
||||
<ConceptTimelineModal
|
||||
isOpen={isTimelineModalOpen}
|
||||
|
||||
Reference in New Issue
Block a user