import dayjs, { type Dayjs } from 'dayjs'; import React from 'react'; import { Lunar, HolidayUtil } from 'lunar-typescript'; import { createStyles } from 'antd-style'; import classNames from 'classnames'; import { Calendar, Col, Radio, Row, Select } from 'antd'; import type { CalendarProps } from 'antd'; const useStyle = createStyles(({ token, css, cx }) => { const lunar = css` color: ${token.colorTextTertiary}; font-size: ${token.fontSizeSM}px; `; return { wrapper: css` width: 450px; border: 1px solid ${token.colorBorderSecondary}; border-radius: ${token.borderRadiusOuter}; padding: 5px; `, dateCell: css` position: relative; &:before { content: ''; position: absolute; left: 0; right: 0; top: 0; bottom: 0; margin: auto; max-width: 40px; max-height: 40px; background: transparent; transition: background 300ms; border-radius: ${token.borderRadiusOuter}px; border: 1px solid transparent; box-sizing: border-box; } &:hover:before { background: rgba(0, 0, 0, 0.04); } `, today: css` &:before { border: 1px solid ${token.colorPrimary}; } `, text: css` position: relative; z-index: 1; `, lunar, current: css` color: ${token.colorTextLightSolid}; &:before { background: ${token.colorPrimary}; } &:hover:before { background: ${token.colorPrimary}; opacity: 0.8; } .${cx(lunar)} { color: ${token.colorTextLightSolid}; opacity: 0.9; } `, monthCell: css` width: 120px; color: ${token.colorTextBase}; border-radius: ${token.borderRadiusOuter}px; padding: 5px 0; &:hover { background: rgba(0, 0, 0, 0.04); } `, monthCellCurrent: css` color: ${token.colorTextLightSolid}; background: ${token.colorPrimary}; &:hover { background: ${token.colorPrimary}; opacity: 0.8; } `, }; }); const App: React.FC = () => { const { styles } = useStyle({ test: true }); const [selectDate, setSelectDate] = React.useState(dayjs()); const onPanelChange = (value: Dayjs, mode: CalendarProps['mode']) => { console.log(value.format('YYYY-MM-DD'), mode); }; const onDateChange: CalendarProps['onSelect'] = (value, selectInfo) => { if (selectInfo.source === 'date') { setSelectDate(value); } }; const cellRender: CalendarProps['fullCellRender'] = (date, info) => { const d = Lunar.fromDate(date.toDate()); const lunar = d.getDayInChinese(); const solarTerm = d.getJieQi(); const h = HolidayUtil.getHoliday(date.get('year'), date.get('month') + 1, date.get('date')); const displayHoliday = h?.getTarget() === h?.getDay() ? h?.getName() : undefined; if (info.type === 'date') { return React.cloneElement(info.originNode, { ...info.originNode.props, className: classNames(styles.dateCell, { [styles.current]: selectDate.isSame(date, 'date'), [styles.today]: date.isSame(dayjs(), 'date'), }), children: (
{date.get('date')} {info.type === 'date' && (
{displayHoliday || solarTerm || lunar}
)}
), }); } if (info.type === 'month') { // Due to the fact that a solar month is part of the lunar month X and part of the lunar month X+1, // when rendering a month, always take X as the lunar month of the month const d2 = Lunar.fromDate(new Date(date.get('year'), date.get('month'))); const month = d2.getMonthInChinese(); return (
{date.get('month') + 1}月({month}月)
); } }; const getYearLabel = (year: number) => { const d = Lunar.fromDate(new Date(year + 1, 0)); return `${d.getYearInChinese()}年(${d.getYearInGanZhi()}${d.getYearShengXiao()}年)`; }; const getMonthLabel = (month: number, value: Dayjs) => { const d = Lunar.fromDate(new Date(value.year(), month)); const lunar = d.getMonthInChinese(); return `${month + 1}月(${lunar}月)`; }; return (
{ const start = 0; const end = 12; const monthOptions = []; let current = value.clone(); const localeData = value.localeData(); const months = []; for (let i = 0; i < 12; i++) { current = current.month(i); months.push(localeData.monthsShort(current)); } for (let i = start; i < end; i++) { monthOptions.push({ label: getMonthLabel(i, value), value: i, }); } const year = value.year(); const month = value.month(); const options = []; for (let i = year - 10; i < year + 10; i += 1) { options.push({ label: getYearLabel(i), value: i, }); } return ( { const now = value.clone().month(newMonth); onChange(now); }} /> onTypeChange(e.target.value)} value={type} > ); }} />
); }; export default App;