日期组件

自定义日期组件

DatePicker.vue

<template>
  <div class="e-calendar-wrapper">
    <div class="e-calendar">
      <div class="e-date-select">
        <el-button
          type="primary"
          style="background: #43BAFE"
          icon="el-icon-plus"
          size="small"
          @click="addMeeting"
        >添加会议</el-button>
      </div>
      <div class="e-calendar-container" v-show="!showYear">
        <div class="e-calendar-toolbar">
          <div class="e-calendar-svg" @click="prevMonth">
            <svg viewBox="0 0 24 24" class="e-calendar-svg-icon">
              <path d="M15.41 7.41L14 6l-6 6 6 6 1.41-1.41L10.83 12z"></path>
            </svg>
            <transition name="e_calendar_svg_btn">
              <div class="e-calendar-svg-cover" v-if="prevMonthClick"></div>
            </transition>
          </div>
          <div class="e-calendar-toolbar-title">
            <transition :name="fadeXType">
              <div :key="showDate.monthStr" class="e-calendar-toolbar-title-content">
                <strong>{{showDate.year}}年</strong>
                <span>{{ showDate.monthStr }}</span>
              </div>
            </transition>
          </div>
          <div class="e-calendar-svg" @click="nextMonth">
            <svg viewBox="0 0 24 24" class="e-calendar-svg-icon">
              <path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"></path>
            </svg>
            <transition name="e_calendar_svg_btn">
              <div class="e-calendar-svg-cover" v-if="nextMonthClick"></div>
            </transition>
          </div>
        </div>
        <div class="e-calendar-week">
          <span class="e-calendar-week-day">日</span>
          <span class="e-calendar-week-day">一</span>
          <span class="e-calendar-week-day">二</span>
          <span class="e-calendar-week-day">三</span>
          <span class="e-calendar-week-day">四</span>
          <span class="e-calendar-week-day">五</span>
          <span class="e-calendar-week-day">六</span>
        </div>
        <div class="e-calendar-monthday">
          <transition :name="fadeXType">
            <div :key="showDate.monthStr" class="e-calendar-monthday-wrapper">
              <div class="e-calendar-monthday-row" v-for="( row,index) in rows" :key="index">
                <span
                  v-for="(day,index) in row"
                  :key="index"
                  class="e-calendar-monthday-row-day"
                  @click="selectDay(day)"
                  :class="{'active': day.selected, 'disabled': day.disabled, 'pointer': day.value !== ''}"
                >
                  <span v-text="day.value" class="e-calendar-monthday-row-day-value"></span>
                  <transition name="e_calendar_day">
                    <span class="e-calendar-monthday-row-day-cover" v-if="day.selected"></span>
                  </transition>
                </span>
              </div>
            </div>
          </transition>
        </div>
      </div>
      <ul class="e-calendar-year" v-show="showYear" ref="yearList">
        <li
          v-for="item in yearList"
          :key="item"
          v-text="item"
          :class="{'active': item === selectDate.year}"
          @click="selectYear(item)"
        ></li>
      </ul>
      <div class="e-calendar-actions">
        <button class="btn" @click="cancel">取消</button>
        <button class="btn" @click="confirm">确定</button>
      </div>
    </div>
  </div>
