Commit c46f385a83c9bca751bed6a7d764d4a88dee3573
1 parent
10f622ac
update DatePicker
update DatePicker
Showing
9 changed files
with
565 additions
and
56 deletions
Show diff stats
src/components/date-picker/base/date-table.vue
... | ... | @@ -6,7 +6,7 @@ |
6 | 6 | <div :class="[prefixCls + '-header']"> |
7 | 7 | <span>日</span><span>一</span><span>二</span><span>三</span><span>四</span><span>五</span><span>六</span> |
8 | 8 | </div> |
9 | - <span :class="getCellCls(cell)" v-for="cell in cells"><em>{{ cell.text }}</em></span> | |
9 | + <span :class="getCellCls(cell)" v-for="cell in cells"><em :index="$index">{{ cell.text }}</em></span> | |
10 | 10 | </div> |
11 | 11 | </template> |
12 | 12 | <script> |
... | ... | @@ -126,8 +126,40 @@ |
126 | 126 | } |
127 | 127 | }, |
128 | 128 | methods: { |
129 | - handleClick () { | |
129 | + handleClick (event) { | |
130 | + const target = event.target; | |
131 | + if (target.tagName === 'EM') { | |
132 | + const cell = this.cells[parseInt(event.target.getAttribute('index'))]; | |
133 | + if (cell.disabled) return; | |
130 | 134 | |
135 | + let year = this.year; | |
136 | + let month = this.month; | |
137 | + let day = cell.text; | |
138 | + | |
139 | + if (cell.type === 'prev-month') { | |
140 | + if (month === 0) { | |
141 | + month = 11; | |
142 | + year--; | |
143 | + } else { | |
144 | + month--; | |
145 | + } | |
146 | + } else if (cell.type === 'next-month') { | |
147 | + if (month === 11) { | |
148 | + month = 0; | |
149 | + year++; | |
150 | + } else { | |
151 | + month++; | |
152 | + } | |
153 | + } | |
154 | + | |
155 | + const newDate = new Date(year, month, day); | |
156 | + | |
157 | + if (this.selectionMode === 'range') { | |
158 | + // todo | |
159 | + } else { | |
160 | + this.$emit('on-pick', newDate); | |
161 | + } | |
162 | + } | |
131 | 163 | }, |
132 | 164 | handleMouseMove () { |
133 | 165 | ... | ... |
src/components/date-picker/base/month-table.vue
1 | 1 | <template> |
2 | - <div>month</div> | |
2 | + <div :class="classes" @click="handleClick"> | |
3 | + <span :class="getCellCls(cell)" v-for="cell in cells"><em :index="$index">{{ cell.text }}月</em></span> | |
4 | + </div> | |
3 | 5 | </template> |
4 | 6 | <script> |
7 | + import { deepCopy } from '../../../utils/assist'; | |
8 | + const prefixCls = 'ivu-date-picker-cells'; | |
9 | + | |
5 | 10 | export default { |
6 | -// props: {}, | |
7 | - data () { | |
8 | - return {} | |
11 | + props: { | |
12 | + date: {}, | |
13 | + month: { | |
14 | + type: Number | |
15 | + }, | |
16 | + disabledDate: {} | |
9 | 17 | }, |
10 | - computed: {}, | |
11 | - methods: {} | |
18 | + computed: { | |
19 | + classes () { | |
20 | + return [ | |
21 | + `${prefixCls}`, | |
22 | + `${prefixCls}-month` | |
23 | + ] | |
24 | + }, | |
25 | + cells () { | |
26 | + let cells = []; | |
27 | + const cell_tmpl = { | |
28 | + text: '', | |
29 | + selected: false, | |
30 | + disabled: false | |
31 | + }; | |
32 | + | |
33 | + for (let i = 0; i < 12; i++) { | |
34 | + const cell = deepCopy(cell_tmpl); | |
35 | + cell.text = i + 1; | |
36 | + | |
37 | + const date = new Date(this.date); | |
38 | + date.setMonth(i); | |
39 | + cell.disabled = typeof this.disabledDate === 'function' && this.disabledDate(date); | |
40 | + | |
41 | + cell.selected = Number(this.month) === i; | |
42 | + cells.push(cell); | |
43 | + } | |
44 | + | |
45 | + return cells; | |
46 | + } | |
47 | + }, | |
48 | + methods: { | |
49 | + getCellCls (cell) { | |
50 | + return [ | |
51 | + `${prefixCls}-cell`, | |
52 | + { | |
53 | + [`${prefixCls}-cell-selected`]: cell.selected, | |
54 | + [`${prefixCls}-cell-disabled`]: cell.disabled | |
55 | + } | |
56 | + ] | |
57 | + }, | |
58 | + handleClick (event) { | |
59 | + const target = event.target; | |
60 | + if (target.tagName === 'EM') { | |
61 | + const index = parseInt(event.target.getAttribute('index')); | |
62 | + const cell = this.cells[index]; | |
63 | + if (cell.disabled) return; | |
64 | + | |
65 | + this.$emit('on-pick', index); | |
66 | + } | |
67 | + } | |
68 | + } | |
12 | 69 | } |
13 | 70 | </script> |
14 | 71 | \ No newline at end of file | ... | ... |
src/components/date-picker/base/year-table.vue
1 | 1 | <template> |
2 | - <div>year</div> | |
2 | + <div :class="classes" @click="handleClick"> | |
3 | + <span :class="getCellCls(cell)" v-for="cell in cells"><em :index="$index">{{ cell.text }}</em></span> | |
4 | + </div> | |
3 | 5 | </template> |
4 | 6 | <script> |
7 | + import { deepCopy } from '../../../utils/assist'; | |
8 | + const prefixCls = 'ivu-date-picker-cells'; | |
9 | + | |
5 | 10 | export default { |
6 | -// props: {}, | |
7 | - data () { | |
8 | - return {} | |
11 | + props: { | |
12 | + date: {}, | |
13 | + year: {}, | |
14 | + disabledDate: {} | |
9 | 15 | }, |
10 | - computed: {}, | |
11 | - methods: {} | |
16 | + computed: { | |
17 | + classes () { | |
18 | + return [ | |
19 | + `${prefixCls}`, | |
20 | + `${prefixCls}-year` | |
21 | + ] | |
22 | + }, | |
23 | + startYear() { | |
24 | + return Math.floor(this.year / 10) * 10; | |
25 | + }, | |
26 | + cells () { | |
27 | + let cells = []; | |
28 | + const cell_tmpl = { | |
29 | + text: '', | |
30 | + selected: false, | |
31 | + disabled: false | |
32 | + }; | |
33 | + | |
34 | + for (let i = 0; i < 10; i++) { | |
35 | + const cell = deepCopy(cell_tmpl); | |
36 | + cell.text = this.startYear + i; | |
37 | + | |
38 | + const date = new Date(this.date); | |
39 | + date.setFullYear(cell.text); | |
40 | + cell.disabled = typeof this.disabledDate === 'function' && this.disabledDate(date); | |
41 | + | |
42 | + cell.selected = Number(this.year) === cell.text; | |
43 | + cells.push(cell); | |
44 | + } | |
45 | + | |
46 | + return cells; | |
47 | + } | |
48 | + }, | |
49 | + methods: { | |
50 | + getCellCls (cell) { | |
51 | + return [ | |
52 | + `${prefixCls}-cell`, | |
53 | + { | |
54 | + [`${prefixCls}-cell-selected`]: cell.selected, | |
55 | + [`${prefixCls}-cell-disabled`]: cell.disabled | |
56 | + } | |
57 | + ] | |
58 | + }, | |
59 | + nextTenYear() { | |
60 | + this.$emit('on-pick', Number(this.year) + 10, false); | |
61 | + }, | |
62 | + prevTenYear() { | |
63 | + this.$emit('on-pick', Number(this.year) - 10, false); | |
64 | + }, | |
65 | + handleClick (event) { | |
66 | + const target = event.target; | |
67 | + if (target.tagName === 'EM') { | |
68 | + const cell = this.cells[parseInt(event.target.getAttribute('index'))]; | |
69 | + if (cell.disabled) return; | |
70 | + this.$emit('on-pick', cell.text); | |
71 | + } | |
72 | + } | |
73 | + } | |
12 | 74 | } |
13 | 75 | </script> |
14 | 76 | \ No newline at end of file | ... | ... |
src/components/date-picker/panel/date.vue
... | ... | @@ -10,25 +10,25 @@ |
10 | 10 | <div :class="[datePrefixCls + '-header']" v-show="currentView !== 'time'"> |
11 | 11 | <span |
12 | 12 | :class="iconBtnCls('prev', '-double')" |
13 | - @click="prevYear"></span> | |
13 | + @click="prevYear"><Icon type="ios-arrow-left"></Icon></span> | |
14 | 14 | <span |
15 | 15 | :class="iconBtnCls('prev')" |
16 | 16 | @click="prevMonth" |
17 | - v-show="currentView === 'date'"></span> | |
17 | + v-show="currentView === 'date'"><Icon type="ios-arrow-left"></Icon></span> | |
18 | 18 | <span |
19 | 19 | :class="[datePrefixCls + '-header-label']" |
20 | - @click="showYearPicker">{{ }}</span> | |
20 | + @click="showYearPicker">{{ yearLabel }}</span> | |
21 | 21 | <span |
22 | 22 | :class="[datePrefixCls + '-header-label']" |
23 | 23 | @click="showMonthPicker" |
24 | - v-show="currentView === 'date'">{{ }}</span> | |
24 | + v-show="currentView === 'date'">{{ month + 1 + '月' }}</span> | |
25 | + <span | |
26 | + :class="iconBtnCls('next', '-double')" | |
27 | + @click="nextYear"><Icon type="ios-arrow-right"></Icon></span> | |
25 | 28 | <span |
26 | 29 | :class="iconBtnCls('next')" |
27 | 30 | @click="nextMonth" |
28 | - v-show="currentView === 'date'"></span> | |
29 | - <span | |
30 | - :class="iconBtnCls('next', '-double')" | |
31 | - @click="nextYear"></span> | |
31 | + v-show="currentView === 'date'"><Icon type="ios-arrow-right"></Icon></span> | |
32 | 32 | </div> |
33 | 33 | <div :class="[prefixCls + '-content']"> |
34 | 34 | <date-table |
... | ... | @@ -39,25 +39,38 @@ |
39 | 39 | :value="value" |
40 | 40 | :week="week" |
41 | 41 | :selection-mode="selectionMode" |
42 | - :disabled-date="disabledDate"></date-table> | |
42 | + :disabled-date="disabledDate" | |
43 | + @on-pick="handleDatePick"></date-table> | |
43 | 44 | <year-table |
44 | - v-show="currentView === 'year'"></year-table> | |
45 | + v-ref:year-table | |
46 | + v-show="currentView === 'year'" | |
47 | + :year="year" | |
48 | + :date="date" | |
49 | + :disabled-date="disabledDate" | |
50 | + @on-pick="handleYearPick"></year-table> | |
45 | 51 | <month-table |
46 | - v-show="currentView === 'month'"></month-table> | |
52 | + v-ref:month-table | |
53 | + v-show="currentView === 'month'" | |
54 | + :month="month" | |
55 | + :date="date" | |
56 | + :disabled-date="disabledDate" | |
57 | + @on-pick="handleMonthPick"></month-table> | |
47 | 58 | </div> |
48 | 59 | </div> |
49 | 60 | </div> |
50 | 61 | </template> |
51 | 62 | <script> |
63 | + import Icon from '../../icon/icon.vue'; | |
52 | 64 | import DateTable from '../base/date-table.vue'; |
53 | 65 | import YearTable from '../base/year-table.vue'; |
54 | 66 | import MonthTable from '../base/month-table.vue'; |
67 | + import { formatDate, parseDate } from '../util'; | |
55 | 68 | |
56 | 69 | const prefixCls = 'ivu-picker-panel'; |
57 | 70 | const datePrefixCls = 'ivu-date-picker'; |
58 | 71 | |
59 | 72 | export default { |
60 | - components: { DateTable, YearTable, MonthTable }, | |
73 | + components: { Icon, DateTable, YearTable, MonthTable }, | |
61 | 74 | data () { |
62 | 75 | return { |
63 | 76 | prefixCls: prefixCls, |
... | ... | @@ -78,10 +91,20 @@ |
78 | 91 | } |
79 | 92 | }, |
80 | 93 | computed: { |
81 | - | |
94 | + yearLabel () { | |
95 | + const year = this.year; | |
96 | + if (!year) return ''; | |
97 | + if (this.currentView === 'year') { | |
98 | + const startYear = Math.floor(year / 10) * 10; | |
99 | + return `${startYear}年 - ${startYear + 9}年`; | |
100 | + } | |
101 | + return `${year}年` | |
102 | + } | |
82 | 103 | }, |
83 | 104 | watch: { |
84 | 105 | value (newVal) { |
106 | + console.log(12331) | |
107 | + if (!newVal) return; | |
85 | 108 | newVal = new Date(newVal); |
86 | 109 | if (!isNaN(newVal)) { |
87 | 110 | // todo |
... | ... | @@ -95,6 +118,10 @@ |
95 | 118 | } |
96 | 119 | }, |
97 | 120 | methods: { |
121 | + handleClear() { | |
122 | + this.date = new Date(); | |
123 | + this.$emit('on-pick', ''); | |
124 | + }, | |
98 | 125 | handleShortcutClick (shortcut) { |
99 | 126 | |
100 | 127 | }, |
... | ... | @@ -105,23 +132,98 @@ |
105 | 132 | `${datePrefixCls}-${direction}-btn-arrow${type}`, |
106 | 133 | ] |
107 | 134 | }, |
135 | + resetDate () { | |
136 | + this.date = new Date(this.date); | |
137 | + }, | |
108 | 138 | prevYear () { |
109 | - | |
139 | + if (this.currentView === 'year') { | |
140 | + this.$refs.yearTable.prevTenYear(); | |
141 | + } else { | |
142 | + this.year--; | |
143 | + this.date.setFullYear(this.year); | |
144 | + this.resetDate(); | |
145 | + } | |
110 | 146 | }, |
111 | 147 | nextYear () { |
112 | - | |
148 | + if (this.currentView === 'year') { | |
149 | + this.$refs.yearTable.nextTenYear(); | |
150 | + } else { | |
151 | + this.year++; | |
152 | + this.date.setFullYear(this.year); | |
153 | + this.resetDate(); | |
154 | + } | |
113 | 155 | }, |
114 | 156 | prevMonth () { |
115 | - | |
157 | + this.month--; | |
158 | + if (this.month < 0) { | |
159 | + this.month = 11; | |
160 | + this.year--; | |
161 | + } | |
116 | 162 | }, |
117 | 163 | nextMonth () { |
118 | - | |
164 | + this.month++; | |
165 | + if (this.month > 11) { | |
166 | + this.month = 0; | |
167 | + this.year++; | |
168 | + } | |
119 | 169 | }, |
120 | 170 | showYearPicker () { |
121 | - | |
171 | + this.currentView = 'year'; | |
122 | 172 | }, |
123 | 173 | showMonthPicker () { |
174 | + this.currentView = 'month'; | |
175 | + }, | |
176 | + handleYearPick(year, close = true) { | |
177 | + this.year = year; | |
178 | + if (!close) return; | |
124 | 179 | |
180 | + this.date.setFullYear(year); | |
181 | + if (this.selectionMode === 'year') { | |
182 | + this.$emit('on-pick', new Date(year)); | |
183 | + } else { | |
184 | + this.currentView = 'month'; | |
185 | + } | |
186 | + | |
187 | + this.resetDate(); | |
188 | + }, | |
189 | + handleMonthPick (month) { | |
190 | + this.month = month; | |
191 | + const selectionMode = this.selectionMode; | |
192 | + if (selectionMode !== 'month') { | |
193 | + this.date.setMonth(month); | |
194 | + this.currentView = 'date'; | |
195 | + this.resetDate(); | |
196 | + } else { | |
197 | + this.date.setMonth(month); | |
198 | + this.year && this.date.setFullYear(this.year); | |
199 | + this.resetDate(); | |
200 | + const value = new Date(this.date.getFullYear(), month, 1); | |
201 | + this.$emit('on-pick', value); | |
202 | + } | |
203 | + }, | |
204 | + handleDatePick (value) { | |
205 | + if (this.selectionMode === 'day') { | |
206 | + if (!this.showTime) { | |
207 | + this.$emit('on-pick', new Date(value.getTime())); | |
208 | + } | |
209 | + this.date.setFullYear(value.getFullYear()); | |
210 | + this.date.setMonth(value.getMonth()); | |
211 | + this.date.setDate(value.getDate()); | |
212 | + } | |
213 | + | |
214 | + this.resetDate(); | |
215 | + }, | |
216 | + resetView() { | |
217 | + if (this.selectionMode === 'month') { | |
218 | + this.currentView = 'month'; | |
219 | + } else if (this.selectionMode === 'year') { | |
220 | + this.currentView = 'year'; | |
221 | + } else { | |
222 | + this.currentView = 'date'; | |
223 | + } | |
224 | + | |
225 | + this.year = this.date.getFullYear(); | |
226 | + this.month = this.date.getMonth(); | |
125 | 227 | } |
126 | 228 | }, |
127 | 229 | compiled () { |
... | ... | @@ -133,9 +235,6 @@ |
133 | 235 | this.year = this.date.getFullYear(); |
134 | 236 | this.month = this.date.getMonth(); |
135 | 237 | } |
136 | - }, | |
137 | - beforeDestroy () { | |
138 | - | |
139 | 238 | } |
140 | 239 | } |
141 | 240 | </script> |
142 | 241 | \ No newline at end of file | ... | ... |
src/components/date-picker/picker.vue
1 | 1 | <template> |
2 | 2 | <div |
3 | 3 | :class="[prefixCls]" |
4 | - v-clickoutside="handleClose" | |
5 | - @mouseenter="handleMouseenter" | |
6 | - @mouseleave="handleMouseleave"> | |
4 | + v-clickoutside="handleClose"> | |
7 | 5 | <i-input |
8 | 6 | v-el:reference |
9 | 7 | :class="[prefixCls + '-editor']" |
... | ... | @@ -11,10 +9,13 @@ |
11 | 9 | :disabled="disabled" |
12 | 10 | :size="size" |
13 | 11 | :placeholder="placeholder" |
14 | - :value.sync="visualValue" | |
12 | + :value="visualValue" | |
13 | + @on-change="handleInputChange" | |
15 | 14 | @on-focus="handleFocus" |
16 | 15 | @on-blur="handleBlur" |
17 | 16 | @on-click="handleIconClick" |
17 | + @mouseenter="handleInputMouseenter" | |
18 | + @mouseleave="handleInputMouseleave" | |
18 | 19 | :icon="iconType"></i-input> |
19 | 20 | <Drop v-show="visible" :placement="placement" transition="slide-up" v-ref:drop> |
20 | 21 | <div v-el:picker></div> |
... | ... | @@ -27,13 +28,14 @@ |
27 | 28 | import Drop from '../../components/select/dropdown.vue'; |
28 | 29 | import clickoutside from '../../directives/clickoutside'; |
29 | 30 | import { oneOf } from '../../utils/assist'; |
30 | - import { formatDate } from './util'; | |
31 | + import { formatDate, parseDate } from './util'; | |
31 | 32 | |
32 | 33 | const prefixCls = 'ivu-date-picker'; |
33 | 34 | |
34 | 35 | const DEFAULT_FORMATS = { |
35 | 36 | date: 'yyyy-MM-dd', |
36 | 37 | month: 'yyyy-MM', |
38 | + year: 'yyyy', | |
37 | 39 | datetime: 'yyyy-MM-dd HH:mm:ss', |
38 | 40 | time: 'HH:mm:ss', |
39 | 41 | timerange: 'HH:mm:ss', |
... | ... | @@ -41,6 +43,96 @@ |
41 | 43 | datetimerange: 'yyyy-MM-dd HH:mm:ss' |
42 | 44 | }; |
43 | 45 | |
46 | + const RANGE_SEPARATOR = ' - '; | |
47 | + | |
48 | + const DATE_FORMATTER = function(value, format) { | |
49 | + return formatDate(value, format); | |
50 | + }; | |
51 | + const DATE_PARSER = function(text, format) { | |
52 | + return parseDate(text, format); | |
53 | + }; | |
54 | + const RANGE_FORMATTER = function(value, format) { | |
55 | + if (Array.isArray(value) && value.length === 2) { | |
56 | + const start = value[0]; | |
57 | + const end = value[1]; | |
58 | + | |
59 | + if (start && end) { | |
60 | + return formatDate(start, format) + RANGE_SEPARATOR + formatDate(end, format); | |
61 | + } | |
62 | + } | |
63 | + return ''; | |
64 | + }; | |
65 | + const RANGE_PARSER = function(text, format) { | |
66 | + const array = text.split(RANGE_SEPARATOR); | |
67 | + if (array.length === 2) { | |
68 | + const range1 = array[0]; | |
69 | + const range2 = array[1]; | |
70 | + | |
71 | + return [parseDate(range1, format), parseDate(range2, format)]; | |
72 | + } | |
73 | + return []; | |
74 | + }; | |
75 | + | |
76 | + const TYPE_VALUE_RESOLVER_MAP = { | |
77 | + default: { | |
78 | + formatter(value) { | |
79 | + if (!value) return ''; | |
80 | + return '' + value; | |
81 | + }, | |
82 | + parser(text) { | |
83 | + if (text === undefined || text === '') return null; | |
84 | + return text; | |
85 | + } | |
86 | + }, | |
87 | + date: { | |
88 | + formatter: DATE_FORMATTER, | |
89 | + parser: DATE_PARSER | |
90 | + }, | |
91 | + datetime: { | |
92 | + formatter: DATE_FORMATTER, | |
93 | + parser: DATE_PARSER | |
94 | + }, | |
95 | + daterange: { | |
96 | + formatter: RANGE_FORMATTER, | |
97 | + parser: RANGE_PARSER | |
98 | + }, | |
99 | + datetimerange: { | |
100 | + formatter: RANGE_FORMATTER, | |
101 | + parser: RANGE_PARSER | |
102 | + }, | |
103 | + timerange: { | |
104 | + formatter: RANGE_FORMATTER, | |
105 | + parser: RANGE_PARSER | |
106 | + }, | |
107 | + time: { | |
108 | + formatter: DATE_FORMATTER, | |
109 | + parser: DATE_PARSER | |
110 | + }, | |
111 | + month: { | |
112 | + formatter: DATE_FORMATTER, | |
113 | + parser: DATE_PARSER | |
114 | + }, | |
115 | + year: { | |
116 | + formatter: DATE_FORMATTER, | |
117 | + parser: DATE_PARSER | |
118 | + }, | |
119 | + number: { | |
120 | + formatter(value) { | |
121 | + if (!value) return ''; | |
122 | + return '' + value; | |
123 | + }, | |
124 | + parser(text) { | |
125 | + let result = Number(text); | |
126 | + | |
127 | + if (!isNaN(text)) { | |
128 | + return result; | |
129 | + } else { | |
130 | + return null; | |
131 | + } | |
132 | + } | |
133 | + } | |
134 | + }; | |
135 | + | |
44 | 136 | const PLACEMENT_MAP = { |
45 | 137 | left: 'bottom-start', |
46 | 138 | center: 'bottom-center', |
... | ... | @@ -101,6 +193,47 @@ |
101 | 193 | }, |
102 | 194 | placement () { |
103 | 195 | return PLACEMENT_MAP[this.align]; |
196 | + }, | |
197 | + selectionMode() { | |
198 | + if (this.type === 'week') { | |
199 | + return 'week'; | |
200 | + } else if (this.type === 'month') { | |
201 | + return 'month'; | |
202 | + } else if (this.type === 'year') { | |
203 | + return 'year'; | |
204 | + } | |
205 | + | |
206 | + return 'day'; | |
207 | + }, | |
208 | + visualValue: { | |
209 | + get () { | |
210 | + const value = this.internalValue; | |
211 | + if (!value) return; | |
212 | + const formatter = ( | |
213 | + TYPE_VALUE_RESOLVER_MAP[this.type] || | |
214 | + TYPE_VALUE_RESOLVER_MAP['default'] | |
215 | + ).formatter; | |
216 | + const format = DEFAULT_FORMATS[this.type]; | |
217 | + | |
218 | + return formatter(value, this.format || format); | |
219 | + }, | |
220 | + | |
221 | + set (value) { | |
222 | + if (value) { | |
223 | + const type = this.type; | |
224 | + const parser = ( | |
225 | + TYPE_VALUE_RESOLVER_MAP[type] || | |
226 | + TYPE_VALUE_RESOLVER_MAP['default'] | |
227 | + ).parser; | |
228 | + const parsedValue = parser(value, this.format || DEFAULT_FORMATS[type]); | |
229 | + | |
230 | + if (parsedValue) { | |
231 | + if (this.picker) this.picker.value = parsedValue; | |
232 | + } | |
233 | + return; | |
234 | + } | |
235 | + if (this.picker) this.picker.value = value; | |
236 | + } | |
104 | 237 | } |
105 | 238 | }, |
106 | 239 | methods: { |
... | ... | @@ -113,29 +246,51 @@ |
113 | 246 | handleBlur () { |
114 | 247 | |
115 | 248 | }, |
116 | - handleMouseenter () { | |
249 | + handleInputChange (val) { | |
250 | + this.visualValue = val; | |
251 | + }, | |
252 | + handleInputMouseenter () { | |
117 | 253 | if (this.readonly || this.disabled) return; |
118 | 254 | if (this.visualValue) { |
119 | 255 | this.showClose = true; |
120 | 256 | } |
121 | 257 | }, |
122 | - handleMouseleave () { | |
258 | + handleInputMouseleave () { | |
123 | 259 | this.showClose = false; |
124 | 260 | }, |
125 | 261 | handleIconClick () { |
126 | - | |
262 | + if (!this.showClose) return; | |
263 | + this.visible = false; | |
264 | + this.internalValue = ''; | |
265 | + this.value = ''; | |
127 | 266 | }, |
128 | 267 | showPicker () { |
129 | 268 | if (!this.picker) { |
130 | 269 | this.picker = new Vue(this.panel).$mount(this.$els.picker); |
131 | 270 | this.picker.value = this.internalValue; |
271 | + this.picker.selectionMode = this.selectionMode; | |
132 | 272 | if (this.format) this.picker.format = this.format; |
133 | 273 | |
134 | 274 | const options = this.options; |
135 | 275 | for (const option in options) { |
136 | 276 | this.picker[option] = options[option]; |
137 | 277 | } |
278 | + | |
279 | + this.picker.$on('on-pick', (date, visible = false) => { | |
280 | + this.$emit('on-change', date); | |
281 | + this.value = date; | |
282 | + this.visible = visible; | |
283 | + this.picker.resetView && this.picker.resetView(); | |
284 | + }); | |
285 | + | |
286 | + // todo $on('on-range') | |
138 | 287 | } |
288 | + if (this.internalValue instanceof Date) { | |
289 | + this.picker.date = new Date(this.internalValue.getTime()); | |
290 | + } else { | |
291 | + this.picker.value = this.internalValue; | |
292 | + } | |
293 | + this.picker.resetView && this.picker.resetView(); | |
139 | 294 | } |
140 | 295 | }, |
141 | 296 | watch: { |
... | ... | @@ -143,14 +298,24 @@ |
143 | 298 | if (val) { |
144 | 299 | this.showPicker(); |
145 | 300 | this.$refs.drop.update(); |
301 | + this.$emit('on-open-change', true); | |
146 | 302 | } else { |
303 | + if (this.picker) { | |
304 | + this.picker.resetView && this.picker.resetView(); | |
305 | + } | |
147 | 306 | this.$refs.drop.destroy(); |
307 | + this.$emit('on-open-change', false); | |
308 | + } | |
309 | + }, | |
310 | + internalValue(val) { | |
311 | + if (!val && this.picker && typeof this.picker.handleClear === 'function') { | |
312 | + this.picker.handleClear(); | |
148 | 313 | } |
149 | 314 | }, |
150 | 315 | value: { |
151 | 316 | immediate: true, |
152 | 317 | handler (val) { |
153 | - this.internalValue = formatDate(val); | |
318 | + this.internalValue = val; | |
154 | 319 | } |
155 | 320 | } |
156 | 321 | }, | ... | ... |
src/components/date-picker/picker/date-picker.js
src/components/input/input.vue
... | ... | @@ -14,7 +14,8 @@ |
14 | 14 | v-model="value" |
15 | 15 | @keyup.enter="handleEnter" |
16 | 16 | @focus="handleFocus" |
17 | - @blur="handleBlur"> | |
17 | + @blur="handleBlur" | |
18 | + @change="handleChange"> | |
18 | 19 | <div :class="[prefixCls + '-group-append']" v-if="append" v-show="slotReady" v-el:append><slot name="append"></slot></div> |
19 | 20 | </template> |
20 | 21 | <textarea |
... | ... | @@ -31,7 +32,8 @@ |
31 | 32 | v-model="value" |
32 | 33 | @keyup.enter="handleEnter" |
33 | 34 | @focus="handleFocus" |
34 | - @blur="handleBlur"> | |
35 | + @blur="handleBlur" | |
36 | + @change="handleChange"> | |
35 | 37 | </textarea> |
36 | 38 | </div> |
37 | 39 | </template> |
... | ... | @@ -52,7 +54,7 @@ |
52 | 54 | value: { |
53 | 55 | type: [String, Number], |
54 | 56 | default: '', |
55 | - twoWay: true | |
57 | +// twoWay: true | |
56 | 58 | }, |
57 | 59 | size: { |
58 | 60 | validator (value) { |
... | ... | @@ -139,6 +141,9 @@ |
139 | 141 | handleBlur () { |
140 | 142 | this.$emit('on-blur'); |
141 | 143 | }, |
144 | + handleChange () { | |
145 | + this.$emit('on-change', this.value); | |
146 | + }, | |
142 | 147 | resizeTextarea () { |
143 | 148 | const autosize = this.autosize; |
144 | 149 | if (!autosize || this.type !== 'textarea') { |
... | ... | @@ -152,11 +157,10 @@ |
152 | 157 | } |
153 | 158 | }, |
154 | 159 | watch: { |
155 | - value (val) { | |
160 | + value () { | |
156 | 161 | this.$nextTick(() => { |
157 | 162 | this.resizeTextarea(); |
158 | 163 | }); |
159 | - this.$emit('on-change', val); | |
160 | 164 | } |
161 | 165 | }, |
162 | 166 | ready () { | ... | ... |
src/styles/components/date-picker.less
1 | 1 | @date-picker-prefix-cls: ~"@{css-prefix}date-picker"; |
2 | +@picker-prefix-cls: ~"@{css-prefix}picker"; | |
2 | 3 | |
3 | 4 | .@{date-picker-prefix-cls} { |
4 | 5 | position: relative; |
5 | 6 | .@{select-dropdown-prefix-cls} { |
6 | 7 | width: auto; |
8 | + padding: 0; | |
7 | 9 | overflow: visible; |
8 | 10 | max-height: none; |
9 | 11 | } |
... | ... | @@ -100,4 +102,75 @@ |
100 | 102 | } |
101 | 103 | } |
102 | 104 | } |
105 | + | |
106 | + &-cells-year,&-cells-month{ | |
107 | + margin-top: 14px; | |
108 | + span{ | |
109 | + width: 40px; | |
110 | + height: 28px; | |
111 | + line-height: 28px; | |
112 | + margin: 10px 12px; | |
113 | + border-radius: @btn-border-radius-small; | |
114 | + em{ | |
115 | + width: 40px; | |
116 | + height: 28px; | |
117 | + line-height: 28px; | |
118 | + margin: 0; | |
119 | + } | |
120 | + } | |
121 | + } | |
122 | + | |
123 | + &-header{ | |
124 | + height: 32px; | |
125 | + line-height: 32px; | |
126 | + text-align: center; | |
127 | + border-bottom: 1px solid @border-color-split; | |
128 | + &-label{ | |
129 | + cursor: pointer; | |
130 | + transition: color @transition-time @ease-in-out; | |
131 | + &:hover{ | |
132 | + color: @primary-color; | |
133 | + } | |
134 | + } | |
135 | + } | |
136 | + &-prev-btn{ | |
137 | + float: left; | |
138 | + &-arrow-double{ | |
139 | + margin-left: 10px; | |
140 | + i:after{ | |
141 | + content: "\F3D2"; | |
142 | + } | |
143 | + } | |
144 | + } | |
145 | + &-next-btn{ | |
146 | + float: right; | |
147 | + &-arrow-double{ | |
148 | + margin-right: 10px; | |
149 | + i:after{ | |
150 | + content: "\F3D3"; | |
151 | + } | |
152 | + } | |
153 | + } | |
154 | +} | |
155 | + | |
156 | +.@{picker-prefix-cls} { | |
157 | + &-panel{ | |
158 | + &-icon-btn{ | |
159 | + display: inline-block; | |
160 | + width: 20px; | |
161 | + height: 24px; | |
162 | + line-height: 26px; | |
163 | + margin-top: 4px; | |
164 | + text-align: center; | |
165 | + cursor: pointer; | |
166 | + color: @btn-disable-color; | |
167 | + transition: color @transition-time @ease-in-out; | |
168 | + &:hover{ | |
169 | + color: @primary-color; | |
170 | + } | |
171 | + i{ | |
172 | + font-size: 14px; | |
173 | + } | |
174 | + } | |
175 | + } | |
103 | 176 | } |
104 | 177 | \ No newline at end of file | ... | ... |
test/routers/date.vue
1 | 1 | <template> |
2 | 2 | <div style="margin: 50px"> |
3 | - <date-picker style="width:200px" placeholder="请选择日期" :value.sync="value" :options="options"></date-picker> | |
3 | + <br> | |
4 | + <row> | |
5 | + <i-col span="4"> | |
6 | + <date-picker style="width:200px" placeholder="请选择日期" :value.sync="value" :options="options" @on-change="change" @on-open-change="change2" :format="format"></date-picker> | |
7 | + </i-col> | |
8 | + <i-col span="4"> | |
9 | + <date-picker type="year" style="width:200px" placeholder="请选择日期" :value.sync="value" :options="options"></date-picker> | |
10 | + </i-col> | |
11 | + <i-col span="4"> | |
12 | + <date-picker type="month" style="width:200px" placeholder="请选择日期" :value.sync="value" :options="options"></date-picker> | |
13 | + </i-col> | |
14 | + </row> | |
4 | 15 | </div> |
5 | 16 | </template> |
6 | 17 | <script> |
7 | 18 | export default { |
8 | 19 | data () { |
9 | 20 | return { |
10 | - value: '2016-12-18', | |
21 | + value: new Date(), | |
11 | 22 | // value: '', |
12 | 23 | options: { |
13 | 24 | disabledDate(time) { |
14 | 25 | return time.getTime() < Date.now() - 8.64e7; |
15 | 26 | // return time && time.valueOf() < Date.now(); |
16 | 27 | } |
17 | - } | |
28 | + }, | |
29 | + format: 'yyyy/MM/dd' | |
18 | 30 | } |
19 | 31 | }, |
20 | 32 | computed: {}, |
21 | - methods: {} | |
33 | + methods: { | |
34 | + change (date) { | |
35 | +// console.log(date) | |
36 | + }, | |
37 | + change2 (s) { | |
38 | +// console.log(s) | |
39 | + } | |
40 | + } | |
22 | 41 | } |
23 | 42 | </script> |
24 | 43 | \ No newline at end of file | ... | ... |