当前位置:网站首页>Write a mobile date selector component by yourself

Write a mobile date selector component by yourself

2022-07-04 09:35:00 Dandelion_ drq

background

The component written in this article is based on uni-app Under the framework of , But in fact, the framework is not important , It's all the same .

Some students may ask ,uni-app It doesn't exist in itself picker,mode=time Is the time selector when , Why write one by yourself ? That's because our product boss said , Don't fix it at the bottom to pop up the selected , Want to nest in the page filter , Because considering interaction blabla Of …… I thought about it , ok , Give time to say everything , Let's build a wheel by ourselves ~

Effect demonstration

Let's see the effect first ~

  • Full functionality

 Full functionality

  • Year month day mode

 Year month day mode

  • Year month day hour minute second mode

 Year month day hour minute second mode

  • Month month mode

 Month month mode

Ideas

Before starting to work, first brush your mind .

The date filter interaction on the mobile end is more common, which is multi column scrolling , So we can use picker-view To achieve . In addition to basic interaction , The point that components need to pay attention to is the correlation between month, year and day , such as 1 Monthly 31 God ,4 Month is 30 God , Leap year 2 Month is 29 God wait for this , That is, the month, year and day need to be interrelated and dynamic . In addition, you can add the maximum and minimum time range for supporting configuration , Support switching between different time modes ( For example, mm / DD / yyyy / years / Mm / DD / yyyy HHM / S ) etc. .

The main functions of a commonly used date selector component are the above .

For the full code, see :https://github.com/Dandelion-drq/uniapp-datetime-picker

Welcome to my favorite friend star Ha ~

Realization

1. picker-view Realize basic interaction

First encapsulate a multi column scrolling selection component that accepts multiple arrays , It is convenient to support different date mode switching later .

<template>
  <picker-view class="picker-view" :value="indexArr" @change="onChange">
    <picker-view-column class="picker-view-column" v-for="(col, colIdx) in columns" :key="colIdx">
      <view v-for="(item, idx) in col" :key="idx">{
   { item }}</view>
    </picker-view-column>
  </picker-view>
</template>

<script src="./index.js"></script>

<style lang="css" scoped src="./index.css"></style>
.picker-view {
    
  height: 356rpx;
}

.picker-view-column {
    
  font-size: 14px;
  line-height: 34px;
  text-align: center;
  color: #333;
}
export default {
    
  data() {
    
    return {
    };
  },
  props: {
    
    //  All column option data 
    columns: {
    
      type: Array,
      default: () => []
    },
    //  The default selected value array for each column , The first item is selected by default 
    selectVals: {
    
      type: Array,
      default: () => []
    }
  },
  computed: {
    
    //  Index of selected items in each column , When the default selection value changes, this value also changes 
    indexArr: {
    
      //  Multidimensional arrays , Deep monitoring 
      cache: false,
      get() {
    
        // console.log('indexArr', this.selectVals, this.columns);
        if (this.selectVals.length > 0) {
    
          return this.columns.map((col, cIdx) => {
    
            return col.findIndex((i) => i == this.selectVals[cIdx]);
          });
        } else {
    
          return [].fill(0, 0, this.columns.length);
        }
      }
    }
  },
  methods: {
    
    onChange(e) {
    
      const {
     value } = e.detail;
      // console.log('pickerview change ', value, this.columns);

      let ret = this.columns.map((item, index) => {
    
        let idx = value[index];
        if (idx < 0) {
    
          idx = 0;
        }
        if (idx > item.length - 1) {
    
          idx = item.length - 1;
        }
        return item[idx];
      });
      // console.log(' Selected value ', ret);

      this.$emit('onChange', {
    
        value: ret
      });
    }
  }
};

2. Dynamic configuration of year, month and day, and support the maximum and minimum dates

The year is relatively simple , It is good to generate an array from the configured minimum date year to the maximum date year . Pay attention to the month when the selected year happens to be the smallest / The year of the maximum optional date , The month should start from the smallest / The maximum optional date starts / end , Other months are 1~12. First, list the number of days per person in a normal year , Then pay attention to leap years 2 Month is 29 God , Also, like the month, it should be noted that if the selected year and month happen to be the smallest / The year, month and hour of the maximum optional date , From the smallest / The maximum optional date starts / end . The same goes for hours, minutes and seconds .

<template>
  <view class="datetime-picker">
    <CustomPickerView :columns="dateConfig" :selectVals="selectVals" @onChange="onChangePickerValue" />
  </view>
</template>

<script src="./index.js"></script>
import CustomPickerView from '../customPickerView/index.vue';
import DateUtil from '../dateTimePicker/dateUtil';

