vue实现日历签到效果

发布于:2025-03-07 ⋅ 阅读:(12) ⋅ 点赞:(0)

在工作任务进行时,有一个签到日历的功能需求要实现,经过文档查询和样式优化实现了需求,在此记录一下。

技术背景:vue2+vant(样式控件)+ less + 一个公共样式文件

html实现部分:

 
<div class="calenderBox re_margin_top_16 re_sizing">
    <!-- 切换月份 -->   
    <div class="re_width100 re_flex_between">
        <div class="left">
            <van-icon @click="dateOperate('down')" class="icon" name="arrow-left" size="16" />
        </div>
        <div class="re_font_14">{{ date[0] }}年{{ date[1] }}月</div>
        <div class="right">
            <van-icon @click="dateOperate('up')" class="icon" name="arrow" size="16" />
        </div>
    </div>
    <!-- 日期列表 -->
    <div class="date-list re_width100 re_margin_top_16">
        <div class="date-content">
            <!-- 日历头 -->
            <div class="re_font_10 re_color_light re_flex_center" v-for="item in header" :key="item">
                {{ item }}
            </div>
            <!-- 日列表 -->
            <div class="re_margin_top_8 showday re_text_c re_sizing " v-for="(s, k) in dayList" :key="s + '-' + k">
                <template v-if="s.month == date[1]">
                    <div v-if="s.signInStatus == 1" class="re_flex_center everyDay">
                        <img class="star" src="../../../assets/task/gray.png" alt="">
                    </div>
                    <!-- 补签 -->
                    <div @click="signIn(s, 2)" class="bu" v-else-if="s.signInStatus == 4">
                        <img class="bu" src="../../../assets/task/no.png" alt="">
                    </div>
                    <!--  -->
                    <div class="bu" v-else-if="s.signInStatus == 2">
                        <img class="bu"  src="../../../assets/task/checkIn.png" alt="">
                    </div>
                    <!-- 签到 -->
                    <div @click="signIn(s, 1)" v-else-if="s.signInStatus == 0 && getDayText(s) == '今天'" class="bu re_sizing jintian re_sizing">
                        <img class="star" src="../../../assets/task/star.png" alt="">
                        <span class="re_color_calendar_color">+{{ s.getMoney }}</span>
                    </div>
                    <div v-else-if="s.signInStatus == 0 && getDayText(s) != '今天'" class="bu future re_sizing">
                        <img class="star" src="../../../assets/task/star.png" alt="">
                        <span class="color_redlight re_font_14">+{{ s.getMoney }}</span>
                    </div>
                    <div v-else class="re_flex_center everyDay">
                        <img class="star" src="../../../assets/task/gray.png" alt="">
                    </div>
                    <span :class="[
                        're_font_10',
                        s.month !== date[1] ? 'other-day' : '',
                        s.day === date[2] && s.month === date[1] ? getDayText(s) === '今天' ? 're_color_calendar_color' : 'today' : '',
                    ]">
                        {{ getDayText(s) }}
                    </span>
                </template>
            </div>
        </div>
    </div>
</div>

js实现部分

