vue2仿携程时间选择控件

2024-11-28  本文已影响0人  程序猿的小生活

1.导入calendar.js

function dateToTimestamp(data) {
    return Date.parse(data);
}
function pad(i) {
    if (i < 10) {
        i = '0' + i;
    }

    return i;
}

/**
 * 获取月份的天数
 * @param {*} year
 * @param {*} month
 */
function getMonthDays(year, month) {
    return new Date(year, month, 0).getDate();
}

/**
 * 获取某一月的第一天是星期几
 * @param  {[type]} year  年份
 * @param  {[type]} month 月份,如果是0 这表示上一年的12月
 * @return {[type]} 1表示周一, 7表示周日
 */
function getFirsrtweekend(year, month) {
    return new Date(year, month - 1, 0).getDay() + 1;
}

/**
 * 获取下一个月份是多少
 * @param {*} year
 * @param {*} month
 */
function getNextDate(year, month) {
    month++;
    if (month > 12) {
        month = 1;
        year++;
    }

    return {
        year,
        month,
    };
}

/**
 * 获取上一个月份是多少
 * @param {*} year
 * @param {*} month
 */
function getPreDate(year, month) {
    month--;
    if (month <= 0) {
        month = 12;
        year--;
    }

    return {
        year,
        month,
    };
}
function preDaysSolt(year, month) {
    var daysSolt = getFirsrtweekend(year, month);

    if (daysSolt === 7) {
        daysSolt = 0;
    }
    return daysSolt;
}

function getMonthCurDetail(year, month) {
    var monthDetail = [];
    var days = getMonthDays(year, month);
    var curentTime = new Date();
    var currentYear = curentTime.getFullYear();
    var currentMonth = curentTime.getMonth() + 1;
    var currentDay = curentTime.getDate()

    var curentTimeStemp = dateToTimestamp(`${currentYear}-${pad(currentMonth)}-${pad(currentDay)}`)
    for (var i = 1; i <= days; i++) {
        var timeStamp = dateToTimestamp(`${year}-${pad(month)}-${pad(i)}`)

        monthDetail.push({
            tips: 'cur',
            text: i,
            timeStamp: timeStamp,
            disable: timeStamp < curentTimeStemp
        });
    }

    return monthDetail;
}

function getMonthPreDetail(year, month) {
    var monthDetail = [];

    var daysSolt = preDaysSolt(year, month);
    var preDate = getPreDate(year, month);
    var preMonthDays = getMonthDays(preDate.year, preDate.month);

    for (var i = preMonthDays - daysSolt + 1, len = preMonthDays; i <= len; i++) {
        monthDetail.push({
            text: i,
            tips: 'pre',
            timeStamp: dateToTimestamp(
                `${preDate.year}-${pad(preDate.month)}-${pad(i)}`
            ),
        });
    }

    return monthDetail;
}
function getMonthNextDetail(year, month) {
    var monthDetail = [];

    var daysSolt = preDaysSolt(year, month);
    var days = getMonthDays(year, month);
    var nextDate = getNextDate(year, month);
    // console.log())
    var count = 42 - (daysSolt + days)
    count = count >= 7 ? count - 7 : count;
    // console.log(count)
    for (var i = 1, len = count; i <= len; i++) {
        monthDetail.push({
            text: i,
            tips: 'next',
            timeStamp: dateToTimestamp(
                `${nextDate.year}-${pad(nextDate.month)}-${pad(i)}`
            ),
        });
    }
    // console.log( `${nextDate.year}-${pad(nextDate.month)}---->${daysSolt}`)
    return monthDetail;
}

export function timestampToDate(format, timestamp) {
    if (!timestamp) {
        return timestamp;
    }

    // var date = timestamp ? new Date(parseInt(timestamp) * 1000) : new Date(+new Date());
    var date = new Date(parseInt(timestamp));
    var year = date.getFullYear(),
        month = date.getMonth() + 1,
        day = date.getDate(),
        hour = date.getHours(),
        minute = date.getMinutes(),
        second = date.getSeconds();

    var str = format.replace(/y+|m+|d+|h+|s+/gi, function (w) {
        if (w == 'yy' || w == 'YY' || w == 'y' || w == 'Y') {
            return year.toString().substring(2);
        } else if (w == 'yyyy' || w == 'YYYY') {
            return year;
        } else if (w == 'MM') {
            return month >= 10 ? month : '0' + month;
        } else if (w == 'M') {
            return month;
        } else if (w == 'DD' || w == 'dd') {
            return day >= 10 ? day : '0' + day;
        } else if (w == 'D' || w == 'd') {
            return day;
        } else if (w == 'HH' || w == 'hh') {
            return hour >= 10 ? hour : '0' + hour;
        } else if (w == 'H' || w == 'h') {
            return hour;
        } else if (w == 'mm') {
            return minute >= 10 ? minute : '0' + minute;
        } else if (w == 'm') {
            return minute;
        } else if (w == 'ss' || w == 's') {
            return second >= 10 ? second : '0' + second;
        }
    });
    return str;
}

