123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- <template>
- <table @click="handleMonthTableClick" @mousemove="handleMouseMove" class="el-month-table">
- <tbody>
- <tr v-for="(row, key) in rows" :key="key">
- <td :class="getCellStyle(cell)" v-for="(cell, key) in row" :key="key">
- <div>
- <a class="cell">{{ t('el.datepicker.months.' + months[cell.text]) }}</a>
- </div>
- </td>
- </tr>
- </tbody>
- </table>
- </template>
- <script type="text/babel">
- import Locale from 'element-ui/src/mixins/locale';
- import { isDate, range, getDayCountOfMonth, nextDate } from 'element-ui/src/utils/date-util';
- import { hasClass } from 'element-ui/src/utils/dom';
- import { arrayFindIndex, coerceTruthyValueToArray, arrayFind } from 'element-ui/src/utils/util';
- const datesInMonth = (year, month) => {
- const numOfDays = getDayCountOfMonth(year, month);
- const firstDay = new Date(year, month, 1);
- return range(numOfDays).map(n => nextDate(firstDay, n));
- };
- const clearDate = (date) => {
- return new Date(date.getFullYear(), date.getMonth());
- };
- const getMonthTimestamp = function(time) {
- if (typeof time === 'number' || typeof time === 'string') {
- return clearDate(new Date(time)).getTime();
- } else if (time instanceof Date) {
- return clearDate(time).getTime();
- } else {
- return NaN;
- }
- };
- // remove the first element that satisfies `pred` from arr
- // return a new array if modification occurs
- // return the original array otherwise
- const removeFromArray = function(arr, pred) {
- const idx = typeof pred === 'function' ? arrayFindIndex(arr, pred) : arr.indexOf(pred);
- return idx >= 0 ? [...arr.slice(0, idx), ...arr.slice(idx + 1)] : arr;
- };
- export default {
- props: {
- disabledDate: {},
- value: {},
- selectionMode: {
- default: 'month'
- },
- minDate: {},
- maxDate: {},
- defaultValue: {
- validator(val) {
- // null or valid Date Object
- return val === null || isDate(val) || (Array.isArray(val) && val.every(isDate));
- }
- },
- date: {},
- rangeState: {
- default() {
- return {
- endDate: null,
- selecting: false
- };
- }
- }
- },
- mixins: [Locale],
- watch: {
- 'rangeState.endDate'(newVal) {
- this.markRange(this.minDate, newVal);
- },
- minDate(newVal, oldVal) {
- if (getMonthTimestamp(newVal) !== getMonthTimestamp(oldVal)) {
- this.markRange(this.minDate, this.maxDate);
- }
- },
- maxDate(newVal, oldVal) {
- if (getMonthTimestamp(newVal) !== getMonthTimestamp(oldVal)) {
- this.markRange(this.minDate, this.maxDate);
- }
- }
- },
- data() {
- return {
- months: ['jan', 'feb', 'mar', 'apr', 'may', 'jun', 'jul', 'aug', 'sep', 'oct', 'nov', 'dec'],
- tableRows: [ [], [], [] ],
- lastRow: null,
- lastColumn: null
- };
- },
- methods: {
- cellMatchesDate(cell, date) {
- const value = new Date(date);
- return this.date.getFullYear() === value.getFullYear() && Number(cell.text) === value.getMonth();
- },
- getCellStyle(cell) {
- const style = {};
- const year = this.date.getFullYear();
- const today = new Date();
- const month = cell.text;
- const defaultValue = this.defaultValue ? Array.isArray(this.defaultValue) ? this.defaultValue : [this.defaultValue] : [];
- style.disabled = typeof this.disabledDate === 'function'
- ? datesInMonth(year, month).every(this.disabledDate)
- : false;
- style.current = arrayFindIndex(coerceTruthyValueToArray(this.value), date => date.getFullYear() === year && date.getMonth() === month) >= 0;
- style.today = today.getFullYear() === year && today.getMonth() === month;
- style.default = defaultValue.some(date => this.cellMatchesDate(cell, date));
- if (cell.inRange) {
- style['in-range'] = true;
- if (cell.start) {
- style['start-date'] = true;
- }
- if (cell.end) {
- style['end-date'] = true;
- }
- }
- return style;
- },
- getMonthOfCell(month) {
- const year = this.date.getFullYear();
- return new Date(year, month, 1);
- },
- markRange(minDate, maxDate) {
- minDate = getMonthTimestamp(minDate);
- maxDate = getMonthTimestamp(maxDate) || minDate;
- [minDate, maxDate] = [Math.min(minDate, maxDate), Math.max(minDate, maxDate)];
- const rows = this.rows;
- for (let i = 0, k = rows.length; i < k; i++) {
- const row = rows[i];
- for (let j = 0, l = row.length; j < l; j++) {
- const cell = row[j];
- const index = i * 4 + j;
- const time = new Date(this.date.getFullYear(), index).getTime();
- cell.inRange = minDate && time >= minDate && time <= maxDate;
- cell.start = minDate && time === minDate;
- cell.end = maxDate && time === maxDate;
- }
- }
- },
- handleMouseMove(event) {
- if (!this.rangeState.selecting) return;
- let target = event.target;
- if (target.tagName === 'A') {
- target = target.parentNode.parentNode;
- }
- if (target.tagName === 'DIV') {
- target = target.parentNode;
- }
- if (target.tagName !== 'TD') return;
- const row = target.parentNode.rowIndex;
- const column = target.cellIndex;
- // can not select disabled date
- if (this.rows[row][column].disabled) return;
- // only update rangeState when mouse moves to a new cell
- // this avoids frequent Date object creation and improves performance
- if (row !== this.lastRow || column !== this.lastColumn) {
- this.lastRow = row;
- this.lastColumn = column;
- this.$emit('changerange', {
- minDate: this.minDate,
- maxDate: this.maxDate,
- rangeState: {
- selecting: true,
- endDate: this.getMonthOfCell(row * 4 + column)
- }
- });
- }
- },
- handleMonthTableClick(event) {
- let target = event.target;
- if (target.tagName === 'A') {
- target = target.parentNode.parentNode;
- }
- if (target.tagName === 'DIV') {
- target = target.parentNode;
- }
- if (target.tagName !== 'TD') return;
- if (hasClass(target, 'disabled')) return;
- const column = target.cellIndex;
- const row = target.parentNode.rowIndex;
- const month = row * 4 + column;
- const newDate = this.getMonthOfCell(month);
- if (this.selectionMode === 'range') {
- if (!this.rangeState.selecting) {
- this.$emit('pick', {minDate: newDate, maxDate: null});
- this.rangeState.selecting = true;
- } else {
- if (newDate >= this.minDate) {
- this.$emit('pick', {minDate: this.minDate, maxDate: newDate});
- } else {
- this.$emit('pick', {minDate: newDate, maxDate: this.minDate});
- }
- this.rangeState.selecting = false;
- }
- } else if (this.selectionMode === 'months') {
- const value = this.value || [];
- const year = this.date.getFullYear();
- const newValue = arrayFindIndex(value, date => date.getFullYear() === year && date.getMonth() === month) >= 0
- ? removeFromArray(value, date => date.getTime() === newDate.getTime())
- : [...value, newDate];
- this.$emit('pick', newValue);
- } else {
- this.$emit('pick', month);
- }
- }
- },
- computed: {
- rows() {
- // TODO: refactory rows / getCellClasses
- const rows = this.tableRows;
- const disabledDate = this.disabledDate;
- const selectedDate = [];
- const now = getMonthTimestamp(new Date());
- for (let i = 0; i < 3; i++) {
- const row = rows[i];
- for (let j = 0; j < 4; j++) {
- let cell = row[j];
- if (!cell) {
- cell = { row: i, column: j, type: 'normal', inRange: false, start: false, end: false };
- }
- cell.type = 'normal';
- const index = i * 4 + j;
- const time = new Date(this.date.getFullYear(), index).getTime();
- cell.inRange = time >= getMonthTimestamp(this.minDate) && time <= getMonthTimestamp(this.maxDate);
- cell.start = this.minDate && time === getMonthTimestamp(this.minDate);
- cell.end = this.maxDate && time === getMonthTimestamp(this.maxDate);
- const isToday = time === now;
- if (isToday) {
- cell.type = 'today';
- }
- cell.text = index;
- let cellDate = new Date(time);
- cell.disabled = typeof disabledDate === 'function' && disabledDate(cellDate);
- cell.selected = arrayFind(selectedDate, date => date.getTime() === cellDate.getTime());
- this.$set(row, j, cell);
- }
- }
- return rows;
- }
- }
- };
- </script>
|