feat: 提取日历选择器组件
This commit is contained in:
130
src/components/TradeDatePicker/index.tsx
Normal file
130
src/components/TradeDatePicker/index.tsx
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import {
|
||||||
|
HStack,
|
||||||
|
Input,
|
||||||
|
Text,
|
||||||
|
Icon,
|
||||||
|
Tooltip,
|
||||||
|
useColorModeValue,
|
||||||
|
} from '@chakra-ui/react';
|
||||||
|
import { InfoIcon } from '@chakra-ui/icons';
|
||||||
|
import { FaCalendarAlt } from 'react-icons/fa';
|
||||||
|
|
||||||
|
export interface TradeDatePickerProps {
|
||||||
|
/** 当前选中的日期 */
|
||||||
|
value: Date | null;
|
||||||
|
/** 日期变化回调 */
|
||||||
|
onChange: (date: Date) => void;
|
||||||
|
/** 默认日期(组件初始化时使用) */
|
||||||
|
defaultDate?: Date;
|
||||||
|
/** 最新交易日期(用于显示提示) */
|
||||||
|
latestTradeDate?: Date | null;
|
||||||
|
/** 最大可选日期,默认今天 */
|
||||||
|
maxDate?: Date;
|
||||||
|
/** 标签文字,默认"交易日期" */
|
||||||
|
label?: string;
|
||||||
|
/** 输入框宽度 */
|
||||||
|
inputWidth?: string | object;
|
||||||
|
/** 是否显示标签图标 */
|
||||||
|
showIcon?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 交易日期选择器组件
|
||||||
|
*
|
||||||
|
* 提供日期输入框和最新交易日期提示,供概念中心、个股中心等页面复用。
|
||||||
|
* 快捷按钮(今天、昨天等)由各页面自行实现。
|
||||||
|
*/
|
||||||
|
const TradeDatePicker: React.FC<TradeDatePickerProps> = ({
|
||||||
|
value,
|
||||||
|
onChange,
|
||||||
|
defaultDate,
|
||||||
|
latestTradeDate,
|
||||||
|
maxDate,
|
||||||
|
label = '交易日期',
|
||||||
|
inputWidth = { base: '100%', lg: '200px' },
|
||||||
|
showIcon = true,
|
||||||
|
}) => {
|
||||||
|
// 颜色主题
|
||||||
|
const labelColor = useColorModeValue('purple.700', 'purple.300');
|
||||||
|
const iconColor = useColorModeValue('purple.500', 'purple.400');
|
||||||
|
const inputBorderColor = useColorModeValue('purple.200', 'purple.600');
|
||||||
|
const tipBg = useColorModeValue('blue.50', 'blue.900');
|
||||||
|
const tipBorderColor = useColorModeValue('blue.200', 'blue.600');
|
||||||
|
const tipTextColor = useColorModeValue('blue.600', 'blue.200');
|
||||||
|
const tipIconColor = useColorModeValue('blue.500', 'blue.300');
|
||||||
|
|
||||||
|
// 使用默认日期初始化(仅在 value 为 null 且有 defaultDate 时)
|
||||||
|
React.useEffect(() => {
|
||||||
|
if (value === null && defaultDate) {
|
||||||
|
onChange(defaultDate);
|
||||||
|
}
|
||||||
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
||||||
|
|
||||||
|
// 处理日期变化
|
||||||
|
const handleDateChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
const dateStr = e.target.value;
|
||||||
|
if (dateStr) {
|
||||||
|
const date = new Date(dateStr);
|
||||||
|
onChange(date);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化日期为 YYYY-MM-DD
|
||||||
|
const formatDateValue = (date: Date | null): string => {
|
||||||
|
if (!date) return '';
|
||||||
|
return date.toISOString().split('T')[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
// 计算最大日期
|
||||||
|
const maxDateStr = maxDate
|
||||||
|
? formatDateValue(maxDate)
|
||||||
|
: new Date().toISOString().split('T')[0];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{/* 标签 */}
|
||||||
|
<HStack spacing={3}>
|
||||||
|
{showIcon && <Icon as={FaCalendarAlt} color={iconColor} boxSize={5} />}
|
||||||
|
<Text fontWeight="bold" color={labelColor}>
|
||||||
|
{label}:
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
|
||||||
|
{/* 日期输入框 */}
|
||||||
|
<Input
|
||||||
|
type="date"
|
||||||
|
value={formatDateValue(value)}
|
||||||
|
onChange={handleDateChange}
|
||||||
|
max={maxDateStr}
|
||||||
|
width={inputWidth}
|
||||||
|
focusBorderColor="purple.500"
|
||||||
|
borderColor={inputBorderColor}
|
||||||
|
borderRadius="lg"
|
||||||
|
fontWeight="medium"
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* 最新交易日期提示 */}
|
||||||
|
{latestTradeDate && (
|
||||||
|
<Tooltip label="数据库中最新的交易日期">
|
||||||
|
<HStack
|
||||||
|
spacing={2}
|
||||||
|
bg={tipBg}
|
||||||
|
px={3}
|
||||||
|
py={1.5}
|
||||||
|
borderRadius="full"
|
||||||
|
border="1px solid"
|
||||||
|
borderColor={tipBorderColor}
|
||||||
|
>
|
||||||
|
<Icon as={InfoIcon} color={tipIconColor} boxSize={3} />
|
||||||
|
<Text fontSize="sm" color={tipTextColor} fontWeight="medium">
|
||||||
|
最新: {latestTradeDate.toLocaleDateString('zh-CN')}
|
||||||
|
</Text>
|
||||||
|
</HStack>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default TradeDatePicker;
|
||||||
@@ -87,6 +87,7 @@ import { keyframes } from '@emotion/react';
|
|||||||
import ConceptTimelineModal from './ConceptTimelineModal';
|
import ConceptTimelineModal from './ConceptTimelineModal';
|
||||||
import ConceptStatsPanel from './components/ConceptStatsPanel';
|
import ConceptStatsPanel from './components/ConceptStatsPanel';
|
||||||
import ConceptStocksModal from '@components/ConceptStocksModal';
|
import ConceptStocksModal from '@components/ConceptStocksModal';
|
||||||
|
import TradeDatePicker from '@components/TradeDatePicker';
|
||||||
// 导航栏已由 MainLayout 提供,无需在此导入
|
// 导航栏已由 MainLayout 提供,无需在此导入
|
||||||
// 导入订阅权限管理
|
// 导入订阅权限管理
|
||||||
import { useSubscription } from '../../hooks/useSubscription';
|
import { useSubscription } from '../../hooks/useSubscription';
|
||||||
@@ -1082,23 +1083,23 @@ const ConceptCenter = () => {
|
|||||||
align={{ base: 'stretch', lg: 'center' }}
|
align={{ base: 'stretch', lg: 'center' }}
|
||||||
gap={4}
|
gap={4}
|
||||||
>
|
>
|
||||||
<HStack spacing={3}>
|
{/* 使用通用日期选择器组件 */}
|
||||||
<Icon as={FaCalendarAlt} color="purple.500" boxSize={5} />
|
<TradeDatePicker
|
||||||
<Text fontWeight="bold" color="purple.700">交易日期:</Text>
|
value={selectedDate}
|
||||||
</HStack>
|
onChange={(date) => {
|
||||||
|
const dateStr = date.toISOString().split('T')[0];
|
||||||
<Input
|
const previousDate = selectedDate ? selectedDate.toISOString().split('T')[0] : null;
|
||||||
type="date"
|
trackFilterApplied('date', dateStr, previousDate);
|
||||||
value={selectedDate ? selectedDate.toISOString().split('T')[0] : ''}
|
setSelectedDate(date);
|
||||||
onChange={handleDateChange}
|
setCurrentPage(1);
|
||||||
max={new Date().toISOString().split('T')[0]}
|
updateUrlParams({ date: dateStr, page: 1 });
|
||||||
width={{ base: '100%', lg: '200px' }}
|
fetchConcepts(searchQuery, 1, date, sortBy);
|
||||||
focusBorderColor="purple.500"
|
}}
|
||||||
borderColor="purple.200"
|
latestTradeDate={latestTradeDate}
|
||||||
borderRadius="lg"
|
label="交易日期"
|
||||||
fontWeight="medium"
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{/* 快捷按钮保留在页面内 */}
|
||||||
<ButtonGroup size="sm" variant="outline" flexWrap="wrap">
|
<ButtonGroup size="sm" variant="outline" flexWrap="wrap">
|
||||||
<Button
|
<Button
|
||||||
onClick={() => handleQuickDateSelect(0)}
|
onClick={() => handleQuickDateSelect(0)}
|
||||||
@@ -1149,25 +1150,6 @@ const ConceptCenter = () => {
|
|||||||
一月前
|
一月前
|
||||||
</Button>
|
</Button>
|
||||||
</ButtonGroup>
|
</ButtonGroup>
|
||||||
|
|
||||||
{latestTradeDate && (
|
|
||||||
<Tooltip label="数据库中最新的交易日期">
|
|
||||||
<HStack
|
|
||||||
spacing={2}
|
|
||||||
bg="blue.50"
|
|
||||||
px={3}
|
|
||||||
py={1.5}
|
|
||||||
borderRadius="full"
|
|
||||||
border="1px solid"
|
|
||||||
borderColor="blue.200"
|
|
||||||
>
|
|
||||||
<Icon as={InfoIcon} color="blue.500" boxSize={3} />
|
|
||||||
<Text fontSize="sm" color="blue.600" fontWeight="medium">
|
|
||||||
最新: {latestTradeDate.toLocaleDateString('zh-CN')}
|
|
||||||
</Text>
|
|
||||||
</HStack>
|
|
||||||
</Tooltip>
|
|
||||||
)}
|
|
||||||
</Flex>
|
</Flex>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user