</template>
<script>
import Vue from "vue";
import { Button } from "element-ui";
Vue.use(Button);
// 阿拉伯数字 转 汉字数字的基本库
const weekJson = {
  1: "星期日",
  2: "星期一",
  3: "星期二",
  4: "星期三",
  5: "星期四",
  6: "星期五",
  7: "星期六"
};
const monthJson = {
  1: "1月",
  2: "2月",
  3: "3月",
  4: "4月",
  5: "5月",
  6: "6月",
  7: "7月",
  8: "8月",
  9: "9月",
  10: "10月",
  11: "11月",
  12: "12月"
};
export default {
  name: "DatePicker",
  props: {
    // 打开date picker的初始值,必传,格式是(2017-08-11)
    date: {
      type: String,
      required: true
    },
    // 日期最小值
    minDate: {
      type: String,
      default: "1970-01-01"
    },
    // 日期最大值
    maxDate: {
      type: String,
      default: "2020-12-31"
    }
  },
  computed: {
    yearList() {
      const result = [];
      for (let i = this.copyMinDate.year; i <= this.copyMaxDate.year; i += 1) {
        result.push(i);
      }
      return result;
    },
    title() {
      // calendar 上面的所有 title 信息包括二部分
      return {
        year: "",
        monthday: ""
      };
    },
    rows() {
      const { year, month } = this.showDate;
      const months = new Date(year, month, 0).getDate();
      const result = [];
      let row = [];
      let weekValue;
      // 按照星期分组
      for (let i = 1; i <= months; i += 1) {
        // 根据日期获取星期,并让开头是1,而非0
        weekValue = new Date(year, month, i).getDay() + 1;
        // 判断月第一天在星期几,并填充前面的空白区域
        if (i === 1 && weekValue !== 1) {
          this.addRowEmptyValue(row, weekValue);
          this.addRowDayValue(row, i);
        } else {
          this.addRowDayValue(row, i);
          // 判断月最后一天在星期几,并填充后面的空白区域
          if (i === months && weekValue !== 7) {
            this.addRowEmptyValue(row, 7 - weekValue + 1);
          }
        }
        // 按照一周分组
        if (weekValue % 7 === 0 || i === months) {
          result.push(row);
          row = [];
        }
      }
      this.showDate.monthStr = monthJson[this.showDate.month + 1];
      console.log("rows", result);
      return result;
    }
  },
  data() {
    return {
      selectDate: {
        year: "",
        month: "",
        day: "",
        week: "",
        date: "",
        weekStr: "",
        monthStr: ""
      }, // 选择的时间,默认是用户传的date时间
      showDate: {
        year: "",
        month: "",
        day: "",
        week: "",
        date: "",
        monthStr: "",
        weekStr: ""
      },
      copyMinDate: {
        year: "",
        month: "",
        day: ""
      },
      copyMaxDate: {
        year: "",
        month: "",
        day: ""
      },
      toolbar: "",
      fadeXType: "fadeX_Prev",
      nextMonthClick: false,
      prevMonthClick: false,
      showYear: false
    };
  },
  created() {
    this.initDatePicker();
  },
  methods: {
    addMeeting() {
      alert(111);
    },
    initDatePicker() {
      this.showDate = { ...this.splitDate(this.date, true) };
      this.copyMinDate = { ...this.splitDate(this.minDate) };
      this.copyMaxDate = { ...this.splitDate(this.maxDate) };
      this.selectDate = { ...this.showDate };
    },
    splitDate(date, addStr) {
      let result = {};
      const splitValue = date.split("-");
      try {
        if (!splitValue || splitValue.length < 3) {
          throw new Error("时间格式不正确");
        }
        result = {
          year: Number(splitValue[0]),
          month: Number(splitValue[1]) - 1,
          day: Number(splitValue[2])
        };
        if (addStr) {
          result.week =
            new Date(result.year, result.month, result.day).getDay() + 1;
          result.monthStr = monthJson[result.month + 1];
          result.weekStr = weekJson[result.week];
        }
      } catch (error) {
        console.error(error);
      }
      return result;
    },
    addRowEmptyValue(row, count) {
      for (let w = 1; w < count; w += 1) {
        row.push({
          value: ""
        });
      }
    },
    addRowDayValue(row, i) {
      const value = { value: i };
      const { day, month, year } = this.selectDate;
      const showDate = this.showDate;
      // 判断已经选择的
      if (year === showDate.year && month === showDate.month && day === i) {
        value.selected = true;
      }
      // 当日期在最小值之外,禁止点击
      if (this.isMinLimitMonth() && i < this.copyMinDate.day) {
        value.disabled = true;
      }
      // 当日期在最大值之外,禁止点击
      if (this.isMaxLimitMonth() && i > this.copyMinDate.day) {
        value.disabled = true;
      }
      row.push(value);
    },
    /**
     * 切换到上一个月
     */
    prevMonth() {
      if (this.prevMonthClick) {
        return;
      }
      this.prevMonthClick = true;
      setTimeout(() => {
        this.prevMonthClick = false;
      }, 500);
      this.fadeXType = "fadeX_Prev";
      // 如何当前月份已经小于等于minMonth 就不让其在执行
      if (this.isMinLimitMonth()) {
        return;
      }
      const { year, month } = this.showDate;
      // 判断当前月份,如果已经等于1(1就是一月,而不是二月)
      if (month <= 0) {
        this.showDate.year = year - 1;
        this.showDate.month = 11;
      } else {
        this.showDate.month -= 1;
      }
    },
    /**
     * 切换到下一个月
     */
    nextMonth() {
      if (this.nextMonthClick) {
        return;
      }
      this.nextMonthClick = true;
      setTimeout(() => {
        this.nextMonthClick = false;
      }, 500);
      this.fadeXType = "fadeX_Next";
      // 如何当前月份已经大于等于maxMonth 就不让其在执行
      if (this.isMaxLimitMonth()) {
        return;
      }
      const { year, month } = this.showDate;
      // 判断当前月份,如果已经等于12(12就是十二月)
      if (month >= 11) {
        this.showDate.year = year + 1;
        this.showDate.month = 0;
      } else {
        this.showDate.month += 1;
      }
    },
    resetSelectDate(dayValue) {
      this.selectDate = { ...this.showDate };
      this.selectDate.day = dayValue;
      this.selectDate.week =
        new Date(this.showDate.year, this.showDate.month, dayValue).getDay() +
        1;
      this.selectDate.weekStr = weekJson[this.selectDate.week];
    },
    selectDay(day) {
      if (day.disabled || day.selected || day.value === "") {
        return;
      }
      this.resetSelectDate(day.value);
    },
    selectYear(value) {
      this.showYear = false;
      this.showDate.year = value;
      let type;
      // 当日期在最小值之外,月份换成最小值月份 或者 当日期在最大值之外,月份换成最大值月份
      if (this.isMinLimitMonth()) {
        type = "copyMinDate";
      } else if (this.isMaxLimitMonth()) {
        // 当日期在最大值之外,月份换成最大值月份
        type = "copyMaxDate";
      }
      if (type) {
        this.showDate.month = this[type].month;
        this.showDate.day = this[type].day;
        this.resetSelectDate(this.showDate.day);
        return;
      }
      let dayValue = this.selectDate.day;
      // 判断日是最大值,防止另一个月没有这个日期
      if (this.selectDate.day > 28) {
        const months = new Date(
          this.showDate.year,
          this.showDate.month,
          0
        ).getDate();
        // 当前月份没有这么多天,就把当前月份最大值赋值给day
        dayValue = months < dayValue ? months : dayValue;
      }
      this.resetSelectDate(dayValue);
    },
    isMinLimitMonth() {
      return (
        this.showDate.year <= this.copyMinDate.year &&
        this.showDate.month <= this.copyMinDate.month
      );
    },
    isMaxLimitMonth() {
      return (
        this.showDate.year >= this.copyMaxDate.year &&
        this.showDate.month >= this.copyMaxDate.month
      );
    },
    openYearList() {
      if (this.showYear) {
        this.showYear = false;
        return;
      }
      const index = this.yearList.indexOf(this.selectDate.year);
      this.showYear = true;
      setTimeout(() => {
        this.$refs.yearList.scrollTop = (index - 3) * 40;
      });
    },
    openCalendarList() {
      this.showYear = false;
    },
    // 保持两位数
    keepDoubleDigit(number) {
      return number > 9 ? number : `0${number}`;
    },
    confirm() {
      const { year, month, day, week, weekStr, monthStr } = this.selectDate;
      this.$emit("confirm", {
        date: `${year}-${this.keepDoubleDigit(month)}-${this.keepDoubleDigit(
          day
        )}`,
        year,
        month,
        week,
        monthStr,
        weekStr,
        day
      });
    },
    cancel() {
      this.$emit("cancel");
    }
  }
};
</script>

