Vue3 百度日期

134 阅读2分钟

直接上代码

技术栈:Vue3

<script setup>
import { ref, reactive, onMounted, computed, nextTick } from 'vue';
const state = reactive({
  year: '',
  month: '',
  date: '',
  dataCount: [],
  raw: {
    year: '',
    month: '',
    date: '',
  },
  last: null,
  next: null,
  dataCard: [],
  active: '',
  tabActive: '',
});
const spanIcon1 = ref(null);
const spanIcon2 = ref(null);
const spanIcon3 = ref(null);
const month = ref(null);
const year = ref(null);
const holiday = ref(null);
const card = ref(null);
onMounted(() => {
  getTime();
});
const isCurrentDate = computed(() => {
  const { year, month, date } = state.raw;
  return (v) => {
    if (v.month == state.month) {
      if (v.year == year && v.month == month && v.date == date) {
        return 'active3';
      }
    } else {
      //
      if (v.month == month) {
        return 'active1';
      }
      return 'active2';
    }
  };
});
// 判断当前是否是休息日
const isHoliday = computed(() => {
  return (v) => {
    let year = v.year;
    let month = v.month - 1;
    let date = v.date;
    if (month == 0) {
      console.log('进入');
      year--;
    }
    const y = new Date(year, month, date).getDay();
    if (y == 6 || y == 0) {
      return 'isHoliday';
    }
    console.log(year, month - 1, date);
    return '';
  };
});
// 获取当前年月份
function getTime() {
  const dt = new Date();
  state.year = dt.getFullYear();
  state.month = dt.getMonth() + 1;
  state.date = dt.getDate();
  //
  state.raw.year = state.year;
  state.raw.month = state.month;
  state.raw.date = state.date;
  getPush();
}
// 获取当月天数以及当月第一天星期
function getDayCounts(year, month) {
  // 获取总天数
  const total = new Date(year, month, 0).getDate();
  // 获取第一天星期
  const firstWeekDay = new Date(year, month - 1, 1).getDay();
  return { total, firstWeekDay };
}
function getPush() {
  // 获取当月的
  const { total, firstWeekDay } = getDayCounts(state.year, state.month);
  const month = [];
  // 灌入当前月份的天数
  for (let i = 1; i <= total; i++) {
    month.push({ year: state.year, month: state.month, date: i });
  }
  // 获取上月的
  const lastMonth = [state.year, state.month];
  if (state.month == 1) {
    lastMonth[0] = lastMonth[0] - 1;
    lastMonth[1] = 12;
  } else {
    lastMonth[1] = lastMonth[1] - 1;
  }
  const { total: lastTotal } = getDayCounts(lastMonth[0], lastMonth[1]);
  // 灌入上月份的
  for (let i = lastTotal; i >= lastTotal - firstWeekDay + 2; i--) {
    month.unshift({
      year: lastMonth[0],
      month: lastMonth[1],
      date: i,
    });
  }
  // 获取下个月的
  const nextMonth = [state.year, state.month];
  if (state.month == 12) {
    nextMonth[0] = nextMonth[0] + 1;
    nextMonth[1] = 1;
  } else {
    nextMonth[1] = nextMonth[1] + 1;
  }
  // console.log(42 - month.length);
  // const { total: nextTotal } = getDayCounts(lastMonth[0], lastMonth[1]);
  const length = month.length;
  for (let i = 1; i <= 42 - length; i++) {
    month.push({
      year: nextMonth[0],
      month: nextMonth[1],
      date: i,
    });
  }
  dateAnimation();
  state.dataCount = month;
}
function dateAnimation() {
  const last = createChildElementRectMap(document.querySelector('.box_body'));
  nextTick(() => {
    const next = createChildElementRectMap(document.querySelector('.box_body'));
    last.forEach((v, k) => {
      const tag = next.get(k);
      const style = {
        left: tag.left - v.left,
        top: tag.top - v.top,
      };
      const keyframes = [
        { transform: `translate(${style.left}px,${style.top}px)` },
        { transform: `translate(0,0)` },
      ];
      v.dom.animate(keyframes, {
        duration: 400,
        easing: 'cubic-bezier(0.25, 0.8, 0.25, 1)',
      });
    });
  });
}
function createChildElementRectMap(dom) {
  const element = Array.from(dom.children);
  return new Map(
    element.map((item) => {
      return [
        item.getAttribute('data-index'),
        {
          left: item.getBoundingClientRect().left,
          top: item.getBoundingClientRect().top,
          dom: item,
        },
      ];
    })
  );
}
// 切换月份
function changeMonth(val) {
  if (val == 'prev') {
    state.month--;
    if (state.month == 0) {
      state.month = 12;
      state.year--;
    }
    return getPush();
  }
  state.month++;
  if (state.month == 13) {
    state.month = 1;
    state.year++;
  }
  getPush();
}
// 返回今天
function toDay() {
  state.year = state.raw.year;
  state.month = state.raw.month;
  state.date = state.raw.date;
  getPush();
}
//让箭头旋转起来
function changeTime(v, dom, dom2) {
  if (state.tabActive != v) {
    state.tabActive = v;
    const style = dom.getAttribute('style');
    let deg = 180;
    if (style) {
      deg = Number(style.match(/[0-9]{3,}/)[0]) + 180;
    }
    dom.style.transform = `rotate(${deg}deg)`;
    transitionFn(dom2);
    cardData(v);
  } else {
    card.value.style.display = 'none';
    state.tabActive = '';
  }
}
// 数据
function cardData(v) {
  switch (v) {
    case 'year':
      const map = new Array(70).fill(null).map((item, index) => {
        return `${index + 1990}年`;
      });
      state.dataCard = map;
      break;
    case 'month':
      const map1 = new Array(12)
        .fill(null)
        .map((item, index) => `${index + 1}月`);
      state.dataCard = map1;
      break;
    default:
      state.dataCard = [
        '假期安排',
        '元旦节',
        '除夕',
        '春节',
        '清明节',
        '劳动节',
        '端午节',
        '中秋节',
        '国庆节',
      ];
      break;
  }
}
// 动画效果
function transitionFn(dom2) {
  const last = card.value.offsetLeft;
  card.value.style.left = dom2.offsetLeft + 'px';
  card.value.style.display = 'block';
  const keyframes = [
    {
      transform: `translate(${last - card.value.offsetLeft}px, 0px)`,
    },
    { transform: `translate(0px, 0px)` },
  ];
  card.value.animate(keyframes, {
    duration: 400,
    easing: 'cubic-bezier(0.25, 0.8, 0.25, 1)',
  });
  window.addEventListener('click', clickFN, false);
}
function clickFN() {
  if (state.tabActive) {
    state.tabActive = '';
    card.value.style.display = 'none';
    window.removeEventListener('click', clickFN);
  }
}
// 选择日期
function selectDate(item) {
  state.active = item;
  // 日期
  const v = /[0-9]{1,}[年|月]$/.test(item);
  if (v) {
    card.value.style.display = 'none';
    state.tabActive = '';
    const date = item.split(/[月|年]/);
    if (date[0].length <= 2) {
      state.month = date[0];
      return getPush();
    }
    state.year = date[0];
    return getPush();
  }
  // 假期安排
}
</script>

