element-plus/packages/components/calendar/src/date-table.vue

207 lines
5.2 KiB
Vue
Raw Normal View History

<template>
<table
:class="{
'el-calendar-table': true,
'is-range': isInRange
}"
cellspacing="0"
cellpadding="0"
>
<thead v-if="!hideHeader">
<th v-for="day in weekDays" :key="day">{{ day }}</th>
</thead>
<tbody>
<tr
v-for="(row, index) in rows"
:key="index"
:class="{
'el-calendar-table__row': true,
'el-calendar-table__row--hide-border': index === 0 && hideHeader
}"
>
<td
v-for="(cell, key) in row"
:key="key"
:class="getCellClass(cell)"
@click="pickDay(cell)"
>
<div class="el-calendar-day">
<slot
name="dateCell"
:data="getSlotData(cell)"
>
<span>{{ cell.text }}</span>
</slot>
</div>
</td>
</tr>
</tbody>
</table>
</template>
<script lang="ts">
import {
computed,
defineComponent,
ref,
} from 'vue'
import { PropType } from 'vue'
import dayjs, { Dayjs } from 'dayjs'
import localeData from 'dayjs/plugin/localeData'
import { useLocaleInject } from '@element-plus/hooks'
import { rangeArr } from '@element-plus/components/time-picker'
dayjs.extend(localeData)
export const getPrevMonthLastDays = (date: Dayjs, amount) => {
const lastDay = date.subtract(1, 'month').endOf('month').date()
return rangeArr(amount).map((_, index) => lastDay - (amount - index - 1))
}
export const getMonthDays = (date: Dayjs) => {
const days = date.daysInMonth()
return rangeArr(days).map((_, index) => index + 1)
}
export default defineComponent({
props: {
selectedDay: {
2020-12-09 12:00:43 +08:00
type: Object as PropType<Dayjs>,
},
range: {
type: Array as PropType<Array<Dayjs>>,
},
date: {
2020-12-09 12:00:43 +08:00
type: Object as PropType<Dayjs>,
},
hideHeader: {
type: Boolean,
},
},
emits: ['pick'],
setup(props, ctx) {
const { lang } = useLocaleInject()
const WEEK_DAYS = ref(dayjs().locale(lang.value).localeData().weekdaysShort())
const now = dayjs().locale(lang.value)
// todo better way to get Day.js locale object
const firstDayOfWeek = (now as any).$locale().weekStart || 0
const toNestedArr = days => {
return rangeArr(days.length / 7).map((_, index) => {
const start = index * 7
return days.slice(start, start + 7)
})
}
const getFormattedDate = (day, type): Dayjs => {
let result
if (type === 'prev') {
result = props.date.startOf('month').subtract(1, 'month').date(day)
} else if (type === 'next') {
result = props.date.startOf('month').add(1, 'month').date(day)
} else {
result = props.date.date(day)
}
return result
}
const getCellClass = ({ text, type }) => {
const classes = [type]
if (type === 'current') {
const date_ = getFormattedDate(text, type)
if (date_.isSame(props.selectedDay, 'day')) {
classes.push('is-selected')
}
if (date_.isSame(now, 'day')) {
classes.push('is-today')
}
}
return classes
}
const pickDay = ({ text, type }) => {
const date = getFormattedDate(text, type)
ctx.emit('pick', date)
}
const getSlotData = ({ text, type }) => {
const day = getFormattedDate(text, type)
return {
isSelected: day.isSame(props.selectedDay),
type: `${type}-month`,
day: day.format('YYYY-MM-DD'),
date: day.toDate(),
}
}
const isInRange = computed(() => {
return props.range && props.range.length
})
const rows = computed(() => {
let days = []
if (isInRange.value) {
const [start, end] = props.range
const currentMonthRange = rangeArr(
end.date() - start.date() + 1,
).map((_, index) => ({
text: start.date() + index,
type: 'current',
}))
let remaining = currentMonthRange.length % 7
remaining = remaining === 0 ? 0 : 7 - remaining
const nextMonthRange = rangeArr(remaining).map((_, index) => ({
text: index + 1,
type: 'next',
}))
days = currentMonthRange.concat(nextMonthRange)
} else {
const firstDay = props.date.startOf('month').day() || 7
const prevMonthDays = getPrevMonthLastDays(
props.date,
firstDay - firstDayOfWeek,
).map(day => ({
text: day,
type: 'prev',
}))
const currentMonthDays = getMonthDays(props.date).map(day => ({
text: day,
type: 'current',
}))
days = [...prevMonthDays, ...currentMonthDays]
const nextMonthDays = rangeArr(42 - days.length).map((_, index) => ({
text: index + 1,
type: 'next',
}))
days = days.concat(nextMonthDays)
}
return toNestedArr(days)
})
const weekDays = computed(() => {
const start = firstDayOfWeek
if (start === 0) {
return WEEK_DAYS.value
} else {
return WEEK_DAYS.value
.slice(start)
.concat(WEEK_DAYS.value.slice(0, start))
}
})
return {
isInRange,
weekDays,
rows,
getCellClass,
pickDay,
getSlotData,
}
},
})
</script>