<style lang="scss">
@import "@/assets/css/basic.scss";
@import "@/assets/css/borderBox.scss";
.e- {
  &calendar-wrapper {
    position: fixed;
    left: 0;
    top: 0;
    bottom: 0;
    right: 0;
    // background-color: rgba(0, 0, 0, 0.5);
    background: #333333;
    border: 1px solid #a5a5a5;
    box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.5);
    border-radius: 5px;
    z-index: 99999;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: row;
  }
  &calendar {
    //background-color: #a5a5a5;
     310px;
    color: #e6e6e6;
    opacity: 0.8;
  }
  &date-select {
    //background-color: #00bcd4;
    padding: 3px 3px;
    color: #ffffff;
  }
  &date-year {
    font-size: 18px;
    padding-bottom: 4px;
    position: relative;
     66px;
    height: 25px;
    overflow: hidden;
    &-select {
      position: absolute;
      opacity: 0.7;
      &.active {
        opacity: 1;
      }
    }
  }
  &date-monthday {
    font-size: 26px;
    position: relative;
     100%;
    height: 36px;
    overflow: hidden;
    &-select {
      position: absolute;
      opacity: 0.7;
      &.active {
        opacity: 1;
      }
    }
  }
  &calendar-container {
     auto;
  }
  &calendar-toolbar {
    margin: 5px 10px 5px 10px;
    height: 40px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    &-title {
      position: relative;
       100px;
      height: 22px;
      text-align: center;
      &-content {
        position: absolute;
         100%;
        font-size: 16px;
      }
    }
  }
  &calendar-svg {
    padding: 8px;
    position: relative;
    height: 40px;
     40px;
    &-icon {
      display: block;
      fill: currentColor;
      height: 24px;
       24px;
      user-select: none;
      position: relative;
      z-index: 2;
    }
    &-cover {
      position: absolute;
      left: 0;
      top: 0;
      z-index: 1;
       100%;
      height: 100%;
      background-color: #e0e0e0;
      border-radius: 50%;
      opacity: 0;
      display: inline-block;
    }
  }
  &calendar-week {
     100%;
    font-size: 12px;
    //color: rgba(0, 0, 0, 0.87);
    color: #ffffff;
    opacity: 0.7;
    display: flex;
    justify-content: center;
    align-items: center;
    height: 16px;
    &-day {
      flex: 1;
      text-align: center;
    }
  }
  &calendar-monthday {
    padding-top: 10px;
    font-size: 14px;
    position: relative;
     100%;
    min-height: 210px;
    overflow: hidden;
    &-wrapper {
      position: absolute;
       100%;
      height: 100%;
    }
    &-row {
      display: flex;
      justify-content: center;
      align-items: center;
      &-day {
        display: flex;
        justify-content: center;
        align-items: center;
        flex: 1;
        position: relative;
        height: 35px;
        &.pointer {
          cursor: pointer;
        }
        &-value {
          position: relative;
          z-index: 1;
        }
        &-cover {
           25px;
          height: 25px;
          background-color: #00bcd4;
          position: absolute;
          left: 10px;
          top: 5px;
          transform: translate3d(0, 0, 0);
          z-index: 0;
          border-radius: 50%;
          opacity: 1;
          display: block;
        }
        &.active {
          color: #ffffff;
        }
        &.disabled {
          opacity: 0.4;
          cursor: not-allowed;
        }
      }
    }
  }
  &calendar-year {
    height: 276px;
    overflow: auto;
    li {
      padding: 10px;
      text-align: center;
      font-size: 16px;
      &.active {
        color: #00bcd4;
        font-size: 20px;
        font-weight: bold;
      }
    }
  }
  &calendar-actions {
    padding: 0 20px 15px;
    display: flex;
    justify-content: flex-end;
    align-items: center;
    .btn {
      color: #00bcd4;
      margin-left: 40px;
      font-size: 16px;
      background-color: transparent;
    }
  }
}
.fadeX_Prev-enter-active,
.fadeX_Prev-leave-active,
.fadeX_Next-enter-active,
.fadeX_Next-leave-active,
.fadeY-enter-active,
.fadeY-leave-active {
  transition: all 0.3s;
}
.fadeX_Prev-enter {
  transform: translateX(-100px);
  opacity: 0;
}
.fadeX_Prev-leave-active {
  transform: translateX(100px);
  opacity: 0;
}
.fadeX_Next-enter {
  transform: translateX(100px);
  opacity: 0;
}
.fadeX_Next-leave-active {
  transform: translateX(-100px);
  opacity: 0;
}
.fadeY-enter {
  transform: translateY(30px);
  opacity: 0;
}
.fadeY-leave-active {
  transform: translateY(-30px);
  opacity: 0;
}
.e_calendar_svg_btn-enter-active,
.e_calendar_svg_btn-leave-active {
  transition: all 1s;
}
.e_calendar_svg_btn-enter {
  opacity: 1;
}
.e_calendar_day-enter-active {
  transition: all 0.2s;
}
.e_calendar_svg_btn-leave-active,
.e_calendar_day-enter {
  opacity: 0;
}
.e_calendar_day-enter {
   0;
  height: 0;
  transform: translate3d(12px, 12px, 0);
}
</style>

 

