<template>
  <div class="es-calendar">
    <div class="es-calendar-title" v-show="showTitle">
      <i class="es-calendar-prev es-icon-zuo" @click="prev"></i>
      <div class="es-title">
        <slot name="title" :year="year" :month="month">
          <es-date-picker
            class="es-calendar-picker"
            v-model="value"
            type="month"
            placeholder="选择日期时间"
            format="yyyy年M月"
            @change="handleChange"
          >
          </es-date-picker>
        </slot>
      </div>
      <i class="es-calendar-next es-icon-you" @click="next"></i>
    </div>
    <ul class="es-calendar-weeks">
      <li v-for="(item, index) in weeks" :key="index" class="es-week">
        {{ item }}
      </li>
    </ul>
    <ul class="es-calendar-days" :style="{ height: _height }">
      <li
        class="es-day"
        @click="handleClick(item)"
        v-for="(item, index) in lists"
        :key="`${item.year}-${item.month}-${item.date}`"
      >
        <div
          class="es-day-item"
          :class="{
            'es-disabled-day': item.type == 0,
            'es-today': isSelect(item, today) && mark,
            'es-select': isSelect(item, select),
            'es-disabled': item.disabled
          }"
        >
          {{ item.date }}
          <slot name="mark" :data="item"></slot>
        </div>
      </li>
    </ul>
    <div class="es-calendar-switch" v-show="fold">
      <i
        class="es-icon-shang es-switch-icon"
        name=""
        @click="handleHide"
        v-show="all"
      ></i>
      <i
        class="es-icon-xia es-switch-icon"
        @click="handleShow"
        v-show="!all"
      ></i>
    </div>
  </div>
</template>