<template>
  <div class="container">
    <div class="box">
      <div class="box_main">
        <div class="header">
          <div
            class="year"
            ref="year"
            @click.stop="changeTime('year', spanIcon1, year)"
          >
            <span>{{ state.year }}年</span>
            <span ref="spanIcon1"
              ><el-icon><ArrowUp /></el-icon
            ></span>
          </div>
          <div
            class="isMonth"
            @click.stop="changeTime('month', spanIcon2, month)"
          >
            <div class="leftIcon" @click.stop="changeMonth('prev')"></div>
            <div class="month" ref="month">
              <span>{{ state.month }}月</span>
              <span ref="spanIcon2"
                ><el-icon><ArrowUp /></el-icon
              ></span>
            </div>
            <div class="rightIcon" @click.stop="changeMonth('next')"></div>
          </div>
          <div
            class="holiday"
            ref="holiday"
            @click.stop="changeTime('holiday', spanIcon3, holiday)"
          >
            <span>假期安排</span>
            <span ref="spanIcon3"
              ><el-icon><ArrowUp /></el-icon
            ></span>
          </div>
          <div class="toDay" @click.stop="toDay">返回今天</div>
        </div>
        <div class="box_nav">
          <div>一</div>
          <div>二</div>
          <div>三</div>
          <div>四</div>
          <div>五</div>
          <div>六</div>
          <div>日</div>
        </div>
        <div class="box_body">
          <div
            class="item"
            v-for="(item, index) in state.dataCount"
            :key="index"
            :class="isCurrentDate(item)"
            :data-index="`${item.date}`"
          >
            <span :class="isHoliday(item)">{{ item.date }}</span>
          </div>
        </div>
      </div>
      <div class="box_msg"></div>
      <!--  -->
      <div class="card" ref="card">
        <el-scrollbar>
          <ul>
            <li
              v-for="item in state.dataCard"
              :key="item"
              :class="{ active: state.active == item }"
              @click="selectDate(item)"
            >
              {{ item }}
            </li>
          </ul>
        </el-scrollbar>
      </div>
    </div>
  </div>
