行程日历(待处理事件日历)微信小程序

目录

效果图:月视图:​编辑

周视图:

​编辑

功能:

完整代码:


效果图:
月视图:

周视图:

功能:

1.月视图下,可以显示日期和当日的事件,上下滑动可以切换月份,点击日期可以切换到周视图查看明细。

2.周视图下,可以查看日期当日事件数量,左右滑动切换上周或者下周日期,点击日期可以查看事件明细。

3.右上角图标点击后返回月视图并锁定到当天日期。

完整代码:

<template>
  <view class="box">
    <view class="navbar"> 我的日程 </view>
    <u-tabs
      :list="list"
      lineColor="#15B3B4"
      activeColor="#15B3B4"
      @change="changeTabs"
      :current="currentIndex"
      :activeStyle="{fontWeight: '600'}"
      :itemStyle="{fontSize: '32rpx',lineHeight: '48rpx'}"
    ></u-tabs>
    <view class="dateCenter">
      <view class="date">
        {{ currentMonth }}月
      </view>
      <image src="@/static/images/interview/calendar.png" mode="aspectFit" class="calendar" @click="changeContract"></image>
    </view>
    <view class="weekdays">
      <text v-for="(day, index) in weekdays" :key="index" class="weekday">{{ day }}</text>
    </view>
    <view class="month" v-if="!contract">
      <text>{{ currentMonth }}月</text>
    </view>
    <scroll-view 
  scroll-x
  class="week-date-scroll"
  v-if="contract"
  @touchstart="handleWeekTouchStart"
  @touchend="handleWeekTouchEnd"
>
  <view v-for="(date, index) in weekDates" :key="index" class="week-date">
    <view class='week-date-item'>
      <view 
        :class="['itemDate', date.getDate() === selectedDate ? 'weekSelected' : '']"
        @click="selectWeekDate(date)"
      >
        {{ date.getDate() }}
      </view>
      <view v-if="getTripForWeekDate(date)" class="trip2Box">
        <!-- 遍历所有的 type -->
        <view v-for="(trip, idx) in getTripForWeekDate(date)" :key="idx" :class="['trip2-label',trip.type === 1 ? 'label1' : 'label2']">
          <text class="labelText">{{ trip.number }}</text>
        </view>
      </view>
    </view>
  </view>
</scroll-view>
    <scroll-view
      class="dates"
      style="width: 100%; height: 1000rpx; overflow-y: auto; overflow-x: hidden;"
      scroll-y
      @scroll="handleScroll"
      @touchstart="handleTouchStart"
      @touchend="handleTouchEnd"
      v-if="!contract"
    >
      <view class="dates-container">
        <!-- 添加空盒子占位 -->
        <view v-if="startOffset < 7" v-for="n in startOffset" :key="'empty-' + n" class="empty-slot"></view>
        <!-- 日期内容 -->
        <view
          v-for="(date, index) in dates"
          :key="index"
          class="date-item"
          @click="selectDate(date)"
          :style="{ color: (index + startOffset) % 7 === 6 || (index + startOffset) % 7 === 0 ? '#999' : '' }"
        >
          <view class="itemStyle" :class="{ selected: date === selectedDate }">
            {{ date }}
          </view>
          <!-- 显示标签 -->
          <view
            v-if="getTripForDate(date)"
            class="trip-label"
          >
            <text v-for="(trip, i) in getTripForDate(date).list" :key="i" :class="['trip-item', getTripForDate(date).type === 1 ? 'item1' : 'item2']">
              {{ trip }}
            </text>
          </view>
        </view>
        <!-- 添加空盒子占位 -->
        <!-- <view v-for="n in (7 - (dates.length + startOffset) % 7) % 7" :key="'empty-end-' + n" class="empty-slot"></view> -->
      </view>
    </scroll-view>
    <scroll-view 
      scroll-x
    >

    </scroll-view>
  </view>
</template>