basic.scss
@import "./color";

//reset element style
html {
-ms-overflow-style:none;
overflow:-moz-scrollbars-none;
}
html::-webkit-scrollbar{0}
body,div,span,header,footer,nav,section,aside,article,ul, li, a, p, textarea, button, input, select  {
  padding: 0;
  margin: 0;
  list-style: none;
  font-weight: normal;
  font-style: normal;
  text-decoration: none;
  border: none;
  font-family: 'Hiragino Sans GB', 'Hiragino Sans GB W3', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif;
  -webkit-tap-highlight-color:transparent;
  &:focus {
    outline: none;
  }
}
html{
  -webkit-overflow-scrolling : touch !important;
  overflow: auto !important;
  height: 100% !important;
}
body{
  -webkit-overflow-scrolling : touch !important;
  overflow: auto !important;
  position: relative;
  height:100% !important;
  100%;
  background:$c-little-gray;
  font-size: 12px;
}
.clearfix {
  zoom: 1;
  &::after {
    visibility: hidden;
    display: block;
    font-size: 0;
    content: " ";
    clear: both;
    height: 0;
  }
}
.vertical-middle {
  height: 100%;
   0;
  display: inline-block;
  vertical-align: middle;
}

.pull-left{
  float:left;
}
.pull-right{
  float:right;
}
.mt6{
  margin-top: 6px;
}
.ml60{
  margin-left: 60px;
}