</template>

<style lang="scss" scoped>
.container {
  width: 100%;
  height: 100%;
  background-color: #edf1f4;
  overflow: hidden;
  user-select: none;
  .box {
    width: 592px;
    height: 472px;
    display: flex;
    margin: 200px auto;
    background-color: white;
    border-radius: 16px;
    position: relative;
    .box_main {
      flex-grow: 1;
      border-radius: 16px 0 0 16px;
      padding: 15px 15px 0;
      border: 2px solid #4e6ef2;
      border-right: none;
      box-sizing: border-box;
      .header {
        height: 30px;
        // background-color: red;
        display: flex;
        color: #333;
        font-size: 13px;
        .year {
          width: 96px;
          height: 30px;
          margin-right: 9px;
          border-radius: 6px;
          border: 1px solid #d7d9e0;
          box-sizing: border-box;
          background: #ffffff;
          user-select: none;
          padding: 0 7px;
          display: flex;
          justify-content: space-between;
          align-items: center;
          cursor: pointer;
          span {
            transition: 0.4s;
          }
        }
        .isMonth {
          display: flex;
          margin-right: 9px;
          .leftIcon {
            width: 30px;
            height: 30px;
            background: url(https://pss.bdstatic.com/r/www/cache/static/aladdin-san/app/ms_calendar/img/arrow_left_aaa9af6.png)
              no-repeat center;
            background-size: 16px auto;
            cursor: pointer;
          }
          .rightIcon {
            width: 30px;
            height: 30px;
            background: url(https://pss.bdstatic.com/r/www/cache/static/aladdin-san/app/ms_calendar/img/arrow_right_d3e2f09.png)
              no-repeat center;
            background-size: 16px auto;
            cursor: pointer;
          }
          .month {
            width: 80px;
            height: 30px;
            border-radius: 6px;
            border: 1px solid #d7d9e0;
            box-sizing: border-box;
            background: #ffffff;
            user-select: none;
            padding: 0 7px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            cursor: pointer;
            span {
              transition: 0.4s;
            }
          }
        }
        .holiday {
          width: 96px;
          height: 30px;
          border-radius: 6px;
          border: 1px solid #d7d9e0;
          box-sizing: border-box;
          background: #ffffff;
          user-select: none;
          margin-right: 30px;
          padding: 0 7px;
          display: flex;
          justify-content: space-between;
          align-items: center;
          cursor: pointer;
          span {
            transition: 0.4s;
          }
        }
        .toDay {
          width: 68px;
          height: 30px;
          line-height: 30px;
          text-align: center;
          background: #f5f5f6;
          border-radius: 6px;
          color: #333;
          cursor: pointer;
        }
      }
      .box_nav {
        height: 36px;
        margin-top: 14px;
        display: flex;
        div {
          width: 64px;
          line-height: 36px;
          text-align: center;
          font-size: 13px;
          color: #333333;
        }
      }
      .box_body {
        display: flex;
        flex-wrap: wrap;
        .item {
          width: 60px;
          height: 56px;
          line-height: 56px;
          border: 2px solid transparent;
          text-align: center;
          // box-sizing: border-box;
          font-size: 18px;
          cursor: pointer;
          border-radius: 5px;
          flex-shrink: 0;
          .isHoliday {
            color: #f73131;
          }
        }
        .item:hover {
          border: 2px solid #cc00ff;
        }
        .active1 {
          color: #adadad;
        }
        .active2 {
          color: #adadad;
        }
        .active2:hover {
          border: 2px solid #e5e5e9;
        }
        .active1:hover {
          border: 2px solid #bdbfc8;
        }
        .active3 {
          border: 2px solid #4e6ef2;
        }
      }
    }
    .box_msg {
      width: 112px;
      height: 100%;
      border-radius: 0 16px 16px 0;
      background: #4e6ef2;
      flex-shrink: 0;
    }
    .card {
      position: absolute;
      left: 20px;
      width: 94px;
      box-sizing: border-box;
      box-shadow: 0 1px 10px 0 rgb(0 0 0 / 10%);
      border-radius: 6px;
      background: #ffffff;
      top: 50px;
      max-height: 312px;
      display: none;
      ul {
        list-style: none;
        margin: 0;
        padding: 0;
        text-align: center;
        height: 100%;
        li {
          font-size: 13px;
          padding: 7px 0;
          cursor: pointer;
        }
        .active {
          color: #315efb;
        }
        li:hover {
          color: #315efb;
        }
      }
    }
  }
}
</style>
<style scoped>
.card:deep(.el-scrollbar__wrap) {
  max-height: 312px;
  overflow-x: hidden;
}
</style>

未完! 代码有BUG