zoukankan      html  css  js  c++  java
  • element calendar组件源码

    src/main.vue

    <template>
      <div class="el-calendar">
        <div class="el-calendar__header">
          <div class="el-calendar__title">
            {{ i18nDate }}
          </div>
          <div
            class="el-calendar__button-group"
            v-if="validatedRange.length === 0">
            <el-button-group>
              <el-button
                type="plain"
                size="mini"
                @click="selectDate('prev-month')">
                {{ t('el.datepicker.prevMonth') }}
              </el-button>
              <el-button
                type="plain"
                size="mini"
                @click="selectDate('today')">
                {{ t('el.datepicker.today') }}
              </el-button>
              <el-button
                type="plain"
                size="mini"
                @click="selectDate('next-month')">
                {{ t('el.datepicker.nextMonth') }}
              </el-button>
            </el-button-group>
          </div>
        </div>
        <div
          class="el-calendar__body"
          v-if="validatedRange.length === 0"
          key="no-range">
          <date-table
            :date="date"
            :selected-day="realSelectedDay"
            @pick="pickDay" />
        </div>
        <div
          v-else
          class="el-calendar__body"
          key="has-range">
          <date-table
            v-for="(range, index) in validatedRange"
            :key="index"
            :date="range[0]"
            :selected-day="realSelectedDay"
            :range="range"
            :hide-header="index !== 0"
            @pick="pickDay" />
        </div>
      </div>
    </template>
    
    <script>
    import Locale from 'element-ui/src/mixins/locale';
    import fecha from 'element-ui/src/utils/date';
    import DateTable from './date-table';
    import { validateRangeInOneMonth } from 'element-ui/src/utils/date-util';
    
    const validTypes = ['prev-month', 'today', 'next-month'];
    const oneDay = 86400000;
    
    export default {
      name: 'ElCalendar',
    
      mixins: [Locale],
    
      components: {
        DateTable
      },
    
      props: {
        value: [Date, String, Number],
        range: {
          type: Array,
          validator(range) {
            if (Array.isArray(range)) {
              return range.length === 2 && range.every(
                item => typeof item === 'string' ||
                typeof item === 'number' ||
                item instanceof Date);
            } else {
              return true;
            }
          }
        }
      },
    
      provide() {
        return {
          elCalendar: this
        };
      },
    
      methods: {
        // 接收子组件选中的日期
        pickDay(day) {
          // 设置为选中日
          this.realSelectedDay = day;
        },
        /**
          点击上月今天下月
         */
        selectDate(type) {
          if (validTypes.indexOf(type) === -1) {
            throw new Error(`invalid type ${type}`);
          }
          let day = '';
          if (type === 'prev-month') {
            // 上月第一天
            day = `${this.prevMonthDatePrefix}-01`;
          } else if (type === 'next-month') {
            // 下月第一天
            day = `${this.nextMonthDatePrefix}-01`;
          } else {
            // 今天
            day = this.formatedToday;
          }
    
          if (day === this.formatedDate) return;
          this.pickDay(day);
        },
    
        toDate(val) {
          if (!val) {
            throw new Error('invalid val');
          }
          return val instanceof Date ? val : new Date(val);
        }
      },
    
      computed: {
        prevMonthDatePrefix() {
          const temp = new Date(this.date.getTime());
          // 获取上个月最后一天
          temp.setDate(0);
          // 返回上个月年月
          return fecha.format(temp, 'yyyy-MM');
        },
    
        curMonthDatePrefix() {
          return fecha.format(this.date, 'yyyy-MM');
        },
    
        nextMonthDatePrefix() {
          const temp = new Date(this.date.getFullYear(), this.date.getMonth() + 1, 1);
          return fecha.format(temp, 'yyyy-MM');
        },
    
        formatedDate() {
          return fecha.format(this.date, 'yyyy-MM-dd');
        },
    
        i18nDate() {
          const year = this.formatedDate.slice(0, 4);
          const month = this.formatedDate.slice(5, 7).replace('0', '');
          return `${year} ${this.t('el.datepicker.year')} ${this.t('el.datepicker.month' + month)}`;
        },
    
        formatedToday() {
          return fecha.format(this.now, 'yyyy-MM-dd');
        },
        // 动态计算选中日,重写get,set方法
        realSelectedDay: {
          get() {
            if (!this.value) return this.selectedDay;
            return this.formatedDate;
          },
          set(val) {
            this.selectedDay = val;
            const date = new Date(val);
            // 此处双向绑定,相当于v-model
            this.$emit('input', date);
          }
        },
        // 没有range时,把date传给子组件date-table
        date() {
          if (!this.value) {
            if (this.realSelectedDay) {
              return new Date(this.selectedDay);
            } else if (this.validatedRange.length) {
              return this.validatedRange[0][0];
            }
            return this.now;
          } else {
            return this.toDate(this.value);
          }
        },
    
        // if range is valid, we get a two-digit array
        validatedRange() {
          let range = this.range;
          if (!range) return [];
          const expetedMap = {
            0: {
              value: 1,
              message: 'start of range should be Monday.'
            },
            1: {
              value: 0,
              message: 'end of range should be Sunday.'
            }
          };
          range = range.reduce((prev, val, index) => {
            const date = this.toDate(val);
            if (date.getDay() !== expetedMap[index].value) {
              console.warn('[ElementCalendar]', expetedMap[index].message, ' invalid range will be ignored');
            } else {
              prev = prev.concat(date);
            }
            return prev;
          }, []);
          if (range.length === 2) {
            const [start, end] = range;
            if (start > end) {
              console.warn('[ElementCalendar]end time should be greater than start time');
              return [];
            }
            // start time and end time in one month
            if (validateRangeInOneMonth(start, end)) {
              return [
                [start, end]
              ];
            }
            const data = [];
            let startDay = new Date(start.getFullYear(), start.getMonth() + 1, 1);
            const lastDay = this.toDate(startDay.getTime() - oneDay);
            if (!validateRangeInOneMonth(startDay, end)) {
              console.warn('[ElementCalendar]start time and end time interval must not exceed two months');
              return [];
            }
            data.push([
              start,
              lastDay
            ]);
            let interval = startDay.getDay();
            interval = interval <= 1 ? Math.abs(interval - 1) : (8 - interval);
            startDay = this.toDate(startDay.getTime() + interval * oneDay);
            if (startDay.getDate() < end.getDate()) {
              data.push([
                startDay,
                end
              ]);
            }
            return data;
          }
          return [];
        }
      },
    
      data() {
        return {
          selectedDay: '',
          now: new Date()
        };
      }
    };
    </script>

    src/date-table.vue

    <script>
    import fecha from 'element-ui/src/utils/date';
    import { range as rangeArr, getFirstDayOfMonth, getPrevMonthLastDays, getMonthDays, getI18nSettings, validateRangeInOneMonth } from 'element-ui/src/utils/date-util';
    export default {
    
      props: {
        selectedDay: String, // formated date yyyy-MM-dd
        range: {
          type: Array,
          validator(val) {
            if (!(val && val.length)) return true;
            const [start, end] = val;
            return validateRangeInOneMonth(start, end);
          }
        },
        date: Date,
        hideHeader: Boolean
      },
    
      inject: ['elCalendar'],
    
      methods: {
        // 本月分割成7个数组,赋给rows,动态渲染
        toNestedArr(days) {
          return rangeArr(days.length / 7).map((_, index) => {
            const start = index * 7;
            return days.slice(start, start + 7);
          });
        },
    
        getFormateDate(day, type) {
          if (!day || ['prev', 'current', 'next'].indexOf(type) === -1) {
            throw new Error('invalid day or type');
          }
          let prefix = this.curMonthDatePrefix;
          if (type === 'prev') {
            prefix = this.prevMonthDatePrefix;
          } else if (type === 'next') {
            prefix = this.nextMonthDatePrefix;
          }
          day = `00${day}`.slice(-2);
          return `${prefix}-${day}`;
        },
    
        getCellClass({ text, type}) {
          const classes = [type];
          if (type === 'current') {
            const date = this.getFormateDate(text, type);
            if (date === this.selectedDay) {
              classes.push('is-selected');
            }
            if (date === this.formatedToday) {
              classes.push('is-today');
            }
          }
          return classes;
        },
        // 点击哪天事件往上传递,日期传出去,父组件接收pick事件
        pickDay({ text, type }) {
          const date = this.getFormateDate(text, type);
          this.$emit('pick', date);
        },
    
        cellRenderProxy({ text, type }) {
          let render = this.elCalendar.$scopedSlots.dateCell;
          if (!render) return <span>{ text }</span>;
    
          const day = this.getFormateDate(text, type);
          const date = new Date(day);
          const data = {
            isSelected: this.selectedDay === day,
            type: `${type}-month`,
            day
          };
          return render({ date, data });
        }
      },
    
      computed: {
        prevMonthDatePrefix() {
          const temp = new Date(this.date.getTime());
          temp.setDate(0);
          return fecha.format(temp, 'yyyy-MM');
        },
    
        curMonthDatePrefix() {
          return fecha.format(this.date, 'yyyy-MM');
        },
    
        nextMonthDatePrefix() {
          const temp = new Date(this.date.getFullYear(), this.date.getMonth() + 1, 1);
          return fecha.format(temp, 'yyyy-MM');
        },
    
        formatedToday() {
          return this.elCalendar.formatedToday;
        },
    
        isInRange() {
          return this.range && this.range.length;
        },
        /*
          动态计算rows根据接收的date
         */
        rows() {
          let days = [];
          // if range exists, should render days in range.
          if (this.isInRange) {
            const [start, end] = this.range;
            const currentMonthRange = rangeArr(end.getDate() - start.getDate() + 1).map((_, index) => ({
              text: start.getDate() + index,
              type: 'current'
            }));
            let remaining = currentMonthRange.length % 7;
            remaining = remaining === 0 ? 0 : 7 - remaining;
            const nextMonthRange = rangeArr(remaining).map((_, index) => ({
              text: index + 1,
              type: 'next'
            }));
            days = currentMonthRange.concat(nextMonthRange);
          } else {
            const date = this.date;
            // 获取当月第一天
            const firstDay = getFirstDayOfMonth(date);
            // 根据当月第一天计算当月中上个月显示几天
            const prevMonthDays = getPrevMonthLastDays(date, firstDay - 1).map(day => ({
              text: day,
              type: 'prev'
            }));
            // 获取本月多少天
            const currentMonthDays = getMonthDays(date).map(day => ({
              text: day,
              type: 'current'
            }));
            days = [...prevMonthDays, ...currentMonthDays];
            // 日历一共6行每周7天共42天,42-上个月-本月=下月天数
            const nextMonthDays = rangeArr(42 - days.length).map((_, index) => ({
              text: index + 1,
              type: 'next'
            }));
            // 连起来共多少天
            days = days.concat(nextMonthDays);
          }
          return this.toNestedArr(days);
        }
      },
    
      data() {
        const dayNames = getI18nSettings().dayNames;
        return {
          DAYS: dayNames.slice(1).concat(dayNames[0])
        };
      },
    
      render() {
        const thead = this.hideHeader ? null : (<thead>
          {
            this.DAYS.map(day => <th key={day}>{ day }</th>)
          }
        </thead>);
        return (
          <table
            class={{
              'el-calendar-table': true,
              'is-range': this.isInRange
            }}
            cellspacing="0"
            cellpadding="0">
            {
              thead
            }
            <tbody>
              {
                this.rows.map((row, index) => <tr
                  class={{
                    'el-calendar-table__row': true,
                    'el-calendar-table__row--hide-border': index === 0 && this.hideHeader
                  }}
                  key={index}>
                  {
                    row.map((cell, key) => <td key={key}
                      class={ this.getCellClass(cell) }
                      onClick={this.pickDay.bind(this, cell)}>
                      <div class="el-calendar-day">
                        {
                          this.cellRenderProxy(cell)
                        }
                      </div>
                    </td>)
                  }
                </tr>)
              }
            </tbody>
          </table>);
      }
    };
    </script>
  • 相关阅读:
    Java中Io类-File类的构造方法
    hadoop的wordcount例子运行
    关于“javax.servlet.include.request_uri”属性值
    如何高效地分析框架源码
    代码重构的技巧——合理使用@Deprecated
    spring的事件机制
    在eclipse中使用jetty插件替代m2e开发调试maven web项目
    maven多配目配置总结
    如何禁止anonymous用户访问nexus
    efront二次开发记要
  • 原文地址:https://www.cnblogs.com/wsk1576025821/p/10916268.html
Copyright © 2011-2022 走看看