<template>
  <transition name="fade">
    <div id="mobile-datepicker" v-show="showPickerModel" @click.self="handleCancel">
      <div class="mdp_container">
        <!-- 组件头部开始 -->
        <div class="mdp_header">
          <div
            class="mdp_current_year"
            :class="{'active':changeContentFlag}"
            @click="showYearPicker()"
          >{{currentYear}}{{isEnglish?"":"年"}}</div>
          <br />
          <div
            class="mdp_current_date"
            :class="{'active':!changeContentFlag}"
            @click="showDatePicker()"
          >{{currentDateText}}</div>
        </div>
        <!-- 组件头部结束 -->
        <!-- 组件主体内容开始 -->
        <div class="mdp_content">
          <!-- 组件日期主体内容开始 -->
          <div class="mdp_date_content" v-show="!changeContentFlag">
            <div class="mdp_switch_month">
              <span
                class="mdp_arrow_action"
                :class="{'mdp_arrow_hide':startDate-new Date(year,month,1)>=0}"
                @click="preMonth"
              >
                <span class="mdp_left mdp_arrow"></span>
              </span>
              <span class="mdp_current_month">{{currentMonthText}}</span>
              <span
                class="mdp_arrow_action"
                :class="{'mdp_arrow_hide':endDate-new Date(year,month+1,1)<=0}"
                @click="nextMonth"
              >
                <span class="mdp_right mdp_arrow"></span>
              </span>
            </div>
            <div class="mdp_weeks">
              <span
                class="mdp_week"
                :class="{'mark':markWeekend&&(index==0||index==6)}"
                v-for="(item,index) in isEnglish?enShortWeeks:weeks"
                :key="index"
              >{{isEnglish?item[0]:item}}</span>
            </div>
            <div class="mdp_pick_day">
              <span
                class="mdp_day"
                :class="{'active':activeDay(item),'mdp_day_disable':dayIsDisable(item),'mark': markWeekend&&dayIsWeekend(index)}"
                v-for="(item,index) in days"
                :key="index"
                @click="handleClickDay(item)"
              >{{item?item:' '}}</span>
            </div>
          </div>
          <!-- 组件年份主体内容结束 -->
          <div class="mdp_year_content" v-show="changeContentFlag">
            <ul ref="mdp_year_list">
              <li
                v-for="(item,index) in years"
                :key="index"
                :class="{'active':item==currentYear}"
                @click="handleClickYear(item)"
              >{{item}}</li>
            </ul>
          </div>
        </div>
        <!-- 组件主体内容结束 -->
        <!-- 组件操作按钮开始 -->
        <div class="mdp_operate_button">
          <button
            class="mdp_cancel_button mdp_button"
            @click="handleCancel"
          >{{isEnglish?"CANCEL":"取消"}}</button>
          <button
            class="mdp_sure_button mdp_button"
            @click="handleConfirm"
          >{{isEnglish?"OK":"确认"}}</button>
        </div>
        <!-- 组件操作按钮结束 -->
      </div>
  </div>
  </transition>
</template>
 