export function Calender(config) {
    var { date, mixDate, maxDate } = config;

    // date = date.split("-");
    var dateObj = new Date(date);

    this.year = dateObj.getFullYear();
    this.month = dateObj.getMonth() + 1;
    this.mixDate = null;
    this.maxDate = null;
    // this.selecting = false;

    if (mixDate) {
        let timeStamp = timestampToDate('yyyy-MM-DD', dateToTimestamp(mixDate));
        this.mixDate = dateToTimestamp(timeStamp);
    }

    if (maxDate) {
        let timeStamp = timestampToDate('yyyy-MM-DD', dateToTimestamp(maxDate));
        this.maxDate = dateToTimestamp(timeStamp);
    }

    this.rows = [];
}

Calender.prototype = {
    switchPreMonth() {
        var dateObj = getPreDate(this.year, this.month);
        this.year = dateObj.year;
        this.month = dateObj.month;

        return this.getList();
    },
    switchNextMonth() {
        var dateObj = getNextDate(this.year, this.month);
        this.year = dateObj.year;
        this.month = dateObj.month;
        return this.getList();
    },
    getList() {
        let currentDayDetail = getMonthCurDetail(this.year, this.month);

        let mix = Math.min(this.mixDate, this.maxDate);
        let max = Math.max(this.mixDate, this.maxDate);
        this.mixDate = mix;
        this.maxDate = max;

        this.rows = this.markRange(this.mixDate, this.maxDate, currentDayDetail);
        this.rows.unshift(...getMonthPreDetail(this.year, this.month));
        this.rows.push(...getMonthNextDetail(this.year, this.month));

        return this.rows;
    },
    getValue() {
        return [this.mixDate, this.maxDate];
    },

    markRange(mixDate, maxDate, rows) {
        for (let item of rows) {
            var time = item.timeStamp;
            item.inRange = mixDate && time >= mixDate && time <= maxDate;
            item.start = mixDate && time === mixDate;
            item.end = maxDate && time === maxDate;
        }

        return rows;
    },
};

2.导入riqi.vue

<template>

  <!-- <m-nav-bar :bar-style="{background: ''}" :fill-nav-bar="true"></m-nav-bar> -->
<!--  <div style="height: 50px;width: 100%;background-color:#E6EDF7"></div>-->
  <transition name="slider">
    <div
        class="hl-calendar-wrap"
        v-show="visibility"
    >

      <p   class="hl-calendar-week align-center ig-xq" >
        <span style="color: #026FF2">日</span>
        <span>一</span>
        <span>二</span>
        <span>三</span>
        <span>四</span>
        <span>五</span>
        <span style="color: #026FF2">六</span>
      </p>
      <div
          @click="handleClick"
          class="calendar-content clearfix align-center"
          ref="calendarWrap"
      >
        <div
            :key="item.title"
            v-for="item in list"
        >
          <div class="ig-title">
            {{ item.title }}

          </div>
          <div class="calendar-content_list-wrap clearfix">
            <div
                :class="{
                'ig-range': info.inRange,
                'ig-visibility': info.tips === 'pre' || info.tips === 'next',
                'ig-disable': info.disable,
                'ig-start': info.start,
                'ig-end': info.end,
                'ig-cursor': info.tips === 'cur'
              }"
                :date-timeStamp="info.timeStamp"
                :key="info.timeStamp"
                class="ig-cal-list"
                v-for="info in item.list"
                :id="item.title+'-'+info.text"
            >

              <div>

                <span
                    class="page-table-note"
                    v-if="info.start"
                >开始</span>
                <span
                    class="page-table-note"
                    v-if="info.end"
                >结束</span>

                <span  v-if="ss==(item.title+'-'+info.text)" style="color:red;">今日<!--{{ info.text }}--></span>
                <span v-else>{{ info.text }}</span>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </transition>

