vue/H5的日历组件可简单定制
在components创建riliZujian.vue
<template>
<div class="max_box">
<!-- 日历 文字 -->
<div class="month">
<div @click="lastMonth" class="monthText13">上月</div>
<div class="monthText2">{{ year }}年{{ month }}月</div>
<div class="monthText13" @click="nextMonth">下月</div>
</div>
<!-- 日历 周 -->
<div class="week">
<div v-for="weeks in weekArr" :key="weeks">
{{ weeks }}
</div>
</div>
<!-- 日历 日 -->
<div class="day">
<div
:class="[{ checkday: !days.date }, { choose: days.flag }]"
v-for="(days, index) in dayArr"
:key="index"
>
{{ days.day }}
<img src="./yidaka.png" class="dayImg" v-if="days.flag" />
</div>
</div>
</div>
</template>
<script>
export default {
props: {
already: {
type: Array
}
},
watch: {
already: {
handler (v) {
// 监听getAlready方法 获得已打卡的样式
this.getAlready()
},
immediate: true, // 第一次赋值时触发
deep: true // 深度监听
}
},
name: 'rili-zujian',
data () {
return {
dayArr: [], // 当前月每日
day: new Date().getDate(), // 当前日
year: new Date().getFullYear(), // 当前年
month: new Date().getMonth() + 1, // 当前月
weekArr: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'], // 每周
aheadDay: 0 // 前方空白天数数量
}
},
mounted () {
const that = this
// 初始日期
that.initDate()
},
methods: {
// 渲染已经签到的日期
getAlready () {
for (const i in this.dayArr) {
if (
this.already.indexOf(new Date(this.dayArr[i].date).getTime()) === -1
) {
this.$set(this.dayArr[i], 'flag', false)
} else {
this.$set(this.dayArr[i], 'flag', true)
}
}
},
// 初始化日期
initDate () {
const that = this
that.dayArr = []
// 当前月总天数
const totalDay = new Date(that.year, that.month, 0).getDate()
// 遍历总天数将日期逐个添加至数组
for (let i = 1; i <= totalDay; i++) {
// 得到需补充天数
const value = new Date(that.year, that.month - 1, i).getDay()
// 补充前面空白日期
if (i === 1 && value !== 0) {
that.addBefore(value)
that.aheadDay = value
}
// 添加本月日期
const obj = {}
obj.date =
that.year +
'-' +
that.formatNum(that.month) +
'-' +
that.formatNum(i)
obj.day = i
that.dayArr.push(obj)
// 补充后面空白日期
if (i === totalDay && value !== 6) that.addAfter(value)
}
},
// 补充前面空白日期
addBefore (value) {
const that = this
const totalDay = new Date(that.year, that.month - 1, 0).getDate()
for (let i = 0; i < value; i++) {
const obj = {}
obj.date = ''
obj.day = totalDay - (value - i) + 1
that.dayArr.push(obj)
}
},
// 补充后空白日期
addAfter (value) {
const that = this
for (let i = 0; i < 6 - value; i++) {
const obj = {}
obj.date = ''
obj.day = i + 1
that.dayArr.push(obj)
}
},
// 格式化日期位数
formatNum (num) {
return num < 10 ? '0' + num : num
},
// 上一个月
lastMonth () {
const that = this
if (that.month === 1) {
that.year -= 1
that.month = 12
} else {
that.month -= 1
}
that.initDate()
that.getAlready()
},
// 下一个月
nextMonth () {
const that = this
if (that.month === 12) {
that.year += 1
that.month = 1
} else {
that.month += 1
}
that.initDate()
that.getAlready()
}
}
}
</script>
<style lang="less" scoped>
// 最大的盒子
.max_box {
width: 375px;
box-sizing: border-box;
background-color: none;
}
// 月周日的样式 flex布局
.month,
.week,
.day {
width: 375px;
display: flex;
align-items: center;
justify-content: space-between;
}
// 月的文字的样式
.month {
height: 50px;
}
.monthText13 {
width: 50px;
height: 50px;
line-height: 50px;
text-align: center;
}
.monthText2 {
width: 100px;
height: 50px;
line-height: 50px;
text-align: center;
}
// 日 flex超出 的换行
.day {
flex-wrap: wrap;
}
// 周和日 的div样式
.week > div,
.day > div {
width: 32px;
height: 32px;
line-height: 32px;
margin: 9px;
text-align: center;
position: relative;
}
// 本月之外的数字 改成白色 隐藏起来
.checkday {
color: #f9f9f9;
}
// 已打卡 的日期 的样式
.choose {
color: #ffffff;
background: #b5443a;
border-radius: 10px;
position: relative;
}
.dayImg {
width: 36px;
height: 13px;
position: absolute;
top: -3px;
left: 0;
}
</style>
在业务代码中引入 riliZujian 组件
<template>
<!-- 打卡领取道具礼包 -->
<div class="maxBox">
<!-- 导航栏 只有返回键 -->
<div
class="title"
:style="{ top: this.$route.query.statusBarHeight + 'px' }"
>
<img src="./climbersImg/title01.png" class="title01" @click="goBack" />
<div class="title03"></div>
</div>
<!-- 打卡 背景图 -->
<img src="./climbersImg/dakaMaxBg.png" class="dakabeijingtu" />
<!-- 内容 -->
<div class="content">
<!-- 用户信息 -->
<div class="content_top">
<!-- 打卡 打卡规则 子绝父相 -->
<img
src="./climbersImg/dakaguize.png"
class="dakaguize"
@click="onDakaguize"
/>
<img :src="userInfo.userHeadUrl" class="content_top_img" />
<div class="content_top_text">
<div class="content_top_text_1">
<img
src="./climbersImg/dakaText1.png"
class="content_top_text_1_img"
/>
<div class="content_top_text_1_number">
{{ userInfo.signCount }}
</div>
<img
src="./climbersImg/dakaText2.png"
class="content_top_text_1_img"
/>
</div>
<div class="content_top_text_2">
<div class="content_top_text_2_text">
<span style="color: #b5443a">{{ userInfo.signCount }}</span
>/{{ userInfo.amount }}元
</div>
<div class="content_top_text_2_text">
满{{ userInfo.amount }}元自动发放到钱包
</div>
</div>
</div>
</div>
<!-- 日历 -->
<riliZujian :already="dakaData" @changeMonth="historysign" />
</div>
<!-- 弹出框 打卡规则 -->
<van-popup v-model="showDakaguize" position="bottom" style="background: none;">
<img src="./climbersImg/dakaguizeBig.png" class="guizetanchuang" />
</van-popup>
</div>
</template>
<script>
// getDakaSub,
import { getDakaUserInfo, getDakaRili } from '@/api/zhidou'
import riliZujian from '@/components/rilizujian.vue'
export default {
name: 'climbers-dakalinglibao',
data () {
return {
showDakaguize: false, // 弹出框 展示打卡规则的字段
dakaData: [], // 打卡的日历的接收字段
userInfo: {} // 用户的打卡和基本信息
}
},
components: {
riliZujian
},
mounted () {
this.getUserInfo()
this.historysign()
},
methods: {
// 打开 打卡规则 的方法
onDakaguize () {
console.log(123)
this.showDakaguize = true
},
// 获取当前用户的打卡基础信息
getUserInfo () {
getDakaUserInfo({ userId: this.$route.query.userId }).then((res) => {
// console.log(res, 'res---')
if (res.status === 200) {
this.userInfo = res.data
} else {
this.$toast.fail(res.msg)
}
})
},
// 获取当前用户的打卡日历信息
historysign () {
getDakaRili({
userId: 107,
time: '2025-03-17 12:26:43'
}).then((res) => {
if (res.status === 200) {
for (const i in res.data) {
// 接口返回的每一项 如果为 true 则进来
if (res.data[i]) {
this.dakaData.push(new Date(i).getTime())
}
}
// console.log(this.dakaData)
} else {
this.$toast.fail(res.msg)
}
})
},
// 返回到上级页面
goBack () {
this.$router.push({
path: '/上级页面地址',
query: {
userId: this.$route.query.userId,
statusBarHeight: this.$route.query.statusBarHeight
}
})
}
}
}
</script>
<style lang="less" scoped>
.maxBox {
width: 375px;
position: relative;
}
.title {
width: 375px;
height: 44px;
display: flex;
justify-content: space-between;
align-items: center;
position: absolute;
left: 0;
.title01 {
width: 28px;
height: 28px;
margin-left: 16px;
}
.title03 {
width: 44px;
height: 20px;
}
}
.dakabeijingtu {
width: 375px;
}
.content {
width: 375px;
// height: 500px;
background-image: url(./climbersImg/dakaMinBg.png);
background-size: 100% 100%;
position: absolute;
top: 250px;
bottom: 0;
.content_top {
width: 343px;
height: 94px;
margin: 0 16px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
display: flex;
position: relative;
.dakaguize {
width: 34px;
height: 96px;
position: absolute;
top: -23px;
right: -18px;
}
.content_top_img {
width: 56px;
height: 56px;
border-radius: 56px;
margin: 21px 15px 0 18px;
}
.content_top_text {
width: 254px;
margin-top: 20px;
.content_top_text_1 {
width: 254px;
height: 32px;
display: flex;
align-items: center;
.content_top_text_1_img {
height: 16px;
}
.content_top_text_1_number {
height: 32px;
line-height: 32px;
font-size: 27px;
font-weight: 700;
color: #b5443a;
margin: 0 5px;
}
}
.content_top_text_2 {
width: 254px;
height: 20px;
display: flex;
.content_top_text_2_text {
font-size: 14px;
font-weight: 400;
color: rgba(0, 0, 0, 0.5);
margin-right: 8px;
}
}
}
}
}
.guizetanchuang {
width: 375px;
}
::v-deep .van-calendar__header {
background: rgba(255, 255, 255, 0.1);
}
::v-deep .van-calendar__body {
background: rgba(255, 255, 255, 0.1);
}
::v-deep .van-calendar__header-title {
display: none;
}
</style>
参考资料:签到组件、可签到和补签,统计签到天数和积分 - DCloud 插件市场