RMCalendar.ets 9.57 KB
import { RMCalendarBean } from './RMCalendarBean'
import { RMCalenderCell } from './RMCalendarCell'

const TAG = "RMCalendar"

@Component
export struct RMCalendar {
  @State selectItem: RMCalendarBean = new RMCalendarBean()
  private today: Date = new Date() // 当天
  // 开始日期
  startDate: Date = new Date()
  // 截止日期
  endDate: Date = new Date()
  //当前日期-当前显示的月份的第一天
  private startDay: Date = new Date(
    this.today.getFullYear(),
    this.today.getMonth(),
    1
  )
  // 是否有上一个月
  @State private hasPre: boolean = true
  // 是否有下一个月
  @State private hasNext: boolean = true
  // 标题栏高度
  titleHeight: Length = '50vp'
  // 星期标题
  weeks: string[] = ["日", "一", "二", "三", "四", "五", "六",]
  // 星期标题字体大小
  weekTitleFontSize: number | string | Resource = 12
  // 星期标题字体颜色
  weekTitleFontColor: ResourceColor = "#999999"
  // 星期标栏高度
  weekTitleHeight: Length = 30
  weekFontWeight: FontWeight = FontWeight.Bold
  // 标题字体大小
  titleFontSize: number | string | Resource = 16
  // 标题字体颜色
  titleFontColor: ResourceColor = "#333333"
  titleFontWeight: FontWeight = FontWeight.Bold
  // 日期每一项字体大小
  itemFontSize: number | string | Resource = 14
  itemFontColor: ResourceColor = "#333333"
  itemFontWeight: FontWeight = FontWeight.Bold
  // 今日字体颜色
  todayFontColor: ResourceColor = "#ED2800"
  // 不能使用的日期字体颜色
  disabledFontColor: ResourceColor = "#CCCCCC"
  // 选中日期字体颜色
  selectFontColor: ResourceColor = "#FFFFFF"
  // 选中日期背景颜色, 默认与todayFontColor一致
  selectItemBgColor: ResourceColor = "#ED2800"
  @State private title: string = ''
  // 计算的总加载
  @State dates: Array<RMCalendarBean> = new Array()
  // 已选日期
  @State selectedDates: Array<RMCalendarBean> = new Array()
  // 自定义每一项布局
  public cellLayout?: (item: RMCalendarBean) => void
  // 仅自定义 今日 样式,当使用cellLayout时,tadayLayout无效
  todayLayout?: (item: RMCalendarBean) => void
  // 计算item时,如需添加更多自定义属性时使用
  reBuildDateItem?: (item: RMCalendarBean) => RMCalendarBean
  // 选择变化监听,
  onDateChange?: (date1: RMCalendarBean) => void
  onMonthChange?: (after: Date, befor: Date) => void
  // 不可选中项的点击事件
  disableCellClick?: (item: RMCalendarBean) => void

  @Builder
  createWeekTitle(item: string) {
    Text(item)
      .textAlign(TextAlign.Center)
      .fontColor(this.weekTitleFontColor)
      .fontSize(this.weekTitleFontSize)
      .fontWeight(this.weekFontWeight)
      .layoutWeight(1)
  }

  @Builder
  createCell() {
    ForEach(this.dates, (item: RMCalendarBean) => {
      RMCalenderCell({
        item: item,
        cellLayout: this.cellLayout,
        itemFontSize: this.itemFontSize,
        itemFontColor: this.itemFontColor,
        today: this.today.getTime(),
        itemFontWeight: this.itemFontWeight,
        todayFontColor: this.todayFontColor,
        todayLayout: this.todayLayout,
        selectItem: $selectItem,
        selectFontColor: this.selectFontColor,
        selectItemBgColor: this.selectItemBgColor,
        selectedDates: $selectedDates,
        disabledFontColor: this.disabledFontColor,
        hasPre: this.hasPre,
        hasNext: this.hasNext,
        disableClick: (item: RMCalendarBean) => {
          if (this.disableCellClick) {
            this.disableCellClick(item)
          }
        },
        cellClick: (item: RMCalendarBean) => {
          if (item.isPre) {
            this.preMonth()
          } else if (item.isNext) {
            this.nextMonth()
          }
          if (this.onDateChange) {
            this.onDateChange(item)
            if (item.fullYear && item.month) {
              this.title = `${item.fullYear}年${item.month + 1}月`
            }
          }
        }
      })
        .width(`14.28%`)
    }, (item: RMCalendarBean) => JSON.stringify(item))
  }

  /**
   * 属性初始化
   */
  initAttr() {
    if (!this.selectItemBgColor) {
      this.selectItemBgColor = this.todayFontColor
    }
    this.today = new Date(
      this.today.getFullYear(),
      this.today.getMonth(),
      this.today.getDate(),
    )

    // 开始日期
    if (!this.startDate) {
      this.startDate = new Date(1970, 0, 1)
    }
    // 截止日期
    if (!this.endDate) {
      this.endDate = new Date(this.today.getFullYear() + 10, 11, 31)
    }

    if (this.today.getTime() < this.startDate.getTime()) {
      this.startDay.setTime(this.startDate.getTime())
    } else if (this.today.getTime() > this.endDate.getTime()) {
      this.startDay.setTime(this.endDate.getTime())
    } else {
      this.startDay.setTime(this.today.getTime())
    }
  }