<script>
export default {
  data() {
    const today = new Date();
    return {
      list: [
        { name: "全部" },
        { name: "面试" },
        { name: "培训" }
      ],
      currentIndex: 0,
      weekdays: ['日', '一', '二', '三', '四', '五', '六'],
      currentMonth: today.getMonth() + 1,
      dates: [],
      selectedDate: today.getDate(),
      touchStartY: 0,
      touchEndY: 0,
      startOffset: 0,
      atTop: false,
      atBottom: false,
      contract:false,
      weekDates:[],
      trip:{
        20250415:{
          type:1,
          list:[
          '面试面试1',
          '面试面试2',
          '面试面试3'
        ]
        },
        20250414:{
          type:2,
          list:[
          '培训培训培训',
          '培训培训培训',
          '培训培训培训'
        ]
        },
        20250515:{
          type:1,
          list:[
          '面试面试1',
          '面试面试2',
          '面试面试3'
        ]
        },
        20250514:{
          type:2,
          list:[
          '培训培训培训',
          '培训培训培训',
          '培训培训培训'
        ]
        }
      },
      trip2: {
  "20250416": [
    { type: 1, number: 2 },
    { type: 2, number: 3 }
  ],
  "20250414": [
    { type: 1, number: 1 }
  ],
  "20250515": [
    { type: 2, number: 4 }
  ]
      },
      weekTouchStartX: 0,
      weekTouchEndX: 0,
      currentTrip:{},
    }
  },
  created() {
    this.updateDates();
  },
  methods: {
    changeTabs(index) {
      this.currentIndex = index;
    },
    selectDate(date) {
      this.selectedDate = date;
      this.contract = true;
      this.updateWeekDates(date);

      
    },
    updateWeekDates(selectedDateOrDateObject) {
  // 支持传入日期数字或 Date 对象
  const baseDate =
    selectedDateOrDateObject instanceof Date
      ? selectedDateOrDateObject
      : new Date(new Date().getFullYear(), this.currentMonth - 1, selectedDateOrDateObject);

  const weekDay = baseDate.getDay(); // 获取当天是周几
  this.weekDates = Array.from({ length: 7 }, (_, i) => {
    return new Date(baseDate.getTime() + (i - weekDay) * 24 * 60 * 60 * 1000);
  });

  // 统计 weekDates 中的月份分布
  const monthCount = this.weekDates.reduce((acc, date) => {
    const month = date.getMonth() + 1; // 获取月份 (0 ~ 11) 转为 1 ~ 12
    acc[month] = (acc[month] || 0) + 1;
    return acc;
  }, {});

  // 更新 currentMonth 为出现最多的月份
  const predominantMonth = Object.keys(monthCount).reduce((a, b) =>
    monthCount[a] > monthCount[b] ? a : b
  );
  this.currentMonth = parseInt(predominantMonth, 10);

  // 调试输出
  // console.log("Updated weekDates:", this.weekDates.map(d => d.toISOString().split("T")[0]));
  // console.log("Updated currentMonth:", this.currentMonth);
},
    changeMonth(direction) {
      if (direction === 'up') {
        this.currentMonth = (this.currentMonth - 1) || 12;
      } else if (direction === 'down') {
        this.currentMonth = (this.currentMonth % 12) + 1;
      }
      this.updateDates();
    },
    handleTouchStart(e) {
    this.touchStartY = e.touches[0].clientY; // 记录初始触摸位置
  },
  handleTouchEnd(e) {
    this.touchEndY = e.changedTouches[0].clientY; // 记录结束触摸位置
    const distance = this.touchEndY - this.touchStartY; // 计算滑动距离
    if (this.atTop && distance > 150) {
      // 触顶且从上往下滑动超过阈值
      this.changeMonth("up");
    } else if (this.atBottom && distance < -150) {
      // 触底且从下往上滑动超过阈值
      this.changeMonth("down");
    }
  },
  handleScroll(e) {
    
    const target = e.currentTarget || e.target;
    if (target) {
      this.scrollTop = target.scrollTop || 0; // 当前滚动高度
      this.scrollHeight = target.scrollHeight || 0; // 内容总高度
      this.offsetHeight = target.offsetHeight || 0; // 容器可见高度

      // 实时更新触顶和触底状态
      this.atTop = this.scrollTop <= 0;
      this.atBottom =
        this.scrollTop + this.offsetHeight >= this.scrollHeight - 1;
    }
  },
    updateDates() {
      const year = new Date().getFullYear();
      const month = this.currentMonth - 1;
      const daysInMonth = new Date(year, month + 1, 0).getDate();
      this.dates = Array.from({ length: daysInMonth }, (_, i) => i + 1);
      const firstDayOfMonth = new Date(year, month, 1).getDay();
      this.startOffset = (firstDayOfMonth + 6) % 7 + 1; 
    },
    getTripForDate(date) {
      const year = new Date().getFullYear();
      const month = this.currentMonth.toString().padStart(2, "0");
      const day = date.toString().padStart(2, "0");
      const key = `${year}${month}${day}`;
      return this.trip[key] || null;
    },
    changeContract(){
      this.contract = false;
      const today = new Date();
      this.selectedDate = today.getDate();
      this.currentMonth = today.getMonth() + 1;
      this.updateWeekDates(today);
    },
    getDateForWeek(n) {
      const today = new Date();
      const weekDay = today.getDay();
      const date = new Date(today.getTime() + (n - weekDay - 1) * 24 * 60 * 60 * 1000);
      return date.getDate();
    },
    selectWeekDate(date) {
      this.selectedDate = date.getDate();
      this.updateWeekDates(date);
    },
    handleWeekTouchStart(e) {
      this.weekTouchStartX = e.touches[0].clientX;
    },
    handleWeekTouchEnd(e) {
  this.weekTouchEndX = e.changedTouches[0].clientX;
  const distance = this.weekTouchEndX - this.weekTouchStartX;

  if (distance > 50) {
    // 从左往右滑动:上一周
    const baseDate = this.weekDates[0]; // 当前周的第一天
    const newDate = new Date(baseDate.getTime() - 7 * 24 * 60 * 60 * 1000);
    this.updateWeekDates(newDate);
  } else if (distance < -50) {
    // 从右往左滑动:下一周
    const baseDate = this.weekDates[this.weekDates.length - 1]; // 当前周的最后一天
    const newDate = new Date(baseDate.getTime() + 7 * 24 * 60 * 60 * 1000);
    this.updateWeekDates(newDate);
  }
},
getTripForWeekDate(date) {
  const year = date.getFullYear();
  const month = (date.getMonth() + 1).toString().padStart(2, "0");  // 月份补零
  const day = date.getDate().toString().padStart(2, "0");  // 日期补零
  const key = `${year}${month}${day}`;  // 格式化为例如 20250415 的键
  
  return this.trip2[key] || null;  // 返回该日期所有的 trip2 数据
}
  },
  watch: {
    currentMonth() {
      this.updateDates();
    }
  }
};
</script>

