uniapp小程序日历组件

效果如图


VeryCapture_20251009150221.gif

一.日期函数

  1. 计算当前月份的天数
get_month_days(nowMonth, nowYear) {
      let month_arr = [1, 3, 5, 7, 8, 10, 12]
      let days = 0
      if (nowMonth == 2) {
        if (nowYear % 4 == 0) {
          days = 29
        } else {
          days = 28
        }
      } else if (month_arr.indexOf(nowMonth) >= 0) {
        days = 31
      } else {
        days = 30
      }
      return days
    },
  1. 根据月份进行填补,构建一个6行7列二维数组
get_date(value = "") {
      let date = new Date()
      if (value) {
        date = new Date(value)
      }
      let nowMonth = date.getMonth() + 1,
        nowYear = date.getFullYear(),
        nowDay = date.getDate(),
        nowTime = date.getTime()

      let days = this.get_month_days(nowMonth, nowYear)
      let start_date = new Date(nowYear, nowMonth - 1, 1)
      let end_date = new Date(nowYear, nowMonth - 1, days)

      let prev_date = new Date(start_date.getTime() - 1)
      let prev_date_days = prev_date.getDate()

      let next_date = new Date(end_date.getTime() + 86401 * 1000)
      let next_date_days = next_date.getDate()

      let start_week = start_date.getDay()
      let date_arrs = []

      let week_list = []
      let count_days = 42

      for (let i = prev_date_days - start_week + 2; i <= prev_date_days; i++) {
        date_arrs.push({
          day: i,
          type: "prev",
          date: `${prev_date.getFullYear()}-${prev_date.getMonth() + 1}-${i}`,
        })
      }

      for (let i = 1; i <= days; i++) {
        date_arrs.push({
          day: i,
          type: "month",
          today: i == nowDay ? true : false,
          date: `${nowYear}-${nowMonth}-${i}`,
        })

        if (i == nowDay) {
          this.date = `${nowYear}-${nowMonth}-${i}`
        }
      }
      
      let date_arrs_length = date_arrs.length

      for (let i = 1; i <= count_days - date_arrs_length; i++) {
        date_arrs.push({
          day: i,
          type: "next",
          date: `${next_date.getFullYear()}-${next_date.getMonth() + 1}-${i}`,
        })
      }

      for (let i = 0; i < date_arrs.length / 7; i++) {
        let arr = []
        for (let j = 0; j < 7; j++) {
          if (date_arrs[i * 7 + j].today) {
            this.to_week_index = i
          }
          arr.push(date_arrs[i * 7 + j])
        }
        week_list.push(arr)
      }
}

使用这个函数构建的数组如图
一个项代表一天


image.png

image.png

二.左右滚动切换日期

使用uniapp自带的<swiper>滚动来实现

image.png

背景年月日 在<swiper-item>标签内使用绝对定位实现
image.png

三.点击下方滑块时切换月份和周

使用监听切换显示的数组

computed: {
    week_list_prev_co() {
      return this.retract ? this.week_list_prev_week : this.week_list_prev
    },
    week_list_next_co() {
      return this.retract ? this.week_list_next_week : this.week_list_next
    },
  },

<swiper>高度计算

<swiper class="swiper-box" :style="{ height: (retract ? 80 : week_list.length * 80) + 'rpx' }">

组件完整代码如下:

