效果如图
VeryCapture_20251009150221.gif
一.日期函数
- 计算当前月份的天数
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
},
- 根据月份进行填补,构建一个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>