Skip to content
On this page

实现一个日历图的一个展示图

TIP

本文主要是实现一个日历月份的展示,支持高亮某些日子(当月的可以是多个)。展示效果如下图:

示意图

实现细节

  • 区分每个月份几天(1-12),需要特俗处理2月份区分是不是闰月,其他月写个map映射下就好了。
  • 每个月画6行7列进行展示
  • 需要找每个月的开始绘制日期(比如10月1号是周5,那咱们就要在第一行的第五个开始绘制文本)。

vue 实现逻辑

TIP

源码实现如下 关键判断((row * 7) + (column + 1) >= startDay) && (row * 7) + (column + 1) < startDay + countDay 解读:限制最小开始日期 && 限制最大日期

vue
<!-- calendar-chart.vue -->
<template>
  <div class="calendar-container">
    <div class="calendar-container-header">
      <slot name="header" />
    </div>
    <div class="calendar-container-body">
      <div
        v-for="row in [0,1,2,3,4,5]"
        :key="`row-${row}`"
        class="calendar-row"
      >
        <div
          v-for="column in [0,1,2,3,4,5,6]"
          :key="`column-${column}`"
          class="calendar-column"
          :class="[
            {'is-action': actionDate.includes((row * 7) + (column + 1) - (startDay - 1))},
            {'is-have': ((row * 7) + (column + 1) >= startDay) && (row * 7) + (column + 1) < startDay + countDay}
          ]"
        >
          <span v-if="((row * 7) + (column + 1) >= startDay) && (row * 7) + (column + 1) < startDay + countDay">
            {{ (row * 7) + (column + 1) - (startDay - 1) }}
          </span>
        </div>
      </div>
    </div>
    <div class="calendar-container-footer">
      {{ footerValue }}
    </div>
  </div>
</template>

<script>
import {
  MONTH_MAP, getCurrentMonth, getFebruaryDay, getStartDay, getDate, getFullYear,
} from './utils';

export default {
  name: 'CalendarChart',

  props: {
    date: {
      type: Array,
      required: true,
      default: () => ['2021.10.23'],
    },
  },

  computed: {
    startDay() {
      return getStartDay(this.date[0]); // 用月份找到1号的开始值
    },

    countDay() { // 每个月的总共时间
      return MONTH_MAP[getCurrentMonth(this.date[0])] || getFebruaryDay(this.date[0]);
    },

    actionDate() { // 计算要高亮的天
      return this.date.map((item) => getDate(item));
    },

    footerValue() { // title
      return `${getFullYear(this.date[0])}${getCurrentMonth(this.date[0])}`;
    },
  },
};
</script>

<style lang="scss" scoped>
%flex-base {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
}
.calendar-container {
    display: inline-block;
    width: 280px;
    margin: 10px;
    .calendar-container-body {
      @extend %flex-base;
      flex-direction: column;
      .calendar-row {
        @extend %flex-base;
         border-top: 1px solid #E3E3E3;
        .calendar-column {
          text-align: center;
          width: 40px;
          height: 40px;
          line-height: 40px;
          border-left: 1px solid #E3E3E3;
          background-color: #F7F4F5;

          &.is-have {
            background-color: #F1EFF1;
          }

          &.is-action {
            background-color: #3461DA;
            color: #fff;
          }
        }

        >:last-child {
            border-right: 1px solid #E3E3E3;
        }

        &:last-child {
            border-bottom: 1px solid #E3E3E3;
        }
      }
    }

    .calendar-container-footer {
      width: 100%;
      height: 40px;
      line-height: 40px;
      text-align: center;
      font-weight: 700;
      font-size: 14px;
    }
}
</style>
js
// utils.js
export const getCurrentWeekDay = (date) => {
  const week = new Date(date).getDay();
  return week === 0 ? 7 : week;
};

export const getCurrentMonth = (date) => {
  const month = new Date(date).getMonth();
  return month + 1;
};

export const getFullYear = (date) => new Date(date).getFullYear();

export const isLeapYear = (date) => {
  const year = getFullYear(date);
  return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0;
};

export const getFebruaryDay = (date) => (isLeapYear(date) ? 29 : 28);

export const getDate = (date) => new Date(date).getDate();

export const getStartDay = (date) => {
  const formatDate = new Date(date).toLocaleString();
  let sum = 0;
  let findIndex = -1;
  for (let index = 0; index < formatDate.length; index++) {
    const element = formatDate[index];
    if (element === '/') {
      sum += 1;
    }
    if (sum === 2) {
      findIndex = index;
      break;
    }
  }
  return getCurrentWeekDay(formatDate.substring(0, findIndex));
};

export const MONTH_MAP = {
  1: 31,
  3: 31,
  5: 31,
  7: 31,
  8: 31,
  10: 31,
  12: 31,
  4: 30,
  6: 30,
  9: 30,
  11: 30,
};

使用方式

TIP

使用组件

vue
<template>
  <div>
    <calendar-chart :date=['2022.10.23']/>
    <calendar-chart :date=['2022.10.23']/>
    <calendar-chart :date=['2022.10.23']/>
  </div>
</template>

<script>
import CalendarChart from './calendar-chart.vue'

export default {
  components: { CalendarChart, }
}
</script>

Released under the MIT License.