antd Calendar组件个性化

灵感相当于自己对每个日历格进行了样式个性化设置,所以自由度挺高的。

效果图中圈中的地方都是可自定义的

效果图

相关代码

1. 引入相关资源,还需要再建个index.less

import { Calendar, Badge, Typography, Tooltip } from 'antd';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
import type { CalendarProps } from 'antd';
import { ProFormDatePicker, ProForm } from "@ant-design/pro-components";
import moment from 'moment';
import React, { useRef, useState } from 'react';
import './index.less'; // !!!这个是需要的


// 日历格的个性化操作,几乎都在这里研发  这个在组件外
const useStyle = createStyles(({ token, css }) => {
  return {
    wrapper: css`
      width: 450px;
      border: 1px solid ${token.colorBorderSecondary};
      border-radius: 0px;
      padding: 5px;
    `,
    dateCell: css`
      margin-top: 6px;
      box-sizing: border-box;
      position: relative;
      height: 100%;
      width:96%;
      margin-left: 2%;
      text-align: right;
      padding:0 6px 6px 6px;
      overflow: hidden;
      border-top: 2px solid #f0f0f0;
      &:before {
        content: '';
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
        margin: auto;
        width:100%;
        height: 100%;
        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);
      }
    `,
    dateWeekCell: css`
      margin-top: 6px;
      box-sizing: border-box;
      position: relative;
      height: 100%;
      width:96%;
      margin-left: 2%;
      text-align: right;
      padding:0 6px 6px 6px;
      overflow: hidden;
      border-top: 2px solid #f0f0f0;
      &:before {
        content: '';
        position: absolute;
        left: 0;
        right: 0;
        top: 0;
        bottom: 0;
        margin: auto;
        width:100%;
        height: 100%;
        background: rgba(251, 194, 28,.1);
        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`
      border-top: 2px solid #0079fe;
      &:before {
        background: transparent;
      }
      &:hover:before {
        background: rgba(0, 0, 0, 0.04);
      }
    `,
    text: css`
      position: relative;
      z-index: 1;
    `,
    current: css`
      color: ${token.colorPrimary};
      height: 100%;
      width:96%;
      margin-left: 2%;
      border-top: 2px solid #d5e6f0;
      &:before {
        background: ${token.colorPrimary};
        opacity: 0.1;
      }
      &:hover:before {
        background: ${token.colorPrimary};
        opacity: 0.1;
      }
    `,
    plan: css`
      border: 2px solid ${token.colorError};
    `,
    title: css`
      font-size: 16px;
      position: absolute;
      z-index: 5;
      top:-5px;
      right:5px;
      height: 20px;
    `,
    bigTitle: css`
      font-size: 20px;
      position: absolute;
      width:100%;
      text-align: center;
      z-index: 5;
      min-height: 100px;
      height: calc(15vh - 20px - 8px);
      display: grid;
      place-items: center;
    `,
    weekend: css`
      &.gray {
        opacity: 0.4;
      }
    `,
  };
});

2.  index.less相关代码

.my-calendar .ant-picker-calendar .ant-picker-content td{
  min-height: 150px;
  height: 15vh;
  padding: 0;
  box-sizing: border-box;
}
.my-calendar .ant-picker-calendar .ant-picker-cell::before{
  width: 100%;
  height: 100%;
  padding: 0;
}

.my-calendar  .calendar-date{
  position: absolute;
  top: 20px;
  overflow: auto;
  width: 100%;
  min-height: 80px;
  height: calc(15vh - 20px - 8px);
  text-align: left;
  padding-bottom: 10px;
  color: #000;
}

3. 相关个性化日历操作代码

1const { styles } = useStyle({ test: true });
  const [selectDate, setSelectDate] = useState(moment());
  const [calendardata, setCalendarData] = useState<API.LogWindTurbineData | null>(null);

  //查询或首次触发的请求当月的日时间数据的函数 
  const onFinish = async () => {
    try {
      const values = await formRef.current?.validateFieldsReturnFormatValue?.();
      // 一般传月份
      const res = await handelList(values); // 自己的请求接口
      if (res.data) {
        setCalendarData(data)
        // data数据格式 {'2023-12-23':[{},{}],2023-12-24':[{},{}]}
      } else {
        setCalendarData(null)
      }

    } catch (err) {
      console.log('err', err)
    }
  }


  // 事件相关的展示
  const dateCellRender = (value: any) => {
    const date = value.format('YYYY-MM-DD') + '';
    const listData = calendardata ? calendardata[date] ? calendardata[date] : [] : [];
    const hasData = listData.length > 0
    // warning success  error
    if (hasData) {
      return <Tooltip title={`${date}`}>
        <ul className="calendar-date">
          {listData.map((item, index) => (
            <li key={item.content + index}>
              <Badge status="warning" text={item.content} />
            </li>
          ))}
        </ul>
      </Tooltip>
    }
  };


 // 日历格相关的,所有样式在这里处理
  const cellRender: CalendarProps<any>['fullCellRender'] = (date, info) => {
    const isWeekend = date.day() === 6 || date.day() === 0;
    // 判断这天是否有数据
    const hasData = (vdate: any) => {
      const fdate = vdate.format('YYYY-MM-DD') + '';
      const listData = calendardata ? calendardata[fdate] ? calendardata[fdate] : [] : [];
      return listData.length > 0
    }

    if (info.type === 'date') {
      return React.cloneElement(info.originNode, {
        ...info.originNode.props,
        className: classNames(isWeekend ? styles.dateWeekCell : styles.dateCell, {
          [styles.current]: selectDate.isSame(date, 'date'),
          [styles.today]: date.isSame(moment(), 'date'),
        }),
        children: (
          <div className={styles.text}>
            <div
              className={classNames({
                [styles.weekend]: isWeekend,
                [styles.title]: hasData(date),
                [styles.bigTitle]: !hasData(date)
              })}
            >
              {date.get('date')}
            </div>
            {info.type === 'date' && dateCellRender(date)}
          </div>
        ),
      });
    }

    return info.originNode;
  };


  const disabledDate = (current: any) => {
    return current && current > new Date(new Date().getTime());
  };

  const onPanelChange = (value: any) => {
    // 这里是换页时,请求操作,因为我自定义了自定义日历头部内容。
    const date = value.format('YYYY-MM') + '';
    formRef.current?.setFieldsValue({ 'yearMonth': date });
    onFinish()
  }

  // 点击单个日历格时的操作,会对当前点击日历格的样式发生变化
  const onSelectCalendar = (value: any, selectInfo: any) => {
    if (selectInfo.source === 'date') {
      setSelectDate(value);
    }

  }

4. 组件使用

  return (
    <div className='my-calendar'>
      <Calendar style={{ padding: '0 20px' }}
        fullCellRender={cellRender}
        fullscreen={false}
        onSelect={onSelectCalendar}
        onPanelChange={onPanelChange} disabledDate={disabledDate}
        headerRender={({ value, onChange }) => {
          return (
            <div style={{ padding: 15, display: 'flex', justifyContent: 'spacebetween'}}>
              <ProForm submitter={false} autoFocusFirstInput={false} layout='horizontal' formRef={formRef} style={{ display: 'flex' }}>
                  <ProFormDatePicker.Month
                    name="yearMonth"
                    label="月份:"
                    allowClear={false}
                    fieldProps={{
                      format: (value) => value.format('YYYY-MM'),
                      disabledDate: disabledDate,
                      onChange: (value) => {
                        onChange(value)
                        onFinish()
                      }
                    }}
                    initialValue={value.format('YYYY-MM')}
                    rules={[
                      {
                        required: true,
                        message: '请选择时间!',
                      },
                    ]}
                  />
                </ProForm>
              </div>
          );
        }}
      />
    </div>
  )

  • 6
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值