Custom Calendar with Element UI
To create a custom calendar with Element UI, we customize date cells to display additional data (e.g., people count, working hours) and control visible dates (only the target month: previous, current, or next).
Template Structure
<el-calendar v-model="currentMonthFirstDay">
<template
slot="dateCell"
slot-scope="{ date, data }"
>
<!-- Render date only if it's in the valid month -->
<template v-if="validDates.includes(data.day)">
<div class="date-title">
{{ data.day.split('-').slice(2).join('-') }}
<i class="el-icon-edit"></i>
</div>
<!-- Render data if available for this date -->
<template v-if="calendarDataMap[data.day]">
<div class="calendar-left-content blue">
<div>People</div>
<div>{{ calendarDataMap[data.day].numPeople }} people</div>
</div>
<div class="calendar-left-content orange">
<div>Hours</div>
<div>{{ calendarDataMap[data.day].workingHours }} hours</div>
</div>
</template>
</template>
</template>
</el-calendar>
### Computed Property: Valid Dates
The `validDates` property generates a list of dates for the selected month (previous, current, or next) to control which dates appear in the calendar:
```javascript
validDates() {
const now = this.currentDate; // Assumes `currentDate` is a Date instance
let monthOffset = 0;
switch (this.tabPosition) {
case 'lastMonth':
monthOffset = -1;
break;
case 'thisMonth':
monthOffset = 0;
break;
case 'nextMonth':
monthOffset = 1;
break;
default:
monthOffset = 0;
}
let year = now.getFullYear();
let month = now.getMonth() + 1 + monthOffset; // Convert to 1-based month
// Adjust year for month overflow (e.g., month 13 → year +1, month 1)
if (month > 12) {
year += 1;
month = 1;
} else if (month < 1) {
year -= 1;
month = 12;
}
// Get total days in the target month
const daysInMonth = new Date(year, month, 0).getDate();
const validDatesList = [];
// Generate all date in the month (formatted as YYYY-MM-DD)
for (let day = 1; day <= daysInMonth; day++) {
const formattedDay = String(day).padStart(2, '0');
const formattedMonth = String(month).padStart(2, '0');
const dateStr = `${year}-${formattedMonth}-${formattedDay}`;
validDatesList.push(dateStr);
}
return validDatesList;
}
Mapping Calendar Data
To efficiently access data by date, we restructure the backend response into a map (using reduce):
processCalendarData() {
this.calendarDataMap = this.calendarDatas.reduce((acc, item) => {
acc[item.data] = { ...item }; // `item.data` is the date string (YYYY-MM-DD)
return acc;
}, {});
}
CSS: Disable Non-Target Month Dates
To ensure only the target month’s dates are interactive, we disable clicks on dates from other months:
/* Hide/Disable dates from other months */
:deep(.el-calendar-table td.next) {
pointer-events: none;
display: none;
}
:deep(.el-calendar-table td.prev) {
pointer-events: none;
}
Key Logic
- Date Validation: The
validDatesproperty ensures only dates from the selected month are rendered. - Data Lookup:
calendarDataMapalllows quick access to date-specific data (e.g., people, hours) in the template. - Interaction Control: CSS disables interaction with non-target month dates to enforce the calender’s scope.