  aboutToAppear() {
    this.initAttr()
    let temp = new RMCalendarBean()
    temp.time = this.today.getTime()
    this.selectItem = temp
    this.calcDatas()
  }

  /**
   * 下一个月
   */
  private nextMonth() {
    // this.dates.slice(0, this.dates.length)
    this.dates = []
    const beforDate = new Date(this.startDay.getFullYear(), this.startDay.getMonth())
    this.startDay.setMonth(this.startDay.getMonth() + 1)
    if (this.onMonthChange) {
      this.onMonthChange(new Date(this.startDay.getFullYear(), this.startDay.getMonth()), beforDate)
    }
    this.calcDatas()
  }

  /**
   * 上一个月
   */
  private preMonth() {
    // this.dates.slice(0, this.dates.length)
    this.dates = []
    const beforDate = new Date(this.startDay.getFullYear(), this.startDay.getMonth())
    this.startDay.setMonth(this.startDay.getMonth() - 1)
    if (this.onMonthChange) {
      this.onMonthChange(new Date(this.startDay.getFullYear(), this.startDay.getMonth()), beforDate)
    }
    this.calcDatas()
  }

  /**
   * 具体计算
   */
  private calcDatas() {
    const startDay = this.startDay
    this.title = `${startDay.getFullYear()}年${startDay.getMonth() + 1}月`
    startDay.setDate(1)

    if (startDay.getFullYear() < this.startDate.getFullYear()
      || (startDay.getFullYear() == this.startDate.getFullYear() && startDay.getMonth() <= this.startDate.getMonth())) {
      this.hasPre = false
    } else {
      this.hasPre = true
    }

    if (startDay.getFullYear() > this.endDate.getFullYear()
      || (startDay.getFullYear() == this.endDate.getFullYear() && startDay.getMonth() >= this.endDate.getMonth())) {
      this.hasNext = false
    } else {
      this.hasNext = true
    }

    // 计算第一个月
    // 获取第一个月总天数
    let endDay: Date = new Date(
      startDay.getFullYear(),
      startDay.getMonth() + 1,
      0, 23, 59, 59)

    let tempDate: Date = new Date(
      startDay.getFullYear(),
      startDay.getMonth(),
      startDay.getDate()
    )

    const count = endDay.getDate()
    const preCount = startDay.getDay()
    // const nextCount = 6 - endDay.getDay()
    const nextCount = 0
    const finilCount = count + preCount + nextCount

    // 补齐上一个月
    tempDate.setDate(tempDate.getDate() - preCount)

    // 添加日期
    for (let index = 0; index < finilCount; index++) {
      let item = new RMCalendarBean(
        tempDate.getFullYear(),
        tempDate.getMonth(),
        tempDate.getDate(),
        tempDate.getDay(),
        tempDate.getTime(),
        // @ ts-ignore
        // LunarCalendar.convertSolarToLunar(tempDate),
        (index < preCount ? true : false) || this.startDate.getTime() > tempDate.getTime(),
        (index >= preCount + count ? true : false) || this.endDate.getTime() < tempDate.getTime(),
      )
      if (this.reBuildDateItem) {
        this.reBuildDateItem(item)
      }
      this.dates.push(item)
      tempDate.setDate(tempDate.getDate() + 1)
    }
  }

  build() {
    Column() {
      Image($r("app.media.iv_e_news_pager_calendar_arrow_up"))
        .width(18).height(8.5)
      Column() {
        Row() {
          Column() {
            Image(this.hasPre ? $r("app.media.iv_e_news_pager_calendar_arrow_pre")
              : $r("app.media.iv_e_news_pager_calendar_arrow_pre_gray"))
              .width(22)
              .aspectRatio(1)
          }
          .justifyContent(FlexAlign.Center)
          .height("100%")
          .aspectRatio(1)
          .onClick(() => {
            if (this.hasPre) {
              this.preMonth()
            }
          })

          Blank()
          Row() {
            Text(this.title)
              .fontSize(this.titleFontSize)
              .fontColor(this.titleFontColor)
              .fontWeight(this.titleFontWeight)
          }

          Blank()
          Column() {
            Image(this.hasNext ? $r("app.media.iv_e_news_pager_calendar_arrow_next")
              : $r("app.media.iv_e_news_pager_calendar_arrow_next_gray"))
              .width(22)
              .aspectRatio(1)
          }
          .justifyContent(FlexAlign.Center)
          .height("100%")
          .aspectRatio(1)
          .onClick(() => {
            if (this.hasNext) {
              this.nextMonth()
            }
          })
        }
        .alignItems(VerticalAlign.Center)
        .width("100%")
        .height(this.titleHeight)

        // 星期title
        Row() {
          ForEach(this.weeks, (item: string) => {
            this.createWeekTitle(item)
          }, (item: string) => {
            return item
          })
        }
        .alignItems(VerticalAlign.Center)
        .height(this.weekTitleHeight)

        Flex({ wrap: FlexWrap.Wrap }) {
          this.createCell()
        }
        .width("100%")
      }
      .backgroundColor(Color.White)
      .margin({
        left: 35,
        right: 35
      })
      .border({ radius: 4 })
    }
  }
}