微信小程序日历(包含可约、约满状态)

效果图:

在这里插入图片描述

代码如下:

components/calendar/calendar.wxml

<!--components/calendar/calendar.wxml-->
<wxs module="func">
  module.exports = {
    // *星号替换
    //将时间转为时间戳,在页面中判断时间戳小于昨天时间戳的呈灰色
    getTime: function (value) {
      if (value) return getDate(value).getTime()
      else return getDate().getTime() - 86400000
    }
  }
</wxs>

<view class="calendar">
  <view class="title flex">
    <view class="flex">
      <!-- <picker value="{{selectDay.year+'-'+selectDay.month}}" bindchange="editMonth" mode="date" fields="month" class="year-month">{{selectDay.year}}.{{selectDay.month>9?selectDay.month:"0"+selectDay.month}}</picker> -->
      <view class="icon" bindtap="lastMonth" style="transform:rotate(180deg);{{selectDay.month == newMonth ? 'color:#B3B3B3;':''}}">
        <view class="iconfont icon-playfill"></view>
      </view>
      <view class="year-month">{{selectDay.year}}年, {{selectDay.month>9?selectDay.month:"0"+selectDay.month}}月</view>
      <view class="icon" bindtap="nextMonth" style="{{selectDay.month > newMonth + 1 ? 'color:#B3B3B3;':''}}">
        <view class="iconfont icon-playfill"></view>
      </view>
    </view>
    <!-- <view catchtap="openChange" class="flex open">
      <view>{{open?"收起":"展开"}}</view>
      <view style="margin-left:6rpx;font-size:20rpx" class="iconfont icon-{{open?'fold':'unfold'}}"></view>
    </view> -->
  </view>

  <!-- 日历头部 -->
  <view class="flex-around calendar-week">
    <view class="view">日</view>
    <view class="view">一</view>
    <view class="view">二</view>
    <view class="view">三</view>
    <view class="view">四</view>
    <view class="view">五</view>
    <view class="view">六</view>
  </view>

  <!-- 日历主体 -->
  <view class="flex-start flex-wrap calendar-main" style="height:{{dateList.length/7*112}}rpx">
    <view wx:for="{{dateList}}" wx:key="dateList" class="day">
      <view class="bg {{(item.year === selectDay.year && item.month === selectDay.month) ? (item.day === selectDay.day?'select':''): 'other-month'}}" catchtap="selectChange" data-day="{{item.day}}" data-year="{{item.year}}" data-month="{{item.month}}" data-date-string="{{item.dateString}}" data-appointment="{{item.appointment}}" wx:if="{{func.getTime(item.dateString) > func.getTime()}}">
        <view wx:if="{{(!item.spot && !item.appointment) || (item.year === selectDay.year && item.month === selectDay.month && item.day === selectDay.day)}}">{{item.day}}</view>
        <view wx:elif="{{item.spot}}" class="reducibleBox">
          <view class="reducibleDay">{{item.day}}</view>
          <view class="reducibleText">可约</view>
        </view>
        <view wx:elif="{{item.appointment}}" class="appointmentBox">约满</view>
      </view>
      <view wx:else class="bg other-month">{{item.day}}</view>
      <!-- <view class="spot" wx:if="{{item.spot}}"></view> -->
    </view>
  </view>
</view>

components/calendar/calendar.js