.pointer{
  cursor: pointer;
}
[v-cloak] {
  display: none;
}
.slide-fade-enter-active {
    transition: all .6s ease;
}

.slide-fade-leave-active {
    transition: all .5s ease;
}

.slide-fade-enter,
.slide-fade-leave-active {
    transform: translateX(60px);
    opacity: 0;
}

color.vue

$c-block : #000;
$c-natural-block : #3B3F44;
$c-gray : #979797;
$c-small-gray: rgb(151, 151, 151);
$c-some-gray: rgb(190,190,190);
$c-light-gray : #BEBEBE;//提示文字
$c-text-main : #3B3D5B;//提示文字
$c-half-main : lighten($c-text-main, 50%);//提示文字
$c-smoky-gray : #DAD7D9;
$c-lightslate-gray: #E0E0E0; //边框颜色
$c-half-lightslate-gray: #F0F0F0; //边框颜色
$c-little-gray: #f9f9f9; //背景

$c-ivory : #f7f7f7;
$c-light-white: #EEEEEE;
$c-white : #FFF;

$c-red : #E34F51;
$c-natural-red : #E44F51;
$c-light-red : #EF7979 ;

$c-pink : #ffa1a1;

$c-orange : #f34e19;

$c-blue : #1975C3;

$c-green : #25C354;

