From 3cf7cfd1de4022361d8e0bae254f9f6e23cc6403 Mon Sep 17 00:00:00 2001
From: 梁灏 <admin@aresn.com>
Date: Mon, 19 Dec 2016 14:44:07 +0800
Subject: [PATCH] update DatePicker

---
 src/components/date-picker/base/date-table.vue  |  67 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
 src/components/date-picker/panel/date-range.vue | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 src/components/date-picker/panel/date.vue       |  20 ++++----------------
 src/components/date-picker/panel/mixin.js       |  18 ++++++++++++++++++
 src/styles/components/date-picker.less          |  40 +++++++++++++++++++++++++++++++++++++---
 test/routers/date.vue                           |  48 +++++++++++++++++++++++++++++++++++++++++++-----
 6 files changed, 319 insertions(+), 34 deletions(-)
 create mode 100644 src/components/date-picker/panel/mixin.js

diff --git a/src/components/date-picker/base/date-table.vue b/src/components/date-picker/base/date-table.vue
index ee83b6b..bfa9d5a 100644
--- a/src/components/date-picker/base/date-table.vue
+++ b/src/components/date-picker/base/date-table.vue
@@ -36,9 +36,7 @@
                 default () {
                     return {
                         endDate: null,
-                        selecting: false,
-                        row: null,
-                        column: null
+                        selecting: false
                     };
                 }
             },
@@ -64,6 +62,8 @@
                 day = (day === 0 ? 7 : day);
                 const today = clearHours(new Date());    // timestamp of today
                 const selectDay = clearHours(new Date(this.value));    // timestamp of selected day
+                const minDay = clearHours(new Date(this.minDate));
+                const maxDay = clearHours(new Date(this.maxDate));
 
                 const dateCountOfMonth = getDayCountOfMonth(date.getFullYear(), date.getMonth());
                 const dateCountOfLastMonth = getDayCountOfMonth(date.getFullYear(), (date.getMonth() === 0 ? 11 : date.getMonth() - 1));
@@ -75,7 +75,10 @@
                     text: '',
                     type: '',
                     selected: false,