// components/calendar/calendar.js
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    spot: {
      type: Array,
      value: []
    },
    appointment: {
      type: Array,
      value: []
    },
    defaultTime: {
      type: String,
      value: ''
    }
  },

  /**
   * 组件的初始数据
   */
  data: {
    dateList: [], //日历主体渲染数组
    selectDay: {}, //选中时间
    open: true,
    newMonth: new Date().getMonth() + 1
  },

  /**
   * 组件的方法列表
   */
  methods: {
    /**
     * 时间戳转化为年 月 日 时 分 秒
     * time: 需要被格式化的时间,可以被new Date()解析即可
     * format:格式化之后返回的格式,年月日时分秒分别为Y, M, D, h, m, s,这个参数不填的话则显示多久前
     */
    formatTime(time, format) {
      function formatNumber(n) {
        n = n.toString()
        return n[1] ? n : '0' + n
      }

      function getDate(time, format) {
        const formateArr = ['Y', 'M', 'D', 'h', 'm', 's']
        const returnArr = []
        const date = new Date(time)
        returnArr.push(date.getFullYear())
        returnArr.push(formatNumber(date.getMonth() + 1))
        returnArr.push(formatNumber(date.getDate()))
        returnArr.push(formatNumber(date.getHours()))
        returnArr.push(formatNumber(date.getMinutes()))
        returnArr.push(formatNumber(date.getSeconds()))
        for (const i in returnArr) {
          format = format.replace(formateArr[i], returnArr[i])
        }
        return format
      }

      function getDateDiff(time) {
        let r = ''
        const ft = new Date(time)
        const nt = new Date()
        const nd = new Date(nt)
        nd.setHours(23)
        nd.setMinutes(59)
        nd.setSeconds(59)
        nd.setMilliseconds(999)
        const d = parseInt((nd - ft) / 86400000)
        switch (true) {
          case d === 0:
            const t = parseInt(nt / 1000) - parseInt(ft / 1000)
            switch (true) {
              case t < 60:
                r = '刚刚'
                break
              case t < 3600:
                r = parseInt(t / 60) + '分钟前'
                break
              default:
                r = parseInt(t / 3600) + '小时前'
            }
            break
          case d === 1:
            r = '昨天'
            break
          case d === 2:
            r = '前天'
            break
          case d > 2 && d < 30:
            r = d + '天前'
            break
          default:
            r = getDate(time, 'Y-M-D')
        }
        return r
      }
      if (!format) {
        return getDateDiff(time)
      } else {
        return getDate(time, format)
      }
    },
    //picker设置月份
    editMonth(e) {
      const arr = e.detail.value.split("-")
      const year = parseInt(arr[0])
      const month = parseInt(arr[1])
      this.setMonth(year, month)
    },
    //上月切换按钮点击
    lastMonth() {
      if (this.data.selectDay.month == new Date().getMonth() + 1) return
      const lastMonth = new Date(this.data.selectDay.year, this.data.selectDay.month - 2)
      const year = lastMonth.getFullYear()
      const month = lastMonth.getMonth() + 1
      this.setMonth(year, month)
    },
    //下月切换按钮点击
    nextMonth() {
      if (this.data.selectDay.month > new Date().getMonth() + 2) return
      const nextMonth = new Date(this.data.selectDay.year, this.data.selectDay.month)
      const year = nextMonth.getFullYear()
      const month = nextMonth.getMonth() + 1
      this.setMonth(year, month)
    },
    //设置月份
    setMonth(setYear, setMonth, setDay) {
      if (this.data.selectDay.year !== setYear || this.data.selectDay.month !== setMonth) {
        const day = Math.min(new Date(setYear, setMonth, 0).getDate(), this.data.selectDay.day)
        const time = new Date(setYear, setMonth - 1, setDay ? setDay : day)
        const data = {
          selectDay: {
            year: setYear,
            month: setMonth,
            day: setDay ? setDay : day,
            dateString: this.formatTime(time, "Y-M-D")
          }
        }
        if (!setDay) {
          data.open = true
        }
        this.setData(data)
        this.dateInit(setYear, setMonth)
        this.setSpot()
        this.triggerEvent("change", this.data.selectDay)
      }
    },
    //展开收起
    openChange() {
      this.setData({
        open: !this.data.open
      })
      this.triggerEvent("aaa", { a: 0 })
      this.dateInit()
      this.setSpot()
    },
    //设置日历底下是否展示小圆点
    setSpot() {
      const timeArr = this.data.spot.map(item => {
        return this.formatTime(item, "Y-M-D")
      })
      const appointmentArr = this.data.appointment.map(item => {
        return this.formatTime(item, "Y-M-D")
      })
      this.data.dateList.forEach(item => {
        if (timeArr.indexOf(item.dateString) !== -1) {
          item.spot = true
        } else {
          item.spot = false
        }
        if (appointmentArr.indexOf(item.dateString) !== -1) {
          item.appointment = true
        } else {
          item.appointment = false
        }
      })
      this.setData({
        dateList: this.data.dateList
      })
    },
    //日历主体的渲染方法
    dateInit(setYear = this.data.selectDay.year, setMonth = this.data.selectDay.month) {
      let dateList = []; //需要遍历的日历数组数据
      let now = new Date(setYear, setMonth - 1)//当前月份的1号
      let startWeek = now.getDay(); //目标月1号对应的星期
      let dayNum = new Date(setYear, setMonth, 0).getDate() //当前月有多少天
      let forNum = Math.ceil((startWeek + dayNum) / 7) * 7 //当前月跨越的周数
      if (this.data.open) {
        //展开状态,需要渲染完整的月份
        for (let i = 0; i < forNum; i++) {
          const now2 = new Date(now)
          now2.setDate(i - startWeek + 1)
          let obj = {};
          obj = {
            day: now2.getDate(),
            month: now2.getMonth() + 1,
            year: now2.getFullYear(),
            dateString: this.formatTime(now2, "Y-M-D")
          };
          dateList[i] = obj;
        }
      } else {
        //非展开状态,只需要渲染当前周
        for (let i = 0; i < 7; i++) {
          const now2 = new Date(now)
          //当前周的7天
          now2.setDate(Math.ceil((this.data.selectDay.day + startWeek) / 7) * 7 - 6 - startWeek + i)
          let obj = {};
          obj = {
            day: now2.getDate(),
            month: now2.getMonth() + 1,
            year: now2.getFullYear(),
            dateString: this.formatTime(now2, "Y-M-D")
          };
          dateList[i] = obj;
        }
      }
      this.setData({
        dateList: dateList
      })
    },
    //一天被点击时
    selectChange(e) {
      if (e.currentTarget.dataset.appointment) return
      const year = e.currentTarget.dataset.year
      const month = e.currentTarget.dataset.month
      const day = e.currentTarget.dataset.day
      const dateString = e.currentTarget.dataset.dateString
      const selectDay = {
        year: year,
        month: month,
        day: day,
        dateString: dateString
      }
      if ((this.data.selectDay.year !== year || this.data.selectDay.month !== month) && (this.data.selectDay.month < new Date().getMonth() + 3)) {
        this.setMonth(year, month, day)
      } else if (this.data.selectDay.day !== day) {
        this.setData({
          selectDay: selectDay
        })
        this.triggerEvent("change", this.data.selectDay)
      }
    }
  },
  lifetimes: {
    attached() {
      let now = this.data.defaultTime ? new Date(this.data.defaultTime) : new Date()
      let selectDay = {
        year: now.getFullYear(),
        month: now.getMonth() + 1,
        day: now.getDate(),
        dateString: this.formatTime(now, "Y-M-D")
      }
      this.setMonth(selectDay.year, selectDay.month, selectDay.day)
    }
  },
  observers: {
    spot: function (spot) {
      this.setSpot()
    }
  }
})