export default {
    
  components: {
    
    CustomPickerView
  },
  data() {
    
    return {
    
      selectYear: new Date().getFullYear(),
      selectMonth: new Date().getMonth() + 1, //  Selected month ,1~12
      selectDay: new Date().getDate(),
      selectHour: new Date().getHours(),
      selectMinute: new Date().getMinutes(),
      selectSecond: new Date().getSeconds()
    };
  },
  props: {
    
    //  Optional minimum date , Default ten years ago 
    minDate: {
    
      type: String,
      default: ''
    },
    //  Optional maximum date , Default ten years later 
    maxDate: {
    
      type: String,
      default: ''
    }
  },
  computed: {
    
    minDateObj() {
    
      let minDate = this.minDate;
      if (minDate) {
    
        if (this.mode == 2 && minDate.replace(/\-/g, '/').split('/').length == 2) {
    
          //  When the date mode is year / month, it may be transmitted minDate yes 2022-02 This format , stay ios Next new Date Will report a mistake , Add the date part to make it compatible 
          minDate += '-01';
        }
        return new Date(DateUtil.handleDateStr(minDate));
      } else {
    
        //  No minimum date is sent , Default ten years ago 
        minDate = new Date();
        minDate.setFullYear(minDate.getFullYear() - 10);
        return minDate;
      }
    },
    maxDateObj() {
    
      let maxDate = this.maxDate;
      if (maxDate) {
    
        if (this.mode == 2 && maxDate.replace(/\-/g, '/').split('/').length == 2) {
    
          //  When the date mode is year / month, it may be transmitted maxDate yes 2022-02 This format , stay ios Next new Date Will report a mistake , Add the date part to make it compatible 
          maxDate += '-01';
        }
        return new Date(DateUtil.handleDateStr(maxDate));
      } else {
    
        //  No minimum date is sent , Default ten years later 
        maxDate = new Date();
        maxDate.setFullYear(maxDate.getFullYear() + 10);
        return maxDate;
      }
    },    
    years() {
    
      let years = [];
      let minYear = this.minDateObj.getFullYear();
      let maxYear = this.maxDateObj.getFullYear();
      for (let i = minYear; i <= maxYear; i++) {
    
        years.push(i);
      }

      return years;
    },
    months() {
    
      let months = [];
      let minMonth = 1;
      let maxMonth = 12;

      //  If the selected year happens to be the year of the minimum optional date , The month should start from the month of the minimum date 
      if (this.selectYear == this.minDateObj.getFullYear()) {
    
        minMonth = this.minDateObj.getMonth() + 1;
      }
      //  If the selected year happens to be the year of the maximum optional date , That month will end in the month of the maximum date 
      if (this.selectYear == this.maxDateObj.getFullYear()) {
    
        maxMonth = this.maxDateObj.getMonth() + 1;
      }

      for (let i = minMonth; i <= maxMonth; i++) {
    
        months.push(i);
      }

      return months;
    },
    days() {
    
      //  In a year 12 The number of days of each month 
      let monthDaysConfig = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
      //  Leap year 2 Monthly 29 God 
      if (this.selectMonth == 2 && this.selectYear % 4 == 0) {
    
        monthDaysConfig[1] = 29;
      }

      let minDay = 1;
      let maxDay = monthDaysConfig[this.selectMonth - 1];

      if (this.selectYear == this.minDateObj.getFullYear() && this.selectMonth == this.minDateObj.getMonth() + 1) {
    
        minDay = this.minDateObj.getDate();
      }
      if (this.selectYear == this.maxDateObj.getFullYear() && this.selectMonth == this.maxDateObj.getMonth() + 1) {
    
        maxDay = this.maxDateObj.getDate();
      }

      let days = [];
      for (let i = minDay; i <= maxDay; i++) {
    
        days.push(i);
      }

      return days;
    },
    hours() {
    
      let hours = [];
      let minHour = 0;
      let maxHour = 23;

      if (
        this.selectYear == this.minDateObj.getFullYear() &&
        this.selectMonth == this.minDateObj.getMonth() + 1 &&
        this.selectDay == this.minDateObj.getDate()
      ) {
    
        minHour = this.minDateObj.getHours();
      }
      if (
        this.selectYear == this.maxDateObj.getFullYear() &&
        this.selectMonth == this.maxDateObj.getMonth() + 1 &&
        this.selectDay == this.maxDateObj.getDate()
      ) {
    
        maxHour = this.maxDateObj.getHours();
      }

      for (let i = minHour; i <= maxHour; i++) {
    
        hours.push(i);
      }

      return hours;
    },
    minutes() {
    
      let mins = [];
      let minMin = 0;
      let maxMin = 59;

      if (
        this.selectYear == this.minDateObj.getFullYear() &&
        this.selectMonth == this.minDateObj.getMonth() + 1 &&
        this.selectDay == this.minDateObj.getDate() &&
        this.selectHour == this.minDateObj.getHours()
      ) {
    
        minMin = this.minDateObj.getMinutes();
      }
      if (
        this.selectYear == this.maxDateObj.getFullYear() &&
        this.selectMonth == this.maxDateObj.getMonth() + 1 &&
        this.selectDay == this.maxDateObj.getDate() &&
        this.selectHour == this.maxDateObj.getHours()
      ) {
    
        maxMin = this.maxDateObj.getMinutes();
      }

      for (let i = minMin; i <= maxMin; i++) {
    
        mins.push(i);
      }

      return mins;
    },
    seconds() {
    
      let seconds = [];
      let minSecond = 0;
      let maxSecond = 59;

      if (
        this.selectYear == this.minDateObj.getFullYear() &&
        this.selectMonth == this.minDateObj.getMonth() + 1 &&
        this.selectDay == this.minDateObj.getDate() &&
        this.selectHour == this.minDateObj.getHours() &&
        this.selectMinute == this.minDateObj.getMinutes()
      ) {
    
        minSecond = this.minDateObj.getSeconds();
      }
      if (
        this.selectYear == this.maxDateObj.getFullYear() &&
        this.selectMonth == this.maxDateObj.getMonth() + 1 &&
        this.selectDay == this.maxDateObj.getDate() &&
        this.selectHour == this.maxDateObj.getHours() &&
        this.selectMinute == this.maxDateObj.getMinutes()
      ) {
    
        maxSecond = this.maxDateObj.getSeconds();
      }

      for (let i = minSecond; i <= maxSecond; i++) {
    
        seconds.push(i);
      }

      return seconds;
    }
  }
}
// DateUtil.js