-                    disabled: false
+                    disabled: false,
+                    range: false,
+                    start: false,
+                    end: false
                 };
                 if (day !== 7) {
                     for (let i = 0; i < day; i++) {
@@ -102,6 +105,10 @@
                     cell.text = i;
                     cell.selected = time === selectDay;
                     cell.disabled = typeof disabledDate === 'function' && disabledDate(new Date(time));
+                    cell.range = time >= minDay && time <= maxDay;
+                    cell.start = this.minDate && time === minDay;
+                    cell.end = this.maxDate && time === maxDay;
+
                     cells.push(cell);
                 }
 
@@ -156,6 +163,30 @@
 
                     if (this.selectionMode === 'range') {
                         // todo
+                        if (this.minDate && this.maxDate) {
+                            const minDate = new Date(newDate.getTime());
+                            const maxDate = null;
+                            this.$emit('on-pick', {minDate, maxDate}, false);
+                            this.rangeState.selecting = true;
+                            this.markRange(this.minDate);
+                        } else if (this.minDate && !this.maxDate) {
+                            if (newDate >= this.minDate) {
+                                const maxDate = new Date(newDate.getTime());
+                                this.rangeState.selecting = false;
+
+                                this.$emit('on-pick', {minDate: this.minDate, maxDate});
+                            } else {
+                                const minDate = new Date(newDate.getTime());
+
+                                this.$emit('on-pick', {minDate, maxDate: this.maxDate}, false);
+                            }
+                        } else if (!this.minDate) {
+                            const minDate = new Date(newDate.getTime());
+
+                            this.$emit('on-pick', {minDate, maxDate: this.maxDate}, false);
+                            this.rangeState.selecting = true;
+                            this.markRange(this.minDate);
+                        }
                     } else {
                         this.$emit('on-pick', newDate);
                     }
@@ -164,15 +195,39 @@
             handleMouseMove () {
 
             },
+            markRange (maxDate) {
+                const startDate = this.startDate;
+                if (!maxDate) {
+                    maxDate = this.maxDate;
+                }
+
+                const rows = this.rows;
+                const minDate = this.minDate;
+                for (var i = 0, k = rows.length; i < k; i++) {
+                    const row = rows[i];
+                    for (var j = 0, l = row.length; j < l; j++) {
+                        if (this.showWeekNumber && j === 0) continue;
+
+                        const cell = row[j];
+                        const index = i * 7 + j + (this.showWeekNumber ? -1 : 0);
+                        const time = startDate.getTime() + DAY_DURATION * index;
+
+                        cell.inRange = minDate && time >= clearHours(minDate) && time <= clearHours(maxDate);
+                        cell.start = minDate && time === clearHours(minDate.getTime());
+                        cell.end = maxDate && time === clearHours(maxDate.getTime());
+                    }
+                }
+            },
             getCellCls (cell) {
                 return [
                     `${prefixCls}-cell`,
                     {
-                        [`${prefixCls}-cell-selected`]: cell.selected,
+                        [`${prefixCls}-cell-selected`]: cell.selected || cell.start || cell.end,
                         [`${prefixCls}-cell-disabled`]: cell.disabled,
                         [`${prefixCls}-cell-today`]: cell.type === 'today',
                         [`${prefixCls}-cell-prev-month`]: cell.type === 'prev-month',
-                        [`${prefixCls}-cell-next-month`]: cell.type === 'next-month'
+                        [`${prefixCls}-cell-next-month`]: cell.type === 'next-month',
+                        [`${prefixCls}-cell-range`]: cell.range && !cell.start && !cell.end
                     }
                 ]
             },
diff --git a/src/components/date-picker/panel/date-range.vue b/src/components/date-picker/panel/date-range.vue
index a65ca94..3ef4899 100644
--- a/src/components/date-picker/panel/date-range.vue
+++ b/src/components/date-picker/panel/date-range.vue
@@ -1,27 +1,179 @@
 <template>
     <div :class="classes">
-
+        <div :class="[prefixCls + '-sidebar']" v-if="shortcuts.length">
+            <div
+                :class="[prefixCls + '-shortcut']"
+                v-for="shortcut in shortcuts"
+                @click="handleShortcutClick(shortcut)">{{ shortcut.text }}</div>
+        </div>
+        <div :class="[prefixCls + '-body']">
+            <div :class="[prefixCls + '-content', prefixCls + '-content-left']">
+                <div :class="[datePrefixCls + '-header']" v-show="currentView !== 'time'">
+                    <span
+                        :class="iconBtnCls('prev', '-double')"
+                        @click="prevYear"><Icon type="ios-arrow-left"></Icon></span>
+                    <span
+                        :class="iconBtnCls('prev')"
+                        @click="prevMonth"
+                        v-show="currentView === 'date'"><Icon type="ios-arrow-left"></Icon></span>
+                    <span
+                        :class="[datePrefixCls + '-header-label']"
+                        @click="showYearPicker">{{ leftYear }} 年</span>
+                    <span
+                        :class="[datePrefixCls + '-header-label']"
+                        @click="showMonthPicker"
+                        v-show="currentView === 'date'">{{ leftMonth + 1 }} 月</span>
+                </div>
+                <date-table
+                    v-show="currentView === 'date'"
+                    :year="leftYear"
+                    :month="leftMonth"
+                    :date="date"
+                    :min-date="minDate"
+                    :max-date="maxDate"
+                    :range-state="rangeState"
+                    :selection-mode="selectionMode"
+                    :disabled-date="disabledDate"
+                    @on-pick="handleDatePick"></date-table>
+            </div>
+            <div :class="[prefixCls + '-content', prefixCls + '-content-right']">
+                <div :class="[datePrefixCls + '-header']" v-show="currentView !== 'time'">
+                    <span
+                        :class="[datePrefixCls + '-header-label']"
+                        @click="showYearPicker">{{ rightYear }} 年</span>
+                    <span
+                        :class="[datePrefixCls + '-header-label']"
+                        @click="showMonthPicker"
+                        v-show="currentView === 'date'">{{ rightMonth + 1 }} 月</span>
+                    <span
+                        :class="iconBtnCls('next', '-double')"
+                        @click="nextYear"><Icon type="ios-arrow-right"></Icon></span>
+                    <span
+                        :class="iconBtnCls('next')"
+                        @click="nextMonth"
+                        v-show="currentView === 'date'"><Icon type="ios-arrow-right"></Icon></span>
+                </div>
+                <date-table
+                    v-show="currentView === 'date'"
+                    :year="rightYear"
+                    :month="rightMonth"
+                    :date="rightDate"
+                    :min-date="minDate"
+                    :max-date="maxDate"
+                    :range-state="rangeState"
+                    :selection-mode="selectionMode"
+                    :disabled-date="disabledDate"
+                    @on-pick="handleDatePick"></date-table>
+            </div>
+        </div>
     </div>
 </template>
 <script>
+    import Icon from '../../icon/icon.vue';
+    import DateTable from '../base/date-table.vue';
+    import { toDate } from '../util';
+
+    import Mixin from './mixin';
+
     const prefixCls = 'ivu-picker-panel';
     const datePrefixCls = 'ivu-date-picker';
 
     export default {
-        props: {},
+        mixins: [Mixin],
+        components: { Icon, DateTable },
         data () {
-            return {}
+            return {
+                prefixCls: prefixCls,
+                datePrefixCls: datePrefixCls,
+                shortcuts: [],
+                date: new Date(),
+                value: '',
+                minDate: '',
+                maxDate: '',
+                rangeState: {
+                    endDate: null,
+                    selecting: false
+                },
+                showTime: false,
+                disabledDate: '',
+                currentView: 'date',
+                selectionMode: 'range'
+            }
         },
         computed: {
             classes () {
                 return [
                     `${prefixCls}-body-wrapper`,
+                    `${datePrefixCls}-with-range`,
                     {
                         [`${prefixCls}-with-sidebar`]: this.shortcuts.length
                     }
                 ]
+            },
+            leftYear() {
+                return this.date.getFullYear();
+            },
+            leftMonth() {
+                return this.date.getMonth();
+            },
+            rightYear() {
+                return this.rightDate.getFullYear();
+            },
+            rightMonth() {
+                return this.rightDate.getMonth();
+            },
+            rightDate() {
+                const newDate = new Date(this.date);
+                const month = newDate.getMonth();
+                newDate.setDate(1);
+
+                if (month === 11) {
+                    newDate.setFullYear(newDate.getFullYear() + 1);
+                    newDate.setMonth(0);
+                } else {
+                    newDate.setMonth(month + 1);
+                }
+                return newDate;
+            }
+        },
+        watch: {
+            value(newVal) {
+                if (!newVal) {
+                    this.minDate = null;
+                    this.maxDate = null;
+                } else if (Array.isArray(newVal)) {
+                    this.minDate = newVal[0] ? toDate(newVal[0]) : null;
+                    this.maxDate = newVal[1] ? toDate(newVal[1]) : null;
+                    if (this.minDate) this.date = new Date(this.minDate);
+//                    this.handleConfirm(true);    // todo 稍后测试
+                }
             }
         },
-        methods: {}
+        methods: {
+            prevYear () {
+
+            },
+            nextYear () {
+
+            },
+            prevMonth () {
+
+            },
+            nextMonth () {
+
+            },
+            showYearPicker () {
+
+            },
+            showMonthPicker () {
+
+            },
+            handleDatePick () {
+
+            },
+            handleConfirm(visible) {
+                this.$emit('on-pick', [this.minDate, this.maxDate], visible);
+            }
+        }
     }
 </script>
\ No newline at end of file
diff --git a/src/components/date-picker/panel/date.vue b/src/components/date-picker/panel/date.vue
index 5bdfcf3..31ffaf5 100644
--- a/src/components/date-picker/panel/date.vue
+++ b/src/components/date-picker/panel/date.vue
@@ -1,6 +1,6 @@
 <template>
     <div :class="classes">
-        <div :class="[prefixCls + '-sidebar']" v-if="shortcuts">
+        <div :class="[prefixCls + '-sidebar']" v-if="shortcuts.length">
             <div
                 :class="[prefixCls + '-shortcut']"
                 v-for="shortcut in shortcuts"
@@ -67,10 +67,13 @@
     import MonthTable from '../base/month-table.vue';
     import { formatDate, parseDate } from '../util';
 
+    import Mixin from './mixin';
+
     const prefixCls = 'ivu-picker-panel';
     const datePrefixCls = 'ivu-date-picker';
 
     export default {
+        mixins: [Mixin],
         components: { Icon, DateTable, YearTable, MonthTable },
         data () {
             return {
@@ -114,13 +117,9 @@
                 if (!newVal) return;
                 newVal = new Date(newVal);
                 if (!isNaN(newVal)) {
-                    // todo
-//                    if (typeof this.disabledDate === 'function' && this.disabledDate(new Date(newVal))) return;
-
                     this.date = newVal;
                     this.year = newVal.getFullYear();
                     this.month = newVal.getMonth();
-//                    this.$emit('on-pick', newVal, true);
                 }
             }
         },
@@ -129,17 +128,6 @@
                 this.date = new Date();
                 this.$emit('on-pick', '');
             },
-            handleShortcutClick (shortcut) {
-                if (shortcut.value) this.$emit('on-pick', shortcut.value());
-                if (shortcut.onClick) shortcut.onClick(this);
-            },
-            iconBtnCls (direction, type = '') {
-                return [
-                    `${prefixCls}-icon-btn`,
-                    `${datePrefixCls}-${direction}-btn`,
-                    `${datePrefixCls}-${direction}-btn-arrow${type}`,
-                ]
-            },
             resetDate () {
                 this.date = new Date(this.date);
             },
diff --git a/src/components/date-picker/panel/mixin.js b/src/components/date-picker/panel/mixin.js
new file mode 100644
index 0000000..1e0e353
--- /dev/null
+++ b/src/components/date-picker/panel/mixin.js
@@ -0,0 +1,18 @@
+const prefixCls = 'ivu-picker-panel';
+const datePrefixCls = 'ivu-date-picker';
+
+export default {
+    methods: {
+        iconBtnCls (direction, type = '') {
+            return [
+                `${prefixCls}-icon-btn`,
+                `${datePrefixCls}-${direction}-btn`,
+                `${datePrefixCls}-${direction}-btn-arrow${type}`,
+            ]
+        },
+        handleShortcutClick (shortcut) {
+            if (shortcut.value) this.$emit('on-pick', shortcut.value());
+            if (shortcut.onClick) shortcut.onClick(this);
+        }
+    }
+}
\ No newline at end of file
diff --git a/src/styles/components/date-picker.less b/src/styles/components/date-picker.less
index 1270c03..66dba3b 100644
--- a/src/styles/components/date-picker.less
+++ b/src/styles/components/date-picker.less
@@ -1,6 +1,8 @@
 @date-picker-prefix-cls: ~"@{css-prefix}date-picker";
 @picker-prefix-cls: ~"@{css-prefix}picker";
 
+@date-picker-cells-width: 196px;
+
 .@{date-picker-prefix-cls} {
     position: relative;
     .@{select-dropdown-prefix-cls} {
@@ -10,7 +12,7 @@
         max-height: none;
     }
     &-cells{
-        width: 196px;
+        width: @date-picker-cells-width;
         margin: 10px;
         span{
             display: inline-block;
@@ -81,7 +83,27 @@
                     }
                 }
             }
-            &-selected,&-selected:hover {
+            &-range{
+                position: relative;
+                em{
+                    position: relative;
+                    z-index: 1;
+                }
+                &:before{
+                    content: '';
+                    display: block;
+                    background: @date-picker-cell-hover-bg;
+                    border-radius: 0;
+                    border: 0;
+                    position: absolute;
+                    top: 2px;
+                    bottom: 2px;
+                    left: 0;
+                    right: 0;
+                }
+            }
+            &-selected,&-selected:hover
+            {
                 em{
                     background: @primary-color;
                     color: #fff;
@@ -93,7 +115,8 @@
                     color: @btn-disable-bg;
                 }
             }
-            &-today&-selected{
+            &-today&-selected
+            {
                 em{
                     &:after{
                         background: #fff;
@@ -151,6 +174,17 @@
             }
         }
     }
+
+    &-with-range{
+        .@{picker-prefix-cls}-panel{
+            &-body{
+                min-width: (@date-picker-cells-width + 20) * 2;
+            }
+            &-content{
+                float: left;
+            }
+        }
+    }
 }
 
 .@{picker-prefix-cls} {
diff --git a/test/routers/date.vue b/test/routers/date.vue
index 518b4ad..88e79f0 100644
--- a/test/routers/date.vue
+++ b/test/routers/date.vue
@@ -1,5 +1,5 @@
 <template>
-    <div style="margin: 150px">
+    <div style="margin: 50px">
         <br>
         <row>
             <i-col span="8">
@@ -11,11 +11,16 @@
                         :options="options"
                         @on-change="change"
                         :format="format"
-                        :editable="false"
                         @on-open-change="change2"></date-picker>
             </i-col>
             <i-col span="8">
-                <date-picker type="daterange" style="width:200px" placeholder="请选择日期" :value.sync="value2" :options="options2"></date-picker>
+                <date-picker
+                        type="daterange"
+                        style="width:200px"
+                        placeholder="请选择日期"
+                        :value.sync="value2"
+                        align="right"
+                        :options="options2"></date-picker>
             </i-col>
         </row>
     </div>
@@ -26,9 +31,42 @@
             return {
 //                value: new Date(),
                 value: '2016-12-25',
-                value2: '',
+                value2: ['2016-12-17', '2017-01-05'],
                 options2: {
-
+                    shortcuts: [
+                        {
+                            text: '今天',
+                            value () {
+//                                return new Date();
+                                return '1/2/19'
+                            },
+                            onClick (picker) {
+                                console.log('点击了今天');
+                            }
+                        },
+                        {
+                            text: '昨天',
+                            value () {
+                                const date = new Date();
+                                date.setTime(date.getTime() - 3600 * 1000 * 24);
+                                return date;
+                            },
+                            onClick () {
+                                console.log('点击了昨天');
+                            }
+                        },
+                        {
+                            text: '最近三个月',
+                            value () {
+                                const date = new Date();
+                                date.setTime(date.getTime() - 3600 * 1000 * 24 * 7);
+                                return date;
+                            },
+                            onClick () {
+                                console.log('点击了一周前');
+                            }
+                        }
+                    ]
                 },
                 options: {
                     disabledDate(time) {
--
libgit2 0.21.4