Browse Source

DatePicker: optimize code, fix #12980, follow up #8156 (#13374)

* date-table: simplify implementation, fix #12980

* date-table: remove rangeState's row and column

* date-table: fix disabled cell highlight

this is a regression introduced in pr #8156, which allows disabled date to be
highlighted.

Also simplifies handleClick's implementation to avoid DOM operations.
Jiewei Qian 6 years ago
parent
commit
9738054dd4
1 changed files with 61 additions and 123 deletions
  1. 61 123
      packages/date-picker/src/basic/date-table.vue

+ 61 - 123
packages/date-picker/src/basic/date-table.vue

@@ -32,16 +32,19 @@
 </template>
 </template>
 
 
 <script>
 <script>
-  import { getFirstDayOfMonth, getDayCountOfMonth, getWeekNumber, getStartDateOfMonth, nextDate, isDate } from '../util';
-  import { hasClass } from 'element-ui/src/utils/dom';
+  import { getFirstDayOfMonth, getDayCountOfMonth, getWeekNumber, getStartDateOfMonth, nextDate, isDate, clearTime as _clearTime} from '../util';
   import Locale from 'element-ui/src/mixins/locale';
   import Locale from 'element-ui/src/mixins/locale';
   import { arrayFindIndex, arrayFind, coerceTruthyValueToArray } from 'element-ui/src/utils/util';
   import { arrayFindIndex, arrayFind, coerceTruthyValueToArray } from 'element-ui/src/utils/util';
 
 
   const WEEKS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
   const WEEKS = ['sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'];
-  const clearHours = function(time) {
-    const cloneDate = new Date(time);
-    cloneDate.setHours(0, 0, 0, 0);
-    return cloneDate.getTime();
+  const getDateTimestamp = function(time) {
+    if (typeof time === 'number' || typeof time === 'string') {
+      return _clearTime(new Date(time)).getTime();
+    } else if (time instanceof Date) {
+      return _clearTime(time).getTime();
+    } else {
+      return NaN;
+    }
   };
   };
 
 
   // remove the first element that satisfies `pred` from arr
   // remove the first element that satisfies `pred` from arr
@@ -92,9 +95,7 @@
         default() {
         default() {
           return {
           return {
             endDate: null,
             endDate: null,
-            selecting: false,
-            row: null,
-            column: null
+            selecting: false
           };
           };
         }
         }
       }
       }
@@ -141,7 +142,7 @@
         const startDate = this.startDate;
         const startDate = this.startDate;
         const disabledDate = this.disabledDate;
         const disabledDate = this.disabledDate;
         const selectedDate = this.selectionMode === 'dates' ? coerceTruthyValueToArray(this.value) : [];
         const selectedDate = this.selectionMode === 'dates' ? coerceTruthyValueToArray(this.value) : [];
