refactor(components): Improve calendar date range validate (#3156)

* fix(components): Improve calendar date range validate method

* chore(components): Add some comments and references

* fix(components): change `console.warn` to `warn`
This commit is contained in:
Aex 2021-09-03 09:59:45 +08:00 committed by GitHub
parent 8654e7d54a
commit 21cdf61d20
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 89 additions and 26 deletions

View File

@ -43,6 +43,18 @@ describe('Calendar.vue', () => {
expect(wrapper.element.querySelector('.el-calendar__button-group')).toBeNull()
})
// https://github.com/element-plus/element-plus/issues/3155
it('range when the start date will be calculated to last month', () => {
const wrapper = _mount(`
<el-calendar :range="[new Date(2021, 1, 2), new Date(2021, 1, 28)]"></el-calendar>
`)
const titleEl = wrapper.find('.el-calendar__title')
expect(/2021.*January/.test((titleEl.element as HTMLElement).innerHTML)).toBeTruthy()
const rows = wrapper.element.querySelectorAll('.el-calendar-table__row')
expect(rows.length).toBe(5)
expect(wrapper.element.querySelector('.el-calendar__button-group')).toBeNull()
})
it('range tow monthes', async() => {
const wrapper = _mount(`
<el-calendar :range="[new Date(2019, 3, 14), new Date(2019, 4, 18)]"></el-calendar>
@ -62,6 +74,26 @@ describe('Calendar.vue', () => {
expect(cell.classList.contains('is-selected')).toBeTruthy()
})
// https://github.com/element-plus/element-plus/issues/3155
it('range tow monthes when the start date will be calculated to last month', async() => {
const wrapper = _mount(`
<el-calendar :range="[new Date(2021, 1, 2), new Date(2021, 2, 21)]"></el-calendar>
`)
const titleEl = wrapper.find('.el-calendar__title')
expect(/2021.*January/.test((titleEl.element as HTMLElement).innerHTML)).toBeTruthy()
const dateTables = wrapper.element.querySelectorAll('.el-calendar-table.is-range')
expect(dateTables.length).toBe(3)
const rows = wrapper.element.querySelectorAll('.el-calendar-table__row')
expect(rows.length).toBe(8)
const cell = rows[rows.length - 1].firstElementChild;
(cell as HTMLElement).click()
await nextTick()
expect(/2021.*March/.test((titleEl.element as HTMLElement).innerHTML)).toBeTruthy()
expect(cell.classList.contains('is-selected')).toBeTruthy()
})
it('firstDayOfWeek', async () => {
// default en locale, weekStart 0 Sunday
const wrapper = _mount(`

View File

@ -64,6 +64,7 @@ import dayjs from 'dayjs'
import ElButton from '@element-plus/components/button'
import { useLocaleInject } from '@element-plus/hooks'
import { warn } from '@element-plus/utils/error'
import DateTable from './date-table.vue'
import type { Dayjs } from 'dayjs'
@ -149,47 +150,77 @@ export default defineComponent({
}
})
// https://github.com/element-plus/element-plus/issues/3155
// Calculate the validate date range according to the start and end dates
const calculateValidatedDateRange = (startDayjs: dayjs.Dayjs,endDayjs: dayjs.Dayjs) => {
const firstDay = startDayjs.startOf('week')
const lastDay = endDayjs.endOf('week')
const firstMonth = firstDay.get('month')
const lastMonth = lastDay.get('month')
// Current mouth
if(firstMonth === lastMonth){
return [[firstDay, lastDay]]
}
// Two adjacent months
else if(firstMonth + 1 === lastMonth ){
const firstMonthLastDay = firstDay.endOf('month')
const lastMonthFirstDay = lastDay.startOf('month')
// Whether the last day of the first month and the first day of the last month is in the same week
const isSameWeek = firstMonthLastDay.isSame(lastMonthFirstDay, 'week')
const lastMonthStartDay = isSameWeek ? lastMonthFirstDay.add(1, 'week') : lastMonthFirstDay
return [[firstDay, firstMonthLastDay], [lastMonthStartDay.startOf('week'), lastDay]]
}
// Three consecutive months (compatible: 2021-01-30 to 2021-02-28)
else if(firstMonth + 2 === lastMonth ){
const firstMonthLastDay = firstDay.endOf('month')
const secondMonthFisrtDay = firstDay.add(1,'month').startOf('month')
// Whether the last day of the first month and the second month is in the same week
const secondMonthStartDay = firstMonthLastDay.isSame(secondMonthFisrtDay, 'week') ? secondMonthFisrtDay.add(1, 'week') : secondMonthFisrtDay
const secondMonthLastDay = secondMonthStartDay.endOf('month')
const lastMonthFirstDay = lastDay.startOf('month')
// Whether the last day of the second month and the last day of the last month is in the same week
const lastMonthStartDay = secondMonthLastDay.isSame(lastMonthFirstDay, 'week') ? lastMonthFirstDay.add(1, 'week') : lastMonthFirstDay
return [
[firstDay, firstMonthLastDay],
[secondMonthStartDay.startOf('week'), secondMonthLastDay],
[lastMonthStartDay.startOf('week'), lastDay],
]
}
// Other cases
else {
warn('ElCalendar', 'start time and end time interval must not exceed two months')
return []
}
}
// if range is valid, we get a two-digit array
const validatedRange = computed(() => {
if (!props.range) return []
const rangeArrDayjs = props.range.map(_ => dayjs(_).locale(lang.value))
const [startDayjs, endDayjs] = rangeArrDayjs
if (startDayjs.isAfter(endDayjs)) {
console.warn(
'[ElementCalendar]end time should be greater than start time',
)
warn('ElCalendar', 'end time should be greater than start time')
return []
}
if (startDayjs.isSame(endDayjs, 'month')) {
// same month
return [[
startDayjs.startOf('week'),
endDayjs.endOf('week'),
]]
return calculateValidatedDateRange(startDayjs, endDayjs)
} else {
// two months
if (startDayjs.add(1, 'month').month() !== endDayjs.month()) {
console.warn(
'[ElementCalendar]start time and end time interval must not exceed two months',
)
warn('ElCalendar', 'start time and end time interval must not exceed two months')
return []
}
const endMonthFirstDay = endDayjs.startOf('month')
const endMonthFirstWeekDay = endMonthFirstDay.startOf('week')
let endMonthStart = endMonthFirstDay
if (!endMonthFirstDay.isSame(endMonthFirstWeekDay, 'month')) {
endMonthStart = endMonthFirstDay.endOf('week').add(1, 'day')
}
return [
[
startDayjs.startOf('week'),
startDayjs.endOf('month'),
],
[
endMonthStart,
endDayjs.endOf('week'),
],
]
return calculateValidatedDateRange(startDayjs, endDayjs)
}
})