mirror of https://github.com/ElemeFE/element
parent
92c0f7a4ec
commit
2f5a489a9c
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
## DatePicker
|
## DatePicker
|
||||||
|
|
||||||
Use Date Picker for date input.
|
Use Date Picker for date input.
|
||||||
|
@ -202,6 +203,74 @@ Picking a date range is supported.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
### Month Range
|
||||||
|
|
||||||
|
Picking a month range is supported.
|
||||||
|
|
||||||
|
:::demo When in range mode, the left and right panels are linked by default. If you want the two panels to switch current years independently, you can use the `unlink-panels` attribute.
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<span class="demonstration">Default</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="value15"
|
||||||
|
type="monthrange"
|
||||||
|
range-separator="To"
|
||||||
|
start-placeholder="Start month"
|
||||||
|
end-placeholder="End month">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<span class="demonstration">With quick options</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="value16"
|
||||||
|
type="monthrange"
|
||||||
|
align="right"
|
||||||
|
unlink-panels
|
||||||
|
range-separator="To"
|
||||||
|
start-placeholder="Start month"
|
||||||
|
end-placeholder="End month"
|
||||||
|
:picker-options="pickerOptions3">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pickerOptions3: {
|
||||||
|
shortcuts: [{
|
||||||
|
text: 'This month',
|
||||||
|
onClick(picker) {
|
||||||
|
picker.$emit('pick', [new Date(), new Date()]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: 'This year',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(new Date().getFullYear(), 0);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: 'Last 6 months',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setMonth(start.getMonth() - 6);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
value15: '',
|
||||||
|
value16: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
### Default Value
|
### Default Value
|
||||||
|
|
||||||
If user hasn't picked a date, shows today's calendar by default. You can use `default-value` to set another date. Its value should be parsable by `new Date()`.
|
If user hasn't picked a date, shows today's calendar by default. You can use `default-value` to set another date. Its value should be parsable by `new Date()`.
|
||||||
|
@ -372,7 +441,7 @@ When picking a date range, you can assign the time part for start date and end d
|
||||||
| placeholder | placeholder in non-range mode | string | — | — |
|
| placeholder | placeholder in non-range mode | string | — | — |
|
||||||
| start-placeholder | placeholder for the start date in range mode | string | — | — |
|
| start-placeholder | placeholder for the start date in range mode | string | — | — |
|
||||||
| end-placeholder | placeholder for the end date in range mode | string | — | — |
|
| end-placeholder | placeholder for the end date in range mode | string | — | — |
|
||||||
| type | type of the picker | string | year/month/date/dates/datetime/ week/datetimerange/daterange | date |
|
| type | type of the picker | string | year/month/date/dates/datetime/ week/datetimerange/daterange/ monthrange | date |
|
||||||
| format | format of the displayed value in the input box | string | see [date formats](#/en-US/component/date-picker#date-formats) | yyyy-MM-dd |
|
| format | format of the displayed value in the input box | string | see [date formats](#/en-US/component/date-picker#date-formats) | yyyy-MM-dd |
|
||||||
| align | alignment | left/center/right | left |
|
| align | alignment | left/center/right | left |
|
||||||
| popper-class | custom class name for DatePicker's dropdown | string | — | — |
|
| popper-class | custom class name for DatePicker's dropdown | string | — | — |
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
## DatePicker
|
## DatePicker
|
||||||
|
|
||||||
Utilice Date Picker para introducir la fecha.
|
Utilice Date Picker para introducir la fecha.
|
||||||
|
@ -204,6 +205,74 @@ Se soporta la selección de un rango de fechas.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
### Month Range
|
||||||
|
|
||||||
|
Picking a month range is supported.
|
||||||
|
|
||||||
|
:::demo When in range mode, the left and right panels are linked by default. If you want the two panels to switch current years independently, you can use the `unlink-panels` attribute.
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<span class="demonstration">Default</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="value15"
|
||||||
|
type="monthrange"
|
||||||
|
range-separator="To"
|
||||||
|
start-placeholder="Start month"
|
||||||
|
end-placeholder="End month">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<span class="demonstration">With quick options</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="value16"
|
||||||
|
type="monthrange"
|
||||||
|
align="right"
|
||||||
|
unlink-panels
|
||||||
|
range-separator="To"
|
||||||
|
start-placeholder="Start month"
|
||||||
|
end-placeholder="End month"
|
||||||
|
:picker-options="pickerOptions3">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pickerOptions3: {
|
||||||
|
shortcuts: [{
|
||||||
|
text: 'This month',
|
||||||
|
onClick(picker) {
|
||||||
|
picker.$emit('pick', [new Date(), new Date()]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: 'This year',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(new Date().getFullYear(), 0);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: 'Last 6 months',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setMonth(start.getMonth() - 6);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
value15: '',
|
||||||
|
value16: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
### Valor por defecto
|
### Valor por defecto
|
||||||
|
|
||||||
Si el usuario no ha escogido una fecha, muestra el calendario de hoy por defecto. Puede utilizar `default-value` para fijar otra fecha. Su valor debe ser definido por `new Date()`.
|
Si el usuario no ha escogido una fecha, muestra el calendario de hoy por defecto. Puede utilizar `default-value` para fijar otra fecha. Su valor debe ser definido por `new Date()`.
|
||||||
|
@ -373,7 +442,7 @@ Al seleccionar un intervalo de fechas, puede asignar la hora para la fecha de in
|
||||||
| placeholder | placeholder cuando el modo NO es rango | string | — | — |
|
| placeholder | placeholder cuando el modo NO es rango | string | — | — |
|
||||||
| start-placeholder | placeholder para la fecha de inicio en modo rango | string | — | — |
|
| start-placeholder | placeholder para la fecha de inicio en modo rango | string | — | — |
|
||||||
| end-placeholder | placeholder para la fecha final en modo rango | string | — | — |
|
| end-placeholder | placeholder para la fecha final en modo rango | string | — | — |
|
||||||
| type | tipo de picker | string | year/month/date/dates/datetime/ week/datetimerange/daterange | date |
|
| type | tipo de picker | string | year/month/date/dates/datetime/ week/datetimerange/daterange/ monthrange | date |
|
||||||
| format | formato en que se muestra el valor en el input | string | ver [date formats](#/es/component/date-picker#date-formats) | yyyy-MM-dd |
|
| format | formato en que se muestra el valor en el input | string | ver [date formats](#/es/component/date-picker#date-formats) | yyyy-MM-dd |
|
||||||
| align | alineación | left/center/right | left | |
|
| align | alineación | left/center/right | left | |
|
||||||
| popper-class | nombre de clase personalizada para el dropdown de DatePicker | string | — | — |
|
| popper-class | nombre de clase personalizada para el dropdown de DatePicker | string | — | — |
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
## DatePicker
|
## DatePicker
|
||||||
|
|
||||||
Utilisez le DatePicker pour les champs de dates.
|
Utilisez le DatePicker pour les champs de dates.
|
||||||
|
@ -202,6 +203,74 @@ Vous pouvez sélectionner une plage de dates.
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
### Month Range
|
||||||
|
|
||||||
|
Picking a month range is supported.
|
||||||
|
|
||||||
|
:::demo When in range mode, the left and right panels are linked by default. If you want the two panels to switch current years independently, you can use the `unlink-panels` attribute.
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<span class="demonstration">Default</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="value15"
|
||||||
|
type="monthrange"
|
||||||
|
range-separator="To"
|
||||||
|
start-placeholder="Start month"
|
||||||
|
end-placeholder="End month">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<span class="demonstration">With quick options</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="value16"
|
||||||
|
type="monthrange"
|
||||||
|
align="right"
|
||||||
|
unlink-panels
|
||||||
|
range-separator="To"
|
||||||
|
start-placeholder="Start month"
|
||||||
|
end-placeholder="End month"
|
||||||
|
:picker-options="pickerOptions3">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pickerOptions3: {
|
||||||
|
shortcuts: [{
|
||||||
|
text: 'This month',
|
||||||
|
onClick(picker) {
|
||||||
|
picker.$emit('pick', [new Date(), new Date()]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: 'This year',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(new Date().getFullYear(), 0);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: 'Last 6 months',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setMonth(start.getMonth() - 6);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
value15: '',
|
||||||
|
value16: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
### Valeur par défaut
|
### Valeur par défaut
|
||||||
|
|
||||||
Si l'utilisateur n'a pas sélectionné de date, vous pouvez montrer la date d'aujourd'hui par défaut. Utilisez `default-value` pour montrer une autre date. Sa valeur doit être parsable par `new Date()`.
|
Si l'utilisateur n'a pas sélectionné de date, vous pouvez montrer la date d'aujourd'hui par défaut. Utilisez `default-value` pour montrer une autre date. Sa valeur doit être parsable par `new Date()`.
|
||||||
|
@ -373,7 +442,7 @@ Lorsque vous choisissez une plage de dates, vous pouvez assigner l'horaire de d
|
||||||
| placeholder | Le placeholder en mode normal. | string | — | — |
|
| placeholder | Le placeholder en mode normal. | string | — | — |
|
||||||
| start-placeholder | Le placeholder pour la date de début en mode plage de dates. | string | — | — |
|
| start-placeholder | Le placeholder pour la date de début en mode plage de dates. | string | — | — |
|
||||||
| end-placeholder | Le placeholder pour la date de fin en mode plage de dates. | string | — | — |
|
| end-placeholder | Le placeholder pour la date de fin en mode plage de dates. | string | — | — |
|
||||||
| type | Type du picker. | string | year/month/date/dates/datetime/ week/datetimerange/daterange | date |
|
| type | Type du picker. | string | year/month/date/dates/datetime/ week/datetimerange/daterange/ monthrange | date |
|
||||||
| format | Format d'affichage dans le champ. | string | Voir [formats de date](#/fr-FR/component/date-picker#formats-de-date). | yyyy-MM-dd |
|
| format | Format d'affichage dans le champ. | string | Voir [formats de date](#/fr-FR/component/date-picker#formats-de-date). | yyyy-MM-dd |
|
||||||
| align | Alignement. | left/center/right | left |
|
| align | Alignement. | left/center/right | left |
|
||||||
| popper-class | Nom de classe pour le menu déroulant du DatePicker. | string | — | — |
|
| popper-class | Nom de classe pour le menu déroulant du DatePicker. | string | — | — |
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
|
||||||
## DatePicker 日期选择器
|
## DatePicker 日期选择器
|
||||||
|
|
||||||
用于选择或输入日期
|
用于选择或输入日期
|
||||||
|
@ -198,6 +199,76 @@
|
||||||
```
|
```
|
||||||
:::
|
:::
|
||||||
|
|
||||||
|
|
||||||
|
### 选择月份范围
|
||||||
|
|
||||||
|
可在一个选择器中便捷地选择一个月份范围
|
||||||
|
|
||||||
|
:::demo 在选择月份范围时,默认情况下左右面板会联动。如果希望两个面板各自独立切换当前年份,可以使用`unlink-panels`属性解除联动。
|
||||||
|
```html
|
||||||
|
<template>
|
||||||
|
<div class="block">
|
||||||
|
<span class="demonstration">默认</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="value15"
|
||||||
|
type="monthrange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始月份"
|
||||||
|
end-placeholder="结束月份">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
<div class="block">
|
||||||
|
<span class="demonstration">带快捷选项</span>
|
||||||
|
<el-date-picker
|
||||||
|
v-model="value16"
|
||||||
|
type="monthrange"
|
||||||
|
align="right"
|
||||||
|
unlink-panels
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始月份"
|
||||||
|
end-placeholder="结束月份"
|
||||||
|
:picker-options="pickerOptions3">
|
||||||
|
</el-date-picker>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pickerOptions3: {
|
||||||
|
shortcuts: [{
|
||||||
|
text: '本月',
|
||||||
|
onClick(picker) {
|
||||||
|
picker.$emit('pick', [new Date(), new Date()]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: '今年至今',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date(new Date().getFullYear(), 0);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
text: '最近六个月',
|
||||||
|
onClick(picker) {
|
||||||
|
const end = new Date();
|
||||||
|
const start = new Date();
|
||||||
|
start.setMonth(start.getMonth() - 6);
|
||||||
|
picker.$emit('pick', [start, end]);
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
value15: '',
|
||||||
|
value16: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
```
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
### 日期格式
|
### 日期格式
|
||||||
|
|
||||||
使用`format`指定输入框的格式;使用`value-format`指定绑定值的格式。
|
使用`format`指定输入框的格式;使用`value-format`指定绑定值的格式。
|
||||||
|
@ -323,7 +394,7 @@
|
||||||
| placeholder | 非范围选择时的占位内容 | string | — | — |
|
| placeholder | 非范围选择时的占位内容 | string | — | — |
|
||||||
| start-placeholder | 范围选择时开始日期的占位内容 | string | — | — |
|
| start-placeholder | 范围选择时开始日期的占位内容 | string | — | — |
|
||||||
| end-placeholder | 范围选择时结束日期的占位内容 | string | — | — |
|
| end-placeholder | 范围选择时结束日期的占位内容 | string | — | — |
|
||||||
| type | 显示类型 | string | year/month/date/dates/ week/datetime/datetimerange/daterange | date |
|
| type | 显示类型 | string | year/month/date/dates/ week/datetime/datetimerange/ daterange/monthrange | date |
|
||||||
| format | 显示在输入框中的格式 | string | 见[日期格式](#/zh-CN/component/date-picker#ri-qi-ge-shi) | yyyy-MM-dd |
|
| format | 显示在输入框中的格式 | string | 见[日期格式](#/zh-CN/component/date-picker#ri-qi-ge-shi) | yyyy-MM-dd |
|
||||||
| align | 对齐方式 | string | left, center, right | left |
|
| align | 对齐方式 | string | left, center, right | left |
|
||||||
| popper-class | DatePicker 下拉框的类名 | string | — | — |
|
| popper-class | DatePicker 下拉框的类名 | string | — | — |
|
||||||
|
|
|
@ -1,46 +1,11 @@
|
||||||
<template>
|
<template>
|
||||||
<table @click="handleMonthTableClick" class="el-month-table">
|
<table @click="handleMonthTableClick" @mousemove="handleMouseMove" class="el-month-table">
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr>
|
<tr v-for="(row, key) in rows" :key="key">
|
||||||
<td :class="getCellStyle(0)">
|
<td :class="getCellStyle(cell)" v-for="(cell, key) in row" :key="key">
|
||||||
<a class="cell">{{ t('el.datepicker.months.jan') }}</a>
|
<div>
|
||||||
</td>
|
<a class="cell">{{ t('el.datepicker.months.' + months[cell.text]) }}</a>
|
||||||
<td :class="getCellStyle(1)">
|
</div>
|
||||||
<a class="cell">{{ t('el.datepicker.months.feb') }}</a>
|
|
||||||
</td>
|
|
||||||
<td :class="getCellStyle(2)">
|
|
||||||
<a class="cell">{{ t('el.datepicker.months.mar') }}</a>
|
|
||||||
</td>
|
|
||||||
<td :class="getCellStyle(3)">
|
|
||||||
<a class="cell">{{ t('el.datepicker.months.apr') }}</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td :class="getCellStyle(4)">
|
|
||||||
<a class="cell">{{ t('el.datepicker.months.may') }}</a>
|
|
||||||
</td>
|
|
||||||
<td :class="getCellStyle(5)">
|
|
||||||
<a class="cell">{{ t('el.datepicker.months.jun') }}</a>
|
|
||||||
</td>
|
|
||||||
<td :class="getCellStyle(6)">
|
|
||||||
<a class="cell">{{ t('el.datepicker.months.jul') }}</a>
|
|
||||||
</td>
|
|
||||||
<td :class="getCellStyle(7)">
|
|
||||||
<a class="cell">{{ t('el.datepicker.months.aug') }}</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td :class="getCellStyle(8)">
|
|
||||||
<a class="cell">{{ t('el.datepicker.months.sep') }}</a>
|
|
||||||
</td>
|
|
||||||
<td :class="getCellStyle(9)">
|
|
||||||
<a class="cell">{{ t('el.datepicker.months.oct') }}</a>
|
|
||||||
</td>
|
|
||||||
<td :class="getCellStyle(10)">
|
|
||||||
<a class="cell">{{ t('el.datepicker.months.nov') }}</a>
|
|
||||||
</td>
|
|
||||||
<td :class="getCellStyle(11)">
|
|
||||||
<a class="cell">{{ t('el.datepicker.months.dec') }}</a>
|
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
@ -51,7 +16,7 @@
|
||||||
import Locale from 'element-ui/src/mixins/locale';
|
import Locale from 'element-ui/src/mixins/locale';
|
||||||
import { isDate, range, getDayCountOfMonth, nextDate } from '../util';
|
import { isDate, range, getDayCountOfMonth, nextDate } from '../util';
|
||||||
import { hasClass } from 'element-ui/src/utils/dom';
|
import { hasClass } from 'element-ui/src/utils/dom';
|
||||||
import { arrayFindIndex, coerceTruthyValueToArray } from 'element-ui/src/utils/util';
|
import { arrayFindIndex, coerceTruthyValueToArray, arrayFind } from 'element-ui/src/utils/util';
|
||||||
|
|
||||||
const datesInMonth = (year, month) => {
|
const datesInMonth = (year, month) => {
|
||||||
const numOfDays = getDayCountOfMonth(year, month);
|
const numOfDays = getDayCountOfMonth(year, month);
|
||||||
|
@ -59,46 +24,230 @@
|
||||||
return range(numOfDays).map(n => nextDate(firstDay, n));
|
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;
|
||||||
|
}
|
||||||
|
};
|
||||||
export default {
|
export default {
|
||||||
props: {
|
props: {
|
||||||
disabledDate: {},
|
disabledDate: {},
|
||||||
value: {},
|
value: {},
|
||||||
|
selectionMode: {
|
||||||
|
default: 'month'
|
||||||
|
},
|
||||||
|
minDate: {},
|
||||||
|
|
||||||
|
maxDate: {},
|
||||||
defaultValue: {
|
defaultValue: {
|
||||||
validator(val) {
|
validator(val) {
|
||||||
// null or valid Date Object
|
// null or valid Date Object
|
||||||
return val === null || (val instanceof Date && isDate(val));
|
return val === null || isDate(val) || (Array.isArray(val) && val.every(isDate));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
date: {}
|
date: {},
|
||||||
|
rangeState: {
|
||||||
|
default() {
|
||||||
|
return {
|
||||||
|
endDate: null,
|
||||||
|
selecting: false
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
mixins: [Locale],
|
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: {
|
methods: {
|
||||||
getCellStyle(month) {
|
cellMatchesDate(cell, date) {
|
||||||
|
const value = new Date(date);
|
||||||
|
return this.date.getFullYear() === value.getFullYear() && Number(cell.text) === value.getMonth();
|
||||||
|
},
|
||||||
|
getCellStyle(cell) {
|
||||||
const style = {};
|
const style = {};
|
||||||
const year = this.date.getFullYear();
|
const year = this.date.getFullYear();
|
||||||
const today = new Date();
|
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'
|
style.disabled = typeof this.disabledDate === 'function'
|
||||||
? datesInMonth(year, month).every(this.disabledDate)
|
? datesInMonth(year, month).every(this.disabledDate)
|
||||||
: false;
|
: false;
|
||||||
style.current = arrayFindIndex(coerceTruthyValueToArray(this.value), date => date.getFullYear() === year && date.getMonth() === month) >= 0;
|
style.current = arrayFindIndex(coerceTruthyValueToArray(this.value), date => date.getFullYear() === year && date.getMonth() === month) >= 0;
|
||||||
style.today = today.getFullYear() === year && today.getMonth() === month;
|
style.today = today.getFullYear() === year && today.getMonth() === month;
|
||||||
style.default = this.defaultValue &&
|
style.default = defaultValue.some(date => this.cellMatchesDate(cell, date));
|
||||||
this.defaultValue.getFullYear() === year &&
|
|
||||||
this.defaultValue.getMonth() === month;
|
|
||||||
|
|
||||||
|
if (cell.inRange) {
|
||||||
|
style['in-range'] = true;
|
||||||
|
|
||||||
|
if (cell.start) {
|
||||||
|
style['start-date'] = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cell.end) {
|
||||||
|
style['end-date'] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return style;
|
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) {
|
handleMonthTableClick(event) {
|
||||||
const target = event.target;
|
let target = event.target;
|
||||||
if (target.tagName !== 'A') return;
|
if (target.tagName === 'A') {
|
||||||
if (hasClass(target.parentNode, 'disabled')) return;
|
target = target.parentNode.parentNode;
|
||||||
const column = target.parentNode.cellIndex;
|
}
|
||||||
const row = target.parentNode.parentNode.rowIndex;
|
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 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 {
|
||||||
|
this.$emit('pick', month);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,289 @@
|
||||||
|
<template>
|
||||||
|
<transition name="el-zoom-in-top" @after-leave="$emit('dodestroy')">
|
||||||
|
<div
|
||||||
|
v-show="visible"
|
||||||
|
class="el-picker-panel el-date-range-picker el-popper"
|
||||||
|
:class="[{
|
||||||
|
'has-sidebar': $slots.sidebar || shortcuts
|
||||||
|
}, popperClass]">
|
||||||
|
<div class="el-picker-panel__body-wrapper">
|
||||||
|
<slot name="sidebar" class="el-picker-panel__sidebar"></slot>
|
||||||
|
<div class="el-picker-panel__sidebar" v-if="shortcuts">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="el-picker-panel__shortcut"
|
||||||
|
v-for="(shortcut, key) in shortcuts"
|
||||||
|
:key="key"
|
||||||
|
@click="handleShortcutClick(shortcut)">{{shortcut.text}}</button>
|
||||||
|
</div>
|
||||||
|
<div class="el-picker-panel__body">
|
||||||
|
<div class="el-picker-panel__content el-date-range-picker__content is-left">
|
||||||
|
<div class="el-date-range-picker__header">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="leftPrevYear"
|
||||||
|
class="el-picker-panel__icon-btn el-icon-d-arrow-left"></button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
v-if="unlinkPanels"
|
||||||
|
@click="leftNextYear"
|
||||||
|
:disabled="!enableYearArrow"
|
||||||
|
:class="{ 'is-disabled': !enableYearArrow }"
|
||||||
|
class="el-picker-panel__icon-btn el-icon-d-arrow-right"></button>
|
||||||
|
<div>{{ leftLabel }}</div>
|
||||||
|
</div>
|
||||||
|
<month-table
|
||||||
|
selection-mode="range"
|
||||||
|
:date="leftDate"
|
||||||
|
:default-value="defaultValue"
|
||||||
|
:min-date="minDate"
|
||||||
|
:max-date="maxDate"
|
||||||
|
:range-state="rangeState"
|
||||||
|
:disabled-date="disabledDate"
|
||||||
|
@changerange="handleChangeRange"
|
||||||
|
@pick="handleRangePick">
|
||||||
|
</month-table>
|
||||||
|
</div>
|
||||||
|
<div class="el-picker-panel__content el-date-range-picker__content is-right">
|
||||||
|
<div class="el-date-range-picker__header">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
v-if="unlinkPanels"
|
||||||
|
@click="rightPrevYear"
|
||||||
|
:disabled="!enableYearArrow"
|
||||||
|
:class="{ 'is-disabled': !enableYearArrow }"
|
||||||
|
class="el-picker-panel__icon-btn el-icon-d-arrow-left"></button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
@click="rightNextYear"
|
||||||
|
class="el-picker-panel__icon-btn el-icon-d-arrow-right"></button>
|
||||||
|
<div>{{ rightLabel }}</div>
|
||||||
|
</div>
|
||||||
|
<month-table
|
||||||
|
selection-mode="range"
|
||||||
|
:date="rightDate"
|
||||||
|
:default-value="defaultValue"
|
||||||
|
:min-date="minDate"
|
||||||
|
:max-date="maxDate"
|
||||||
|
:range-state="rangeState"
|
||||||
|
:disabled-date="disabledDate"
|
||||||
|
@changerange="handleChangeRange"
|
||||||
|
@pick="handleRangePick">
|
||||||
|
</month-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</transition>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script type="text/babel">
|
||||||
|
import {
|
||||||
|
isDate,
|
||||||
|
modifyWithTimeString,
|
||||||
|
prevYear,
|
||||||
|
nextYear,
|
||||||
|
nextMonth
|
||||||
|
} from '../util';
|
||||||
|
import Clickoutside from 'element-ui/src/utils/clickoutside';
|
||||||
|
import Locale from 'element-ui/src/mixins/locale';
|
||||||
|
import MonthTable from '../basic/month-table';
|
||||||
|
import ElInput from 'element-ui/packages/input';
|
||||||
|
import ElButton from 'element-ui/packages/button';
|
||||||
|
|
||||||
|
const calcDefaultValue = (defaultValue) => {
|
||||||
|
if (Array.isArray(defaultValue)) {
|
||||||
|
return [new Date(defaultValue[0]), new Date(defaultValue[1])];
|
||||||
|
} else if (defaultValue) {
|
||||||
|
return [new Date(defaultValue), nextMonth(new Date(defaultValue))];
|
||||||
|
} else {
|
||||||
|
return [new Date(), nextMonth(new Date())];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
export default {
|
||||||
|
mixins: [Locale],
|
||||||
|
|
||||||
|
directives: { Clickoutside },
|
||||||
|
|
||||||
|
computed: {
|
||||||
|
btnDisabled() {
|
||||||
|
return !(this.minDate && this.maxDate && !this.selecting && this.isValidValue([this.minDate, this.maxDate]));
|
||||||
|
},
|
||||||
|
|
||||||
|
leftLabel() {
|
||||||
|
return this.leftDate.getFullYear() + ' ' + this.t('el.datepicker.year');
|
||||||
|
},
|
||||||
|
|
||||||
|
rightLabel() {
|
||||||
|
return this.rightDate.getFullYear() + ' ' + this.t('el.datepicker.year');
|
||||||
|
},
|
||||||
|
|
||||||
|
leftYear() {
|
||||||
|
return this.leftDate.getFullYear();
|
||||||
|
},
|
||||||
|
|
||||||
|
rightYear() {
|
||||||
|
return this.rightDate.getFullYear() === this.leftDate.getFullYear() ? this.leftDate.getFullYear() + 1 : this.rightDate.getFullYear();
|
||||||
|
},
|
||||||
|
|
||||||
|
enableYearArrow() {
|
||||||
|
return this.unlinkPanels && this.rightYear > this.leftYear + 1;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
popperClass: '',
|
||||||
|
value: [],
|
||||||
|
defaultValue: null,
|
||||||
|
defaultTime: null,
|
||||||
|
minDate: '',
|
||||||
|
maxDate: '',
|
||||||
|
leftDate: new Date(),
|
||||||
|
rightDate: nextYear(new Date()),
|
||||||
|
rangeState: {
|
||||||
|
endDate: null,
|
||||||
|
selecting: false,
|
||||||
|
row: null,
|
||||||
|
column: null
|
||||||
|
},
|
||||||
|
shortcuts: '',
|
||||||
|
visible: '',
|
||||||
|
disabledDate: '',
|
||||||
|
format: '',
|
||||||
|
arrowControl: false,
|
||||||
|
unlinkPanels: false
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
value(newVal) {
|
||||||
|
if (!newVal) {
|
||||||
|
this.minDate = null;
|
||||||
|
this.maxDate = null;
|
||||||
|
} else if (Array.isArray(newVal)) {
|
||||||
|
this.minDate = isDate(newVal[0]) ? new Date(newVal[0]) : null;
|
||||||
|
this.maxDate = isDate(newVal[1]) ? new Date(newVal[1]) : null;
|
||||||
|
if (this.minDate) {
|
||||||
|
this.leftDate = this.minDate;
|
||||||
|
if (this.unlinkPanels && this.maxDate) {
|
||||||
|
const minDateYear = this.minDate.getFullYear();
|
||||||
|
const maxDateYear = this.maxDate.getFullYear();
|
||||||
|
this.rightDate = minDateYear === maxDateYear
|
||||||
|
? nextYear(this.maxDate)
|
||||||
|
: this.maxDate;
|
||||||
|
} else {
|
||||||
|
this.rightDate = nextYear(this.leftDate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.leftDate = calcDefaultValue(this.defaultValue)[0];
|
||||||
|
this.rightDate = nextYear(this.leftDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
defaultValue(val) {
|
||||||
|
if (!Array.isArray(this.value)) {
|
||||||
|
const [left, right] = calcDefaultValue(val);
|
||||||
|
this.leftDate = left;
|
||||||
|
this.rightDate = val && val[1] && left.getFullYear() !== right.getFullYear() && this.unlinkPanels
|
||||||
|
? right
|
||||||
|
: nextYear(this.leftDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
methods: {
|
||||||
|
handleClear() {
|
||||||
|
this.minDate = null;
|
||||||
|
this.maxDate = null;
|
||||||
|
this.leftDate = calcDefaultValue(this.defaultValue)[0];
|
||||||
|
this.rightDate = nextYear(this.leftDate);
|
||||||
|
this.$emit('pick', null);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleChangeRange(val) {
|
||||||
|
this.minDate = val.minDate;
|
||||||
|
this.maxDate = val.maxDate;
|
||||||
|
this.rangeState = val.rangeState;
|
||||||
|
},
|
||||||
|
|
||||||
|
handleRangePick(val, close = true) {
|
||||||
|
const defaultTime = this.defaultTime || [];
|
||||||
|
const minDate = modifyWithTimeString(val.minDate, defaultTime[0]);
|
||||||
|
const maxDate = modifyWithTimeString(val.maxDate, defaultTime[1]);
|
||||||
|
if (this.maxDate === maxDate && this.minDate === minDate) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.onPick && this.onPick(val);
|
||||||
|
this.maxDate = maxDate;
|
||||||
|
this.minDate = minDate;
|
||||||
|
|
||||||
|
// workaround for https://github.com/ElemeFE/element/issues/7539, should remove this block when we don't have to care about Chromium 55 - 57
|
||||||
|
setTimeout(() => {
|
||||||
|
this.maxDate = maxDate;
|
||||||
|
this.minDate = minDate;
|
||||||
|
}, 10);
|
||||||
|
if (!close) return;
|
||||||
|
this.handleConfirm();
|
||||||
|
},
|
||||||
|
|
||||||
|
handleShortcutClick(shortcut) {
|
||||||
|
if (shortcut.onClick) {
|
||||||
|
shortcut.onClick(this);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// leftPrev*, rightNext* need to take care of `unlinkPanels`
|
||||||
|
leftPrevYear() {
|
||||||
|
this.leftDate = prevYear(this.leftDate);
|
||||||
|
if (!this.unlinkPanels) {
|
||||||
|
this.rightDate = prevYear(this.rightDate);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
rightNextYear() {
|
||||||
|
if (!this.unlinkPanels) {
|
||||||
|
this.leftDate = nextYear(this.leftDate);
|
||||||
|
}
|
||||||
|
this.rightDate = nextYear(this.rightDate);
|
||||||
|
},
|
||||||
|
|
||||||
|
// leftNext*, rightPrev* are called when `unlinkPanels` is true
|
||||||
|
leftNextYear() {
|
||||||
|
this.leftDate = nextYear(this.leftDate);
|
||||||
|
},
|
||||||
|
|
||||||
|
rightPrevYear() {
|
||||||
|
this.rightDate = prevYear(this.rightDate);
|
||||||
|
},
|
||||||
|
|
||||||
|
handleConfirm(visible = false) {
|
||||||
|
if (this.isValidValue([this.minDate, this.maxDate])) {
|
||||||
|
this.$emit('pick', [this.minDate, this.maxDate], visible);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
isValidValue(value) {
|
||||||
|
return Array.isArray(value) &&
|
||||||
|
value && value[0] && value[1] &&
|
||||||
|
isDate(value[0]) && isDate(value[1]) &&
|
||||||
|
value[0].getTime() <= value[1].getTime() && (
|
||||||
|
typeof this.disabledDate === 'function'
|
||||||
|
? !this.disabledDate(value[0]) && !this.disabledDate(value[1])
|
||||||
|
: true
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
resetView() {
|
||||||
|
// NOTE: this is a hack to reset {min, max}Date on picker open.
|
||||||
|
// TODO: correct way of doing so is to refactor {min, max}Date to be dependent on value and internal selection state
|
||||||
|
// an alternative would be resetView whenever picker becomes visible, should also investigate date-panel's resetView
|
||||||
|
this.minDate = this.value && isDate(this.value[0]) ? new Date(this.value[0]) : null;
|
||||||
|
this.maxDate = this.value && isDate(this.value[0]) ? new Date(this.value[1]) : null;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
components: { MonthTable, ElInput, ElButton }
|
||||||
|
};
|
||||||
|
</script>
|
|
@ -114,6 +114,7 @@ const DEFAULT_FORMATS = {
|
||||||
week: 'yyyywWW',
|
week: 'yyyywWW',
|
||||||
timerange: 'HH:mm:ss',
|
timerange: 'HH:mm:ss',
|
||||||
daterange: 'yyyy-MM-dd',
|
daterange: 'yyyy-MM-dd',
|
||||||
|
monthrange: 'yyyy-MM',
|
||||||
datetimerange: 'yyyy-MM-dd HH:mm:ss',
|
datetimerange: 'yyyy-MM-dd HH:mm:ss',
|
||||||
year: 'yyyy'
|
year: 'yyyy'
|
||||||
};
|
};
|
||||||
|
@ -126,6 +127,7 @@ const HAVE_TRIGGER_TYPES = [
|
||||||
'month',
|
'month',
|
||||||
'year',
|
'year',
|
||||||
'daterange',
|
'daterange',
|
||||||
|
'monthrange',
|
||||||
'timerange',
|
'timerange',
|
||||||
'datetimerange',
|
'datetimerange',
|
||||||
'dates'
|
'dates'
|
||||||
|
@ -213,6 +215,10 @@ const TYPE_VALUE_RESOLVER_MAP = {
|
||||||
formatter: RANGE_FORMATTER,
|
formatter: RANGE_FORMATTER,
|
||||||
parser: RANGE_PARSER
|
parser: RANGE_PARSER
|
||||||
},
|
},
|
||||||
|
monthrange: {
|
||||||
|
formatter: RANGE_FORMATTER,
|
||||||
|
parser: RANGE_PARSER
|
||||||
|
},
|
||||||
datetimerange: {
|
datetimerange: {
|
||||||
formatter: RANGE_FORMATTER,
|
formatter: RANGE_FORMATTER,
|
||||||
parser: RANGE_PARSER
|
parser: RANGE_PARSER
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
import Picker from '../picker';
|
import Picker from '../picker';
|
||||||
import DatePanel from '../panel/date';
|
import DatePanel from '../panel/date';
|
||||||
import DateRangePanel from '../panel/date-range';
|
import DateRangePanel from '../panel/date-range';
|
||||||
|
import MonthRangePanel from '../panel/month-range';
|
||||||
|
|
||||||
const getPanel = function(type) {
|
const getPanel = function(type) {
|
||||||
if (type === 'daterange' || type === 'datetimerange') {
|
if (type === 'daterange' || type === 'datetimerange') {
|
||||||
return DateRangePanel;
|
return DateRangePanel;
|
||||||
|
} else if (type === 'monthrange') {
|
||||||
|
return MonthRangePanel;
|
||||||
}
|
}
|
||||||
return DatePanel;
|
return DatePanel;
|
||||||
};
|
};
|
||||||
|
|
|
@ -50,14 +50,11 @@
|
||||||
@include when(left) {
|
@include when(left) {
|
||||||
border-right: 1px solid $--datepicker-inner-border-color;
|
border-right: 1px solid $--datepicker-inner-border-color;
|
||||||
}
|
}
|
||||||
|
.el-date-range-picker__header {
|
||||||
|
|
||||||
@include when(right) {
|
div {
|
||||||
.el-date-range-picker__header {
|
margin-left: 50px;
|
||||||
|
margin-right: 50px;
|
||||||
div {
|
|
||||||
margin-left: 50px;
|
|
||||||
margin-right: 50px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,14 +7,22 @@
|
||||||
|
|
||||||
td {
|
td {
|
||||||
text-align: center;
|
text-align: center;
|
||||||
padding: 20px 3px;
|
padding: 8px 0px;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
& div {
|
||||||
|
height: 48px;
|
||||||
|
padding: 6px 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
&.today {
|
&.today {
|
||||||
.cell {
|
.cell {
|
||||||
color: $--color-primary;
|
color: $--color-primary;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
&.start-date .cell,
|
||||||
|
&.end-date .cell {
|
||||||
|
color: $--color-white;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.disabled .cell {
|
&.disabled .cell {
|
||||||
|
@ -28,18 +36,45 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.cell {
|
.cell {
|
||||||
width: 48px;
|
width: 60px;
|
||||||
height: 32px;
|
height: 36px;
|
||||||
display: block;
|
display: block;
|
||||||
line-height: 32px;
|
line-height: 36px;
|
||||||
color: $--datepicker-color;
|
color: $--datepicker-color;
|
||||||
margin: 0 auto;
|
margin: 0 auto;
|
||||||
|
border-radius: 18px;
|
||||||
&:hover {
|
&:hover {
|
||||||
color: $--datepicker-text-hover-color;
|
color: $--datepicker-text-hover-color;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&.in-range div {
|
||||||
|
background-color: $--datepicker-inrange-color;
|
||||||
|
&:hover {
|
||||||
|
background-color: $--datepicker-inrange-hover-color;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
&.start-date div,
|
||||||
|
&.end-date div {
|
||||||
|
color: $--color-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.start-date .cell,
|
||||||
|
&.end-date .cell {
|
||||||
|
color: $--color-white;
|
||||||
|
background-color: $--datepicker-active-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.start-date div {
|
||||||
|
border-top-left-radius: 24px;
|
||||||
|
border-bottom-left-radius: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.end-date div {
|
||||||
|
border-top-right-radius: 24px;
|
||||||
|
border-bottom-right-radius: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
&.current:not(.disabled) .cell {
|
&.current:not(.disabled) .cell {
|
||||||
color: $--datepicker-active-color;
|
color: $--datepicker-active-color;
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,13 @@
|
||||||
width: 220px;
|
width: 220px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@include m((monthrange)) {
|
||||||
|
&.el-input,
|
||||||
|
&.el-input__inner {
|
||||||
|
width: 300px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@include m((daterange, timerange)) {
|
@include m((daterange, timerange)) {
|
||||||
&.el-input,
|
&.el-input,
|
||||||
&.el-input__inner {
|
&.el-input__inner {
|
||||||
|
|
|
@ -2231,6 +2231,308 @@ describe('DatePicker', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('type:monthrange', () => {
|
||||||
|
it('works', done => {
|
||||||
|
vm = createVue({
|
||||||
|
template: '<el-date-picker type="monthrange" v-model="value" ref="compo" />',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
const rangePicker = vm.$refs.compo;
|
||||||
|
const inputs = rangePicker.$el.querySelectorAll('input');
|
||||||
|
inputs[0].focus();
|
||||||
|
|
||||||
|
setTimeout(_ => {
|
||||||
|
const panels = rangePicker.picker.$el.querySelectorAll('.el-date-range-picker__content');
|
||||||
|
expect(Array.prototype.slice.call(panels)).to.length(2);
|
||||||
|
panels[0].querySelector('td:not(.disabled)').click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
panels[1].querySelector('td:not(.disabled)').click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
inputs[0].focus();
|
||||||
|
setTimeout(_ => {
|
||||||
|
// correct highlight
|
||||||
|
const startDate = rangePicker.picker.$el.querySelectorAll('.start-date');
|
||||||
|
const endDate = rangePicker.picker.$el.querySelectorAll('.end-date');
|
||||||
|
const inRangeDate = rangePicker.picker.$el.querySelectorAll('.in-range');
|
||||||
|
expect(startDate.length).to.equal(1);
|
||||||
|
expect(endDate.length).to.equal(1);
|
||||||
|
expect(inRangeDate.length).to.above(0);
|
||||||
|
// value is array
|
||||||
|
expect(vm.value).to.be.an.instanceof(Array);
|
||||||
|
// input text is something like date string
|
||||||
|
expect(inputs[0].value.length).to.equal(7);
|
||||||
|
expect(inputs[1].value.length).to.equal(7);
|
||||||
|
done();
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('works: reverse selection', done => {
|
||||||
|
vm = createVue({
|
||||||
|
template: '<el-date-picker type="monthrange" v-model="value" ref="compo" />',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
const rangePicker = vm.$refs.compo;
|
||||||
|
const inputs = rangePicker.$el.querySelectorAll('input');
|
||||||
|
inputs[0].focus();
|
||||||
|
|
||||||
|
setTimeout(_ => {
|
||||||
|
const panels = rangePicker.picker.$el.querySelectorAll('.el-date-range-picker__content');
|
||||||
|
expect(Array.prototype.slice.call(panels)).to.length(2);
|
||||||
|
panels[1].querySelector('td:not(.disabled)').click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
panels[0].querySelector('td:not(.disabled)').click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
inputs[0].focus();
|
||||||
|
setTimeout(_ => {
|
||||||
|
// correct highlight
|
||||||
|
const startDate = rangePicker.picker.$el.querySelectorAll('.start-date');
|
||||||
|
const endDate = rangePicker.picker.$el.querySelectorAll('.end-date');
|
||||||
|
const inRangeDate = rangePicker.picker.$el.querySelectorAll('.in-range');
|
||||||
|
expect(startDate.length).to.equal(1);
|
||||||
|
expect(endDate.length).to.equal(1);
|
||||||
|
expect(inRangeDate.length).to.above(0);
|
||||||
|
// value is array
|
||||||
|
expect(vm.value).to.be.an.instanceof(Array);
|
||||||
|
// input text is something like date string
|
||||||
|
expect(inputs[0].value.length).to.equal(7);
|
||||||
|
expect(inputs[1].value.length).to.equal(7);
|
||||||
|
// result array is properly ordered
|
||||||
|
expect(vm.value[0].getTime() < vm.value[1].getTime()).to.be.true;
|
||||||
|
done();
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('type:monthrange unlink:true', done => {
|
||||||
|
vm = createVue({
|
||||||
|
template: '<el-date-picker type="monthrange" unlink-panels v-model="value" ref="compo" />',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: [new Date(2000, 9), new Date(2000, 10)]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
const rangePicker = vm.$refs.compo;
|
||||||
|
const inputs = rangePicker.$el.querySelectorAll('input');
|
||||||
|
setTimeout(_ => {
|
||||||
|
inputs[0].focus();
|
||||||
|
setTimeout(_ => {
|
||||||
|
const panels = rangePicker.picker.$el.querySelectorAll('.el-date-range-picker__content');
|
||||||
|
const left = panels[0].querySelector('.el-date-range-picker__header');
|
||||||
|
const right = panels[1].querySelector('.is-right .el-date-range-picker__header');
|
||||||
|
const leftText = left.textContent.match(/\d+/g).map(i => Number(i));
|
||||||
|
const rightText = right.textContent.match(/\d+/g).map(i => Number(i));
|
||||||
|
expect(rightText[0] - leftText[0]).to.equal(1); // one year
|
||||||
|
done();
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('unlink panels', done => {
|
||||||
|
vm = createTest(DatePicker, {
|
||||||
|
type: 'monthrange',
|
||||||
|
unlinkPanels: true
|
||||||
|
}, true);
|
||||||
|
const input = vm.$el.querySelector('input');
|
||||||
|
input.click();
|
||||||
|
|
||||||
|
setTimeout(_ => {
|
||||||
|
const panels = vm.picker.$el.querySelectorAll('.el-date-range-picker__content');
|
||||||
|
expect(Array.prototype.slice.call(panels)).to.length(2);
|
||||||
|
|
||||||
|
panels[1].querySelector('.el-icon-d-arrow-right').click();
|
||||||
|
|
||||||
|
setTimeout(_ => {
|
||||||
|
const left = panels[0].querySelector('.el-date-range-picker__header');
|
||||||
|
const right = panels[1].querySelector('.is-right .el-date-range-picker__header');
|
||||||
|
const leftText = left.textContent.match(/\d+/g).map(i => Number(i));
|
||||||
|
const rightText = right.textContent.match(/\d+/g).map(i => Number(i));
|
||||||
|
|
||||||
|
expect(rightText[0] - leftText[0]).to.equal(2);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('daylight saving time highlight', done => {
|
||||||
|
// Run test with environment variable TZ=Australia/Sydney
|
||||||
|
// The following test uses Australian Eastern Daylight Time (AEDT)
|
||||||
|
// AEST -> AEDT shift happened on 2016-10-02 02:00:00
|
||||||
|
vm = createVue({
|
||||||
|
template: '<el-date-picker type="monthrange" v-model="value" ref="compo" />',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: [new Date(2016, 6), new Date(2016, 12)]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
const rangePicker = vm.$refs.compo;
|
||||||
|
const inputs = rangePicker.$el.querySelectorAll('input');
|
||||||
|
inputs[0].focus();
|
||||||
|
|
||||||
|
setTimeout(_ => {
|
||||||
|
const startDate = rangePicker.picker.$el.querySelectorAll('.start-date');
|
||||||
|
const endDate = rangePicker.picker.$el.querySelectorAll('.end-date');
|
||||||
|
expect(startDate.length).to.equal(1);
|
||||||
|
expect(endDate.length).to.equal(1);
|
||||||
|
done();
|
||||||
|
}, DELAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('clear value', done => {
|
||||||
|
vm = createVue({
|
||||||
|
template: '<el-date-picker type="monthrange" v-model="value" ref="compo" />',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: [new Date(2000, 9), new Date(2000, 10)]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
vm.$el.querySelector('input').focus();
|
||||||
|
|
||||||
|
setTimeout(_ => {
|
||||||
|
vm.$refs.compo.showClose = true;
|
||||||
|
vm.$refs.compo.handleClickIcon({ stopPropagation: () => null });
|
||||||
|
setTimeout(_ => {
|
||||||
|
expect(vm.value).to.equal(null);
|
||||||
|
done();
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('change event', done => {
|
||||||
|
vm = createVue({
|
||||||
|
template: `
|
||||||
|
<el-date-picker
|
||||||
|
ref="compo"
|
||||||
|
v-model="value"
|
||||||
|
type="monthrange" />`,
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: ''
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
const spy = sinon.spy();
|
||||||
|
vm.$refs.compo.$on('change', spy);
|
||||||
|
|
||||||
|
const input = vm.$el.querySelector('input');
|
||||||
|
|
||||||
|
input.blur();
|
||||||
|
input.focus();
|
||||||
|
|
||||||
|
setTimeout(_ => {
|
||||||
|
const picker = vm.$refs.compo.picker;
|
||||||
|
setTimeout(_ => {
|
||||||
|
picker.$el.querySelector('td:not(.disabled)').click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
picker.$el.querySelector('td:not(.disabled) ~ td:not(.disabled)').click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
expect(spy.calledOnce).to.equal(true);
|
||||||
|
// change event is not emitted if used does not change value
|
||||||
|
// datarange also requires proper array equality check
|
||||||
|
input.blur();
|
||||||
|
input.focus();
|
||||||
|
setTimeout(_ => {
|
||||||
|
const startCell = picker.$el.querySelector('td.start-date');
|
||||||
|
const endCell = picker.$el.querySelector('td.end-date');
|
||||||
|
startCell.click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
endCell.click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
expect(spy.calledOnce).to.equal(true);
|
||||||
|
done();
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('default value', () => {
|
||||||
|
it('single', done => {
|
||||||
|
let defaultValue = '2000-10';
|
||||||
|
let expectValue = [new Date(2000, 9), new Date(2000, 10)];
|
||||||
|
|
||||||
|
vm = createVue({
|
||||||
|
template: '<el-date-picker type="monthrange" v-model="value" ref="compo" :default-value="defaultValue" />',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: '',
|
||||||
|
defaultValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
vm.$el.querySelector('input').focus();
|
||||||
|
setTimeout(_ => {
|
||||||
|
const $el = vm.$refs.compo.picker.$el;
|
||||||
|
const defaultEls = $el.querySelectorAll('.el-month-table td.default');
|
||||||
|
expect(defaultEls.length).to.equal(1);
|
||||||
|
defaultEls[0].click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
$el.querySelector('.el-month-table td.default + td').click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
expect(vm.value).to.eql(expectValue);
|
||||||
|
done();
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('array', done => {
|
||||||
|
let defaultValue = ['2000-01', '2000-03'];
|
||||||
|
let expectValue = [new Date(2000, 0), new Date(2000, 2)];
|
||||||
|
|
||||||
|
vm = createVue({
|
||||||
|
template: '<el-date-picker type="monthrange" v-model="value" ref="compo" :default-value="defaultValue" />',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
value: '',
|
||||||
|
defaultValue
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}, true);
|
||||||
|
|
||||||
|
vm.$el.querySelector('input').focus();
|
||||||
|
setTimeout(_ => {
|
||||||
|
const defaultEls = vm.$refs.compo.picker.$el.querySelectorAll('.el-month-table td.default');
|
||||||
|
expect(defaultEls.length).to.equal(2);
|
||||||
|
defaultEls[0].click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
defaultEls[1].click();
|
||||||
|
setTimeout(_ => {
|
||||||
|
expect(vm.value).to.eql(expectValue);
|
||||||
|
done();
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
}, DELAY);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
const currentMonth = new Date(new Date().getTime());
|
const currentMonth = new Date(new Date().getTime());
|
||||||
currentMonth.setDate(1);
|
currentMonth.setDate(1);
|
||||||
const chineseWeek = ['一', '二', '三', '四', '五', '六', '日'];
|
const chineseWeek = ['一', '二', '三', '四', '五', '六', '日'];
|
||||||
|
|
Loading…
Reference in New Issue