$c-yellow : #F6A623;

$c-primary : #00bcd4;
$c-success : #6AC8D4;
$c-light-blue : #6EB8F1;

$c-purple-grey : #212340;// 导航栏背景

$b-lightslate-gray : 1px solid $c-lightslate-gray;
$b-half-lightslate-gray : 1px solid $c-half-lightslate-gray;
$b-main: 1px solid $c-text-main;
$b-half-main: 1px solid lighten($c-text-main, 50%);
$b-bolder-active-main: 3px solid $c-text-main;
$b-mask: rgba(59,61,91,0.60);
$b-transparent-block: rgba(0,0,0,0);

// app colors
$c-start: #6bf3d4; // 渐变起始色
$c-end: #6eb8f1; // 渐变终止色
$c-light-blue : #6EB8F1;

$c-number: #D67573; //用于数字提示
$c-red: #E44F51; //用于数字提示
$c-bg-tip: #E44F51; //用于数字提示

$c-bg: #f9f9f9; //背景;
$c-bg-white: #ffffff; //白色背景;
$c-gray : #979797;//灰色字体
$c-light-gray : #bebebe;//提示文字
$c-lightslate-gray: #E0E0E0; //边框颜色

$c-divider:#e6e6e6; //用于分割线

$c-tabBar: #667980; // 用于默认Tab bar Icon
$c-introdution: #667980; // 用于默认Tab bar Icon

$c-btnText: #6eb8f1; //适用于高亮按钮文字

$c-title: #4d4d4d; // 适用于标题
$c-text: #4d4d4d; // 适用于正文

$c-title-white: #ffffff; // 适用于标题
$c-text-white: #ffffff; // 适用于正文
$c-text-main : #3B3D5B;//提示文字
$c-text-gold: #FFE98D;// 金字
$c-border-main : #3B3D5B;//提示文字

$c-assistTxt: #808080; //适用于辅助文字
$c-subtitle: #808080; //适用于副标题

$c-tipText: #b3b3b3; //适用于提示性文字
$c-timeText: #b3b3b3; //适用于时间文字

$c-border:#e6e6e6; //适用于边框
$c-light-border:#f0f0f0; //浅颜色边框
$c-bg-authenticate:#F5A623;// 认证边框色

$c-status: #6AC8D4; //选中的状态
$c-border-common:#e0e0e0;
$c-border-half-common: rgba(224,224,224,0.5);

$b-border-half-common: 1px solid $c-border-common;
$b-border-common: 1px solid $c-border-half-common;

  borderBox.scss

body,div,span,header,footer,nav,section,aside,article,ul, li, a, p, h1, h2, h3, h4,h5, i, b, textarea, button, input, select  {
  box-sizing: border-box;
}

  使用

 <date-picker
        v-if="showDatePicker"
        :date="date"
        :min-date="minDate"
        :max-date="maxDate"
        @confirm="confirm"
        @cancel="cancel"
      ></date-picker>

<script>
import DatePicker from "@/components/DatePicker.vue";
export default {
  components: {
    DatePicker
  },
  data() {
    return {
      showDatePicker: false,
      date: "2019-01-28",
      minDate: "2000-09-11",
      maxDate: "2020-09-11",
      selectedDate: "点击选择日期"
    };
  },
  computed: {},
  methods: {
    confirm(value) {
      this.showDatePicker = false;
      this.selectedDate = value;
    },
    cancel() {
      this.showDatePicker = false;
    }
  }
};
</script>

  

原文地址:https://www.cnblogs.com/xuqp/p/10329767.html