components/calendar/calendar.wxss

/* components/calendar/calendar.wxss */
@font-face {
  font-family: "iconfont";
  src: url('iconfont.eot?t=1596614903470');
  /* IE9 */
  src: url('iconfont.eot?t=1596614903470#iefix') format('embedded-opentype'),
    /* IE6-IE8 */
    url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAL8AAsAAAAABxQAAAKvAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCDHAqBVIFEATYCJAMQCwoABCAFhG0HQhswBsgekiRFQgQU8AMAmmCIh3/HXu9L8hFVWwXoyALpqqoqwAEfD6g66abEPBux/13TPwBUeZ0lAldI75LS0ctlzLY8NwVk3NQm5Co3lqhaWPE+z+X0pi+Q1bez3MZatCYtPuoFGAcU4F7UiyIroBPkltpFHBi88q89JtA0p1jEYe/wNMQryKpAPHbYMMT3CooStetCdeZgEd9U1NPX9ADgQf8+/kFlxJNUMrLx5LpHhPaf+GUsavP/JpxJQpHgHq8gYx0oxO1s41yUiMQoTWOibQN1dYmf+Dn0MhYO2zgfttC/PEKSiSrS3QabXjPzE3ON4GdII/FrrEsGhPjr/dYGcOyzM56gg6PM1BXvcbZIxY7zzRyS4znJs/m2VmjuRNq9edF1kcx9FbW85/nRgbXX6s5q/AX+vsGh/kBhUUHAYszwFx4acaAA6l7k9bgjo8Vbue4acDcbCqFqcBru+htf4l/I4EnzzQAa8lT+kNoAyG/TJuTdyDcsG+NB4r05rfm73irgx/jRrw4XMB+gyga24dQGHCqlg8RKyZ3cWuWLEWhFQlMTmOwwFLrCPwH3E+omB/I1sy2yunmysOuoaNlEVd0Omtb0HW8ZYaJEaWDVnoPQd4ek6zuyvkeysE+omPpCVT+i0HQaWRe2LIYjsYsRzImIrEvIIagKZQmeaHoniW1Owqzr0NIsYbqjjCpKy4ftPqIQdsUWfd5WyTlFlKky6nWeI5KkIo2pTiLwUjvnWmNZGZ37UKmgypDoAENgHCFCrJYgDgKVgsrxXOLo+5MImzkSjK3oFuazCEbnmB6pUKocQPZ5FZDuV97RzbOpxHEUQjEqGdLrzCMkEhWizc9zIgRcKfuBmqZRGR1Fod7S5/3yH56AJnJDiRQ5SlRU3yhUlcRoMStek/ASdUgSAAA=') format('woff2'),
    url('iconfont.woff?t=1596614903470') format('woff'),
    url('iconfont.ttf?t=1596614903470') format('truetype'),
    /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
    url('iconfont.svg?t=1596614903470#iconfont') format('svg');
  /* iOS 4.1- */
}

