div 封装日历

 案例图:

代码: 

<template>
  <el-dialog 
    :title="dialogTitle" 
    :visible.sync="showCalendar" 
    width="27%" 
    @close="handleClose"
  >
    <div class="custom-calendar">
      <!-- 月份切换区域 -->
      <div class="calendar-header">
        <el-button icon="el-icon-arrow-left" plain @click="prevMonth">
        </el-button>
        <span>{{ currentYear }}年{{ currentMonth }}月</span>
        <el-button icon="el-icon-arrow-right" plain @click="nextMonth">
        </el-button>
      </div>
      <!-- 星期标题区域 -->
      <div class="weekdays">
        <div 
          v-for="(weekday, index) in weekdays" 
          :key="index" 
          class="weekday"
        >
          {{ weekday }}
        </div>
      </div>
      <!-- 日期单元格区域 -->
      <div class="days-grid">
        <!-- 填充空白日期 -->
        <div 
          v-for="(empty, index) in firstDayOffset" 
          :key="'empty-' + index" 
          class="day empty"
        >
        </div>
        <!-- 遍历展示当前月日期 -->
        <div 
          v-for="(dateObj, index) in visibleDates" 
          :key="index" 
          class="day" 
          :class="{ 'selected': isSelected(dateObj.dateStr) }" 
          @click="toggleSelection(dateObj.dateStr)"
        >
          {{ dateObj.day }}
        </div>
      </div>
      <!-- 底部按钮 -->
      <div slot="footer" class="dialog-footer">
        <el-button @click="cancel">
          {{ console.log('点击取消按钮,执行 cancel 方法') }}
          取消
        </el-button>
        <el-button type="primary" @click="confirm">
          {{ console.log('点击确认按钮,执行 confirm 方法') }}
          确定
        </el-button>
      </div>
    </div>
  </el-dialog>
</template>