<script>
export default {
  name: "MobileDatepicker",
  props: {
    showPickerModel: {
      //控制显隐
      type: Boolean,
      default: false
    },
    defaultDate: {
      //默认选中日期
      type: Date,
      default: () => {
        return new Date();
      }
    },
    startDate: {
      //可选范围开始日期
      type: Date,
      default: () => {
        return new Date("1900-1-1");
      }
    },
    endDate: {
      //可选范围结束日期
      type: Date,
      default: () => {
        return new Date();
      }
    },
    disableDate: {
      //禁用日期函数
      type: Function
    },
    markWeekend: {
      //标记周末
      type: Boolean,
      default: false
    },
    language: {
      //语言
      type: String,
      default: "zh"
    }
  },
  created() {
    if (this.showPickerModel && this.defaultDate) {
      //保证刷新，数据不丢失
      this.initDate(this.defaultDate); //初始化日期
    }
  },
  data() {
    return {
      weeks: ["日", "一", "二", "三", "四", "五", "六"], //周 数组
      enMonth: [
        "January",
        "February",
        "March",
        "April",
        "May",
        "June",
        "July",
        "August",
        "September",
        "October",
        "November",
        "December"
      ], //英文 月份
      enShortWeeks: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"], //英文 周
      days: [], //某年某月的日期数组，如[0,0,1,2,3,4,....,31]
      years: [], //可选范围的年份数组，如[1991,1992,...,2019]
      monthLastDay: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], //某年对应月份最大天数
      year: null,
      currentYear: null,
      month: null,
      currentMonth: null,
      day: null,
      currentDay: null,
      week: null,
      changeContentFlag: false //切换主体内容显示为日期或年份
    };
  },
  watch: {
    showPickerModel(newVal, oldVal) {
      //监测showPickerModel的变动
      if (newVal) {
        //showPickerModel由false变为true
        if (
          this.defaultDate >= this.startDate &&
          this.defaultDate <= this.endDate
        ) {
          //默认时间在起止时间之内
          this.initDate(this.defaultDate); //初始化
        } else {
          this.$emit("cancel"); //触发取消事件
        }
      }
    }
  },
  computed: {
    isEnglish() { //语言版本是否为英语
      return this.language == "en";
    },
    currentDateText(){  //当前日期显示文本
      if(this.isEnglish){
        if(this.enMonth[this.currentMonth]){
          return (this.enShortWeeks[this.weeks.indexOf(this.week)])+", "+this.enMonth[this.currentMonth].substring(0,3)+" "+this.currentDay;
        }else{
          return (this.enShortWeeks[this.weeks.indexOf(this.week)])+", ";
        }
      }else{
        return (this.currentMonth+1)+"月"+this.currentDay+"日周"+this.week;
      }
    },
    currentMonthText(){  //当前月份显示文本
      if(this.isEnglish){
        return this.enMonth[this.month]+" "+this.year
      }else{
        return this.year+"年"+(this.month+1)+"月"
      }
    }
  },
  methods: {
    handleCancel() {
      //处理取消点击
      this.changeContentFlag = false; //关闭年份选择面板
      this.$emit("cancel"); //触发取消事件
    },
    handleConfirm() {
      //处理确认点击
      this.changeContentFlag = false; //关闭年份选择面板
      this.$emit(
        "confirm",
        this.currentYear + "-" + (this.currentMonth + 1) + "-" + this.currentDay
      ); //触发确认事件，参数为选择的日期，如2019-7-7
    },
    initDate(defaultDate) {
      //初始化日期
      this.currentYear = this.year = defaultDate.getFullYear(); //年
      this.currentMonth = this.month = defaultDate.getMonth(); //月
      this.currentDay = this.day = defaultDate.getDate(); //日
      this.week = this.weeks[defaultDate.getDay()]; //周
      this.years = [];
      for (
        let i = 0;
        i <= this.endDate.getFullYear() - this.startDate.getFullYear();
        i++
      ) {
        this.years.push(Number(this.startDate.getFullYear()) + i); //初始化年份列表
      }
      this.updateDate(this.year, this.month); //根据年月更新日期
    },
    updateDate(year, month) {
      //根据年月更新日期
      this.monthLastDay[1] = 28; //平年
      if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
        //闰年
        this.monthLastDay[1] = 29; //2月有29天
      }
      let firstDay = new Date(year, month, 1).getDay(); //根据年月获取第一天是周几
      this.days = [];
      for (let i = 0; i < firstDay; i++) {
        this.days.push(0); //往第一天前面补0，如第一天周二，则需前面补2个0
      }
      for (let j = 1; j <= this.monthLastDay[month]; j++) {
        this.days.push(j); //补完0后，根据该月最大的天数，从累加，如1,2,3...
      }
    },
    preMonth() {
      //上一个月
      if (this.month == 0) {
        //如果当前月为第一个月，年份减一，月份置十二
        this.year = this.year - 1;
        this.month = 11;
      } else {
        this.month--;
      }
      this.updateDate(this.year, this.month); //根据年月更新日期
    },
    nextMonth() {
      //下一个月
      if (this.month == 11) {
        //如果当前月为最后一个月，年份加一，月份置一
        this.year = this.year + 1;
        this.month = 0;
      } else {
        this.month++;
      }
      this.updateDate(this.year, this.month); //根据年月更新日期
    },
    handleClickDay(item) {
      //处理日期点击事件
      if (
        this.startDate > new Date(this.year, this.month, item) ||
        this.endDate < new Date(this.year, this.month, item) ||
        item == 0
      ) {
        return; //不在指定范围的，属于禁用的日期不做点击处理
      }
      if (
        typeof this.disableDate == "function" &&
        this.disableDate(this.year + "-" + (this.month + 1) + "-" + item)
      ) {
        ////禁用函数存在，符合禁用规则
        return;
      }
      this.currentYear = this.year; //更新到当前点击日期
      this.currentMonth = this.month;
      this.currentDay = this.day = item;
      let date = new Date(this.currentYear, this.currentMonth, this.currentDay);
      this.week = this.weeks[date.getDay()]; //获取更新日期周
    },
    handleClickYear(item) {
      //处理年份点击事件
      if (new Date(item, this.month, this.day) < this.startDate) {
        //超出最小可选日期，置为最小日期
        this.year = this.currentYear = this.startDate.getFullYear();
        this.month = this.currentMonth = this.startDate.getMonth();
        this.day = this.currentDay = this.startDate.getDate();
        this.week = this.weeks[this.startDate.getDay()];
      } else if (new Date(item, this.month, this.day) > this.endDate) {
        //超出最大可选日期，置为最大日期
        this.year = this.currentYear = this.endDate.getFullYear();
        this.month = this.currentMonth = this.endDate.getMonth();
        this.day = this.currentDay = this.endDate.getDate();
        this.week = this.weeks[this.endDate.getDay()];
      } else {
        //更新到当前点击日期
        if (
          typeof this.disableDate == "function" &&
          this.disableDate(
            item + "-" + (this.currentMonth + 1) + "-" + this.day
          )
        ) {
          //禁用函数存在，符合禁用规则
          this.year = item; //更新切换面板年月
          this.updateDate(this.year, this.month); //根据年月更新日期
          this.changeContentFlag = false; //关闭年份选择面板
          return;
        }
        if (
          !((item % 4 == 0 && item % 100 != 0) || item % 400 == 0) &&
          this.currentMonth == 1 &&
          this.currentDay == 29
        ) {
          //从闰年2月29日到平年
          this.year = item; //更新切换面板年月
          this.updateDate(this.year, this.month); //根据年月更新日期
          this.changeContentFlag = false; //关闭年份选择面板
          return;
        }
        this.year = this.currentYear = item;
        this.month = this.currentMonth;
        this.day = this.currentDay; //更新切换面板年月日
        let date = new Date(this.year, this.month, this.day);
        this.week = this.weeks[date.getDay()];
      }
      this.updateDate(this.year, this.month); //根据年月更新日期
      this.changeContentFlag = false; //关闭年份选择面板
    },
    showDatePicker() {
      //显示选择日期面板
      if (this.changeContentFlag) {
        this.changeContentFlag = false;
      }
    },
    showYearPicker() {
      //显示选择年份面板
      if (!this.changeContentFlag) {
        this.changeContentFlag = true;
        let scrollYearListY; //年份列表Y轴上滚动距离
        this.$nextTick(() => {
          //使用nextTick为了保证dom元素都已经渲染完毕
          scrollYearListY =
            (this.currentYear - this.startDate.getFullYear() - 3) *
            this.$refs.mdp_year_list.firstElementChild.getBoundingClientRect().height;
          scrollYearListY = scrollYearListY < 0 ? 0 : scrollYearListY;
          this.$refs.mdp_year_list.scrollTop = scrollYearListY;
        });
      }
    },
    activeDay(day){  //选中日期
      return (day==this.currentDay&&this.month==this.currentMonth)&&!this.dayIsDisable(day)
    },
    dayIsDisable(item) {
      //禁用日期，返回true为禁用
      if (item == 0) {
        return false;
      }
      if (
        this.startDate -
          new Date(this.year + "-" + (this.month + 1) + "-" + item) >
          0 ||
        new Date(this.year + "-" + (this.month + 1) + "-" + item) -
          this.endDate >
          0
      ) {
        //日期不在起止日期范围禁用
        return true;
      } else if (this.disableDate && typeof this.disableDate == "function") {
        //禁用函数存在，符合函数规则，禁用
        if (this.disableDate(this.year + "-" + (this.month + 1) + "-" + item))
          return true;
      }
      return false;
    },
    dayIsWeekend(index) {  //是否为周末
      if (index % 7 == 0 || index % 7 == 6) {
        return true;
      }
      return false;
    }
  }
};
</script>
 