<template>
  <view class="date-calendar">
    <view class="head" v-if="false">
      <view class="icon-box" @click="switch_month_week('prev', true)"><text class="iconfont icon-box-fanhui" /></view>
      <view class="title">{{ nowYear + "年" + nowMonth + "月" }}</view>
      <view class="icon-box" @click="switch_month_week('next', true)"><text class="iconfont next icon-box-fanhui" /></view>
    </view>
    <view>
      <view class="date_dl-calendar">
        <view class="dd week-box" v-for="(item, index) in week" :key="index">{{ item }}</view>
      </view>
      <swiper class="swiper-box" :style="{ height: (retract ? 80 : week_list.length * 80) + 'rpx' }" :current="current" circular @change="change_date">
        <!-- 上个月 -->
        <swiper-item style="position: relative">
          <view class="background-box">
            <view>
              <view>{{ `${nowMonth}月` }}</view>
              <view style="font-size: 100rpx">{{ `${nowYear}年` }}</view>
            </view>
          </view>
          <view class="date_dl-calendar" v-show="!retract || index == to_prev_week_index" v-for="(item, index) in week_list_prev_co" :key="index">
            <view class="dd" @click="item_click(vo, index, key)" v-for="(vo, key) in item" :key="key">
              <view class="num" :class="{ today: vo.today, month: vo.type == 'month', 'current-box': todayfun(vo) === '今' }">
                {{ todayfun(vo) }}
              </view>
              <view class="dot" :style="{ background: dotFun(vo) }"></view>
            </view>
          </view>
        </swiper-item>
        <!-- 本月 -->
        <swiper-item style="position: relative">
          <view class="background-box">
            <view>
              <view>{{ `${nowMonth}月` }}</view>
              <view style="font-size: 100rpx">{{ `${nowYear}年` }}</view>
            </view>
          </view>
          <view class="date_dl-calendar" v-show="!retract || index == to_week_index" v-for="(item, index) in week_list" :key="index">
            <view class="dd" @click="item_click(vo, index, key)" v-for="(vo, key) in item" :key="key">
              <view class="num" :class="{ today: vo.today, month: vo.type == 'month', 'current-box': todayfun(vo) === '今' }">
                {{ todayfun(vo) }}
              </view>
              <view class="dot" :style="{ background: dotFun(vo) }"></view>
            </view>
          </view>
        </swiper-item>
        <!-- 下个月 -->
        <swiper-item style="position: relative">
          <view class="background-box">
            <view>
              <view>{{ `${nowMonth}月` }}</view>
              <view style="font-size: 100rpx">{{ `${nowYear}年` }}</view>
            </view>
          </view>
          <view class="date_dl-calendar" v-show="!retract || index == to_next_week_index" v-for="(item, index) in week_list_next_co" :key="index">
            <view class="dd" @click="item_click(vo, index, key)" v-for="(vo, key) in item" :key="key">
              <view class="num" :class="{ today: vo.today, month: vo.type == 'month', 'current-box': todayfun(vo) === '今' }">
                {{ todayfun(vo) }}
              </view>
              <view class="dot" :style="{ background: dotFun(vo) }"></view>
            </view>
          </view>
        </swiper-item>
      </swiper>
      <view @click="open" class="retract-box icon-box">
        <image v-if="retract" :src="imgUrl_new + '/upload/common/2025/09/09/aedd4c43-0c95-44b9-9904-93ff9170ee7d.png'"></image>
        <image v-else :src="imgUrl_new + '/upload/common/2025/09/09/cf4d494c-66dc-4fd1-bc0e-871ea5fb1061.png'"></image>
      </view>
    </view>
  </view>
</template>