-        const now = clearHours(new Date());
+        const now = getDateTimestamp(new Date());
 
 
         for (let i = 0; i < 6; i++) {
         for (let i = 0; i < 6; i++) {
           const row = rows[i];
           const row = rows[i];
@@ -162,9 +163,9 @@
 
 
             const index = i * 7 + j;
             const index = i * 7 + j;
             const time = nextDate(startDate, index - offset).getTime();
             const time = nextDate(startDate, index - offset).getTime();
-            cell.inRange = time >= clearHours(this.minDate) && time <= clearHours(this.maxDate);
-            cell.start = this.minDate && time === clearHours(this.minDate);
-            cell.end = this.maxDate && time === clearHours(this.maxDate);
+            cell.inRange = time >= getDateTimestamp(this.minDate) && time <= getDateTimestamp(this.maxDate);
+            cell.start = this.minDate && time === getDateTimestamp(this.minDate);
+            cell.end = this.maxDate && time === getDateTimestamp(this.maxDate);
             const isToday = time === now;
             const isToday = time === now;
 
 
             if (isToday) {
             if (isToday) {
@@ -220,32 +221,27 @@
 
 
     watch: {
     watch: {
       'rangeState.endDate'(newVal) {
       'rangeState.endDate'(newVal) {
-        this.markRange(newVal);
+        this.markRange(this.minDate, newVal);
       },
       },
 
 
       minDate(newVal, oldVal) {
       minDate(newVal, oldVal) {
-        if (newVal && !oldVal) {
-          this.rangeState.selecting = true;
-          this.markRange(newVal);
-        } else if (!newVal) {
-          this.rangeState.selecting = false;
-          this.markRange(newVal);
-        } else {
-          this.markRange();
+        if (getDateTimestamp(newVal) !== getDateTimestamp(oldVal)) {
+          this.markRange(this.minDate, this.maxDate);
         }
         }
       },
       },
 
 
       maxDate(newVal, oldVal) {
       maxDate(newVal, oldVal) {
-        if (newVal && !oldVal) {
-          this.rangeState.selecting = false;
-          this.markRange(newVal);
+        if (getDateTimestamp(newVal) !== getDateTimestamp(oldVal)) {
+          this.markRange(this.minDate, this.maxDate);
         }
         }
       }
       }
     },
     },
 
 
     data() {
     data() {
       return {
       return {
-        tableRows: [ [], [], [], [], [], [] ]
+        tableRows: [ [], [], [], [], [], [] ],
+        lastRow: null,
+        lastColumn: null
       };
       };
     },
     },
 
 
@@ -329,14 +325,13 @@
         return year === valueYear && getWeekNumber(newDate) === getWeekNumber(this.value);
         return year === valueYear && getWeekNumber(newDate) === getWeekNumber(this.value);
       },
       },
 
 
-      markRange(maxDate) {
-        const startDate = this.startDate;
-        if (!maxDate) {
-          maxDate = this.maxDate;
-        }
+      markRange(minDate, maxDate) {
+        minDate = getDateTimestamp(minDate);
+        maxDate = getDateTimestamp(maxDate) || minDate;
+        [minDate, maxDate] = [Math.min(minDate, maxDate), Math.max(minDate, maxDate)];
 
 
+        const startDate = this.startDate;
         const rows = this.rows;
         const rows = this.rows;
-        const minDate = this.minDate;
         for (let i = 0, k = rows.length; i < k; i++) {
         for (let i = 0, k = rows.length; i < k; i++) {
           const row = rows[i];
           const row = rows[i];
           for (let j = 0, l = row.length; j < l; j++) {
           for (let j = 0, l = row.length; j < l; j++) {
@@ -346,15 +341,9 @@
             const index = i * 7 + j + (this.showWeekNumber ? -1 : 0);
             const index = i * 7 + j + (this.showWeekNumber ? -1 : 0);
             const time = nextDate(startDate, index - this.offsetDay).getTime();
             const time = nextDate(startDate, index - this.offsetDay).getTime();
 
 
-            if (maxDate && maxDate < minDate) {
-              cell.inRange = minDate && time >= clearHours(maxDate) && time <= clearHours(minDate);
-              cell.start = maxDate && time === clearHours(maxDate.getTime());
-              cell.end = minDate && time === clearHours(minDate.getTime());
-            } else {
-              cell.inRange = minDate && time >= clearHours(minDate) && time <= clearHours(maxDate);
-              cell.start = minDate && time === clearHours(minDate.getTime());
-              cell.end = maxDate && time === clearHours(maxDate.getTime());
-            }
+            cell.inRange = minDate && time >= minDate && time <= maxDate;
+            cell.start = minDate && time === minDate;
+            cell.end = maxDate && time === maxDate;
           }
           }
         }
         }
       },
       },
@@ -362,12 +351,6 @@
       handleMouseMove(event) {
       handleMouseMove(event) {
         if (!this.rangeState.selecting) return;
         if (!this.rangeState.selecting) return;
 
 
-        this.$emit('changerange', {
-          minDate: this.minDate,
-          maxDate: this.maxDate,
-          rangeState: this.rangeState
-        });
-
         let target = event.target;
         let target = event.target;
         if (target.tagName === 'SPAN') {
         if (target.tagName === 'SPAN') {
           target = target.parentNode.parentNode;
           target = target.parentNode.parentNode;
@@ -377,15 +360,25 @@
         }
         }
         if (target.tagName !== 'TD') return;
         if (target.tagName !== 'TD') return;
 
 
-        const column = target.cellIndex;
         const row = target.parentNode.rowIndex - 1;
         const row = target.parentNode.rowIndex - 1;
-        const { row: oldRow, column: oldColumn } = this.rangeState;
-
-        if (oldRow !== row || oldColumn !== column) {
-          this.rangeState.row = row;
-          this.rangeState.column = column;
+        const column = target.cellIndex;
 
 
-          this.rangeState.endDate = this.getDateOfCell(row, column);
+        // 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.getDateOfCell(row, column)
+            }
+          });
         }
         }
       },
       },
 
 
