This repository has been archived on 2025-09-14. You can view files and clone it, but cannot push or open issues or pull requests.
Files
nezha-nezha-fronted/nezha-fronted/src/components/common/myDatePicker/src/panel/date-range.vue
2021-05-06 21:40:56 +08:00

685 lines
22 KiB
Vue

<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,
'has-time': showTime
}, 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-date-range-picker__time-header" v-if="showTime">
<span class="el-date-range-picker__editors-wrap">
<span class="el-date-range-picker__time-picker-wrap">
<el-input
size="small"
:disabled="rangeState.selecting"
ref="minInput"
:placeholder="t('el.datepicker.startDate')"
class="el-date-range-picker__editor"
:value="minVisibleDate"
@input="val => handleDateInput(val, 'min')"
@change="val => handleDateChange(val, 'min')" />
</span>
<span class="el-date-range-picker__time-picker-wrap" v-clickoutside="handleMinTimeClose">
<el-input
size="small"
class="el-date-range-picker__editor"
:disabled="rangeState.selecting"
:placeholder="t('el.datepicker.startTime')"
:value="minVisibleTime"
@focus="minTimePickerVisible = true"
@input="val => handleTimeInput(val, 'min')"
@change="val => handleTimeChange(val, 'min')" />
<time-picker
ref="minTimePicker"
@pick="handleMinTimePick"
:time-arrow-control="arrowControl"
:visible="minTimePickerVisible"
@mounted="$refs.minTimePicker.format=timeFormat">
</time-picker>
</span>
</span>
<span class="el-icon-arrow-right"></span>
<span class="el-date-range-picker__editors-wrap is-right">
<span class="el-date-range-picker__time-picker-wrap">
<el-input
size="small"
class="el-date-range-picker__editor"
:disabled="rangeState.selecting"
:placeholder="t('el.datepicker.endDate')"
:value="maxVisibleDate"
:readonly="!minDate"
@input="val => handleDateInput(val, 'max')"
@change="val => handleDateChange(val, 'max')" />
</span>
<span class="el-date-range-picker__time-picker-wrap" v-clickoutside="handleMaxTimeClose">
<el-input
size="small"
class="el-date-range-picker__editor"
:disabled="rangeState.selecting"
:placeholder="t('el.datepicker.endTime')"
:value="maxVisibleTime"
:readonly="!minDate"
@focus="minDate && (maxTimePickerVisible = true)"
@input="val => handleTimeInput(val, 'max')"
@change="val => handleTimeChange(val, 'max')" />
<time-picker
ref="maxTimePicker"
@pick="handleMaxTimePick"
:time-arrow-control="arrowControl"
:visible="maxTimePickerVisible"
@mounted="$refs.maxTimePicker.format=timeFormat">
</time-picker>
</span>
</span>
</div>
<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"
@click="leftPrevMonth"
class="el-picker-panel__icon-btn el-icon-arrow-left"></button>
<button
type="button"
@click="leftNextYear"
v-if="unlinkPanels"
:disabled="!enableYearArrow"
:class="{ 'is-disabled': !enableYearArrow }"
class="el-picker-panel__icon-btn el-icon-d-arrow-right"></button>
<button
type="button"
@click="leftNextMonth"
v-if="unlinkPanels"
:disabled="!enableMonthArrow"
:class="{ 'is-disabled': !enableMonthArrow }"
class="el-picker-panel__icon-btn el-icon-arrow-right"></button>
<div>{{ leftLabel }}</div>
</div>
<date-table
:timezone="timezone"
selection-mode="range"
:date="leftDate"
:default-value="defaultValue"
:min-date="minDate"
:max-date="maxDate"
:range-state="rangeState"
:disabled-date="disabledDate"
:cell-class-name="cellClassName"
@changerange="handleChangeRange"
:first-day-of-week="firstDayOfWeek"
@pick="handleRangePick">
</date-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"
@click="rightPrevYear"
v-if="unlinkPanels"
:disabled="!enableYearArrow"
:class="{ 'is-disabled': !enableYearArrow }"
class="el-picker-panel__icon-btn el-icon-d-arrow-left"></button>
<button
type="button"
@click="rightPrevMonth"
v-if="unlinkPanels"
:disabled="!enableMonthArrow"
:class="{ 'is-disabled': !enableMonthArrow }"
class="el-picker-panel__icon-btn el-icon-arrow-left"></button>
<button
type="button"
@click="rightNextYear"
class="el-picker-panel__icon-btn el-icon-d-arrow-right"></button>
<button
type="button"
@click="rightNextMonth"
class="el-picker-panel__icon-btn el-icon-arrow-right"></button>
<div>{{ rightLabel }}</div>
</div>
<date-table
:timezone="timezone"
selection-mode="range"
:date="rightDate"
:default-value="defaultValue"
:min-date="minDate"
:max-date="maxDate"
:range-state="rangeState"
:disabled-date="disabledDate"
:cell-class-name="cellClassName"
@changerange="handleChangeRange"
:first-day-of-week="firstDayOfWeek"
@pick="handleRangePick">
</date-table>
</div>
</div>
</div>
<div class="el-picker-panel__footer" v-if="showTime">
<span class="timezone-area" :title='timezone'>{{timezone}}</span>
<span class="timezone-offset">UTC {{timezoneOffset}}</span>
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="text"-->
<!-- class="el-picker-panel__link-btn"-->
<!-- @click="handleClear">-->
<!-- {{ t('el.datepicker.clear') }}-->
<!-- </el-button>-->
<el-button
plain
size="mini"
class="el-picker-panel__link-btn"
:disabled="btnDisabled"
@click="handleConfirm(false)">
{{ t('el.datepicker.confirm') }}
</el-button>
</div>
</div>
</transition>
</template>
<script type="text/babel">
import {
formatDate,
parseDate,
isDate,
modifyDate,
modifyTime,
modifyWithTimeString,
prevYear,
nextYear,
prevMonth,
nextMonth,
nextDate,
extractDateFormat,
extractTimeFormat
} from 'element-ui/src/utils/date-util'
import Clickoutside from 'element-ui/src/utils/clickoutside'
import Locale from 'element-ui/src/mixins/locale'
import TimePicker from './time'
import DateTable from '../basic/date-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), nextDate(new Date(defaultValue), 1)]
} else {
return [new Date(), nextDate(new Date(), 1)]
}
}
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') + ' ' + this.t(`el.datepicker.month${this.leftDate.getMonth() + 1}`)
},
rightLabel () {
return this.rightDate.getFullYear() + ' ' + this.t('el.datepicker.year') + ' ' + this.t(`el.datepicker.month${this.rightDate.getMonth() + 1}`)
},
leftYear () {
return this.leftDate.getFullYear()
},
leftMonth () {
return this.leftDate.getMonth()
},
leftMonthDate () {
return this.leftDate.getDate()
},
rightYear () {
return this.rightDate.getFullYear()
},
rightMonth () {
return this.rightDate.getMonth()
},
rightMonthDate () {
return this.rightDate.getDate()
},
minVisibleDate () {
if (this.dateUserInput.min !== null) return this.dateUserInput.min
if (this.minDate) return formatDate(this.minDate, this.dateFormat)
return ''
},
maxVisibleDate () {
if (this.dateUserInput.max !== null) return this.dateUserInput.max
if (this.maxDate || this.minDate) return formatDate(this.maxDate || this.minDate, this.dateFormat)
return ''
},
minVisibleTime () {
if (this.timeUserInput.min !== null) return this.timeUserInput.min
if (this.minDate) return formatDate(this.minDate, this.timeFormat)
return ''
},
maxVisibleTime () {
if (this.timeUserInput.max !== null) return this.timeUserInput.max
if (this.maxDate || this.minDate) return formatDate(this.maxDate || this.minDate, this.timeFormat)
return ''
},
timeFormat () {
if (this.format) {
return extractTimeFormat(this.format)
} else {
return 'HH:mm:ss'
}
},
dateFormat () {
if (this.format) {
return extractDateFormat(this.format)
} else {
return 'yyyy-MM-dd'
}
},
enableMonthArrow () {
const nextMonth = (this.leftMonth + 1) % 12
const yearOffset = this.leftMonth + 1 >= 12 ? 1 : 0
return this.unlinkPanels && new Date(this.leftYear + yearOffset, nextMonth) < new Date(this.rightYear, this.rightMonth)
},
enableYearArrow () {
return this.unlinkPanels && this.rightYear * 12 + this.rightMonth - (this.leftYear * 12 + this.leftMonth + 1) >= 12
}
},
data () {
return {
popperClass: '',
value: [],
defaultValue: null,
defaultTime: null,
minDate: '',
maxDate: '',
leftDate: new Date(),
rightDate: nextMonth(new Date()),
rangeState: {
endDate: null,
selecting: false,
row: null,
column: null
},
showTime: false,
shortcuts: '',
visible: '',
disabledDate: '',
cellClassName: '',
firstDayOfWeek: 7,
minTimePickerVisible: false,
maxTimePickerVisible: false,
format: '',
arrowControl: false,
unlinkPanels: false,
dateUserInput: {
min: null,
max: null
},
timeUserInput: {
min: null,
max: null
}
}
},
watch: {
minDate (val) {
this.dateUserInput.min = null
this.timeUserInput.min = null
this.$nextTick(() => {
if (this.$refs.maxTimePicker && this.maxDate && this.maxDate < this.minDate) {
const format = 'HH:mm:ss'
this.$refs.maxTimePicker.selectableRange = [
[
parseDate(formatDate(this.minDate, format), format),
parseDate('23:59:59', format)
]
]
}
})
if (val && this.$refs.minTimePicker) {
this.$refs.minTimePicker.date = val
this.$refs.minTimePicker.value = val
}
},
maxDate (val) {
this.dateUserInput.max = null
this.timeUserInput.max = null
if (val && this.$refs.maxTimePicker) {
this.$refs.maxTimePicker.date = val
this.$refs.maxTimePicker.value = val
}
},
minTimePickerVisible (val) {
if (val) {
this.$nextTick(() => {
this.$refs.minTimePicker.date = this.minDate
this.$refs.minTimePicker.value = this.minDate
this.$refs.minTimePicker.adjustSpinners()
})
}
},
maxTimePickerVisible (val) {
if (val) {
this.$nextTick(() => {
this.$refs.maxTimePicker.date = this.maxDate
this.$refs.maxTimePicker.value = this.maxDate
this.$refs.maxTimePicker.adjustSpinners()
})
}
},
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 minDateMonth = this.minDate.getMonth()
const maxDateYear = this.maxDate.getFullYear()
const maxDateMonth = this.maxDate.getMonth()
this.rightDate = minDateYear === maxDateYear && minDateMonth === maxDateMonth
? nextMonth(this.maxDate)
: this.maxDate
} else {
this.rightDate = nextMonth(this.leftDate)
}
} else {
this.leftDate = calcDefaultValue(this.defaultValue)[0]
this.rightDate = nextMonth(this.leftDate)
}
}
},
defaultValue (val) {
if (!Array.isArray(this.value)) {
const [left, right] = calcDefaultValue(val)
this.leftDate = left
this.rightDate = val && val[1] && this.unlinkPanels
? right
: nextMonth(this.leftDate)
}
}
},
methods: {
handleClear () {
this.minDate = null
this.maxDate = null
this.leftDate = calcDefaultValue(this.defaultValue)[0]
this.rightDate = nextMonth(this.leftDate)
this.$emit('pick', null)
},
handleChangeRange (val) {
this.minDate = val.minDate
this.maxDate = val.maxDate
this.rangeState = val.rangeState
},
handleDateInput (value, type) {
this.dateUserInput[type] = value
if (value.length !== this.dateFormat.length) return
const parsedValue = parseDate(value, this.dateFormat)
if (parsedValue) {
if (typeof this.disabledDate === 'function' &&
this.disabledDate(new Date(parsedValue))) {
return
}
if (type === 'min') {
this.minDate = modifyDate(this.minDate || new Date(), parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate())
this.leftDate = new Date(parsedValue)
if (!this.unlinkPanels) {
this.rightDate = nextMonth(this.leftDate)
}
} else {
this.maxDate = modifyDate(this.maxDate || new Date(), parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate())
this.rightDate = new Date(parsedValue)
if (!this.unlinkPanels) {
this.leftDate = prevMonth(parsedValue)
}
}
}
},
handleDateChange (value, type) {
const parsedValue = parseDate(value, this.dateFormat)
if (parsedValue) {
if (type === 'min') {
this.minDate = modifyDate(this.minDate, parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate())
if (this.minDate > this.maxDate) {
this.maxDate = this.minDate
}
} else {
this.maxDate = modifyDate(this.maxDate, parsedValue.getFullYear(), parsedValue.getMonth(), parsedValue.getDate())
if (this.maxDate < this.minDate) {
this.minDate = this.maxDate
}
}
}
},
handleTimeInput (value, type) {
this.timeUserInput[type] = value
if (value.length !== this.timeFormat.length) return
const parsedValue = parseDate(value, this.timeFormat)
if (parsedValue) {
if (type === 'min') {
this.minDate = modifyTime(this.minDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds())
this.$nextTick(_ => this.$refs.minTimePicker.adjustSpinners())
} else {
this.maxDate = modifyTime(this.maxDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds())
this.$nextTick(_ => this.$refs.maxTimePicker.adjustSpinners())
}
}
},
handleTimeChange (value, type) {
const parsedValue = parseDate(value, this.timeFormat)
if (parsedValue) {
if (type === 'min') {
this.minDate = modifyTime(this.minDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds())
if (this.minDate > this.maxDate) {
this.maxDate = this.minDate
}
this.$refs.minTimePicker.value = this.minDate
this.minTimePickerVisible = false
} else {
this.maxDate = modifyTime(this.maxDate, parsedValue.getHours(), parsedValue.getMinutes(), parsedValue.getSeconds())
if (this.maxDate < this.minDate) {
this.minDate = this.maxDate
}
this.$refs.maxTimePicker.value = this.minDate
this.maxTimePickerVisible = false
}
}
},
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 || this.showTime) return
this.handleConfirm()
},
handleShortcutClick (shortcut) {
if (shortcut.onClick) {
shortcut.onClick(this)
}
},
handleMinTimePick (value, visible, first) {
this.minDate = this.minDate || new Date()
if (value) {
this.minDate = modifyTime(this.minDate, value.getHours(), value.getMinutes(), value.getSeconds())
}
if (!first) {
this.minTimePickerVisible = visible
}
if (!this.maxDate || this.maxDate && this.maxDate.getTime() < this.minDate.getTime()) {
this.maxDate = new Date(this.minDate)
}
},
handleMinTimeClose () {
this.minTimePickerVisible = false
},
handleMaxTimePick (value, visible, first) {
if (this.maxDate && value) {
this.maxDate = modifyTime(this.maxDate, value.getHours(), value.getMinutes(), value.getSeconds())
}
if (!first) {
this.maxTimePickerVisible = visible
}
if (this.maxDate && this.minDate && this.minDate.getTime() > this.maxDate.getTime()) {
this.minDate = new Date(this.maxDate)
}
},
handleMaxTimeClose () {
this.maxTimePickerVisible = false
},
// leftPrev*, rightNext* need to take care of `unlinkPanels`
leftPrevYear () {
this.leftDate = prevYear(this.leftDate)
if (!this.unlinkPanels) {
this.rightDate = nextMonth(this.leftDate)
}
},
leftPrevMonth () {
this.leftDate = prevMonth(this.leftDate)
if (!this.unlinkPanels) {
this.rightDate = nextMonth(this.leftDate)
}
},
rightNextYear () {
if (!this.unlinkPanels) {
this.leftDate = nextYear(this.leftDate)
this.rightDate = nextMonth(this.leftDate)
} else {
this.rightDate = nextYear(this.rightDate)
}
},
rightNextMonth () {
if (!this.unlinkPanels) {
this.leftDate = nextMonth(this.leftDate)
this.rightDate = nextMonth(this.leftDate)
} else {
this.rightDate = nextMonth(this.rightDate)
}
},
// leftNext*, rightPrev* are called when `unlinkPanels` is true
leftNextYear () {
this.leftDate = nextYear(this.leftDate)
},
leftNextMonth () {
this.leftDate = nextMonth(this.leftDate)
},
rightPrevYear () {
this.rightDate = prevYear(this.rightDate)
},
rightPrevMonth () {
this.rightDate = prevMonth(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
if (this.minDate && this.maxDate == null) this.rangeState.selecting = false
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: { TimePicker, DateTable, ElInput, ElButton }
}
</script>