export default {
    name: "calendar",
    data() {
        return {
            // 日历头
            header: ["周日", "周一", "周二", "周三", "周四", "周五", "周六"],
            // 选择日期
            date: [],
            // 天列表
            dayList: [],
            // 定时器
            timer: null,
            // 第一天是周几
            weeks: 0,
            userInfo: {},
            setInfo: {},
            successTitle:'签到成功',
            handleSignInfo:{}
        };
    },
    created() { },
    mounted() {
        let time = new Date();
        this.date.push(
            time.getFullYear(),
            this.formatTime(time.getMonth() + 1),
            this.formatTime(time.getDate())
        );
        this.countDay();
        this.getSignInCalendar();
        this.getInfo();
    },
    methods: {
        getInfo(){
            getSetInfo().then(res => {
                this.setInfo = res.data;
            })
        },
        goBack(){
            this.$router.go(-1);
        },
        // beforeSignIn
        beforeSignIn(){
            extraSignIn({
                signInDay: this.setInfo.signInDay
            }).then(res => {
                if(res.code == 1){
                    this.successVisible = true;
                    this.successTitle = '补签成功';
                    this.handleSignInfo = res.data;
                    this.getSignInCalendar();
                    setTimeout(() => {
                        this.successVisible = false;
                    }, 5000);
                    this.coinsVisible = false
                }
                if (res.code == 500) {
                    showDialog({ title: '提示', message: res.msg });
                }
            })
        },
        // 签到
        signIn(s, type){
            if(type == 2){
                this.coinsVisible = true
                this.setInfo.signInDay = s.signInDay
                return
            }
            addSignIn({
                signInDay: s.signInDay
            }).then(res => {
                if(res.code == 1){
                    this.successVisible = true;
                    this.successTitle = '签到成功';
                    this.handleSignInfo = res.data;
                    this.getSignInCalendar();
                    setTimeout(() => {
                        this.successVisible = false;
                    }, 5000);
                }
                
                if (res.code == 500) {
                    showDialog({ title: '提示', message: res.msg });
                }
            })
        },
        getSignInCalendar(){
            signInCalendar({
                signInDay: this.date.join('-')
            }).then(res => {
                this.dayList.forEach(item => {
                    res.data.forEach(item2 => {
                        if(item.signInDay === item2.signInDay){
                            item.getMoney = item2.getMoney;
                            item.signInStatus = item2.signInStatus;
                        }
                    })
                })
            })
            getSignInInfo().then(res => {
                this.userInfo = res.data;
            })
        },
    
        formatTime(time) {
            return time < 10 ? `0${time}` : time;
        },
        // 计算显示的天数据
        countDay() {
            let [y, m, d] = this.date;
            // 获取第一天是周几
            let week = new Date(`${y}/${m}/1`).getDay(),
                // 获取当前月的上个月多少天
                lastDays = this.getDays(y, m - 1),
                // 获取这个月有多少天
                days = this.getDays(y, m);
            // 计算这个月有多少周
            this.weeks = Math.ceil((days - (7 - week)) / 7) + 1;
            // 将当前月份的天数生成数组
            this.dayList = Array.from({ length: this.getDays(y, m) }, (v, k) => {
                return {
                    day: this.formatTime(k + 1),
                    month: m,
                    year: y,
                    signInDay: `${y}-${m}-${this.formatTime(k + 1)}`
                };
            });
            // 将本月1日前的数据补齐
            for (let i = lastDays; i > lastDays - week; i--) {
                this.dayList.unshift({
                    day: i,
                    // 如果当前日期是1月补齐的是去年12月的数据
                    month: +m - 1 === 0 ? 12 : this.formatTime(+m - 1),
                    year: +m - 1 === 0 ? y - 1 : y,
                });
            }
            // 计算需要补齐多少天
            let length = this.weeks * 7 - this.dayList.length;
            // 将本月最后一天的数据补齐
            for (let i = 1; i <= length; i++) {
                this.dayList.push({
                    day: i,
                    // 如果当前日期是12月补齐的是明年年1月的数据
                    month: +m + 1 > 12 ? 1 : this.formatTime(+m + 1),
                    year: +m + 1 > 12 ? y + 1 : y,
                });
            }

        },
        // 校验选择的月份和已选择的日期是否匹配
        checkDay() {
            // 获取选择的年月有多少天 防止这年不是闰年 就将日期跳转到28号,或者有的月份没有31号就跳到30号
            let num = this.getDays(this.date[0], this.date[1]);
            if (num < this.date[2]) {
                this.date.splice(2, 1, num);
            }
        },
        // 获取某个月有多少天
        getDays(year, month) {
            // 一年中每个月的天数
            let days = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
            // 判断是不是闰年 2月29天
            if (year % 4 === 0 && (year % 100 !== 0 || year % 400 === 0)) {
                days[1] = 29;
            }
            return days[month - 1];
        },
        //左右按钮点击事件
        dateOperate(type) {
            let [y, m, d] = this.date;
            // 如果是向后翻
            if (type === "up") {
                // 日期向后翻 切换月份
                if (+m === 12) {
                    this.date.splice(0, 1, y + 1);
                    this.date.splice(1, 1, "01");
                } else {
                    this.date.splice(1, 1, this.formatTime(+m + 1));
                }

                // 如果是前后翻
            } else {
                if (+m === 1) {
                    this.date.splice(0, 1, y - 1);
                    this.date.splice(1, 1, 12);
                } else {
                    this.date.splice(1, 1, this.formatTime(+m - 1));
                }
            }
            this.countDay();
            this.checkDay();
            this.getSignInCalendar()
        },
        // 取消事件
        cancel() {
            this.$emit("cancel");
        },
        // 获取日期显示文本
        getDayText(dateObj) {
            const today = new Date();
            const todayYear = today.getFullYear();
            const todayMonth = this.formatTime(today.getMonth() + 1);
            const todayDay = this.formatTime(today.getDate());
            
            // 检查是否是今天
            if (dateObj.year == todayYear && dateObj.month == todayMonth && dateObj.day == todayDay) {
                return "今天";
            }
            
            // 检查是否是明天
            const tomorrow = new Date(today);
            tomorrow.setDate(today.getDate() + 1);
            const tomorrowYear = tomorrow.getFullYear();
            const tomorrowMonth = this.formatTime(tomorrow.getMonth() + 1);
            const tomorrowDay = this.formatTime(tomorrow.getDate());
            
            if (dateObj.year == tomorrowYear && dateObj.month == tomorrowMonth && dateObj.day == tomorrowDay) {
                return "明天";
            }
            
            // 其他日期显示月.日格式
            return `${dateObj.month}.${dateObj.day}`;
        },
    }
}