/** *  Date time format  * @param {Date} date  Date object to format  * @param {String} fmt  Formatted string ,eg:YYYY-MM-DD HH:mm:ss * @returns  Formatted date string  */
function formatDate(date, fmt) {
    
  if (typeof date == 'string') {
    
    date = new Date(handleDateStr(date));
  }

  var o = {
    
    'M+': date.getMonth() + 1, //  month 
    'd+': date.getDate(), //  Japan 
    'D+': date.getDate(), //  Japan 
    'H+': date.getHours(), //  Hours 
    'h+': date.getHours(), //  Hours 
    'm+': date.getMinutes(), //  branch 
    's+': date.getSeconds(), //  second 
    'q+': Math.floor((date.getMonth() + 3) / 3), //  quarter 
    S: date.getMilliseconds() //  millisecond 
  };

  if (/([y|Y]+)/.test(fmt)) {
    
    fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').slice(4 - RegExp.$1.length));
  }
  for (var k in o) {
    
    if (new RegExp('(' + k + ')').test(fmt)) {
    
      fmt = fmt.replace(RegExp.$1, RegExp.$1.length == 1 ? o[k] : ('00' + o[k]).slice(('' + o[k]).length));
    }
  }

  return fmt;
}

/** *  Processing time string , compatible ios Next new Date() return NaN problem  * @param {*} dateStr  Date string  * @returns */
function handleDateStr(dateStr) {
    
  return dateStr.replace(/\-/g, '/');
}

/** *  Date of judgment 1 Is it on the date 2 Before , That is, the date 1 Less than date 2 * @param {Date} date1 * @param {Date} date2 * @returns */
function isBefore(date1, date2) {
    
  if (typeof date1 == 'string') {
    
    date1 = new Date(handleDateStr(date1));
  }
  if (typeof date2 == 'string') {
    
    date2 = new Date(handleDateStr(date2));
  }
  return date1.getTime() < date2.getTime();
}

/** *  Date of judgment 1 Is it on the date 2 after , That is, the date 1 Greater than date 2 * @param {Date} date1 * @param {Date} date2 * @returns */
function isAfter(date1, date2) {
    
  if (typeof date1 == 'string') {
    
    date1 = new Date(handleDateStr(date1));
  }
  if (typeof date2 == 'string') {
    
    date2 = new Date(handleDateStr(date2));
  }
  return date1.getTime() > date2.getTime();
}

export default {
    
  formatDate,
  handleDateStr,
  isBefore,
  isAfter
};

3. Support different date modes

Support many different date modes , Including mm / DD / yyyy ( Default )、 years 、 year 、 Mm / DD / yyyy HHM / S . The main processing logic is based on mode The change of , To dynamically generate and transmit to pickerView An array of components , And its default selected value , And notice pickerView Components onChange The processing of events also needs to consider different date patterns .

<template>
  <view class="datetime-picker">
    <PickerView :columns="dateConfig" :selectVals="selectVals" @onChange="onChangePickerValue" />
  </view>
</template>

<script src="./index.js"></script>

