那个新来的前端,js写个日历组件会不会?

话不多说,先上在线Demo

1.需求分析

电脑右下角的日历各位观众老爷再熟悉不过了(特别是看还有几天发工资的时候),今天来实现一个阉割版的日历!

2.核心代码

<template>
  <div class="bk-date-picker">
    <div class="date-picker">
      <div class="prev-date" @click="pickPreDay(currentYear, currentMonth, selectDay)">
        <span style="color: #d9d9d9"><</span>
      </div>
      <a-input class="today" v-model="searchDate" size="samll" style="height: 24px; border:none" @change="handlePickerDialog(searchDate)" />
      <div class="next-date" @click="pickNextDay(currentYear, currentMonth, selectDay)">
        <span style="color: #d9d9d9">></span>
      </div>
    </div>
    <a-modal
      class="picker-dialog"
      :destroyOnClose="!0"
      centered
      :width="720"
      :height="500"
      :visible="visible"
      @cancel="cancel"
      footer
      :closable="false"
    >
      <div class="picker-header">
        <div></div>
        <div class="picker-year-month">
          <div class="picker-left">
            <span @click="pickPre(currentYear - 1, currentMonth + 1)" :style="{ color: '#d9d9d9' }" ><<</span>
            <span @click="pickPre(currentYear, currentMonth)" :style="{ color: '#d9d9d9' }" style="margin-left: 20px" ><</span>
          </div>
          <div class="current-month">{{ currentYear }}{{ currentMonth }}</div>
          <div class="picker-right">
            <span @click="pickNext(currentYear, currentMonth)" :style="{ color: '#d9d9d9' }" style="margin-right: 20px" >></span>
            <span @click="pickNext(currentYear + 1, currentMonth - 1)" :style="{ color: '#d9d9d9' }" >>></span>
          </div>
        </div>
      </div>
      <div class="picker-content">
        <div class="picker-week">
          <div class="week" v-for="week in weeks" :key="week">
            <span>{{ week }}</span>
          </div>
          <template v-for="(dayObject, i) in days">
            <div :class="[ 'day', newDate == dayObject.day || isSelectDate(dayObject.day) ? 'selected' : '', isActive(dayObject.day) ? 'active' : '', ]"
              @click="getDayTime(dayObject.day, i)" :key="i" >
              <!-- 非本月 文字加灰色 -->
              <span v-if="dayObject.day.getMonth() + 1 != currentMonth"  class="other-month" @click="getDayTime(dayObject.day)" >{{ dayObject.day.getDate() }}</span>
              <!-- 本月 还需要判断是不是这一天 -->
              <template v-else>
                <div @click="getDayTime(dayObject.day, i)">
                  <div>{{ dayObject.day.getDate() }}</div>
                  <div class="appointment-num" v-if="dayObject.num">({{ dayObject.num }})</div>
                </div>
              </template>
            </div>
          </template>
        </div>
      </div>
    </a-modal>
  </div>
</template>

