time-range.js 9.05 KB
/**
 * @Author: drfu*
 * @Description: 时间区域选择
 * @Date: 2020-10-08 21:22:09*
 * @Last Modified by: drfu
 * @Last Modified time: 2020-10-11 13:56:32
 * */

import { renderCalendar } from '../render'
import {
  logger,
  dateUtil,
  getCalendarConfig,
  getCalendarData
} from '../utils/index'

function pusheNextMonthDateArea(
  dateInfo = {},
  startTimestamp,
  endTimestamp,
  selectedDates = []
) {
  let tempOfSelectedDate = [...selectedDates]
  const dates = dateUtil.calcDates(dateInfo.year, dateInfo.month)
  let datesLen = dates.length
  for (let i = 0; i < datesLen; i++) {
    const date = dates[i]
    const timeStamp = dateUtil.getTimeStamp(date)
    if (timeStamp <= endTimestamp && timeStamp >= startTimestamp) {
      tempOfSelectedDate.push({
        ...date,
        choosed: true
      })
    }
    if (i === datesLen - 1 && timeStamp < endTimestamp) {
      tempOfSelectedDate = pusheNextMonthDateArea(
        dateUtil.getNextMonthInfo(date),
        startTimestamp,
        endTimestamp,
        tempOfSelectedDate
      )
    }
  }
  return tempOfSelectedDate
}
function pushPrevMonthDateArea(
  dateInfo = {},
  startTimestamp,
  endTimestamp,
  selectedDates = []
) {
  let tempOfSelectedDate = [...selectedDates]
  const dates = dateUtil.sortDatesByTime(
    dateUtil.calcDates(dateInfo.year, dateInfo.month),
    'desc'
  )
  let datesLen = dates.length
  let firstDate = dateUtil.getTimeStamp(dates[0])
  for (let i = 0; i < datesLen; i++) {
    const date = dates[i]
    const timeStamp = dateUtil.getTimeStamp(date)
    if (timeStamp >= startTimestamp && timeStamp <= endTimestamp) {
      tempOfSelectedDate.push({
        ...date,
        choosed: true
      })
    }
    if (i === datesLen - 1 && firstDate > startTimestamp) {
      tempOfSelectedDate = pushPrevMonthDateArea(
        dateUtil.getPrevMonthInfo(date),
        startTimestamp,
        endTimestamp,
        tempOfSelectedDate
      )
    }
  }
  return tempOfSelectedDate
}
/**
 * 当设置日期区域非当前时保存其他月份的日期至已选日期数组
 * @param {object} info
 */
function calcDateWhenNotInOneMonth(info) {
  const { firstDate, lastDate, startTimestamp, endTimestamp } = info
  let { selectedDate } = info
  if (dateUtil.getTimeStamp(firstDate) > startTimestamp) {
    selectedDate = pushPrevMonthDateArea(
      dateUtil.getPrevMonthInfo(firstDate),
      startTimestamp,
      endTimestamp,
      selectedDate
    )
  }
  if (dateUtil.getTimeStamp(lastDate) < endTimestamp) {
    selectedDate = pusheNextMonthDateArea(
      dateUtil.getNextMonthInfo(lastDate),
      startTimestamp,
      endTimestamp,
      selectedDate
    )
  }
  return [...selectedDate]
}

/**
 *  指定日期区域转时间戳
 * @param {array} timearea 时间区域
 */
export function convertTimeRangeToTimestamp(timearea = []) {
  const start = timearea[0].split('-')
  const end = timearea[1].split('-')
  if (start.length !== 3 || end.length !== 3) {
    logger.warn('enableArea() 参数格式为: ["2018-2-1", "2018-3-1"]')
    return {}
  }
  const startTimestamp = dateUtil
    .newDate(start[0], start[1], start[2])
    .getTime()
  const endTimestamp = dateUtil.newDate(end[0], end[1], end[2]).getTime()
  return {
    start,
    end,
    startTimestamp,
    endTimestamp
  }
}

/**
 * 校验时间区域是否合法
 * @param {array} dateArea 时间区域
 */
function validateTimeRange(dateArea) {
  const {
    start,
    end,
    startTimestamp,
    endTimestamp
  } = convertTimeRangeToTimestamp(dateArea)
  if (!start || !end) return
  const startMonthDays = dateUtil.getDatesCountOfMonth(start[0], start[1])
  const endMonthDays = dateUtil.getDatesCountOfMonth(end[0], end[1])
  if (start[2] > startMonthDays || start[2] < 1) {
    logger.warn('enableArea() 开始日期错误,指定日期不在当前月份天数范围内')
    return false
  } else if (start[1] > 12 || start[1] < 1) {
    logger.warn('enableArea() 开始日期错误,月份超出1-12月份')
    return false
  } else if (end[2] > endMonthDays || end[2] < 1) {
    logger.warn('enableArea() 截止日期错误,指定日期不在当前月份天数范围内')
    return false
  } else if (end[1] > 12 || end[1] < 1) {
    logger.warn('enableArea() 截止日期错误,月份超出1-12月份')
    return false
  } else if (startTimestamp > endTimestamp) {
    logger.warn('enableArea()参数最小日期大于了最大日期')
    return false
  } else {
    return true
  }
}