<style scoped></style>
{
    
  props: {
    
    //  Date mode ,1: Specific date ,2: years ,3: year ,4: Mm / DD / yyyy HHM / S 
    mode: {
    
      type: Number,
      default: 1
    },
    //  The default selected date ( Note that it should correspond to the date pattern )
    defaultDate: {
    
      type: String,
      default: ''
    }
  }
  computed: {
    
    //  Pass to pickerView An array of components , according to mode To generate different data 
    dateConfig() {
    
      if (this.mode == 2) {
    
        //  Month month mode 
        let years = this.years.map((y) => y + ' year ');
        let months = this.months.map((m) => m + ' month ');
        return [years, months];
      } else if (this.mode == 3) {
    
        //  Only year mode 
        let years = this.years.map((y) => y + ' year ');
        return [years];
      } else if (this.mode == 4) {
    
        //  Year month day hour minute second mode 
        let years = this.years.map((y) => y + ' year ');
        let months = this.months.map((m) => m + ' month ');
        let days = this.days.map((d) => d + ' Japan ');
        let hours = this.hours.map((h) => h + ' when ');
        let minutes = this.minutes.map((m) => m + ' branch ');
        let seconds = this.seconds.map((s) => s + ' second ');
        return [years, months, days, hours, minutes, seconds];
      } else {
    
        //  Default , Year month day mode 
        let years = this.years.map((y) => y + ' year ');
        let months = this.months.map((m) => m + ' month ');
        let days = this.days.map((d) => d + ' Japan ');
        return [years, months, days];
      }
    },
    // pickerView The default value is , according to mode To change the value 
    selectVals() {
    
      if (this.mode == 2) {
    
        return [this.selectYear + ' year ', this.selectMonth + ' month '];
      } else if (this.mode == 3) {
    
        return [this.selectYear + ' year '];
      } else if (this.mode == 4) {
    
        return [
          this.selectYear + ' year ',
          this.selectMonth + ' month ',
          this.selectDay + ' Japan ',
          this.selectHour + ' when ',
          this.selectMinute + ' branch ',
          this.selectSecond + ' second '
        ];
      } else {
    
        return [this.selectYear + ' year ', this.selectMonth + ' month ', this.selectDay + ' Japan '];
      }
    }
  },
  methods: {
    
        onChangePickerValue(e) {
    
      const {
     value } = e;
      // console.log('onChangePickerValue', value);

      if (this.mode == 2 && value[0] && value[1]) {
    
        //  Month month mode 
        this.selectYear = Number(value[0].replace(' year ', ''));
        this.selectMonth = Number(value[1].replace(' month ', ''));
      } else if (this.mode == 3 && value[0]) {
    
        //  Only year mode 
        this.selectYear = Number(value[0].replace(' year ', ''));
      } else if (this.mode == 4 && value[0] && value[1] && value[2] != '' && value[3] && value[4] && value[5]) {
    
        //  Year month day hour minute second mode 
        this.selectYear = Number(value[0].replace(' year ', ''));
        this.selectMonth = Number(value[1].replace(' month ', ''));
        this.selectDay = Number(value[2].replace(' Japan ', ''));
        this.selectHour = Number(value[3].replace(' when ', ''));
        this.selectMinute = Number(value[4].replace(' branch ', ''));
        this.selectSecond = Number(value[5].replace(' second ', ''));
      } else if (value[0] && value[1] && value[2]) {
    
        //  Default , Year month day mode 
        this.selectYear = Number(value[0].replace(' year ', ''));
        this.selectMonth = Number(value[1].replace(' month ', ''));
        this.selectDay = Number(value[2].replace(' Japan ', ''));
      } else {
    
        //  Other situations may be pickerView There is a problem with the returned data , Don't deal with 
        console.log('onChangePickerValue Other situations ');
        return;
      }

      let formatTmpl = 'YYYY-MM-DD';
      if (this.mode == 2) {
    
        formatTmpl = 'YYYY-MM';
      } else if (this.mode == 3) {
    
        formatTmpl = 'YYYY';
      } else if (this.mode == 4) {
    
        formatTmpl = 'YYYY-MM-DD HH:mm:ss';
      }

      this.$emit(
        'onChange',
        DateUtil.formatDate(
          new Date(`${
      this.selectYear}/${
      this.selectMonth}/${
      this.selectDay} ${
      this.selectHour}:${
      this.selectMinute}:${
      this.selectSecond}`),
          formatTmpl
        )
      );
    }
  }
}

Completed the above 3 spot , The date selector component is written , Complete code and use demo see :https://github.com/Dandelion-drq/uniapp-datetime-picker

Welcome to my favorite friend star~

原网站

版权声明
本文为[Dandelion_ drq]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/02/202202141425117086.html