169 lines
5.3 KiB
TypeScript
169 lines
5.3 KiB
TypeScript
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;
|
||
/** 最小可选日期 */
|
||
minDate?: Date;
|
||
/** 最大可选日期,默认今天 */
|
||
maxDate?: Date;
|
||
/** 标签文字,默认"交易日期" */
|
||
label?: string;
|
||
/** 输入框宽度 */
|
||
inputWidth?: string | object;
|
||
/** 是否显示标签图标 */
|
||
showIcon?: boolean;
|
||
/** 是否使用深色模式(强制覆盖 Chakra 颜色模式) */
|
||
isDarkMode?: boolean;
|
||
}
|
||
|
||
/**
|
||
* 交易日期选择器组件
|
||
*
|
||
* 提供日期输入框和最新交易日期提示,供概念中心、个股中心等页面复用。
|
||
* 快捷按钮(今天、昨天等)由各页面自行实现。
|
||
*/
|
||
const TradeDatePicker: React.FC<TradeDatePickerProps> = ({
|
||
value,
|
||
onChange,
|
||
defaultDate,
|
||
latestTradeDate,
|
||
minDate,
|
||
maxDate,
|
||
label = '交易日期',
|
||
inputWidth = { base: '100%', lg: '200px' },
|
||
showIcon = true,
|
||
isDarkMode = false,
|
||
}) => {
|
||
// 颜色主题 - 支持 isDarkMode 强制覆盖
|
||
const defaultLabelColor = useColorModeValue('purple.700', 'purple.300');
|
||
const defaultIconColor = useColorModeValue('purple.500', 'purple.400');
|
||
const defaultInputBorderColor = useColorModeValue('purple.200', 'purple.600');
|
||
const defaultTipBg = useColorModeValue('blue.50', 'blue.900');
|
||
const defaultTipBorderColor = useColorModeValue('blue.200', 'blue.600');
|
||
const defaultTipTextColor = useColorModeValue('blue.600', 'blue.200');
|
||
const defaultTipIconColor = useColorModeValue('blue.500', 'blue.300');
|
||
|
||
// 深色模式专用颜色
|
||
const darkModeColors = {
|
||
labelColor: 'white',
|
||
iconColor: 'cyan.400',
|
||
inputBorderColor: 'whiteAlpha.300',
|
||
inputBg: 'whiteAlpha.50',
|
||
inputColor: 'white',
|
||
tipBg: 'rgba(59, 130, 246, 0.15)',
|
||
tipBorderColor: 'blue.500',
|
||
tipTextColor: 'blue.200',
|
||
tipIconColor: 'blue.300',
|
||
};
|
||
|
||
// 根据 isDarkMode 选择颜色
|
||
const labelColor = isDarkMode ? darkModeColors.labelColor : defaultLabelColor;
|
||
const iconColor = isDarkMode ? darkModeColors.iconColor : defaultIconColor;
|
||
const inputBorderColor = isDarkMode ? darkModeColors.inputBorderColor : defaultInputBorderColor;
|
||
const tipBg = isDarkMode ? darkModeColors.tipBg : defaultTipBg;
|
||
const tipBorderColor = isDarkMode ? darkModeColors.tipBorderColor : defaultTipBorderColor;
|
||
const tipTextColor = isDarkMode ? darkModeColors.tipTextColor : defaultTipTextColor;
|
||
const tipIconColor = isDarkMode ? darkModeColors.tipIconColor : defaultTipIconColor;
|
||
|
||
// 使用默认日期初始化(仅在 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 minDateStr = minDate ? formatDateValue(minDate) : undefined;
|
||
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}
|
||
min={minDateStr}
|
||
max={maxDateStr}
|
||
width={inputWidth}
|
||
focusBorderColor="purple.400"
|
||
borderColor={inputBorderColor}
|
||
borderRadius="lg"
|
||
fontWeight="medium"
|
||
bg={isDarkMode ? darkModeColors.inputBg : undefined}
|
||
color={isDarkMode ? darkModeColors.inputColor : undefined}
|
||
_hover={{ borderColor: isDarkMode ? 'purple.400' : 'purple.300' }}
|
||
sx={isDarkMode ? {
|
||
'&::-webkit-calendar-picker-indicator': {
|
||
filter: 'invert(1)',
|
||
},
|
||
} : undefined}
|
||
/>
|
||
|
||
{/* 最新交易日期提示 */}
|
||
{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;
|