<script>
import util from "@/common/myUtile/utiles.js"
export default {
  props: {
    value: {
      type: [String, Number],
      default: "",
    },
    dot_lists: {
      type: Array,
      default: () => {
        return []
      },
    },
    dot_Obj: {
      type: Object,
      default: () => {},
    },
  },
  data() {
    return {
      imgUrl_new: this.parameter.imgUrl_new,

      scrollTop: 0,
      debug: true,
      week: ["一", "二", "三", "四", "五", "六", "日"],
      week_list: [],
      week_list_prev: [],
      week_list_prev_week: [],
      week_list_next: [],
      week_list_next_week: [],
      now_date: "",
      start_date: "",
      end_date: "",
      prev_date: "",
      next_date: "",
      nowYear: "",
      nowMonth: "",
      nowDay: "",
      retract: true,
      to_week_index: 0,
      to_prev_week_index: 0,
      to_next_week_index: 0,
      nowTime: 0,
      dot_list: [],
      current: 1,
      date: "",
      currentDay: "",
      dotColorObj: {
        1: { color: "#00C297" },
        2: { color: "#346EFF" },
        3: { color: "#F59A23" },
        4: { color: "#FF2A2A" },
        5: { color: "#999999" },
      },
    }
  },
  watch: {
    value(value) {
      this.get_date(this.date_parse(value))
    },

    dot_lists: {
      handler(value) {
        this.dot_list = value
        this.set_doc_lists_update()
      },
    },
  },
  computed: {
    week_list_prev_co() {
      return this.retract ? this.week_list_prev_week : this.week_list_prev
    },
    week_list_next_co() {
      return this.retract ? this.week_list_next_week : this.week_list_next
    },
  },
  created() {
    this.currentDay = util.getCurrentDay()
    this.init()
  },
  methods: {
    // 返回标记点颜色
    dotFun(data) {
      const { dotColor, type } = data
      if (type != "month") return "transparent"
      return dotColor || ""
    },
    // 当天日期显示为‘今’
    todayfun(data) {
      const { date, day } = data
      if (date.replace(/-(\d)(?!\d)/g, "-0$1") === this.currentDay) {
        return "今"
      }
      return day
    },
    // 切换选中日期
    change() {
      let value = {
        fulldate: this.date.replace(/-(\d)(?!\d)/g, "-0$1"),
      }
      this.$emit("change", value)
    },
    // 初始化日历
    init() {
      if (this.value) {
        this.get_date(this.date_parse(this.value))
      } else {
        this.get_date()
      }

      this.doc_list_update()
      this.update_month()
    },
    // 打开关闭
    open() {
      this.retract = !this.retract
      this.get_date(this.nowTime)
      this.set_to_day("week_list_prev")
      this.set_to_day("week_list_next")

      this.change_week()

      if (this.retract) {
        this.update_swiper_item("week")
      } else {
        this.update_swiper_item("month")
      }
      this.set_doc_lists_update()
    },

    // 重置一周
    change_week() {
      console.log(this.to_week_index, this.week_list.length, this.to_week_index < this.week_list.length - 1, "change_week---")

      if (this.to_week_index < this.week_list.length - 1) {
        this.to_next_week_index = this.to_week_index + 1
        this.week_list_next_week = this.week_list
      } else {
        this.to_next_week_index = 0
        this.week_list_next_week = this.week_list_next
      }

      if (this.to_week_index == 0) {
        this.update_month()

        let next_day = this.week_list_prev[this.week_list_prev.length - 1][6].day
        this.to_prev_week_index = this.week_list_prev.length - 1 - Math.ceil(next_day / 7)

        this.week_list_prev_week = JSON.parse(JSON.stringify(this.week_list_prev))
      } else {
        this.to_prev_week_index = this.to_week_index - 1
        this.week_list_prev_week = this.week_list
      }
    },

    change_date_week(type) {
      let week_list = this.week_list
      let to_week_index = this.to_week_index
      if (type == "prev") {
        this.to_week_index = this.to_prev_week_index
        this.to_prev_week_index = this.to_next_week_index
        this.to_next_week_index = to_week_index

        this.week_list = this.week_list_prev_week
        this.week_list_prev_week = this.week_list_next_week
        this.week_list_next_week = week_list
      } else if (type == "next") {
        this.to_week_index = this.to_next_week_index
        this.to_next_week_index = this.to_prev_week_index
        this.to_prev_week_index = to_week_index

        this.week_list = this.week_list_next_week
        this.week_list_next_week = this.week_list_prev_week
        this.week_list_prev_week = week_list
      }

      this.set_to_day_all()
    },
    change_date_month(type) {
      let week_list = this.week_list
      if (type == "prev") {
        this.week_list = this.week_list_prev
        this.week_list_prev = this.week_list_next
        this.week_list_next = week_list
      } else if (type == "next") {
        this.week_list = this.week_list_next
        this.week_list_next = this.week_list_prev
        this.week_list_prev = week_list
      }
    },
    change_date(e) {
      let primary_current = this.current
      let current = e.detail.current

      this.current = current

      if (primary_current - current == -1 || primary_current - current == 2) {
        if (this.retract) {
          this.switch_month_week("next")
          this.change_week()
          if (primary_current - current == -1 && current != 1) {
            this.change_date_week("prev")
          } else if (primary_current - current == 2) {
            this.change_date_week("next")
          }
        } else {
          this.get_date(this.get_month("next"))
          this.update_month()
          if (primary_current - current == -1 && current != 1) {
            this.change_date_month("prev")
          } else if (primary_current - current == 2) {
            this.change_date_month("next")
          }
        }
      } else {
        if (this.retract) {
          this.switch_month_week("prev")
          this.change_week()
          if (primary_current - current == 1 && current != 1) {
            this.change_date_week("next")
          } else if (primary_current - current == -2) {
            this.change_date_week("prev")
          }
        } else {
          this.get_date(this.get_month("prev"))
          this.update_month()
          if (primary_current - current == 1 && current != 1) {
            this.change_date_month("next")
          } else if (primary_current - current == -2) {
            this.change_date_month("prev")
          }
        }
      }

      this.set_to_day_all()
      this.set_doc_lists_update()
      this.change()
    },
    update_month() {
      this.get_date(this.get_month("prev"), "prev")
      this.get_date(this.get_month("next"), "next")
    },
    set_doc_lists_update() {
      this.doc_list_update("week_list")
      this.doc_list_update("week_list_prev")
      this.doc_list_update("week_list_next")
      this.doc_list_update("week_list_prev_week")
      this.doc_list_update("week_list_next_week")
    },
    doc_list_update(week_list = "week_list") {
      let list = []
      this[week_list].map((item, index) => {
        let temp = item.map((vo, key) => {
          let value = this.dot_Obj[vo.date.replace(/-(\d)(?!\d)/g, "-0$1")]
          vo.dotColor = this.dotColorObj[value] ? this.dotColorObj[value].color : ""
          return { ...vo }
        })
        list.push(temp)
      })
      this[week_list] = list
    },

    // 若为今天则today赋值true,否则false
    set_to_day(type) {
      let list = []
      this[type].map((item, index) => {
        list.push(
          item.map((vo, key) => {
            if (vo.date == `${this.date}`) {
              vo.today = true
            } else {
              vo.today = false
            }
            return { ...vo }
          })
        )
      })
      this[type] = list
    },

    item_click(item, item_index = -1) {
      if (!this.retract && item.type !== "month") {
        return false
      }
      this.date = item.date
      if (item.type == "month") {
        this.nowDay = item.day
        if (item_index >= 0) this.to_week_index = item_index
      } else if (this.retract) {
        this.nowDay = item.day
      }

      let now_arr = item.date.split("-")
      this.nowYear = now_arr[0]
      this.nowMonth = now_arr[1]
      this.nowDay = now_arr[2]

      this.set_to_day_all(item_index)

      this.nowTime = this.date_parse(`${item.date}`)

      this.change()
      this.set_doc_lists_update()
    },
    set_to_day_all(item_index) {
      this.set_to_day("week_list")
      this.set_to_day("week_list_prev")
      this.set_to_day("week_list_next")
      this.set_to_day("week_list_prev_week")
      this.set_to_day("week_list_next_week")
    },
    get_month(type) {
      let nowMonth = this.nowMonth
      let nowYear = this.nowYear
      let nowDay = this.nowDay

      if (type == "prev") {
        if (nowMonth == 1) {
          nowMonth = 12
          nowYear = nowYear - 1
        } else {
          nowMonth--
        }
      } else if (type == "next") {
        if (nowMonth == 12) {
          nowMonth = 1
          nowYear = nowYear + 1
        } else {
          nowMonth++
        }
      }

      let days = this.get_month_days(nowMonth, nowYear)
      if (nowDay > days) {
        nowDay = days
      }

      return this.date_parse(`${nowYear}-${nowMonth}-${nowDay}`)
    },

    date_parse(str) {
      return Date.parse(str.replace(/-(\d)(?!\d)/g, "-0$1"))
    },
    switch_month_week(type = "next", update_week = false) {
      if (this.retract) {
        if (type == "prev") {
          this.get_date(this.nowTime - 86400 * 7 * 1000)
        } else if (type == "next") {
          this.get_date(this.nowTime + 86401 * 7 * 1000)
        }
        if (update_week) {
          this.update_swiper_item("week")
          this.set_doc_lists_update()
        }
      } else {
        this.get_date(this.get_month(type))
        this.update_swiper_item("month")
      }
      this.set_doc_lists_update()

      this.set_to_day_all()

      if (update_week) {
        this.change()
      }
    },
    update_swiper_item(type = "month") {
      if (type == "month") {
        if (this.current == 0) {
          this.change_date_month("next")
        } else if (this.current == 2) {
          this.change_date_month("prev")
        }
      } else if (type == "week") {
        if (this.current == 0) {
          this.change_date_week("next")
        } else if (this.current == 2) {
          this.change_date_week("prev")
        }
      }
    },
    next() {
      this.get_date(this.next_date)
    },

    // 获取日期数组
    get_date(value = "", type = "same") {
      let date = new Date()
      if (value) {
        date = new Date(value)
      }
      let nowMonth = date.getMonth() + 1,
        nowYear = date.getFullYear(),
        nowDay = date.getDate(),
        nowTime = date.getTime()

      let days = this.get_month_days(nowMonth, nowYear)
      let start_date = new Date(nowYear, nowMonth - 1, 1)
      let end_date = new Date(nowYear, nowMonth - 1, days)

      let prev_date = new Date(start_date.getTime() - 1)
      let prev_date_days = prev_date.getDate()

      let next_date = new Date(end_date.getTime() + 86401 * 1000)
      let next_date_days = next_date.getDate()

      let start_week = start_date.getDay()
      let date_arrs = []

      let week_list = []
      let count_days = 42

      for (let i = prev_date_days - start_week + 2; i <= prev_date_days; i++) {
        date_arrs.push({
          day: i,
          type: "prev",
          date: `${prev_date.getFullYear()}-${prev_date.getMonth() + 1}-${i}`,
        })
      }

      for (let i = 1; i <= days; i++) {
        date_arrs.push({
          day: i,
          type: "month",
          today: i == nowDay ? true : false,
          date: `${nowYear}-${nowMonth}-${i}`,
        })

        if (i == nowDay && type == "same") {
          this.date = `${nowYear}-${nowMonth}-${i}`
        }
      }
      // if (this.debug) console.log(value, date, this.date, `${next_date.getFullYear()}-${next_date.getMonth() + 1}-${next_date.getDate()}`)
      let date_arrs_length = date_arrs.length

      for (let i = 1; i <= count_days - date_arrs_length; i++) {
        date_arrs.push({
          day: i,
          type: "next",
          date: `${next_date.getFullYear()}-${next_date.getMonth() + 1}-${i}`,
        })
      }

      for (let i = 0; i < date_arrs.length / 7; i++) {
        let arr = []
        for (let j = 0; j < 7; j++) {
          if (date_arrs[i * 7 + j].today) {
            if (type == "same") {
              this.to_week_index = i
            }
          }
          arr.push(date_arrs[i * 7 + j])
        }
        week_list.push(arr)
      }

      if (type == "same") {
        this.week_list = week_list
        this.nowYear = nowYear
        this.nowMonth = nowMonth
        this.nowDay = nowDay
        this.nowTime = nowTime
        this.start_date = start_date
        this.end_date = end_date
        this.prev_date = prev_date
        this.next_date = next_date
        // console.log(week_list, "-----week_list-same")
        this.$emit("init", week_list)
      } else if (type == "prev") {
        this.week_list_prev = week_list
      } else if (type == "next") {
        this.week_list_next = week_list
      }
    },

    // 获取当前月份的总天数
    get_month_days(nowMonth, nowYear) {
      let month_arr = [1, 3, 5, 7, 8, 10, 12]
      let days = 0
      if (nowMonth == 2) {
        if (nowYear % 4 == 0) {
          days = 29
        } else {
          days = 28
        }
      } else if (month_arr.indexOf(nowMonth) >= 0) {
        days = 31
      } else {
        days = 30
      }
      return days
    },
  },
}
</script>

