likes
comments
collection

一个很很很简单的移动端日历

作者站长头像
站长
· 阅读数 15

不知道得多久没有写过文章了,从今年开始心情就一直很浮躁,静不下来...

这次写这个东西其实也很简单,主要是帮其他项目的同事写的,有些东西其实还可以继续封装的,但是我太懒了,就交给jym了。

先上图看看效果吧

一个很很很简单的移动端日历

主要依赖

  • amfe-flexible postcss-pxtorem
  • dayjs
  • vant

这里主要说一下 amfe-flexiblepostcss-pxtorem ,这个就是用来做移动端的适配,就像一般UI给的设计稿都是750px的,但是我们在实际开发的时候可能是不同的,所以用这个来做适配,下面是配置文件

postcss.config.js

const autoprefixer = require('autoprefixer');
const px2rem = require('postcss-pxtorem');

module.exports = {
  plugins: [autoprefixer(), px2rem({
    rootValue: 75, unitPrecision: 5, propList: ['*'], selectorBlackList: [
        //这里样式需要排除vant组件的,因为vant本身就是移动端的样式了
      'van-'
    ]
  })],
};

主要逻辑

工具类

这些工具类就是普通的获取月数什么的,没有什么好讲的

calendar.js

// 判断是否周末
export const isWeekend = (date) => {
  return [0, 6].includes(new Date(date).getDay())
}

// 获取年份
export const getDateYear = (date) => {
  return new Date(date).getFullYear()
}

// 获取月份
export const getDateMonth = (date) => {
  return new Date(date).getMonth() + 1
}

// 获取月份天数
export const getMonthDay = (date) => {
  let year = new Date(date).getFullYear()
  let month = new Date(date).getMonth() + 1
  return new Date(year, month, 0).getDate()
}

// 获取月份第一天是周几
export const getMonthFirstDayOrder = (date) => {
  let year = new Date(date).getFullYear()
  let month = new Date(date).getMonth()
  return new Date(year, month, 1).getDay()
}

// 获取上个月有多少天
export const getPrevMonthDay = (date) => {
  let year = new Date(date).getFullYear()
  let month = new Date(date).getMonth()
  if (month === 0) {
    year--
    month = 12
  }
  return new Date(year, month, 0).getDate()
}

月份处理

这里我是采用二维数组的方式来处理每周的数据,而且我们处理月份的时候需要考虑下面几点

  • 普通月份处理(这不是说废话吗),即每个月的第一天是星期几,排在第几位
  • 上个月/下个月处理,上个月是几月份,多少天,是否在同一年(下个月同理)
  • 周末(这里就不考虑节假日什么的了)
  • 要显示多少周,最多可能有6周(1号在周六,因为我这里是以周日为第一位,然后有31天的情况)

下面就是主要的逻辑处理代码了

calendar.vue

    // 初始化日历数据
    initMonthData(date) {
      this.dateArr = [[], [], [], [], [], []]
      // 每个月第一天排在第几位
      let firstDayOrder = getMonthFirstDayOrder(date)
      // 选择器选择的年份
      let selectedYear = getDateYear(date)
      // 选择器选择的月份
      let selectedMonth = getDateMonth(date)
      // 选择器选择的月份天数
      let selectedMonthDay = getMonthDay(date)
      // 选择器选择的月份的上个月的总天数
      let prevMonthDay = getPrevMonthDay(date)
      // 月份渲染记数
      let selectedCountDay = 0
      // 下个月月份渲染记数
      let nextMonthCountDay = 0
      // 是否下个月
      let isNextMonth = false
      for (let weekIndex = 0; weekIndex < 6; weekIndex++) {
        for (let dayIndex = 0; dayIndex < 7; dayIndex++) {
          if (weekIndex === 0) {
            // 第一周需要对某一个月第一天做定位
            if (firstDayOrder <= dayIndex) {
              // 当前月
              selectedCountDay += 1
              this.$set(this.dateArr[weekIndex], dayIndex, {
                date: new Date(
                  selectedYear,
                  selectedMonth - 1,
                  selectedCountDay
                ),
                status: {
                  isCurr: true, // 是否当月
                  isPrev: false, // 是否上个月
                  isNext: false, // 是否下个月
                  // 是否周末
                  isWeekend: isWeekend(
                    new Date(selectedYear, selectedMonth - 1, selectedCountDay)
                  )
                }
              })
            } else {
              // 上个月 因为选择月份是当前月 上个月需要减去1 然后new Date()还需要再减去1
              let prevMonth = selectedMonth === 1 ? 11 : selectedMonth - 2
              let prevMonthYear =
                selectedMonth - 1 === 0 ? selectedYear - 1 : selectedYear
              this.$set(this.dateArr[weekIndex], dayIndex, {
                date: new Date(
                  prevMonthYear,
                  prevMonth,
                  prevMonthDay - firstDayOrder + dayIndex + 1
                ),
                status: {
                  isCurr: false,
                  isPrev: true,
                  isNext: false,
                  isWeekend: isWeekend(
                    new Date(
                      prevMonthYear,
                      prevMonth,
                      prevMonthDay - firstDayOrder + dayIndex + 1
                    )
                  )
                }
              })
            }
          } else {
            selectedCountDay += 1
            isNextMonth = selectedCountDay > selectedMonthDay
            if (!isNextMonth) {
              // 当前月
              this.$set(this.dateArr[weekIndex], dayIndex, {
                date: new Date(
                  selectedYear,
                  selectedMonth - 1,
                  selectedCountDay
                ),
                status: {
                  isCurr: true,
                  isPrev: false,
                  isNext: false,
                  isWeekend: isWeekend(
                    new Date(selectedYear, selectedMonth - 1, selectedCountDay)
                  )
                }
              })
            } else {
              // 下个月
              let nextMonth = selectedMonth === 12 ? 0 : selectedMonth
              let nextYear =
                selectedMonth === 12 ? selectedYear + 1 : selectedYear
              nextMonthCountDay += 1
              this.$set(this.dateArr[weekIndex], dayIndex, {
                date: new Date(nextYear, nextMonth, nextMonthCountDay),
                status: {
                  isCurr: false,
                  isPrev: false,
                  isNext: true,
                  isWeekend: isWeekend(
                    new Date(nextYear, nextMonth, nextMonthCountDay)
                  )
                }
              })
            }
          }
        }
      }
      while (
        getDateMonth(this.dateArr[this.dateArr.length - 1][0].date) !==
        selectedMonth
      ) {
        // 某些月份不用六个星期 去除不需要的数据
        this.dateArr.pop()
      }
    },

部分样式处理

一个很很很简单的移动端日历

就是根据item里面的状态进行动态class处理

    handleDayClass(item, dayIndex, weekIndex) {
      let classStr = `${
        item.status.isWeekend || item.status.isNext || item.status.isPrev
          ? ' day_prev_next_weekend'
          : ''
      }${
        dayjs(item.date).isSame(dayjs(this.currDate), 'day') ? ' day_curr' : ''
      }${
        dayIndex === this.clickDayIndex && weekIndex === this.clickWeekIndex
          ? ' day_active'
          : ''
      }`
      return classStr
    }

然后那个弹窗就是用vant组件的van-popover,然后再初始化的时候给每一个item设置一个值与对应组件绑定,记得用$set,不然数据可能是视图不对应。

具体代码就放在gitee仓库了,github网络写的时候用不了,后续需要再弄到github吧。

好累,好想回老家混吃等死,但是老家又没有前端行业职位,转行又不知道做什么~~~