</template>
<script>
/**
 酒店日期选择组件

 props参数
 visibility            boolean         选择弹框是否显示      必填        默认false
 format                string          返回的日期格式        非必填      默认:YYYY-MM-DD
 initDate              object          开始选中的日期范围    非必填      默认选中:当天日期~后天日期  传参格式{ startDate: 'xxxx-xx-xx', endDate: 'xxxx-xx-xx' }


 事件
 select-date-range     function        选择两个日期的回调函数    返回选中的日期


 外部可以调用方法
 this.$refs['glHoelCalendar'].getValue()                                 返回选中的日期
 this.$refs['glHoelCalendar'].getCalenderList(date, mixDate, maxDate)    重新渲染列表 date: YYYY-MM 开始月份, mixDate: YYYY-MM-DD 入住日期, maxDte: YYYY-MM-DD 离店日期
 */
import { Calender, timestampToDate } from './calendar'

// let bodyEl = document.body
// let top = 0

// function stopBodyScroll(isFixed) {
//   if (isFixed) {
//     top = window.scrollY

//     bodyEl.style.position = 'fixed'
//     bodyEl.style.top = -top + 'px'
//   } else {
//     bodyEl.style.position = ''
//     bodyEl.style.top = ''

//     window.scrollTo(0, top) // 回到原先的top
//   }
// }