.iconfont {
  font-family: "iconfont" !important;
  font-style: normal;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  line-height: 1;
  font-weight: normal;
}

.icon-unfold:before {
  content: "\e661";
}

.icon-fold:before {
  content: "\e6de";
}

.icon-playfill:before {
  content: "\e74f";
}

.flex {
  display: flex;
  justify-content: center;
  align-items: center;
}

.direction-column {
  flex-direction: column;
}

.flex1 {
  flex: 1;
}

.flex-center {
  display: flex;
  justify-content: center;
  align-items: center;
}

.flex-start {
  display: flex;
  justify-content: flex-start;
  align-items: center;
}

.flex-end {
  display: flex;
  justify-content: flex-end;
  align-items: center;
}

.flex-around {
  display: flex;
  justify-content: center;
  align-items: center;
}

.flex-wrap {
  flex-wrap: wrap;
  justify-content: center;
}

.align-start {
  align-items: flex-start;
}

.align-end {
  align-items: flex-end;
}

.align-stretch {
  align-items: stretch;
}

.calendar {
  background-color: #fff;
}

.calendar .title {
  font-size: 40rpx;
  color: #333;
  padding: 30rpx;
  line-height: 60rpx;
}

.calendar .title .year-month {
  margin: 0 40rpx;
  font-size: 32rpx;
  font-family: SourceHanSansCN-Regular;
  color: #333333;
}

.calendar .title .icon {
  padding: 0 16rpx;
  font-size: 32rpx;
  color: #D61518;
}

.calendar .title .open {
  background-color: #f6f6f6;
  color: #999;
  font-size: 22rpx;
  line-height: 36rpx;
  border-radius: 18rpx;
  padding: 0 14rpx;
}

.calendar .calendar-week {
  line-height: 40rpx;
  padding: 0 25rpx;
  font-size: 28rpx;
  color: #999;
  margin-top: 53rpx;
}

.calendar .calendar-week .view {
  width: 70rpx;
  text-align: center;
  margin: 0 21rpx;
  font-size: 28rpx;
  font-family: SourceHanSansCN-Medium;
  color: #333333;
}

.calendar .calendar-main {
  padding: 30rpx 25rpx;
  transition: height 0.3s;
  align-content: flex-start;
  overflow: hidden;
}

.calendar .calendar-main .day {
  position: relative;
  width: 70rpx;
  height: 70rpx;
  line-height: 70rpx;
  color: #666;
  text-align: center;
  margin: 21rpx 15rpx;
}

.calendar .calendar-main .day .bg {
  font-size: 28rpx;
  font-family: SourceHanSansCN-Regular;
  color: #333333;
}

.calendar .calendar-main .day .bg .reducibleBox {
  width: 70rpx;
  height: 70rpx;
  border: 1px solid #B7141B;
  border-radius: 50%;
  overflow: hidden;
  box-sizing: border-box;
}

.calendar .calendar-main .day .bg .reducibleBox .reducibleDay {
  width: 100%;
  height: 50%;
  text-align: center;
  font-size: 24rpx;
  font-family: SourceHanSansCN-Regular;
  line-height: 36rpx;
  color: #D21A23;
}

.calendar .calendar-main .day .bg .reducibleBox .reducibleText {
  width: 100%;
  height: 50%;
  text-align: center;
  font-size: 20rpx;
  font-family: SourceHanSansCN-Regular;
  line-height: 30rpx;
  color: #FFFFFF;
  background-color: #D21A23;
}

.calendar .calendar-main .day .bg .appointmentBox {
  background: #BDBDBD;
  border-radius: 50%;
  font-size: 24rpx;
  font-family: SourceHanSansCN-Light;
  color: #989898;
}

.calendar .calendar-main .day .select {
  background: #D21A23;
  border-radius: 50%;
  color: #fff;
}

.calendar .calendar-main .day .other-month {
  color: #ccc;
}

.calendar .calendar-main .day .spot {
  width: 8rpx;
  height: 8rpx;
  background-color: #1DCDB8;
  border-radius: 50%;
  margin: 6rpx auto 0;
}

在页面中使用

/pages/index/index.pson

"usingComponents": {
    "calendar":"/components/calendar/calendar"
  },

/pages/index/index.wxml

//spot是可以约的日期的数组   appointment是不可约的日期的数组   dateChange是点击日历中的日期事件
<calendar bind:change="dateChange" spot="{{spot}}" appointment="{{appointment}}"></calendar>

/pages/index/index.js

 spot: ['2022/7/20', '2022/7/28', '2022/7/25', '2022/7/7', '2022/8/7'],
 appointment: ['2022/7/30']

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值