@@ -399,86 +392,31 @@
         }
         }
 
 
         if (target.tagName !== 'TD') return;
         if (target.tagName !== 'TD') return;
-        if (hasClass(target, 'disabled') || hasClass(target, 'week')) return;
-
-        const selectionMode = this.selectionMode;
-
-        if (selectionMode === 'week') {
-          target = target.parentNode.cells[1];
-        }
 
 
-        let year = Number(this.year);
-        let month = Number(this.month);
-
-        const cellIndex = target.cellIndex;
-        const rowIndex = target.parentNode.rowIndex;
-
-        const cell = this.rows[rowIndex - 1][cellIndex];
-        const text = cell.text;
-        const className = target.className;
-
-        const newDate = new Date(year, month, 1);
+        const row = target.parentNode.rowIndex - 1;
+        const column = this.selectionMode === 'week' ? 1 : target.cellIndex;
+        const cell = this.rows[row][column];
 
 
-        if (className.indexOf('prev') !== -1) {
-          if (month === 0) {
-            year = year - 1;
-            month = 11;
-          } else {
-            month = month - 1;
-          }
-          newDate.setFullYear(year);
-          newDate.setMonth(month);
-        } else if (className.indexOf('next') !== -1) {
-          if (month === 11) {
-            year = year + 1;
-            month = 0;
-          } else {
-            month = month + 1;
-          }
-          newDate.setFullYear(year);
-          newDate.setMonth(month);
-        }
+        if (cell.disabled || cell.type === 'week') return;
 
 
-        newDate.setDate(parseInt(text, 10));
+        const newDate = this.getDateOfCell(row, column);
 
 
         if (this.selectionMode === 'range') {
         if (this.selectionMode === 'range') {
-          if (this.minDate && this.maxDate) {
-            const minDate = new Date(newDate.getTime());
-            const maxDate = null;
-
-            this.$emit('pick', { minDate, maxDate }, false);
+          if (!this.rangeState.selecting) {
+            this.$emit('pick', {minDate: newDate, maxDate: null});
             this.rangeState.selecting = true;
             this.rangeState.selecting = true;
-            this.markRange(this.minDate);
-            this.$nextTick(() => {
-              this.handleMouseMove(event);
-            });
-          } else if (this.minDate && !this.maxDate) {
+          } else {
             if (newDate >= this.minDate) {
             if (newDate >= this.minDate) {
-              const maxDate = new Date(newDate.getTime());
-              this.rangeState.selecting = false;
-
-              this.$emit('pick', {
-                minDate: this.minDate,
-                maxDate
-              });
+              this.$emit('pick', {minDate: this.minDate, maxDate: newDate});
             } else {
             } else {
-              const minDate = new Date(newDate.getTime());
-              this.rangeState.selecting = false;
-
-              this.$emit('pick', { minDate, maxDate: this.minDate });
+              this.$emit('pick', {minDate: newDate, maxDate: this.minDate});
             }
             }
-          } else if (!this.minDate) {
-            const minDate = new Date(newDate.getTime());
-
-            this.$emit('pick', { minDate, maxDate: this.maxDate }, false);
-            this.rangeState.selecting = true;
-            this.markRange(this.minDate);
+            this.rangeState.selecting = false;
           }
           }
-        } else if (selectionMode === 'day') {
+        } else if (this.selectionMode === 'day') {
           this.$emit('pick', newDate);
           this.$emit('pick', newDate);
-        } else if (selectionMode === 'week') {
+        } else if (this.selectionMode === 'week') {
           const weekNumber = getWeekNumber(newDate);
           const weekNumber = getWeekNumber(newDate);
-
           const value = newDate.getFullYear() + 'w' + weekNumber;
           const value = newDate.getFullYear() + 'w' + weekNumber;
           this.$emit('pick', {
           this.$emit('pick', {
             year: newDate.getFullYear(),
             year: newDate.getFullYear(),
@@ -486,7 +424,7 @@
             value: value,
             value: value,
             date: newDate
             date: newDate
           });
           });
-        } else if (selectionMode === 'dates') {
+        } else if (this.selectionMode === 'dates') {
           const value = this.value || [];
           const value = this.value || [];
           const newValue = cell.selected
           const newValue = cell.selected
             ? removeFromArray(value, date => date.getTime() === newDate.getTime())
             ? removeFromArray(value, date => date.getTime() === newDate.getTime())