<style>
@media (min-width: 240px) {
  html {
    font-size: 32px;
  }
}
@media (min-width: 320px) {
  html {
    font-size: 42.66667px;
  }
}
@media (min-width: 360px) {
  html {
    font-size: 48px;
  }
}
@media (min-width: 375px) {
  html {
    font-size: 50px;
  }
}
@media (min-width: 384px) {
  html {
    font-size: 51.2px;
  }
}
@media (min-width: 411px) {
  html {
    font-size: 54.8px;
  }
}
@media (min-width: 414px) {
  html {
    font-size: 55.2px;
  }
}
@media (min-width: 424px) {
  html {
    font-size: 56.53333px;
  }
}
@media (min-width: 480px) {
  html {
    font-size: 64px;
  }
}
@media (min-width: 540px) {
  html {
    font-size: 72px;
  }
}
@media (min-width: 640px) {
  html {
    font-size: 85.33333px;
  }
}
@media (min-width: 720px) {
  html {
    font-size: 96px;
  }
}
@media (min-width: 750px) {
  html {
    font-size: 100px;
  }
}
@media (min-width: 768px) {
  html {
    font-size: 102.4px;
  }
}
@media (min-width: 800px) {
  html {
    font-size: 106.66667px;
  }
}
@media (min-width: 980px) {
  html {
    font-size: 130.66667px;
  }
}
@media (min-width: 1024px) {
  html {
    font-size: 136.53333px;
  }
}
@media (min-width: 1080px) {
  html {
    font-size: 144px;
  }
}
@media (min-width: 1152px) {
  html {
    font-size: 153.6px;
  }
}
@media (min-width: 1366px) {
  html {
    font-size: 182.13333px;
  }
}
@media (min-width: 1440px) {
  html {
    font-size: 192px;
  }
}
@media (min-width: 2160px) {
  html {
    font-size: 288px;
  }
}
html {
  font-family: font-apple-system, system-ui, BlinkMacSystemFont, Helvetica Neue,
    PingFang SC, Hiragino Sans GB, Microsoft YaHei, Arial, sans-serif;
}
* {
  margin: 0;
  padding: 0;
}
ol,
ul {
  list-style: none;
}
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
  opacity: 0;
}
#mobile-datepicker {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  display: flex;
  justify-content: center;
  align-items: center;
}
.mdp_container {
  width: 80%;
  height: 10rem;
  background-color: #fff;
}
.mdp_header {
  height: 2rem;
  background-color: #dd2727;
  box-sizing: border-box;
  color: #eee;
  padding: 0.4rem 0.6rem;
  font-size: 0;
}
.mdp_current_year {
  font-size: 0.3125rem;
  display: inline-block;
}
.mdp_current_date {
  font-size: 0.6125rem;
  display: inline-block;
}
.mdp_current_year.active,
.mdp_current_date.active {
  color: #fff;
}
.mdp_content {
  width: 100%;
  height: 6.8rem;
  background: #fff;
}
.mdp_date_content {
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  padding: 0 0.6rem 0.4rem;
}
.mdp_switch_month {
  display: flex;
  justify-content: space-between;
  align-items: center;
  height: 1.2rem;
  font-weight: 500;
  font-size: 0.3125rem;
}
.mdp_arrow_action {
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 0.6rem;
  min-width: 0.6rem;
}
.mdp_arrow {
  display: inline-block;
  width: 0.16rem;
  height: 0.16rem;
  border-top: 0.04rem solid #000;
  border-left: 0.04rem solid #000;
}
.mdp_left {
  transform: rotate(-45deg);
}
.mdp_right {
  transform: rotate(135deg);
}
.mdp_arrow_hide {
  visibility: hidden;
}
.mdp_weeks {
  width: 100%;
  display: flex;
  align-items: center;
  font-size: 0.25rem;
  color: #888;
}
.mdp_week {
  width: 14.2%;
  height: 0.8rem;
  display: flex;
  justify-content: center;
  align-items: center;
}
.mdp_week.mark {
  color: #dd2727;
}
.mdp_pick_day {
  font-size: 0.25rem;
  width: 100%;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
}
.mdp_day {
  width: 14.2%;
  height: 0.8rem;
  display: flex;
  justify-content: center;
  align-items: center;
}
.mdp_day.mdp_day_disable {
  background-color: #f5f7fa;
  cursor: not-allowed;
  color: #c0c4cc !important;
}
.mdp_day.mark {
  color: #dd2727;
}
.mdp_day.active {
  width: 14.2%;
  background-color: #dd2727;
  border-radius: 50%;
  color: #fff;
}
.mdp_year_content {
  width: 100%;
  height: 100%;
  display: flex;
  justify-content: center;
  align-items: center;
  border-bottom: 0.02rem solid #eee;
}
.mdp_year_content ul {
  width: 100%;
  height: 100%;
  overflow: scroll;
  font-size: 0.3125rem;
}
.mdp_year_content ul li {
  padding: 0.4rem 0;
  height: 1rem;
  box-sizing: border-box;
  display: flex;
  justify-content: center;
  align-items: center;
}
.mdp_year_content ul li.active {
  font-size: 0.465rem;
  color: #dd2727;
}
.mdp_operate_button {
  margin: 0 0.6rem;
  height: 1.2rem;
  display: flex;
  justify-content: flex-end;
  align-items: center;
}
.mdp_button {
  border: none;
  outline: none;
  color: #dd2727;
  background-color: #fff;
  font-weight: 500;
  font-size: 0.275rem;
}
.mdp_cancel_button {
  margin-right: 0.6rem;
}
</style>