mirror of
https://github.com/tenrok/vue2-datepicker.git
synced 2026-06-23 13:20:36 +03:00
feat: add multi-select (#444)
This commit is contained in:
@@ -124,6 +124,7 @@ You can also override some of the default locale by `lang`.
|
|||||||
| clearable | if false, don't show the clear icon | `boolean` | true |
|
| clearable | if false, don't show the clear icon | `boolean` | true |
|
||||||
| confirm | if true, need click the button to change value | `boolean` | false |
|
| confirm | if true, need click the button to change value | `boolean` | false |
|
||||||
| confirm-text | the text of confirm button | `string` | 'OK' |
|
| confirm-text | the text of confirm button | `string` | 'OK' |
|
||||||
|
| multiple | if true, multi-select date | `boolean` | false |
|
||||||
| disabled | disable the component | `boolean` | false |
|
| disabled | disable the component | `boolean` | false |
|
||||||
| disabled-date | specify the date that cannot be selected | `(date) => boolean` | - |
|
| disabled-date | specify the date that cannot be selected | `(date) => boolean` | - |
|
||||||
| disabled-time | specify the time that cannot be selected | `(date) => boolean` | - |
|
| disabled-time | specify the time that cannot be selected | `(date) => boolean` | - |
|
||||||
|
|||||||
@@ -122,6 +122,7 @@ import 'vue2-datepicker/locale/zh-cn';
|
|||||||
| clearable | 是否显示清除按钮 | `boolean` | true |
|
| clearable | 是否显示清除按钮 | `boolean` | true |
|
||||||
| confirm | 是否需要确认 | `boolean` | false |
|
| confirm | 是否需要确认 | `boolean` | false |
|
||||||
| confirm-text | 确认按钮的文字 | `string` | 'OK' |
|
| confirm-text | 确认按钮的文字 | `string` | 'OK' |
|
||||||
|
| multiple | 如果是 true, 可以选择多个日期 | `boolean` | false |
|
||||||
| disabled | 禁用组件 | `boolean` | false |
|
| disabled | 禁用组件 | `boolean` | false |
|
||||||
| disabled-date | 禁止选择的日期 | `(date) => boolean` | - |
|
| disabled-date | 禁止选择的日期 | `(date) => boolean` | - |
|
||||||
| disabled-time | 禁止选择的时间 | `(date) => boolean` | - |
|
| disabled-time | 禁止选择的时间 | `(date) => boolean` | - |
|
||||||
|
|||||||
@@ -387,4 +387,35 @@ describe('DatePicker', () => {
|
|||||||
input.trigger('change');
|
input.trigger('change');
|
||||||
expect(wrapper.emitted().input).toEqual([[[text, text]], [[text, text]], [[text, text]]]);
|
expect(wrapper.emitted().input).toEqual([[[text, text]], [[text, text]], [[text, text]]]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('prop: multiple', () => {
|
||||||
|
const value = [new Date(2020, 5, 6), new Date(2020, 6, 7)];
|
||||||
|
wrapper = mount(DatePicker, {
|
||||||
|
propsData: {
|
||||||
|
multiple: true,
|
||||||
|
open: true,
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
wrapper.find('.mx-date-row .active').trigger('click');
|
||||||
|
expect(wrapper.emitted().input[0][0]).toEqual(value.slice(0, 1));
|
||||||
|
wrapper.find('[title="2020-07-15"]').trigger('click');
|
||||||
|
expect(wrapper.emitted().input[1][0]).toEqual(value.concat(new Date(2020, 6, 15)));
|
||||||
|
});
|
||||||
|
|
||||||
|
it('prop: invalid multiple', () => {
|
||||||
|
wrapper = shallowMount(DatePicker, {
|
||||||
|
propsData: {
|
||||||
|
multiple: true,
|
||||||
|
range: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const { vm } = wrapper;
|
||||||
|
expect(vm.validMultipleType).toBe(false);
|
||||||
|
wrapper.setProps({
|
||||||
|
range: false,
|
||||||
|
type: 'datetime',
|
||||||
|
});
|
||||||
|
expect(vm.validMultipleType).toBe(false);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -240,7 +240,8 @@ export default {
|
|||||||
initCalendar() {
|
initCalendar() {
|
||||||
let calendarDate = this.calendar;
|
let calendarDate = this.calendar;
|
||||||
if (!isValidDate(calendarDate)) {
|
if (!isValidDate(calendarDate)) {
|
||||||
calendarDate = getValidDate(this.innerValue[0], this.defaultValue);
|
const { length } = this.innerValue;
|
||||||
|
calendarDate = getValidDate(length > 0 ? this.innerValue[length - 1] : this.defaultValue);
|
||||||
}
|
}
|
||||||
this.innerCalendar = calendarDate;
|
this.innerCalendar = calendarDate;
|
||||||
},
|
},
|
||||||
@@ -249,7 +250,7 @@ export default {
|
|||||||
},
|
},
|
||||||
emitDate(date, type) {
|
emitDate(date, type) {
|
||||||
if (!this.isDisabled(date)) {
|
if (!this.isDisabled(date)) {
|
||||||
this.$emit('select', date, type);
|
this.$emit('select', date, type, this.innerValue);
|
||||||
// someone need get the first selected date to set range value. (#429)
|
// someone need get the first selected date to set range value. (#429)
|
||||||
this.dispatch('DatePicker', 'pick', date, type);
|
this.dispatch('DatePicker', 'pick', date, type);
|
||||||
}
|
}
|
||||||
@@ -287,7 +288,7 @@ export default {
|
|||||||
const nextCalendar = setYear(this.innerCalendar, year);
|
const nextCalendar = setYear(this.innerCalendar, year);
|
||||||
this.updateCalendar(nextCalendar, 'year');
|
this.updateCalendar(nextCalendar, 'year');
|
||||||
this.handelPanelChange('month');
|
this.handelPanelChange('month');
|
||||||
if (this.partialUpdate && this.innerValue[0]) {
|
if (this.partialUpdate && this.innerValue.length === 1) {
|
||||||
const date = setYear(this.innerValue[0], year);
|
const date = setYear(this.innerValue[0], year);
|
||||||
this.emitDate(date, 'year');
|
this.emitDate(date, 'year');
|
||||||
}
|
}
|
||||||
@@ -301,7 +302,7 @@ export default {
|
|||||||
const nextCalendar = setMonth(this.innerCalendar, month);
|
const nextCalendar = setMonth(this.innerCalendar, month);
|
||||||
this.updateCalendar(nextCalendar, 'month');
|
this.updateCalendar(nextCalendar, 'month');
|
||||||
this.handelPanelChange('date');
|
this.handelPanelChange('date');
|
||||||
if (this.partialUpdate && this.innerValue[0]) {
|
if (this.partialUpdate && this.innerValue.length === 1) {
|
||||||
const date = setMonth(setYear(this.innerValue[0], this.calendarYear), month);
|
const date = setMonth(setYear(this.innerValue[0], this.calendarYear), month);
|
||||||
this.emitDate(date, 'month');
|
this.emitDate(date, 'month');
|
||||||
}
|
}
|
||||||
|
|||||||
+47
-7
@@ -90,7 +90,7 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
import { parse, format, getWeek } from 'date-format-parse';
|
import { parse, format, getWeek } from 'date-format-parse';
|
||||||
import { isValidDate, isValidRangeDate } from './util/date';
|
import { isValidDate, isValidRangeDate, isValidDates } from './util/date';
|
||||||
import { pick, isObject, mergeDeep } from './util/base';
|
import { pick, isObject, mergeDeep } from './util/base';
|
||||||
import { getLocale, getLocaleFieldValue } from './locale';
|
import { getLocale, getLocaleFieldValue } from './locale';
|
||||||
import Popup from './popup';
|
import Popup from './popup';
|
||||||
@@ -157,9 +157,15 @@ export default {
|
|||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false,
|
default: false,
|
||||||
},
|
},
|
||||||
|
multiple: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false,
|
||||||
|
},
|
||||||
rangeSeparator: {
|
rangeSeparator: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ' ~ ',
|
default() {
|
||||||
|
return this.multiple ? ',' : ' ~ ';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
lang: {
|
lang: {
|
||||||
type: [String, Object],
|
type: [String, Object],
|
||||||
@@ -222,6 +228,9 @@ export default {
|
|||||||
type: String,
|
type: String,
|
||||||
default: 'OK',
|
default: 'OK',
|
||||||
},
|
},
|
||||||
|
renderInputText: {
|
||||||
|
type: Function,
|
||||||
|
},
|
||||||
shortcuts: {
|
shortcuts: {
|
||||||
type: Array,
|
type: Array,
|
||||||
validator(value) {
|
validator(value) {
|
||||||
@@ -262,6 +271,10 @@ export default {
|
|||||||
},
|
},
|
||||||
innerValue() {
|
innerValue() {
|
||||||
let { value } = this;
|
let { value } = this;
|
||||||
|
if (this.validMultipleType) {
|
||||||
|
value = Array.isArray(value) ? value : [];
|
||||||
|
return value.map(this.value2date);
|
||||||
|
}
|
||||||
if (this.range) {
|
if (this.range) {
|
||||||
value = Array.isArray(value) ? value.slice(0, 2) : [null, null];
|
value = Array.isArray(value) ? value.slice(0, 2) : [null, null];
|
||||||
return value.map(this.value2date);
|
return value.map(this.value2date);
|
||||||
@@ -272,6 +285,9 @@ export default {
|
|||||||
if (this.userInput !== null) {
|
if (this.userInput !== null) {
|
||||||
return this.userInput;
|
return this.userInput;
|
||||||
}
|
}
|
||||||
|
if (typeof this.renderInputText === 'function') {
|
||||||
|
return this.renderInputText(this.innerValue);
|
||||||
|
}
|
||||||
if (!this.isValidValue(this.innerValue)) {
|
if (!this.isValidValue(this.innerValue)) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -290,6 +306,10 @@ export default {
|
|||||||
}
|
}
|
||||||
return getLocale(this.lang);
|
return getLocale(this.lang);
|
||||||
},
|
},
|
||||||
|
validMultipleType() {
|
||||||
|
const types = ['date', 'month', 'year'];
|
||||||
|
return this.multiple && !this.range && types.indexOf(this.type) !== -1;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
innerValue: {
|
innerValue: {
|
||||||
@@ -368,14 +388,30 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
isValidValue(value) {
|
isValidValue(value) {
|
||||||
const validate = this.range ? isValidRangeDate : isValidDate;
|
if (this.validMultipleType) {
|
||||||
return validate(value);
|
return isValidDates(value);
|
||||||
|
}
|
||||||
|
if (this.range) {
|
||||||
|
return isValidRangeDate(value);
|
||||||
|
}
|
||||||
|
return isValidDate(value);
|
||||||
},
|
},
|
||||||
handleSelectDate(val, type) {
|
handleMultipleDates(date, dates) {
|
||||||
|
if (this.validMultipleType && dates) {
|
||||||
|
const nextDates = dates.filter(v => v.getTime() !== date.getTime());
|
||||||
|
if (nextDates.length === dates.length) {
|
||||||
|
nextDates.push(date);
|
||||||
|
}
|
||||||
|
return nextDates;
|
||||||
|
}
|
||||||
|
return date;
|
||||||
|
},
|
||||||
|
handleSelectDate(val, type, dates) {
|
||||||
|
val = this.handleMultipleDates(val, dates);
|
||||||
if (this.confirm) {
|
if (this.confirm) {
|
||||||
this.currentValue = val;
|
this.currentValue = val;
|
||||||
} else {
|
} else {
|
||||||
this.emitValue(val, type);
|
this.emitValue(val, this.validMultipleType ? `multiple-${type}` : type);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
handleClear() {
|
handleClear() {
|
||||||
@@ -421,9 +457,13 @@ export default {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
let date;
|
let date;
|
||||||
if (this.range) {
|
if (this.validMultipleType) {
|
||||||
|
date = text.split(this.rangeSeparator).map(v => this.parseDate(v.trim(), this.format));
|
||||||
|
} else if (this.range) {
|
||||||
let arr = text.split(this.rangeSeparator);
|
let arr = text.split(this.rangeSeparator);
|
||||||
if (arr.length !== 2) {
|
if (arr.length !== 2) {
|
||||||
|
// Maybe the separator during the day is the same as the separator for the date
|
||||||
|
// eg: 2019-10-09-2020-01-02
|
||||||
arr = text.split(this.rangeSeparator.trim());
|
arr = text.split(this.rangeSeparator.trim());
|
||||||
}
|
}
|
||||||
date = arr.map(v => this.parseDate(v.trim(), this.format));
|
date = arr.map(v => this.parseDate(v.trim(), this.format));
|
||||||
|
|||||||
+11
-1
@@ -15,9 +15,19 @@ export function isValidRangeDate(date) {
|
|||||||
return Array.isArray(date) && date.length === 2 && date.every(isValidDate) && date[0] <= date[1];
|
return Array.isArray(date) && date.length === 2 && date.every(isValidDate) && date[0] <= date[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function isValidDates(dates) {
|
||||||
|
return Array.isArray(dates) && dates.every(isValidDate);
|
||||||
|
}
|
||||||
|
|
||||||
export function getValidDate(value, ...backup) {
|
export function getValidDate(value, ...backup) {
|
||||||
const date = new Date(value);
|
const date = new Date(value);
|
||||||
return isValidDate(date) ? date : getValidDate(...backup);
|
if (isValidDate(date)) {
|
||||||
|
return date;
|
||||||
|
}
|
||||||
|
if (backup.length) {
|
||||||
|
return getValidDate(...backup);
|
||||||
|
}
|
||||||
|
return new Date();
|
||||||
}
|
}
|
||||||
|
|
||||||
export function assignTime(target, source) {
|
export function assignTime(target, source) {
|
||||||
|
|||||||
Reference in New Issue
Block a user