设计理念
昨天写了个简单美观的日历签到组件,使用的是Vue3+TypeScript,大概逻辑是先找到本月份第一天是周几,然后开始填充月份日期:weeksArray:[[]]:之后渲染到表格中,对于签到事件触发则先判断是否是今天且还未没有签到,没有就发送请求给后端的接口,然后签到就完成了。
设计UI
代码
详情请看俺滴代码:
<template><div class="calendar"><h1>{{ month }}月每日签到</h1><p>您已经签到了<em>{{ signedDates.length }}</em>天,<span v-show="!isTodaySigned">今天还没签到哦!</span><span v-show="isTodaySigned">今日已经签到!</span></p><a-divider style="margin-top: 0" /><table><thead><tr><th v-for="weekday in weekdays" :key="weekday">{{ weekday }}</th></tr></thead><tbody><tr v-for="week in weeks" :key="week"><tdv-for="day in week":key="day":class="{ today: isToday(day), signed: isSigned(day) }"@click="sign(day)">{{ day }}</td></tr></tbody></table></div>
</template><script lang="ts" setup>
import { computed, onMounted, reactive, ref } from "vue";
import message from "@arco-design/web-vue/es/message";
import { SignAddRequest, SignControllerService } from "../../generated";const currentDate = ref<Date>(new Date()); // 今天
const signedDates = ref(["2023-12-01", "2023-12-05"]); // 已签到的日期
const month = ref(new Date().getMonth() + 1); //本月
const weekdays = computed(() => {const weekdays = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];return weekdays.map((weekday) => weekday.slice(0, 2));
}); //表头
//本月日期情况
const weeks = computed(() => {const year = currentDate.value.getFullYear();const month = currentDate.value.getMonth();const firstDayOfMonth = new Date(year, month, 1).getDay();const daysInMonth = new Date(year, month + 1, 0).getDate();const weeksArray: Array<Array<any>> = [[]];let currentWeek = 0;for (let i = 0; i < firstDayOfMonth; i++) {weeksArray[currentWeek].push("");}for (let day = 1; day <= daysInMonth; day++) {if (weeksArray[currentWeek].length === 7) {weeksArray.push([]);currentWeek++;}weeksArray[currentWeek].push(day);}return weeksArray;
});
// 当前表格是不是今天
const isToday = (day: number) => {const today = new Date();return (day === today.getDate() &¤tDate.value.getMonth() === today.getMonth() &¤tDate.value.getFullYear() === today.getFullYear());
};
// 当前表格是否已经被签到了
const isSigned = (day: number) => {const year = currentDate.value.getFullYear();const month = currentDate.value.getMonth() + 1;const date = day.toString().padStart(2, "0");const dateString = `${year}-${month}-${date}`;return signedDates.value.includes(dateString);
};
// 今天是否已签
const isTodaySigned = computed(() => {const today = new Date();const day = today.getDate();return isSigned(day);
});
// 签到
const sign = (day: number) => {if (isToday(day) && !isSigned(day)) {const year = currentDate.value.getFullYear();const month = currentDate.value.getMonth() + 1;const date = day.toString().padStart(2, "0");const dateString = `${year}-${month}-${date}`;signedDates.value.push(dateString);message.success("签到成功,业精于勤荒于嬉,请继续坚持!!!");addSign(dateString);}
};
//发送签到请求参数
// 发给后端请求当前登录用户本月的签到情况
const addSign = async (date: string) => {SignControllerService.addSignUsingPost(date);
};
const init = async () => {console.log("签到组件加载完成");const res = await SignControllerService.getSignedDateUsingGet();if (res.code === 0) {signedDates.value = res.data as string[];}
};
onMounted(() => {init();
});
</script><style scoped>
.calendar {display: flex;flex-direction: column;justify-content: center;
}.calendar h1 {font-family: 华文仿宋;text-align: center;margin-bottom: 0px;
}.calendar p {font-size: large;font-family: 华文仿宋;text-align: center;
}em {color: lightgreen;font-size: large;font-weight: bold;margin: 0 5px 0 1px;
}table {border-collapse: collapse;width: 95%;margin: 0 auto;
}th,
td {border: 1px solid #ccc;padding: 10px;text-align: center;
}th {background-color: #f0f0f0;
}td.today {background-color: #eaf6ff;cursor: pointer;
}td.signed {background-color: #b3e5fc;
}td.signed:hover {background-color: #80d4f7;
}
</style>
后端主要的SQL语言
主要的SQL语言是:用DATE_FORMAT(CURDATE(), '%Y%m)判断年月是否一样。
SELECT createTime
FROM sign
WHERE userId = 1727616588754034690and DATE_FORMAT(createTime, '%Y%m') = DATE_FORMAT(CURDATE(), '%Y%m')
###判断本月内的签到情况