render.js 10.4 KB
import Day from './day'
import Todo from './todo'
import WxData from './wxData'
import convertSolarLunar from './convertSolarLunar'
import {
  Logger,
  GetDate,
  delRepeatedEnableDay,
  getDateTimeStamp,
  converEnableDaysToTimestamp
} from './utils'

const getDate = new GetDate()
const logger = new Logger()

class Calendar extends WxData {
  constructor(component) {
    super(component)
    this.Component = component
  }
  getCalendarConfig() {
    return this.Component.config
  }
  /**
   * 渲染日历
   * @param {number} curYear 年份
   * @param {number} curMonth  月份
   * @param {number} curDate  日期
   * @param {boolean} disableSelect 是否禁用选中
   */
  renderCalendar(curYear, curMonth, curDate, disableSelect) {
    return new Promise(resolve => {
      const config = this.getCalendarConfig()
      this.calculateEmptyGrids(curYear, curMonth)
      this.calculateDays(curYear, curMonth, curDate, disableSelect).then(() => {
        const { todoLabels, specialStyleDates, enableDays, selectedDay } =
          this.getData('calendar') || {}
        if (
          todoLabels &&
          todoLabels.find(
            item => +item.month === +curMonth && +item.year === +curYear
          )
        ) {
          Todo(this.Component).setTodoLabels()
        }
        if (
          specialStyleDates &&
          specialStyleDates.length &&
          specialStyleDates.find(
            item => +item.month === +curMonth && +item.year === +curYear
          )
        ) {
          Day(this.Component).setDateStyle(specialStyleDates)
        }

        if (
          enableDays &&
          enableDays.length &&
          enableDays.find(item => {
            let ymd = item.split('-')
            return +ymd[1] === +curMonth && +ymd[0] === +curYear
          })
        ) {
          Day(this.Component).enableDays(enableDays)
        }

        if (
          selectedDay &&
          selectedDay.length &&
          selectedDay.find(
            item => +item.month === +curMonth && +item.year === +curYear
          ) &&
          config.mulit
        ) {
          Day(this.Component).setSelectedDays(selectedDay)
        }

        if (!this.Component.firstRender) {
          resolve({
            firstRender: true
          })
        } else {
          resolve({
            firstRender: false
          })
        }
      })
    })
  }
  /**
   * 计算当前月份前后两月应占的格子
   * @param {number} year 年份
   * @param {number} month 月份
   */
  calculateEmptyGrids(year, month) {
    this.calculatePrevMonthGrids(year, month)
    this.calculateNextMonthGrids(year, month)
  }
  /**
   * 计算上月应占的格子
   * @param {number} year 年份
   * @param {number} month 月份
   */
  calculatePrevMonthGrids(year, month) {
    let empytGrids = []
    const prevMonthDays = getDate.thisMonthDays(year, month - 1)
    let firstDayOfWeek = getDate.firstDayOfWeek(year, month)
    const config = this.getCalendarConfig() || {}
    if (config.firstDayOfWeek === 'Mon') {
      if (firstDayOfWeek === 0) {
        firstDayOfWeek = 6
      } else {
        firstDayOfWeek -= 1
      }
    }
    if (firstDayOfWeek > 0) {
      const len = prevMonthDays - firstDayOfWeek
      const { onlyShowCurrentMonth } = config
      const { showLunar } = this.getCalendarConfig()
      for (let i = prevMonthDays; i > len; i--) {
        if (onlyShowCurrentMonth) {
          empytGrids.push('')
        } else {
          empytGrids.push({
            day: i,
            lunar: showLunar
              ? convertSolarLunar.solar2lunar(year, month - 1, i)
              : null
          })
        }
      }
      this.setData({
        'calendar.empytGrids': empytGrids.reverse()
      })
    } else {
      this.setData({
        'calendar.empytGrids': null
      })
    }
  }
  /**
   * 计算下一月日期是否需要多展示的日期
   * 某些月份日期为5排,某些月份6排,统一为6排
   * @param {number} year
   * @param {number} month
   * @param {object} config
   */
  calculateExtraEmptyDate(year, month, config) {
    let extDate = 0
    if (+month === 2) {
      extDate += 7
      let firstDayofMonth = getDate.dayOfWeek(year, month, 1)
      if (config.firstDayOfWeek === 'Mon') {
        if (+firstDayofMonth === 1) extDate += 7
      } else {
        if (+firstDayofMonth === 0) extDate += 7
      }
    } else {
      let firstDayofMonth = getDate.dayOfWeek(year, month, 1)
      if (config.firstDayOfWeek === 'Mon') {
        if (firstDayofMonth !== 0 && firstDayofMonth < 6) {
          extDate += 7
        }
      } else {
        if (firstDayofMonth <= 5) {
          extDate += 7
        }
      }
    }
    return extDate
  }
  /**
   * 计算下月应占的格子
   * @param {number} year 年份
   * @param {number} month  月份
   */
  calculateNextMonthGrids(year, month) {
    let lastEmptyGrids = []
    const thisMonthDays = getDate.thisMonthDays(year, month)
    let lastDayWeek = getDate.dayOfWeek(year, month, thisMonthDays)
    const config = this.getCalendarConfig() || {}
    if (config.firstDayOfWeek === 'Mon') {
      if (lastDayWeek === 0) {
        lastDayWeek = 6
      } else {
        lastDayWeek -= 1
      }
    }
    let len = 7 - (lastDayWeek + 1)
    const { onlyShowCurrentMonth, showLunar } = config
    if (!onlyShowCurrentMonth) {
      len = len + this.calculateExtraEmptyDate(year, month, config)
    }
    for (let i = 1; i <= len; i++) {
      if (onlyShowCurrentMonth) {
        lastEmptyGrids.push('')
      } else {
        lastEmptyGrids.push({
          day: i,
          lunar: showLunar
            ? convertSolarLunar.solar2lunar(year, month + 1, i)
            : null
        })
      }
    }
    this.setData({
      'calendar.lastEmptyGrids': lastEmptyGrids
    })
  }
  /**
   * 日历初始化将默认值写入 selectDay
   * @param {number} year
   * @param {number} month
   * @param {number} curDate
   */
  setSelectedDay(year, month, curDate) {
    let selectedDay = []
    const config = this.getCalendarConfig()
    if (config.noDefault) {
      selectedDay = []
      config.noDefault = false
    } else {
      const data = this.getData('calendar') || {}
      const { showLunar } = this.getCalendarConfig()
      selectedDay = curDate
        ? [
            {
              year,
              month,
              day: curDate,
              choosed: true,
              week: getDate.dayOfWeek(year, month, curDate),
              lunar: showLunar
                ? convertSolarLunar.solar2lunar(year, month, curDate)
                : null
            }
          ]
        : data.selectedDay
    }
    return selectedDay
  }
  __getDisableDateTimestamp() {
    let disableDateTimestamp
    const { date, type } = this.getCalendarConfig().disableMode || {}
    if (date) {
      const t = date.split('-')
      if (t.length < 3) {
        logger.warn('配置 disableMode.date 格式错误')
        return {}
      }
      disableDateTimestamp = getDateTimeStamp({
        year: +t[0],
        month: +t[1],
        day: +t[2]
      })
    }
    return {
      disableDateTimestamp,
      disableType: type
    }
  }
  resetDates() {
    this.setData({
      'calendar.days': []
    })
  }
  /**
   * 设置日历面板数据
   * @param {number} year 年份
   * @param {number} month  月份
   * @param {number} curDate  日期
   * @param {boolean} disableSelect 是否禁用选中
   */
  calculateDays(year, month, curDate, disableSelect) {
    return new Promise(resolve => {
      // 避免切换日期时样式残影
      this.resetDates()
      let days = []
      const {
        disableDays = [],
        chooseAreaTimestamp = [],
        selectedDay: selectedDates = []
      } = this.getData('calendar')
      days = Day(this.Component).buildDate(year, month)
      let selectedDay = selectedDates
      if (!disableSelect) {
        selectedDay = this.setSelectedDay(year, month, curDate)
      }
      const selectedDayStr = selectedDay.map(d => getDate.toTimeStr(d))
      const disableDaysStr = disableDays.map(d => getDate.toTimeStr(d))
      const [areaStart, areaEnd] = chooseAreaTimestamp
      days.forEach(item => {
        const cur = getDate.toTimeStr(item)
        const timestamp = getDateTimeStamp(item)
        if (selectedDayStr.includes(cur) && !disableSelect) {
          item.choosed = true
          if (timestamp > areaEnd || timestamp < areaStart) {
            const idx = selectedDay.findIndex(
              selectedDate =>
                getDate.toTimeStr(selectedDate) === getDate.toTimeStr(item)
            )
            selectedDay.splice(idx, 1)
          }
        } else if (
          areaStart &&
          areaEnd &&
          timestamp >= areaStart &&
          timestamp <= areaEnd &&
          !disableSelect
        ) {
          item.choosed = true
          selectedDay.push(item)
        }
        if (disableDaysStr.includes(cur)) item.disable = true

        const {
          disableDateTimestamp,
          disableType
        } = this.__getDisableDateTimestamp()
        let disabelByConfig = false
        if (disableDateTimestamp) {
          if (
            (disableType === 'before' && timestamp < disableDateTimestamp) ||
            (disableType === 'after' && timestamp > disableDateTimestamp)
          ) {
            disabelByConfig = true
          }
        }
        const isDisable = disabelByConfig || this.__isDisable(timestamp)
        if (isDisable) {
          item.disable = true
          item.choosed = false
        }
      })
      this.setData(
        {
          'calendar.days': days,
          'calendar.selectedDay': [...selectedDay] || []
        },
        () => {
          resolve()
        }
      )
    })
  }
  __isDisable(timestamp) {
    const {
      enableArea = [],
      enableDays = [],
      enableAreaTimestamp = []
    } = this.getData('calendar')
    let setDisable = false
    let expectEnableDaysTimestamp = converEnableDaysToTimestamp(enableDays)
    if (enableArea.length) {
      expectEnableDaysTimestamp = delRepeatedEnableDay(enableDays, enableArea)
    }
    if (enableAreaTimestamp.length) {
      if (
        (+enableAreaTimestamp[0] > +timestamp ||
          +timestamp > +enableAreaTimestamp[1]) &&
        !expectEnableDaysTimestamp.includes(+timestamp)
      ) {
        setDisable = true
      }
    } else if (
      expectEnableDaysTimestamp.length &&
      !expectEnableDaysTimestamp.includes(+timestamp)
    ) {
      setDisable = true
    }
    return setDisable
  }
}

export default component => new Calendar(component)