效果展示:
在父组件中使用:
<DatePicker
minDate={new Date(selectCompany?.node?.createdAt)}
//关闭该时间选择器
onClose={setShowDatePicker.off}
//传递已经选择好的时间
selectedDate={selectedDate}
//将选择好的时间置空,并且关闭该时间选择器
onClear={() => {
setSelectedDate(null);
setShowDatePicker.off();
}}
//将选择好的时间传递给父组件
setSelectedDate={(date: Date) => {
setSelectedDate(date);
setValue("month", format(date, "yyyy/M"));
}}
/>
DatePicker组件:
type PropsType = {
minDate: Date;
onClear: () => void;
onClose: () => void;
selectedDate: Date | null;
setSelectedDate: (date: Date) => void;
};
export const DatePicker: React.FC<PropsType> = ({
minDate,
onClose,
setSelectedDate,
selectedDate,
onClear
}) => {
const toast = useToast();
const { t } = useTranslation(["admin"]);
const [step, setStep] = useState(1);
const yearRef = useRef<HTMLButtonElement | null>(null);
const currentYear = new Date().getFullYear();
const currentMonth = new Date().getMonth() + 1;
const [selectedYear, setSelectedYear] = useState<number | null>(
selectedDate ? new Date(selectedDate).getFullYear() : currentYear
);
const [selectedMonth, setSelectedMonth] = useState<number | null>(
selectedDate ? new Date(selectedDate).getMonth() + 1 : currentMonth
);
const years = Array.from(
{ length: currentYear - 1990 + 1 },
(_, index) => 1990 + index
).reverse();
const months = Array.from({ length: 12 }, (_, index) => index + 1);
const handleSet = useCallback(
(month: number) => {
if (selectedYear && month) {
const enterpriseRegisterYear = new Date(minDate).getFullYear();
const enterpriseRegisterMonth = new Date(minDate).getMonth() + 1;
if (
new Date(selectedYear + "-" + month) >=
new Date(enterpriseRegisterYear + "-" + enterpriseRegisterMonth) &&
new Date(selectedYear + "-" + month) <= new Date()
) {
setSelectedDate(new Date(selectedYear + "-" + month));
setSelectedMonth(month);
} else {
//错误消息提示
toast({
position: "top",
status: "error",
duration: 3000,
isClosable: true,
description:
enterpriseRegisterYear +
t("csv.year") +
"-" +
enterpriseRegisterMonth +
t("csv.month") +
t("csv.monthMessage")
});
}
onClose();
}
},
[minDate, onClose, selectedYear, setSelectedDate, t, toast]
);
useEffect(() => {
if (yearRef.current && step === 1) {
yearRef.current.scrollIntoView({ block: "center" });
}
}, [step]);
return (
<Flex
width="100%"
p="20px"
flexDir="column"
boxShadow="1px 1px 2px 2px #bebebe"
pos="absolute"
backgroundColor={theme.text.white}
zIndex="2"
>
<Flex
align="center"
p="0 5px 5px"
maxW="140px"
cursor="pointer"
mb="10px"
onClick={() => {
step === 1 ? setStep(2) : setStep(1);
}}
>
<Text mr="10px" fontWeight="700">
{selectedYear + t("csv.year") + selectedMonth + t("csv.month")}
</Text>
<ChevronDownIcon
transform={step === 1 ? "rotate(180deg)" : "rotate(0deg)"}
transition="transform 0.5s ease-in-out"
/>
</Flex>
{step === 1 && (
<Flex
overflowY="scroll"
display="grid"
gap="15px"
gridTemplateColumns="1fr 1fr 1fr 1fr"
h="200px"
>
{years.map((year, idx) => (
<Button
key={idx}
p="5px 0"
borderRadius="20px"
bgColor={selectedYear === year ? theme.service.turquoise400 : theme.text.white}
onClick={() => {
setSelectedYear(year);
setStep(2);
}}
ref={selectedYear === year ? yearRef : null}
>
<Text
textAlign="center"
color={selectedYear === year ? theme.text.white : theme.text.black900}
>
{year + "年"}
</Text>
</Button>
))}
</Flex>
)}
{step === 2 && (
<Flex
display="grid"
columnGap="20px"
rowGap="10px"
gridTemplateColumns="1fr 1fr 1fr"
h="200px"
>
{months.map((month, idx) => (
<Button
key={idx}
bgColor={selectedMonth === month ? theme.service.turquoise400 : theme.text.white}
p="5px 0"
borderRadius="20px"
onClick={() => {
handleSet(month);
}}
>
<Text
textAlign="center"
color={selectedMonth === month ? theme.text.white : theme.text.black900}
>
{month + "月"}
</Text>
</Button>
))}
</Flex>
)}
<Flex justify="space-between" mt="10px">
<Button
onClick={() => {
setSelectedMonth(null);
setSelectedYear(null);
onClear();
}}
>
取消
</Button>
<Flex gap="20px">
<Button
onClick={() => {
setSelectedDate(new Date());
onClose();
}}
>
今月
</Button>
</Flex>
</Flex>
</Flex>
);
};