export default {

  props: {
    visibility: {
      type: Boolean,
      default: false
    },
    format: {
      type: String,
      default: 'YYYY-MM-DD'
    },
    initDate: {
      type: Object,
      default() {
        return {
          startDate: '',
          endDate: ''
        }
      }
    },
    showMonth: {
      type: Number,
      default: 3
    }
  },
  data() {
    return {
      ss:"",
      starttimea:"",
      endtimea:"",
      // visibility: false,
      target:0,
      list: [],
      mixDate: null,
      maxDate: null
    }
  },
  // watch: {
  //   visibility(newValue) {
  //     stopBodyScroll(newValue)
  //   }
  // },
  // deactivated() {
  //   stopBodyScroll(false)
  // },
  // created() {
  // },
  mounted() {

    this.createList();
    let thisDate = new Date();
    let year = thisDate.getFullYear();
    let month = thisDate.getMonth() + 1 < 10 ? '0' + (thisDate.getMonth() + 1) : thisDate.getMonth() + 1;
    let day = thisDate.getDate();
    this.ss = year+"-"+month+"-"+day
 //   let thisDate = new Date();
   /* let year = thisDate.getFullYear();
    let month = thisDate.getMonth() + 1 < 10 ? '0' + (thisDate.getMonth() + 1) : thisDate.getMonth() + 1;
    let day = thisDate.getDate();
    let ss = year+"-"+month+"-"+day*/
    //document.getElementById(ss).scrollIntoView({behavior:"smooth",block:"center"})
    console.log(this.visibility)
    this.$nextTick(() => {

     // document.getElementById("2024-11-25").scrollIntoView({behavior:"smooth",block:"center"});
    })
  /*  console.log(this.target)
    document.body.scrollTop = 200;*/

   /* setTimeout(function (){
      document.getElementById("2024-11-25").scrollIntoView();
    },1000)
*/
  },
  methods: {
    //inputdate :父组件 时间显示控件内容为空时跳转到当前时间 不为空不跳转
    refrashtodate(inputdate){
      //this.visibility = true
      console.log("dhhdhdhd")
     // document.getElementById("2024-11-25").scrollIntoView(true);
    //  location.href="#2024-11-25"


      if(inputdate=="") {
        setTimeout(function (){
        let thisDate = new Date();
        let year = thisDate.getFullYear();
        let month = thisDate.getMonth() + 1 < 10 ? '0' + (thisDate.getMonth() + 1) : thisDate.getMonth() + 1;
        let day = thisDate.getDate();
      let   ssaa = year+"-"+month+"-"+day
            document.getElementById(ssaa).scrollIntoView({behavior: "smooth", block: "center"});


      },1000)
     }
      /*this.$nextTick(() => {
        document.getElementById("2024-11-25").scrollIntoView({behavior:"smooth",block:"center"});
      })*/
    },
    createList() {
      const currentTime = new Date("2024-01-01").getTime()//设置日历起至时间
      const date = timestampToDate('yyyy-MM', currentTime)

     // const mixDate = this.initDate.startDate ? this.initDate.startDate : timestampToDate('yyyy-MM-DD', currentTime)
     // const maxDate = this.initDate.endDate ? this.initDate.endDate : timestampToDate('yyyy-MM-DD', currentTime + 86400000)

      this.getCalenderList(date, "", "");
    },
    getCalenderList(date, mixDate, maxDate) {
      const list = []
      this.calender = new Calender({
        date,
        mixDate: mixDate,
        maxDate: maxDate
      })

      this.mixDate = this.calender.mixDate
      this.maxDate = this.calender.maxDate
      // debugger
      list.push({
        title: date,
        list: this.calender.getList()
      })

      for (let i = 0; i < this.showMonth - 1; i++) {
        const listDetail = this.calender.switchNextMonth()
        const month = this.calender.month >= 10 ? this.calender.month : '0' + this.calender.month

        list.push({
          title: this.calender.year + '-' + month,
          list: listDetail
        })
      }
      this.list = list
    },
    getValue() {
      let mix = Math.min(this.mixDate, this.maxDate)
      let max = Math.max(this.mixDate, this.maxDate)

      return {
        startDate: timestampToDate(this.format, mix),
        endDate: timestampToDate(this.format, max),
        seletDays: (max - mix) / 86400000 + 1
      }
    },
    handleClick(event) {
      let target = event.target
      target = this.searchElement(target, 'ig-cal-list')
      // debugger
      if (!target) return

      //if (target.className.indexOf('visibility') > -1 || target.className.indexOf('disable') > -1) return

      var timeStamp = target.getAttribute('date-timestamp')

      if (timeStamp) {
        this._calHandleClick(timeStamp)
      }
    },
    _calHandleClick(selectData) {
      if (this.selecting) {
        this.selecting = false
        this.maxDate = selectData
      } else {
        this.selecting = true
        this.mixDate = selectData
        this.maxDate = null
      }

      let mix = Math.min(this.mixDate, this.maxDate)
      let max = Math.max(this.mixDate, this.maxDate)

      if (mix === max) {
        this.$emit('select-date-range', this.getValue())
        return
      } else {
        this.markRange(mix, max, this.list)
      }

      if (!this.selecting) {
        // console.log('您选择的是:', this.mixDate, this.maxDate);
        this.$emit('select-date-range', this.getValue())
      }
    },
    markRange(mixDate, maxDate, rows) {
      for (let item of rows) {
        item.list.forEach((info) => {
          var time = info.timeStamp
          info.inRange = mixDate && time >= mixDate && time <= maxDate

          if (!mixDate && maxDate) {

            info.start = time === maxDate
            info.end = false
            this.starttimea= info.start
            this.endtimea=info.end
          } else {
            info.start = mixDate && time === mixDate
            info.end = maxDate && time === maxDate
            this.starttimea= info.start
            this.endtimea=info.end
          }
        })
      }

      // console.log(rows);
    },
    /**
     * 搜索元素,解决事件委托颗粒度问题
     * @param  {[type]} el   [description]
     * @param  {[type]} attr [description]
     * @return {[type]}      [description]
     */
    searchElement: function (el, attr) {
      var touchTarget = el,
          count = 0,
          endTarget = 'class'

      while (touchTarget) {
        count++

        if (count > 5 || touchTarget.nodeName.toLowerCase() == 'html') {
          touchTarget = null

          return touchTarget
        } else if (touchTarget.getAttribute(endTarget) != null && touchTarget.getAttribute(endTarget).indexOf(attr) > -1) {
          return touchTarget
        }

        touchTarget = touchTarget.parentNode
      }
    }
  }
}
</script>
<style>
.slider-enter-active,
.slider-leave-active {
  transition: all 0.3s ease;
}
.slider-enter,
.slider-leave-to {
  transform: translateX(100%);
}
.clearfix {
  zoom: 1;
}
.clearfix:after {
  content: '';
  display: block;
  clear: both;
}
.ig-visibility {
  visibility: hidden;
}
.align-center {
  text-align: center;
}
.ig-title {
  background: hsla(210, 8%, 95%, 0.9);
  position: sticky;
  top: 30px;
  z-index: 1;
  height: 30px;
  line-height: 30px;
}
.ig-xq {
  background: #ffffff;
  /*background: hsla(210, 8%, 95%, 0.9);*/
  position: sticky;
  top: 0px;
  z-index: 2;
  height: 30px;
  line-height: 30px;

}
.hl-calendar-wrap {
  position: fixed;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
  overflow: auto;
  background: #fff;
  z-index: 1000;
}
.hl-calendar-wrap > .hl-calendar-week {
  width: 100%;
  height: 30px;
  display: flex;
  line-height: 30px;
  background: #fafafa;
  font-size: 14px;
  margin: 0;
  padding: 0;
}

.hl-calendar-wrap > .hl-calendar-week > span {
  flex: 1;
}

.ig-cal-list {
  float: left;
  width: 14.2%;
  margin: 5px 0;
  position: relative;
  height: 60px;
  color: #666;
  display: flex;
  align-items: center;
  justify-content: center;
}
.ig-cal-list:nth-of-type(7n + 1),
.ig-cal-list:nth-of-type(7n + 7) {
  color: #026FF2 !important;
 /* color: red !important;*/
}
.ig-cal-list.ig-disable {
  color: #666 !important;
}