export default () => {
  return {
    name: 'timeRange',
    beforeRender(calendarData = {}, calendarConfig = {}) {
      const {
        chooseAreaTimestamp = [],
        dates = [],
        selectedDates = []
      } = calendarData
      let __dates = dates
      let __selectedDates = selectedDates
      const [startDateTimestamp, endDateTimestamp] = chooseAreaTimestamp
      if (chooseAreaTimestamp.length === 2) {
        __selectedDates = []
        __dates = dates.map(d => {
          const date = { ...d }
          const dateTimeStamp = dateUtil.getTimeStamp(date)
          if (
            dateTimeStamp >= startDateTimestamp &&
            endDateTimestamp >= dateTimeStamp
          ) {
            date.choosed = true
            __selectedDates.push(date)
          } else {
            date.choosed = false
            __selectedDates = __selectedDates.filter(
              item => dateUtil.getTimeStamp(item) !== dateTimeStamp
            )
          }
          return date
        })
        const { year: startYear, month: startMonth } = dateUtil.formatTimestamp(
          startDateTimestamp
        )
        const { year: endYear, month: endMonth } = dateUtil.formatTimestamp(
          endDateTimestamp
        )
        if (startMonth !== endMonth || startYear !== endYear) {
          __selectedDates = calcDateWhenNotInOneMonth({
            firstDate: __dates[0],
            lastDate: __dates[__dates.length - 1],
            startTimestamp: startDateTimestamp,
            endTimestamp: endDateTimestamp,
            selectedDate: __selectedDates
          })
        }
      }
      return {
        calendarData: {
          ...calendarData,
          dates: __dates,
          selectedDates: dateUtil.sortDatesByTime(
            dateUtil.uniqueArrayByDate(__selectedDates)
          )
        },
        calendarConfig
      }
    },
    onTapDate(tapedDate, calendarData = {}, calendarConfig = {}) {
      if (!calendarConfig.chooseAreaMode) {
        return {
          calendarData,
          calendarConfig
        }
      }
      let {
        tempChooseAreaTimestamp = [],
        chooseAreaTimestamp: existChooseAreaTimestamp = [],
        selectedDates = [],
        dates = []
      } = calendarData
      const timestamp = dateUtil.getTimeStamp(tapedDate)
      let __dates = [...dates]
      let __selectedDates = [...selectedDates]
      if (
        tempChooseAreaTimestamp.length === 2 ||
        existChooseAreaTimestamp.length === 2
      ) {
        tempChooseAreaTimestamp = [tapedDate]
        __selectedDates = []
        __dates.forEach(d => (d.choosed = false))
      } else if (tempChooseAreaTimestamp.length === 1) {
        const preChoosedDate = tempChooseAreaTimestamp[0]
        const preTimestamp = dateUtil.getTimeStamp(preChoosedDate)
        if (preTimestamp <= timestamp) {
          tempChooseAreaTimestamp.push(tapedDate)
        } else if (preTimestamp > timestamp) {
          tempChooseAreaTimestamp.unshift(tapedDate)
        }
      } else {
        tempChooseAreaTimestamp = [tapedDate]
      }
      let chooseAreaTimestamp = []
      if (tempChooseAreaTimestamp.length === 2) {
        const [startDate, endDate] = tempChooseAreaTimestamp
        const startDateTimestamp = dateUtil.getTimeStamp(startDate)
        const endDateTimestamp = dateUtil.getTimeStamp(endDate)
        chooseAreaTimestamp = [startDateTimestamp, endDateTimestamp]
      }
      return {
        calendarData: {
          ...calendarData,
          chooseAreaTimestamp,
          tempChooseAreaTimestamp,
          dates: __dates,
          selectedDates: __selectedDates
        },
        calendarConfig: {
          ...calendarConfig,
          multi: true
        }
      }
    },
    methods(component) {
      return {
        /**
         * 设置连续日期选择区域
         * @param {array} dateArea 区域开始结束日期数组
         */
        chooseDateArea: (dateArea = []) => {
          if (dateArea.length === 1) {
            dateArea = dateArea.concat(dateArea)
          }
          if (dateArea.length !== 2) return
          const isRight = validateTimeRange(dateArea)
          if (!isRight) return
          const config = getCalendarConfig(component) || {}
          const { startTimestamp, endTimestamp } = convertTimeRangeToTimestamp(
            dateArea
          )
          const existCalendarData = getCalendarData('calendar', component)
          return renderCalendar.call(
            component,
            {
              ...existCalendarData,
              chooseAreaTimestamp: [startTimestamp, endTimestamp]
            },
            {
              ...config,
              multi: true,
              chooseAreaMode: true
            }
          )
        }
      }
    }
  }
}