<script>
import moment from "moment";
export default {
  name: "DatePicker",
  model: {
    prop: "searchDate",
    event: "selectDate"
  },
  props: {
    searchDate: {
      type: String,
      required: true
    }
  },
  data() {
    return {
      visible: true,
      weeks: ["一", "二", "三", "四", "五", "六", "日"],
      days: [],
      currentDay: 1,
      currentMonth: 1,
      currentYear: 2022,
      currentWeek: 1,
      currYearMonthDay: undefined,
      selectDay: undefined,
      newDate: moment(new Date()).format("YYYY-MM-DD")
    };
  },
  created() {},
  mounted() {
    this.currYearMonthDay = this.searchDate; // 当前年月日
    let dateArr = this.searchDate.split("-");
    this.selectDay = dateArr[dateArr.length - 1]; // 当月的几号
    this.initData(null);
  },
  methods: {
    // 初始化日历(按月加载,月初1号前置灰部分根据当天是周几开始往前补,月末从下月第一天补)
    initData(cur) {
      let date;
      if (cur) {
        date = new Date(cur.replace(/-/g, "/"));
      } else {
        let now = new Date();
        let d = new Date(
          this.formatDate(now.getFullYear(), now.getMonth(), 1).replace(
            /-/g,
            "/"
          )
        );
        d.setDate(41); // 每次加载6周,每周7天
        date = new Date(
          this.formatDate(d.getFullYear(), d.getMonth() + 1, 1).replace(
            /-/g,
            "/"
          )
        );
      }
      this.currentYear = date.getFullYear();
      this.currentMonth = date.getMonth() + 1;
      this.currentDay = date.getDate();
      this.currentWeek = date.getDay() - 1;
      // 如果本月1号为周日,往前补6天
      if (this.currentWeek === -1) {
        this.currentWeek = 6;
      }
      let str = this.formatDate(
        this.currentYear,
        this.currentMonth,
        this.currentDay
      );
      this.days.length = 0;
      let daysArr = [];
      // 先获取本月1号为周几,再往前补满本周
      for (let i = this.currentWeek; i >= 0; i--) {
        let d2 = new Date(str.replace(/-/g, "/"));
        d2.setDate(d2.getDate() - i);
        let dayObjectSelf = {}; // 用一个对象包装Date对象,便为以后预定功能添加属性
        dayObjectSelf.day = d2;
        daysArr.push(dayObjectSelf); // 将日期放入data 中的days数组 供页面渲染使用
      }
      // 其他周
      for (let j = 1; j <= 41 - this.currentWeek; j++) {
        let d3 = new Date(str.replace(/-/g, "/"));
        d3.setDate(d3.getDate() + j);
        let dayObjectOther = {};
        dayObjectOther.day = d3;
        daysArr.push(dayObjectOther);
      }
      this.days = daysArr;
    },
    // 返回 类似 2022-10-15 格式的字符串
    formatDate(year, month, day) {
      let y = year;
      let m = month;
      if (m < 10) {
        m = "0" + m;
      }
      let d = day;
      if (d < 10) {
        d = "0" + d;
      }
      return y + "-" + m + "-" + d;
    },
    getDayTime(el, index) {
      let value = moment(el).format("YYYY-MM-DD");
      this.$emit("selectDate", value);
      this.cancel();
      this.newDate = el;
      this.currYearMonthDay = value;
      this.selectDay = moment(el).format("DD");
    },
    // 上一天
    pickPreDay(year, month, day) {
      if (year && month && day) {
        let value = "";
        let daysArr = this.days; // 日历数组(仅展示当月)
        let currDay = this.formatDate(year, month, Number(day)); // 当前日期
        // 日历中加载到第一天时,初始化日历更新 days
        if (moment(daysArr[0].day).format("YYYY-MM-DD") === currDay) {
          // 日历第一天是周一并且为当月1号,则更新上月日历
          if (daysArr[0].day.getDay() === 1 && daysArr[0].day.getDate() === 1) {
            month--;
          }
          daysArr = this.initData(this.formatDate(year, month, 1));
        }
        // console.log(daysArr);
        if (daysArr?.length > 0) {
          for (let i = 1; i < daysArr.length; i++) {
            if (moment(daysArr[i].day).format("YYYY-MM-DD") === currDay) {
              this.currentYear = daysArr[i - 1].day.getFullYear();
              this.currentMonth = daysArr[i - 1].day.getMonth() + 1;
              this.selectDay = daysArr[i - 1].day.getDate();
              value = this.formatDate(
                this.currentYear,
                this.currentMonth,
                this.selectDay
              );
              // console.log(value);
              this.$emit("selectDate", value);
            }
          }
        }
      }
    },
    // 下一天
    pickNextDay(year, month, day) {
      if (year && month && day) {
        let value = "";
        let daysArr = this.days; // 日历数组(仅展示当月)
        let currDay = this.formatDate(year, month, Number(day)); // 当前日期
        // 日历中加载到倒数第二天时,初始化日历更新 days
        if (
          moment(daysArr[daysArr.length - 2].day).format("YYYY-MM-DD") ===
          currDay
        ) {
          daysArr = this.initData(this.formatDate(year, month, 1));
        }
        // console.log(daysArr);
        if (daysArr?.length > 0) {
          for (let i = 1; i < daysArr.length; i++) {
            if (moment(daysArr[i].day).format("YYYY-MM-DD") === currDay) {
              this.currentYear = daysArr[i + 1].day.getFullYear();
              this.currentMonth = daysArr[i + 1].day.getMonth() + 1;
              this.selectDay = daysArr[i + 1].day.getDate();
              value = this.formatDate(
                this.currentYear,
                this.currentMonth,
                this.selectDay
              );
              // console.log(value);
              this.$emit("selectDate", value);
            }
          }
        }
      }
    },
    // 上个月、去年
    pickPre(year, month) {
      // setDate(0); 上月最后一天 setDate(-1); 上月倒数第二天 setDate(dx) 参数dx为 上月最后一天的前后dx天
      if (year && month) {
        let d = new Date(this.formatDate(year, month, 1).replace(/-/g, "/"));
        d.setDate(0);
        this.currYearMonthDay = this.formatDate(
          d.getFullYear(),
          d.getMonth() + 1,
          1
        );
        this.initData(this.formatDate(d.getFullYear(), d.getMonth() + 1, 1));
      }
    },
    // 下个月、明年
    pickNext(year, month) {
      if (year && month) {
        let d = new Date(this.formatDate(year, month, 1).replace(/-/g, "/"));
        d.setDate(41);
        this.currYearMonthDay = this.formatDate(
          d.getFullYear(),
          d.getMonth() + 1,
          1
        );
        this.initData(this.formatDate(d.getFullYear(), d.getMonth() + 1, 1));
      }
    },
    // 是否为查询日期
    isSelectDate(day) {
      let { searchDate } = this;
      let ergodicDate = this.formatDate(
        day.getFullYear(),
        day.getMonth() + 1,
        day.getDate()
      );
      return searchDate == ergodicDate;
    },
    // 是否为当天
    isActive(day) {
      return (
        day.getFullYear() == new Date().getFullYear() &&
        day.getMonth() == new Date().getMonth() &&
        day.getDate() == new Date().getDate()
      );
    },
    handlePickerDialog(searchDate) {
      let strArr = searchDate.split("-");
      this.visible = true;
      this.currYearMonthDay = searchDate;
      this.initData(this.formatDate(strArr[0], strArr[1], 1));
    },
    cancel() {
      // this.visible = false;
    }
  }
};
</script>