.ig-cal-list.ig-disable:nth-of-type(7n + 1),
.ig-cal-list.ig-disable:nth-of-type(7n + 7) {
  color: #026FF2 !important;
  /*color: red !important;*/
}

.ig-range {
  background: #D4EAFF;
}
.ig-start,
.ig-end {
  background: #026FF2;
}

.hl-calendar-wrap .ig-start,
.hl-calendar-wrap .ig-end {
  color: #fff !important;
}
.hl-calendar-wrap .ig-disable.ig-start,
.hl-calendar-wrap .ig-disable.ig-end {
  color: #fff !important;
}
.cal-list > div {
  width: 100%;
}
.ig-cal-list .page-table-note {
  display: block;
  font-size: 12px;
}
</style>

3.调用

<template>
  <div style="width:100%;">
<div style="display: flex;flex-direction: row;align-items: center;position: relative">
          <el-input  ref="inp" clearable readonly @focus="openPicker('1')" style="margin-right: 10px;flex: 1;margin-left: 10px" v-model="inputdate" placeholder="选择日期"></el-input>
          <img style="position: absolute;right: 15px;height: 20px;width: 20px;object-fit: contain " src="../../../assets/dbimg/chahao.png" @click="cleardate()"/>
          <!--          <div class="searchdatestyle"  @click="openPicker('1')" style="margin-right: 10px;flex: 1">{{selectstarttime}}</div>
                    <div style=" font-size: 16px;color: #606266;">至</div>
                    <div class="searchdatestyle" @click="openPicker('2')" style="margin-right: 10px;flex: 1">{{selectendtime}}</div>-->
        </div>
 <riqi
        ref="hotelCalendar"
        :visibility="show"
        @select-date-range="selectDateRangeHandle"
        :show-month="120"
        format="YYYY-MM-DD"
    ></riqi>
  </div>

</template>
<script>
import riqi from "@/components/riqi";
export default {
beforeRouteLeave(to,from,next){//当行防卫 组件离开之前调用
 //console.log(next)
console.log(this.show)
  if(this.show){
    this.show = false
    this.$refs.inp.blur()//使 el-input失去焦点
      next(false)
  }else{
  next()
  }

},
data() {
    return {
      inputdate:"",
      show:false,
      datetype:"",
      searchtext:"",
      options:[{
        code:"",
        dictdata_value:"全部"
      }],

      taskvalue:"",
      selectstarttime:"选择日期",//选择时间
      selectendtime:"选择日期",

     
    }
  },
components:{
    riqi
  },
methods: {
    cleardate(){
      this.$refs['hotelCalendar'].getCalenderList("2024-01-01", "", "")
      this.inputdate=""
      this.selectstarttime="选择日期"
      this.selectendtime="选择日期"
    },
    selectDateRangeHandle(e) {
      this.show = false;
      this.dateRange = `开始${e.startDate}结束${e.endDate}, 共${e.seletDays - 1}天`
      console.log( this.dateRange)
      this.selectstarttime = e.startDate
      this.selectendtime = e.endDate
      this.inputdate=e.startDate+" 至 "+e.endDate
    },
openPicker(datetype) {
      this.datetype = datetype
      console.log(this.show)
      this.$refs['hotelCalendar'].refrashtodate(this.inputdate)
      this. show = true
    },
}
}
</script>
<style scoped>
.searchdatestyle{
  line-height: 25px;
  font-size: 15px;
  color: #606266;
  padding-left: 8px;
  padding-right: 8px;
  padding-bottom: 3px;
  padding-top: 3px;
  border-radius: 6px;
  background-color: #EEEEEE;
  margin-left: 10px;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center
}
/deep/ .el-input__inner{
  font-size: 15px;
/*  background-color: #EEEEEE;*/
  height: 30px;


}
/deep/ .el-input__inner::placeholder {
  color: #909090; /* 将颜色改为灰色 */
}
/deep/ .picker-item.picker-selected{
  color:#3875c6 ;
  font-size: 20px;
}
/*控制 mt-datetime-picker 组件只显示年月*/
/*/deep/ .picker-items>:nth-child(3){
  display: none;
}*/
.titlestyle{
  width: 100%;
  text-align: center;
  font-size: 18px;
  color: #3875c6;
  margin-top: 20px;
  margin-bottom: 20px;

}
</style>
上一篇 下一篇

猜你喜欢

热点阅读