<style lang="scss" scoped>
.box{
  width: 100%;
  height: calc(100vh - 100rpx);
  overflow: hidden;
}

.navbar{
  width: 100%;
  height: 160rpx;
  padding: 16rpx;
  padding-top:110rpx;
  font-size: 32rpx;
  line-height: 48rpx;
  font-weight: 600;
  box-sizing: border-box;
}

.dateCenter{
  width: 100%;
  padding: 24rpx 16rpx 0;
  display: flex;
  align-items: center;
  justify-content: space-between;

  .date{
    font-size: 48rpx;
    line-height: 48rpx;
    font-weight: 600;
  }

  .calendar{
    width: 48rpx;
    height: 48rpx;
  }
}

.month{
  margin: 20rpx 20rpx;
  text-align: right;
  font-size: 40rpx;
  line-height: 56rpx;
  font-weight: 600;
}

.weekdays {
  display: flex;
  justify-content: space-around;
  margin: 20rpx 0;
  font-size: 28rpx;
}

.week-date-scroll {
  white-space: nowrap;
  // padding: 0 16rpx 16rpx;
  padding-bottom: 16rpx;
}

.week-date {
  display: inline-block;
  width: 14.28%;
  height: 106rpx;
  // text-align: center;
  
}

.week-date-item {
  width: 100%;
  height: 100%;
  display: flex;
  flex-direction: column;
  // justify-content: center;
  align-items: center;
  
  .itemDate{
    width: 70rpx;
    height: 70rpx;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 36rpx;
    font-weight: 400;
    line-height: 48rpx;
    border-radius: 50%;
    box-sizing: border-box;
  }

.trip2Box{
  display: flex;
  flex-wrap: nowrap;
  align-items: center;
  justify-content: center;
}

  .trip2-label{
    margin: 4rpx 4rpx;
    padding: 4rpx 8rpx;
    width: 28rpx;
    height: 28rpx;
    display: flex;
    align-items: center;
    // justify-content: center;
    box-sizing: border-box;
    border-radius: 50%;
    text-align: center;
    
    .labelText{
      color: #fff;
      font-size: 20rpx;
      font-weight: 500;
      line-height: 28rpx;
      text-align: center;
      white-space: nowrap;
    }
  }

  .label1{
    background-color: #15b3b4;
  }
  .label2{
    background-color: #fb602d;
  }
}

.weekSelected {
  background-color: #15b3b4;
  color: #fff !important;
  // border-radius: 50%;
}

.dates {
  // display: flex;
  // flex-wrap: wrap;
  position: relative;
  padding: 0 16rpx;
}

.dates-container {
  display: flex;
  flex-wrap: wrap;
}


.date-item {
  flex: 0 0 14.28%; /* 每行 7 个 */
  height: 208rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  border-top: 2rpx solid #e5e5e5;
  padding-top: 8rpx;

  .itemStyle{
      width: 70rpx;
      height: 70rpx;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 36rpx;
      font-weight: 500;
      line-height: 48rpx;
  border-radius: 50%;
  box-sizing: border-box;
  }
}

.selected {
  background-color: #15b3b4;
  color: #fff !important;
  border-radius: 50%;
}

.empty-slot {
  flex: 0 0 14.28%;
  height: 208rpx;
}

.trip-label {
  margin-top: 16rpx;
  width: 86rpx;
  height: 104rpx;
  display: flex;
  flex-direction: column;
  align-items: center;
  
  .trip-item{
    width: 86rpx;
    height: 28rpx;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  margin: 4rpx 0;
  padding: 0 8rpx;
  border-radius: 4rpx;
  font-size: 20rpx;
  line-height: 28rpx;
  
  }

  .item1{
    color: #15b3b4;
  background-color: #e8f8f8;
  }
  .item2{
    color:#fb602d;
    background-color: #fff0ea;
  }
}


</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值