小程序中实现 calendar 组件

https://s3-us-west-2.amazonaws.com/secure.notion-static.com/658b4511-98bd-4eb1-99db-6e616f10180d/Untitled.png

通过上面截图可以看出

  1. 点击上月和下月切换月份
  2. 点击具体的日期可以在页面上显示选中日期
  3. 点击确定将会获得选中日期
  4. 点击灰色的日期可以跳转对应月份并高亮日期

结构

日历结构主要分为 mask 和 calender

  • 代码

    <view class="mask {{show ? 'show' : ''}}" catch:tap="onClickMask"></view>

    <view class="calender {{onceShow ? 'show' : ''}} {{show ? 'enter' : 'leave'}}">

    <view class="header">

    <view class="header-title">

    <view catch:tap="onToggleMonthClick" data-type="prev">上月</view>

    <view class="title">{{year}}年{{month+1}}月</view>

    <view catch:tap="onToggleMonthClick" data-type="next">下月</view>

    </view>

    <!-- 星期 -->

    <view class="weekdays">

    <view class="weekday">日</view>

    <view class="weekday">一</view>

    <view class="weekday">二</view>

    <view class="weekday">三</view>

    <view class="weekday">四</view>

    <view class="weekday">五</view>

    <view class="weekday">六</view>

    </view>

    </view>

    <view class="body">

    <view class="month">

    <!-- 日期 -->

    <view class="dates">

    <block wx:for="{{thisMonthDates}}" wx:key="*this">

    <view

    class="date {{item.isToday ? 'today' :''}} {{item.isSelectedDate ? 'selected-date' : ''}} {{item.isEmptyDate ? 'empty-date' : ''}}"

    data-index="{{index}}"

    data-type="{{item.monthState}}"

    data-formatDate="{{item.formatDate}}"

    catch:tap="onSelectDateClick"

    >{{item.date}}

    </view>

    </block>

    </view>

    </view>

    <view class="footer">确定</view>

    </view>

    </view>

mask 是遮罩层,当日历弹出时,显示在日历后面,calender 之所以没有放在里面,是因为组件弹出时需要做动画,组件显示的时候 mask 需要立即出现。

calender 分为有两种操作

  • 上月和下月切换
  • 日期点击高亮

根据日历结构,写出日历样式

  • 日期的样式分为当前月、上月、下月,上月下月为灰色
  • 高亮当天
  • 切换日期时,当天的颜色是金色
  • 样式代码

核心代码

日历组件最重要的是如何计算当月多少天,以及对应星期几。

好在 js 提供了 new Date().getDate() 可以获取今天是几号,Date 可接受 3 个参数 year、month、date,如果 date 大于这个月的最大天数,就会自动换算成下个月的几号,同理如何小于 1 号,就会自动换算出上个月的几号。利用这个特性,date 传递 0 进去就能知道有多少天了。

newDate(2020, 5, 2).getDate()   // 2

newDate(2020, 5, 1).getDate() // 1

newDate(2020, 5, 0).getDate() // 31

通过 new Date().getDay() 可以获取 date 是星期几,它的值是 0~6,正好可以用 0 代表星期日。

  • 代码

    getThisDateWeek({year, month, date}: YearMonthDate) {

    returnnewDate(Date.UTC(year, month, date)).getDay()

    },

    getThisMonthDays(year: number, month: number) {

    returnnewDate(year, month + 1, 0).getDate()

    }

知道了当月多少天,怎么知道当月前面和后面有多少天呢?

  • 代码

    methods: {

    // 当月

    createDays() {

    const thisMonthDates: any[] = []

    const {year, month} = this.data

    const thisMonthTotalDate = this.getThisMonthDays(year, month)

    for (let date = 1; date <= thisMonthTotalDate; date++) {

    const monthGrid = this.formatMonthGrid({year, month, date})

    thisMonthDates.push(monthGrid)

    }

    this.data.thisMonthDates = thisMonthDates

    this.setData({thisMonthTotalDate})

    },

    // 单月的上月和下月有多少天

    createEmptyDays() {

    let {year, month, thisMonthDates} = this.data

    // 当月第一天是星期几

    const firstDayWeek = this.getThisDateWeek({year, month, date: 1})

    const emptyGridsBefore = this.getBeforeMonthEmpty(firstDayWeek)

    const emptyGridsAfter = this.getAfterMonthEmpty(firstDayWeek)

    thisMonthDates = [...emptyGridsBefore, ...thisMonthDates, ...emptyGridsAfter]

    this.setData({thisMonthDates})

    },

    // 上月

    getBeforeMonthEmpty(firstDayWeek: number) {

    const {year, month} = this.data

    const emptyGridsBefore: any[] = []

    const {prevYear, prevMonth} = this.prevYear(year, month)

    const prevMonthDay = this.getThisMonthDays(year, prevMonth)

    // 上月 补满 1 号前面的日期

    for (let i = 1; i <= firstDayWeek; i++) {

    const date = prevMonthDay - (firstDayWeek - i)

    const monthGrid = this.formatMonthGrid({year: prevYear, month: prevMonth, date}, MonthState.Prev)

    emptyGridsBefore.push(monthGrid)

    }

    return emptyGridsBefore

    },

    // 下月

    getAfterMonthEmpty(firstDayWeek: number) {

    const emptyGridsAfter: any[] = []

    const {thisMonthTotalDate, year, month} = this.data

    const {nextYear, nextMonth} = this.nextYear(year, month)

    // 日期默认显示 42 天,-7 是为了如果 35 天能显示全的话就用 35 天

    const nextMonthDay = 42 - thisMonthTotalDate - firstDayWeek - 7 >= 0 ?

    42 - thisMonthTotalDate - firstDayWeek - 7 :

    42 - thisMonthTotalDate - firstDayWeek

    // 下月补满当月最后一天后面的日期

    for (let date = 1; date <= nextMonthDay; date++) {

    const monthGrid = this.formatMonthGrid({year: nextYear, month: nextMonth, date}, MonthState.Next)

    emptyGridsAfter.push(monthGrid)

    }

    return emptyGridsAfter

    },

    // 如果是 1 月,上月就是上年 12 月

    prevYear(year: number, month: number) {

    const prevYear = month === 0 ? year - 1 : year

    const prevMonth = month === 0 ? 11 : month - 1

    return {prevYear, prevMonth}

    },

    // 如果是 12 月,下月就是下年 1月

    nextYear(year: number, month: number) {

    const nextYear = month === 11 ? year + 1 : year

    const nextMonth = month === 11 ? 0 : month + 1

    return {nextYear, nextMonth}

    },

    }

