123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234 |
- <template>
- <view class="page adffc">
- <div class="week adf">
- <div class="week-pre adfacjc" v-for="(w, i) in weeks" :class="{ active: i === 0 || i === 6 }" :key="i">{{ w }}</div>
- </div>
- <div class="list">
- <div class="list-item" v-for="(c,i) in calendarsCopy" :key="i">
- <div class="title">{{c.monthTitle}}</div>
- <div class="days">
- <div class="days-pre"
- :class="{'active':d.selected&&d.isCurrentMonth,'in':d.inrange&&d.isCurrentMonth}"
- v-for="(d,di) in c.days" :key="di">
- <div class="today" v-if="d.isToday">今天</div>
- <div class="day adfacjc" :class="{'hui':d.isPast}">{{d.isCurrentMonth?d.day:''}}</div>
- <div class="se" v-if="d.start&&d.isCurrentMonth">开始</div>
- <div class="se" v-if="d.end&&d.isCurrentMonth">结束</div>
- </div>
- </div>
- </div>
- </div>
- </view>
- </template>
- <script setup name="ActivityCalendar">
- import { ref, onMounted } from 'vue';
- const weeks = ['日', '一', '二', '三', '四', '五', '六'];
- const calendars = ref([]);
- const calendarsCopy = ref([]);
- const createCalendar = (months) => {
- return new Promise((resolve,reject)=>{
- const calendarList = [];
- const today = new Date();
- today.setHours(0, 0, 0, 0); // 标准化日期
-
- for (let i = 0; i < months; i++) {
- // --- 1. 计算目标月份 ---
- const targetDate = new Date();
- targetDate.setHours(0, 0, 0, 0);
- targetDate.setMonth(targetDate.getMonth() + i, 1);
-
- const year = targetDate.getFullYear();
- const month = targetDate.getMonth(); // 0-11
-
- const monthData = {
- monthTitle: `${year}/${String(month + 1).padStart(2, '0')}`,
- days: []
- };
-
- // --- 2. 计算日历网格的起始和结束日期 ---
- let startDate;
- const firstDayOfMonth = new Date(year, month, 1);
- const lastDayOfMonth = new Date(year, month + 1, 0);
-
- // **核心逻辑:根据是否为第一个月,决定网格的起始日期**
- if (i === 0) {
- // **第一个月(当前月):** 网格从今天所在周的周日开始
- startDate = new Date(today);
- startDate.setDate(today.getDate() - today.getDay());
- } else {
- // **后续月份:** 网格从该月第一天所在周的周日开始,保证月份的完整性
- startDate = new Date(firstDayOfMonth);
- startDate.setDate(firstDayOfMonth.getDate() - firstDayOfMonth.getDay());
- }
-
- // **网格的结束日期总是该月最后一天所在周的周六**
- const endDate = new Date(lastDayOfMonth);
- endDate.setDate(lastDayOfMonth.getDate() + (6 - lastDayOfMonth.getDay()));
-
- // --- 3. 循环填充日历网格中的每一天 ---
- for (let d = new Date(startDate); d <= endDate; d.setDate(d.getDate() + 1)) {
- const currentYear = d.getFullYear();
- const currentMonth = d.getMonth() + 1;
- const currentDay = d.getDate();
-
- monthData.days.push({
- day: currentDay,
- dateStr: `${currentYear}-${String(currentMonth).padStart(2, '0')}-${String(currentDay).padStart(2, '0')}`,
- isToday: d.getTime() === today.getTime(),
- // 标识是否属于当前正在生成的月份 (e.g., 在7月数据中,8月1日这项为 false)
- isCurrentMonth: d.getMonth() === month,
- // 标识是否为过去日期 (仅在第一个月生效)
- isPast: i === 0 && d.getTime() < today.getTime()
- });
- }
-
- calendarList.push(monthData);
- }
- resolve(calendarList)
- })
- };
- const setStartEndDay = type => {//type 0:全部时间 1:一周内 2:一月内 3:本周末
- let d = new Date();
- let s = new Date().Format('yyyy-MM-dd');
- let e = null;
- switch(type){
- case 0:
- s = e = '';
- break;
- case 1:
- e = new Date(d.setDate(d.getDate(s)+6)).Format('yyyy-MM-dd');
- break;
- case 2:
- e = new Date(d.setDate(d.getDate(s)+30)).Format('yyyy-MM-dd');
- break;
- case 3:
- let sw = new Date(s).getDay();
- if(sw===0){//当天是周日
- s = new Date(d.setDate(d.getDate(s)-1)).Format('yyyy-MM-dd');
- e = new Date(s).Format('yyyy-MM-dd');
- }else{
- s = new Date(d.setDate(d.getDate(s)+(6-sw))).Format('yyyy-MM-dd');
- e = new Date(d.setDate(d.getDate(s)+1)).Format('yyyy-MM-dd');
- }
- break;
- }
- calendarsCopy.value = JSON.parse(JSON.stringify(calendars.value));
- if(!s||!e) return
- calendarsCopy.value.forEach(c=>{
- c?.days.forEach(d=>{
- if(d.dateStr===s||d.dateStr===e){
- d.selected = true;
- d.start = d.dateStr===s;
- d.end = d.dateStr===e;
- }
- if(Date.parse(new Date(d.dateStr))>Date.parse(new Date(s))&&Date.parse(new Date(d.dateStr))<Date.parse(new Date(e))){
- d.inrange = true;
- }
- })
- })
- }
- onMounted(async () => {
- calendars.value = await createCalendar(2);
- calendarsCopy.value = JSON.parse(JSON.stringify(calendars.value));
- });
- defineExpose({
- setStartEndDay
- })
- </script>
- <style scoped lang="scss">
- .page {
- width: 100%;
- height: 100%;
- background: #f7f7f7;
- .week {
- width: 100%;
- height: 81rpx;
- background: #ffffff;
- &-pre {
- width: calc(100% / 7);
- height: 100%;
- font-family: PingFangSC, PingFang SC;
- font-weight: 400;
- font-size: 24rpx;
- color: #6b7280;
- line-height: 33rpx;
- &.active {
- font-weight: bold;
- color: #70cf52;
- }
- }
- }
- .list {
- flex: 1;
- overflow-y: auto;
- &-item{
- .title{
- font-family: PingFang-SC, PingFang-SC;
- font-weight: bold;
- font-size: 30rpx;
- color: #252525;
- line-height: 30rpx;
- text-align: center;
- margin-top: 30rpx;
- }
- .days{
- display: flex;
- flex-wrap: wrap;
- margin-top: 40rpx;
- &-pre{
- width: calc(100% / 7);
- height: 120rpx;
- position: relative;
- &.active{
- background: #B7F358;
- }
- &.in{
- background: rgba(183, 243,88, .4);
- }
- .day{
- width: 100%;
- height: 100%;
- font-family: PingFang-SC, PingFang-SC;
- font-weight: bold;
- font-size: 30rpx;
- color: #252525;
- line-height: 42rpx;
- &.hui{
- color: #989998;
- }
- }
- .today{
- width: 100%;
- font-family: PingFangSC, PingFang SC;
- font-weight: 400;
- font-size: 20rpx;
- color: #989998;
- line-height: 28rpx;
- text-align: center;
- position: absolute;
- top: 10rpx;
- }
- .se{
- width: 100%;
- font-family: PingFangSC, PingFang SC;
- font-weight: bold;
- font-size: 20rpx;
- color: #989998;
- line-height: 28rpx;
- text-align: center;
- position: absolute;
- bottom: 15rpx;
- }
- }
- }
- }
- }
- }
- </style>
|