<style lang="less">
.bk-date-picker {
  .date-picker {
    // width: 200px;
    height: 32px;
    width: 720px;
    border: 1px solid #d9d9d9;
    border-radius: 4px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    transition: 0.3s;
    .prev-date,
    .next-date {
      width: 30px;
      height: 100%;
      .icon {
        height: 100%;
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
      }
    }
    .prev-date {
      border-right: 1px solid #d9d9d9;
    }
    .next-date {
      border-left: 1px solid #d9d9d9;
    }
    .today {
      line-height: 30px;
    }
  }
  .date-picker:hover {
    border: 1px solid #0074ff;
  }
}
.picker-dialog {
  .ant-modal-content {
    .ant-modal-body {
      padding: 0px;
      .picker-header {
        padding: 0 17px;
        height: 64px;
        border-bottom: 1px solid #eeeeee;
        display: flex;
        justify-content: space-between;
        align-content: center;
        .picker-year-month {
          display: flex;
          justify-content: space-between;
          align-items: center;

          .current-month {
            margin: 0 40px;
            font-size: 16px;
            font-weight: 500;
            color: rgba(0, 0, 0, 0.85);
            line-height: 22px;
          }
        }
      }
      .picker-content {
        border: 1px solid #d9d9d9;
        .picker-week {
          display: grid;
          margin: auto;
          row-gap: 10px;
          justify-content: space-evenly;
          align-items: center;
          text-align: center;
          grid-template-columns: repeat(7, 50px);
          grid-template-rows: repeat(7, 50px);
          .week {
            width: 50px;
          }
          .day {
            width: 50px;
            height: 50px;
            font-size: 16px;
            .other-month {
              color: gainsboro;
            }
            .appointment-num {
              font-size: 14px;
              color: #0074ff;
            }
            .active {
              width: 50px;
              height: 50px;
              background: rgba(0, 116, 255, 0.1);
              border-radius: 4px;
              border: 1px solid #0074ff;
              color: #0074ff;
            }
          }
          .lineFlex {
            display: inline-flex;
            flex-direction: column;
            align-items: center;
            justify-content: center;
          }
          .active {
            width: 50px;
            height: 50px;
            background: rgba(0, 116, 255, 0.1);
            border-radius: 4px;
            border: 1px solid #0074ff;
            color: #0074ff;
          }
          .selected {
            width: 50px;
            height: 50px;
            border-radius: 4px;
            border: 1px solid #0074ff;
            color: #0074ff;
          }
          .day:hover {
            border-radius: 4px;
            border: 1px solid #0074ff;
            cursor: pointer;
          }
        }
      }
    }
  }
}
</style>

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值