功能代码

  • 代码

    methods: {

    // state 是用来判断上月还是下月

    formatMonthGrid({year, month, date}: YearMonthDate, state?: string) {

    const formatDate = this.formatDate({year, month, date})

    const isSelectedDate = this.defaultSelectedDateGrid(formatDate, state)

    const isEmptyDate = this.isEmptyDateGrid(month)

    const week = this.getThisDateWeek({year, month, date})

    const isToday = this.isToday(formatDate)

    return {

    year,

    month,

    date,

    formatDate,

    week,

    isSelectedDate,

    isEmptyDate,

    isToday,

    monthState: state

    }

    },

    // 点击日期高亮

    setSelectedDateGrid(formatDate: string) {

    const {thisMonthDates} = this.data

    let selectedDate: string = ''

    thisMonthDates.forEach((thisMonthDate: any) => {

    if (thisMonthDate.formatDate === formatDate) {

    thisMonthDate.isSelectedDate = true

    selectedDate = thisMonthDate.formatDate

    } else {

    thisMonthDate.isSelectedDate = false

    }

    })

    this.data.selectedDate = selectedDate

    this.setData({thisMonthDates})

    },

    // 默认日期高亮

    defaultSelectedDateGrid(formatDate: string, state: string) {

    const {selectedDate} = this.data

    // 只高亮当月,上月和下月的不高亮

    return formatDate === selectedDate && !state

    },

    // 今天

    isToday(formatDate: string) {

    const {value} = this.data

    return value === formatDate

    },

    // 是不是当月

    isEmptyDateGrid(month: number) {

    const {month: thisMonth} = this.data

    return month !== thisMonth

    },

    formatDate({year, month, date}: YearMonthDate) {

    return`${year}-${month + 1}-${date}`

    },

    }

事件

  • 代码

    methods: {

    onSelectDateClick(e: Event) {

    const {formatdate, type} = e.currentTarget.dataset

    const {selectedDate} = this.data

    if (formatdate === selectedDate) return

    this.toggleMonth(type)

    this.setSelectedDateGrid(formatdate)

    },

    // 上月和下月切换

    onToggleMonthClick(e: Event) {

    const {type} = e.currentTarget.dataset

    this.toggleMonth(type)

    },

    toggleMonth(type: string) {

    if (type === MonthState.Prev) {

    this.prevMonth()

    } elseif (type === MonthState.Next) {

    this.nextMonth()

    }

    },

    prevMonth() {

    const {year, month} = this.data

    const {prevYear, prevMonth} = this.prevYear(year, month)

    this.setData({year: prevYear, month: prevMonth}, () => {

    this.createDays()

    this.createEmptyDays()

    })

    },

    nextMonth() {

    const {year, month} = this.data

    const {nextYear, nextMonth} = this.nextYear(year, month)

    this.setData({year: nextYear, month: nextMonth}, () => {

    this.createDays()

    this.createEmptyDays()

    })

    },

    }

Properties

  • 代码

    properties = {

    show: {

    type: Boolean,

    value: false,

    observer(show: boolean) {

    if (show)

    this.setData({enter: show, onceShow: true})

    }

    },

    value: {

    type: String,

    value: '-1',

    observer(value: string) {

    // 2020-6-15

    const year = +(value.split("-")[0])

    const month = +(value.split("-")[1]) - 1

    const date = +(value.split("-")[2])

    this.data.selectedDate = value

    this.setData({year, month, date}, () => {

    this.initDate()

    this.createDays()

    this.createEmptyDays()

    })

    }

    }

    },

    methods = {

    initDate() {

    let {year, month, date} = this.data

    if (year && month && date) return

    year = newDate().getFullYear()

    month = newDate().getMonth()

    date = newDate().getDate()

    this.data.selectedDate = this.formatDate({year, month, date})

    this.setData({year, month, date})

    },

    onClickMask() {

    this.setData({show: false, value: '-1'})

    },

    }

以上是 小程序中实现 calendar 组件 的全部内容, 来源链接: utcz.com/a/33524.html

回到顶部