<script>
export default {
  name: 'EsCalendar',
  props: {
    //显示title
    showTitle: {
      type: Boolean,
      default: true
    },
    //指定日期
    date: [String, Number],
    //是否只显示本周
    brief: Boolean,
    //是否等指定日期传入后再渲染
    wait: Boolean,
    //是否显示收起展开按钮
    fold: {
      type: Boolean,
      default: true
    },
    mark: Boolean,
    disabled: Boolean,
    height: String
  },
  watch: {
    date: {
      immediate: true,
      handler(val) {
        if (this.wait) {
          val && this.init();
        } else {
          this.init();
        }
      }
    },
    brief: {
      immediate: true,
      handler(val) {
        this.all = !val;
      }
    }
  },
  computed: {
    lists() {
      if (this.all) {
        return this.daysList;
      }
      return this.daysList.filter((item) => {
        for (let i = 0; i < this.weekDate.length; i++) {
          let ele = this.weekDate[i];
          if (
            item.year == ele.year &&
            item.month == ele.month &&
            item.date == ele.date
          ) {
            return true;
          }
        }
      });
    },
    _height() {
      return this.height && this.all ? parseInt(this.height) + 'px' : '';
    }
  },
  data() {
    return {
      value: '',
      year: '',
      month: '',
      weeks: ['日', '一', '二', '三', '四', '五', '六'],
      weekDate: [],
      week7: '',
      today: '',
      select: '',
      selectDate: '',
      daysList: [],
      all: true,
      week: 0,
      prevMonthDays: 0,
      nextMonthDays: 0,
      days: 0
    };
  },
  mounted() {},
  methods: {
    resetDate() {
      this.week = 0;
      this.init();
    },
    init(res) {
      this.daysList = [];
      let dates = this.getDays(res ? res : this.date);
      let {
        year,
        month,
        today,
        daysList,
        weekDate,
        prevMonthDays,
        nextMonthDays,
        days
      } = dates;
      if (!res) {
        this.today = `${year}/${month}/${today}`;
        this.selectDate = today;
        this.select = `${year}/${month}/${today}`;
        this.$emit('select', { year, month, date: today, type: 1 });
      }
      this.prevMonthDays = prevMonthDays;
      this.nextMonthDays = nextMonthDays;
      this.days = days;
      this.daysList = daysList;
      this.year = year;
      this.month = month;
      this.value = `${year}年${month}月`;
      //if (!this.weekDate.length) {
      this.weekDate = weekDate;
      //}
      this.$emit('init', dates);
      return dates;
    },
    isShow(res) {
      if (this.all) {
        return true;
      }
      return this.weekDate.filter((item) => {
        return (
          item.year == res.year &&
          item.month == res.month &&
          item.date == res.date
        );
      }).length;
    },
    isSelect(res, target) {
      let { year, month, date } = res;
      return target == `${year}/${month}/${date}`;
    },
    handleClick(res) {
      if (res.disabled) {
        return false;
      }
      let { year, month, date } = res;
      this.selectDate = date;
      this.select = `${year}/${month}/${date}`;
      this.$emit('select', res);
    },
    prev() {
      if (this.all) {
        this.weekDate = [];
        this.prevMonth();
      } else {
        this.weekDate = this.getWeekDay(-1);
        this.$emit('prev', this.weekDate, 'week');
      }
    },
    next() {
      if (this.all) {
        this.weekDate = [];
        this.nextMonth();
      } else {
        this.weekDate = this.getWeekDay(1);
        this.$emit('next', this.weekDate, 'week');
      }
    },
    prevMonth() {
      let year = this.year;
      let month = this.month;
      let dates = this.select.split('/');
      if (month == 1) {
        year -= 1;
        month = 12;
      } else {
        month -= 1;
      }
      let date =
        year == dates[0] && month == dates[1] ? dates[2] : this.prevMonthDays;
      date = date.toString().padStart(2, '0');
      month = month.toString().padStart(2, '0');
      let res = this.init(`${year}/${month}/${date}`);
      this.$emit('prev', res, 'month');
    },
    nextMonth() {
      let year = this.year;
      let month = this.month;
      let dates = this.select.split('/');
      if (month == 12) {
        year += 1;
        month = 1;
      } else {
        month += 1;
      }
      let date = year == dates[0] && month == dates[1] ? dates[2] : 1;
      date = date.toString().padStart(2, '0');
      month = month.toString().padStart(2, '0');
      let res = this.init(`${year}/${month}/${date}`);
      this.$emit('next', res, 'month');
    },
    handleChange(val) {
      let dates = val.replace('月', '').split('年');
      let year = dates[0];
      let month = dates[1];
      month = month.padStart(2, '0');
      let date = '01';
      let res = this.init(`${year}/${month}/${date}`);
      this.$emit('change', res);
    },
    //获取当月或指定日期所在月天数
    getDays(res) {
      const date = res ? new Date(res) : new Date();
      const year = date.getFullYear();
      const month = date.getMonth();
      const today = date.getDate();
      const week = date.getDay();
      //此月天数
      const days = new Date(year, month + 1, 0).getDate();
      //此月第一天周几
      const stratDay = new Date(year, month, 1).getDay();
      //此月最后一天周几
      const endDay = new Date(year, month, days).getDay();
      //上月天数
      const prevMonthDays =
        month == 0
          ? new Date(year - 1, 11, 0).getDate()
          : new Date(year, month, 0).getDate();
      //下月天数
      const nextMonthDays =
        month == 11
          ? new Date(year + 1, 1, 0).getDate()
          : new Date(year, month + 2, 0).getDate();
      //当前日期所在的周
      const weekDate = [{ date: today, year: year, month: month + 1 }];
      for (let i = 0; i < week; i++) {
        let d = i + 1;
        if (today - d > 1) {
          weekDate.unshift({
            date: today - d,
            year: year,
            month: month + 1
          });
        } else {
          weekDate.unshift({
            date: prevMonthDays - i,
            year: month == 0 ? year - 1 : year,
            month: month == 0 ? 12 : month
          });
        }
      }
      for (let i = 0; i < 6 - week; i++) {
        let d = i + 1;
        if (today + d <= days) {
          weekDate.unshift({
            date: today + d,
            year: year,
            month: month + 1
          });
        } else {
          weekDate.push({
            date: today + d - days,
            year: month == 11 ? year + 1 : year,
            month: month == 11 ? 1 : month + 2
          });
        }
      }

      let daysList = [];
      //本月前加入数组
      if (stratDay > 0) {
        for (let i = prevMonthDays; i > prevMonthDays - stratDay; i--) {
          daysList.unshift({
            date: i,
            type: 0,
            year: month == 0 ? year - 1 : year,
            month: month == 0 ? 12 : month,
            disabled: this.disabled
          });
        }
      }
      //本月加入数组
      for (let i = 1; i <= days; i++) {
        daysList.push({
          date: i,
          type: 1,
          year: year,
          month: month + 1,
          row: week < 6 && today + week > days
        });
      }
      //本月后加入数组
      if (endDay < 6) {
        for (var i = 1; i < 6 - endDay + 1; i++) {
          daysList.push({
            date: i,
            type: 0,
            year: month == 11 ? year + 1 : year,
            month: month == 11 ? 1 : month + 2,
            disabled: this.disabled
          });
        }
      }
      return {
        days: days,
        prevMonthDays: prevMonthDays,
        nextMonthDays: nextMonthDays,
        stratDay: stratDay,
        endDay: endDay,
        weekDate: weekDate,
        year: year,
        month: month + 1,
        today: today,
        week: week,
        daysList: daysList
      };
    },
    getWeekDay(val) {
      let flag = true;
      let weekDate = JSON.parse(JSON.stringify(this.weekDate));
      let days = weekDate.map((item) => {
        let { date, month, year } = item;
        date += val * 7;
        if (date < 1) {
          date += this.prevMonthDays;
          month -= 1;
          if (month == 0) {
            month = 12;
            year -= 1;
          }
        } else if (date > this.days) {
          date -= this.days;
          month += 1;
          if (month > 12) {
            month = 1;
            year += 1;
          }
        }
        if (this.month == month) {
          flag = false;
        }
        return { date, month, year };
      });
      if (flag) {
        val == 1 ? this.nextMonth() : this.prevMonth();
      }
      return days;
    },
    handleShow() {
      this.all = true;
      this.$emit('show', true);
    },
    handleHide() {
      this.all = false;
      this.$emit('show', false);
    }
  }
};
</script>