<style>
.date_dl-calendar {
  display: flex;
  width: 100%;
}
</style>
<style scoped lang="scss">
.tip-icon {
  width: 62rpx;
  height: 4rpx;
  background: #d8d8d8;
  border-radius: 2rpx;
}
.swiper-box {
  z-index: 1;
  overflow: hidden;
}
.date-calendar {
  width: 750rpx;
  position: relative;
  .background-box {
    position: absolute;
    top: 0;
    width: 100%;
    height: 480rpx;
    color: #edf2fe;
    font-weight: 800;
    font-size: 220rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding-top: 20rpx;
    box-sizing: border-box;
  }
  .week-box {
    color: #999999;
  }
  .dd {
    flex: 1;
    text-align: center;
    height: 80rpx;
    font-size: 30rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    position: relative;
    .current-box {
      background: #e0f1ff;
      color: #346eff !important;
    }
    .num {
      width: 40rpx;
      height: 40rpx;
      border-radius: 50%;
      line-height: 40rpx;
      color: #999999;
      font-size: 30rpx;
    }
    .month {
      color: #000000;
    }
    .today {
      background: #346eff !important;
      color: #ffffff !important;
    }
    .dot {
      width: 10rpx;
      height: 10rpx;
      border-radius: 50%;
      // background: #346eff;
      position: absolute;
      bottom: 0;
      left: 50%;
      transform: translateX(-50%);
    }
  }
  .retract-box {
    display: flex;
    justify-content: center;
    align-items: flex-end;
    height: 50rpx;
    padding-bottom: 20rpx;
    z-index: 1;
    background-color: #ffffff;
    position: relative;
    image {
      width: 62rpx;
      height: 10rpx;
    }
  }
  .head {
    display: flex;
    align-items: center;
    height: 100rpx;
    justify-content: center;
    border-bottom: 1rpx solid #f5f5f5;
    color: #333333;
    .title {
      width: 200rpx;
      font-size: 30rpx;
      text-align: center;
    }
    .icon-box {
      display: block;
      .next {
        transform: rotate(180deg);
        display: block;
      }
    }
  }
}
</style>

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • """1.个性化消息: 将用户的姓名存到一个变量中,并向该用户显示一条消息。显示的消息应非常简单,如“Hello ...
    她即我命阅读 8,550评论 0 5
  • 为了让我有一个更快速、更精彩、更辉煌的成长,我将开始这段刻骨铭心的自我蜕变之旅!从今天开始,我将每天坚持阅...
    李薇帆阅读 6,001评论 0 3
  • 似乎最近一直都在路上,每次出来走的时候感受都会很不一样。 1、感恩一直遇到好心人,很幸运。在路上总是...
    时间里的花Lily阅读 5,238评论 0 2
  • 1、expected an indented block 冒号后面是要写上一定的内容的(新手容易遗忘这一点); 缩...
    庵下桃花仙阅读 3,582评论 0 1
  • 一、工具箱(多种工具共用一个快捷键的可同时按【Shift】加此快捷键选取)矩形、椭圆选框工具 【M】移动工具 【V...
    墨雅丫阅读 3,561评论 0 0