css实现部分:

<style lang="less" scoped>

.calendar {

    width: 100%;

    min-height: 100vh;

    background: url('../../../assets/task/calenderBg.png') no-repeat center top;

    background-size: 100% 812px;

    padding: 0 16px 16px;

    background-color: #ffe9e9;



    .title {

        padding: 30px 0 18px 0;

        width: 100%;

        font-weight: 600;



        .icon {

            position: absolute;

            left: 0px;

            top: 33px;

        }



        .explain {

            position: absolute;

            right: 0px;

            top: 34px;

        }

    }



    .calenderBox {

        width: 21.44rem;

        min-height: 33.25rem;

        background: #FFFFFF;

        border: 0.06rem solid #FFFFFF;

        border-radius: 1rem;

        padding: 1rem;

    }



    .date-list {

        padding-top: 0;

        display: flex;



        .date-content {

            flex: 1;

            height: 100%;

            display: grid;

            grid-template-columns: 1fr 1fr 1fr 1fr 1fr 1fr 1fr;

            grid-template-rows: auto;

        }

        .showday{

            margin: 8px auto 0;

        }



        .everyDay {

            width: 2.5rem;

            height: 3.38rem;

            background: #F5F7F9;

            border-radius: 6px;

        }

        .bu{

            width: 2.5rem;

            height: 3.38rem;

        }

        .color_redlight{

            color: #FF455E;

        }

        .jintian{

            border: 1px solid #FF455E;

            border-radius: 6px;

            padding-top: 6px;

        }

        .future{

            background: #F5F7F9;

            border-radius: 6px;

            padding-top: 6px;



        }

        .star{

            width: 18px;

            height: 18px;

        }

        .other-day {

            color: #CED1D9;

        }



        .today {

            color: #394365;

        }

    }

}

</style>