<script>
export default (await import('vue')).defineComponent({
  name: 'CustomCalendar',
  // 从父组件接收 dialogTitle,用于动态设置 el-dialog 的 title
  props: {
    dialogTitle: {
      type: String,
      default: '设置工厂日历' 
    },
    customData: {
      type: Object,
      default: () => ({})
    }
  },
  data() {
    return {
      getrowData: null, //父组件-选中行的数据
      showCalendar: false, 
      currentYear: new Date().getFullYear(),  //显示当前的年份
      currentMonth: new Date().getMonth() + 1, //显示当前的月份
      selectedDates: [], //存储选中的日期,格式为'YYYY-MM-DD',用于渲染
      cancelDatas: [], //从接口获取 上一次存入的值(用于-最终生成取消选中的数据,作为参数传递)
      oldSelectDate: [], //从接口获取 上一次存入的值且过滤出当钱日期之前的日期值,(目的:选择日期时,需要判断是否时上一次选择过的,如果时上一次选择的日期,不可取消选中)
      weekdays: ['日', '一', '二', '三', '四', '五', '六'] 
    }
  },
  computed: {
    // 计算当前月要显示的日期数据
    visibleDates() {
      console.log('执行 visibleDates 计算属性');
      const dates = [];
      const firstDay = new Date(this.currentYear, this.currentMonth - 1, 1); 
      const lastDay = new Date(this.currentYear, this.currentMonth, 0).getDate(); 
      for (let i = 1; i <= lastDay; i++) {
        const date = new Date(this.currentYear, this.currentMonth - 1, i); 
        dates.push({
          dateStr: this.formatDate(date), 
          day: i 
        });
      }
      return dates;
    },
    // 计算当前月第一天是星期几,用于填充空白日期
    firstDayOffset() {
      console.log('执行 firstDayOffset 计算属性');
      const firstDay = new Date(this.currentYear, this.currentMonth - 1, 1); 
      return firstDay.getDay(); 
    }
  },
  watch: {
    customData: {
      handler(newValue, oldVal) {
        console.log('监听 customData 变化,newValue===', newValue, 'oldVal===', oldVal);
        if (newValue) {
          this.selectedDates = newValue.workDate || []; 
          this.getrowData = newValue.rowData; 
        }
      },
      deep: true, 
      immediate: true 
    }
  },
  methods: {
    // 弹窗右上角--关闭
    handleClose() {
      this.selectedDates = []; 
      this.cancelDatas = []; 
      this.oldSelectDate = []; 
      this.showCalendar = false; 
    },
    // 格式化日期为 'YYYY - MM - DD' 格式
    formatDate(date) {
      const year = date.getFullYear();
      const month = String(date.getMonth() + 1).padStart(2, '0'); 
      const day = String(date.getDate()).padStart(2, '0'); 
      return `${year}-${month}-${day}`;
    },
    // 判断日期是否已选中
    isSelected(dateStr) {
      return this.selectedDates.includes(dateStr);
    },
    // 切换日期选中状态
    toggleSelection(dateStr) {
      console.log('执行 toggleSelection 方法,切换日期:', dateStr);
      debugger;
      if (this.oldSelectDate.includes(dateStr)) {
        console.log('上次已选择过的值==', dateStr);
        return; 
      }
      if (this.isSelected(dateStr)) {
        // 取消选中
        console.log('取消选中日期:', dateStr);
        this.cancelDatas.push(dateStr); 
        this.selectedDates = this.selectedDates.filter((item) => item !== dateStr); 
      } else {
        // 选中日期
        console.log('选中日期:', dateStr);
        this.selectedDates.push(dateStr); 
      }
    },
    // 切换到上月
    prevMonth() {
      if (this.currentMonth === 1) { 
        this.currentYear--; 
        this.currentMonth = 12; 
      } else {
        this.currentMonth--; 
      }
      // 切换月份后清空已选中日期(可根据需求决定是否保留,目前是保留)
      // this.selectedDates = []; 
    },
    // 切换到下月
    nextMonth() {
      if (this.currentMonth === 12) { 
        this.currentYear++; 
        this.currentMonth = 1; 
      } else {
        this.currentMonth++; 
      }
      // 切换月份后清空已选中日期(可根据需求决定是否保留,目前是保留)
      // this.selectedDates = []; 
    },
    // 取消按钮 
    cancel() {
      this.selectedDates = []; 
      this.cancelDatas = []; 
      this.oldSelectDate = []; 
      this.showCalendar = false; 
      this.$emit('close', false); 
    },
    // 确定,将值传入父组件 
    confirm() {
      console.log('执行 confirm 方法,最终选中的日期:', this.selectedDates);
      // 过滤 selectedDates,只保留和 oldSelectDate 不相等的日期
      const filteredSelectedDates = this.selectedDates.filter(
        (date) => !this.oldSelectDate.includes(date)
      ); 
      const result = this.validateWorkDates(filteredSelectedDates); 
      if (result === true) {
        console.log('所有日期都在允许范围内');
      } else {
        return this.$message.warning(result); 
      }
      // 已选中的值
      const newSelectedDates = this.selectedDates.map((date) => ({
        operateType: 'Y', 
        workDate: date 
      }));
      // 取消选中的数据
      const newCancelDatas = this.cancelDatas.map((date) => ({
        operateType: 'N', 
        workDate: date 
      }));
      const workDates = [...newCancelDatas, ...newSelectedDates]; 
      console.log(workDates, '==进来了==', newCancelDatas); 
      this.getrowData.workDate = workDates; 
      this.$emit('submit', this.getrowData); 
      this.showCalendar = false; 
    },

    // 验证日期是否符合规则
    validateWorkDates(workDate) {
      console.log('执行 validateWorkDates 方法,验证日期:', workDate);
      const today = new Date(); 
      const currentMonth = today.getMonth(); 
      const currentYear = today.getFullYear(); 
      const currentDay = today.getDate(); 
      // 计算允许的最大月份(当前月+5)
      const maxMonth = (currentMonth + 5) % 12; 
      const maxYear =
        currentYear + Math.floor((currentMonth + 5) / 12); 
      // 错误信息收集
      const pastDayDates = []; 
      const pastDates = []; 
      const futureDates = []; 
      // 遍历检查每个日期
      workDate.forEach((dateStr) => {
        const date = new Date(dateStr); 
        if (isNaN(date.getTime())) {
          return; 
        }
        const dateYear = date.getFullYear(); 
        const dateMonth = date.getMonth(); 
        const dateDay = date.getDate(); 
        // 1. 判断是否在当前之前
        if (
          dateYear < currentYear ||
          (dateYear === currentYear && dateMonth < currentMonth)
        ) {
          pastDates.push(dateStr); 
        }
        // 2. 判断是否当前日期
        else if (
          dateYear < currentYear ||
          (dateYear === currentYear && dateMonth < currentMonth) ||
          (dateYear === currentYear &&
            dateMonth === currentMonth &&
            dateDay < currentDay)
        ) {
          pastDayDates.push(dateStr); 
        }
        // 判断是否超过当前月+5个月
        else if (
          dateYear > maxYear ||
          (dateYear === maxYear && dateMonth > maxMonth)
        ) {
          futureDates.push(dateStr); 
        }
      });
      // 生成提示信息
      let message = ''; 
      if (pastDates.length > 0 || pastDayDates.length > 0) {
        message += '当前日期之前的日期不可修改设置\n'; 
      }
      if (futureDates.length > 0) {
        message += '仅支持设置当前月及往后5个月的日历\n'; 
      }
      // 如果有错误,返回错误信息;否则返回 true
      return message ? message : true; 
    }
  }
});
</script>

<style scoped>
/* 日历整体样式 */
.custom-calendar {
  width: 100%;
  border: 1px solid #f9fafc;
  background-color: #f9fafc;
  border-radius: 4px;
  padding: 10px;
  margin: 0 auto;
  font-size: 18px;
}
/* 头部样式 */
.calendar-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 10px;
}
/* 星期标题样式 */
.weekdays {
  display: flex;
  margin-bottom: 10px;
}
.weekday {
  flex: 1;
  text-align: center;
  font-weight: bold;
}
/* 日期网格样式 */
.days-grid {
  display: flex;
  flex-wrap: wrap;
}
/* 日期单元格基础样式 */
.day {
  width: 14.28%; 
  height: 63px;
  line-height: 63px;
  text-align: center;
  border: 1px solid #fffbfb;
  box-sizing: border-box;
  cursor: pointer;
}
/* 空白日期样式(用于填充月初前面的空白) */
.day.empty {
  background-color: #f9f9f9;
  cursor: default;
}
/* 选中日期样式 */
.day.selected {
  background-color: #409eff;
  color: white;
}
/* 底部按钮样式 */
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  margin-top: 10px;
}
.dialog-footer button {
  margin-left: 10px;
}
/**选中日期展示样式*/
.result{
  margin-top:10px;
  text-align:center
}

</style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值