设计理念
昨天写了个简单美观的日历签到组件,使用的是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">
<td
v-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() &&
currentDate.value.getMonth() === today.getMonth() &&
currentDate.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 = 1727616588754034690
and DATE_FORMAT(createTime, '%Y%m') = DATE_FORMAT(CURDATE(), '%Y%m')
###判断本月内的签到情况