Implementing a Custom Vue Calendar Component with Date Generation and Styling
This article explains how to create a reusable Vue calendar component by designing its data structure, generating a 7×6 date matrix, implementing navigation props and events, and applying customizable styles, providing complete code examples for template, script, and scoped CSS.
The article introduces a practical solution for building a calendar component in large-screen projects where third‑party UI libraries often cannot meet custom styling requirements.
Core Idea : The calendar is essentially a 7×6 grid of dates; by calculating the first visible date, the rest of the grid can be generated by incrementing one day at a time, with simple style handling for non‑current‑month cells.
Data Structure : Each date is represented as an object containing label (displayed day), date (full YYYY‑MM‑DD string), active (whether the date belongs to the selected month), and isCurrent (whether it is today). The whole grid is an array of rows, each row holding a time (the first day of the week) and a children array of date objects.
{
label: '30',
date: '2024-06-30',
active: false,
isCurrent: false
}Generating the Date List : The function getTimeListByYearAndMonth obtains the first day of the selected month, determines its weekday, computes the calendar’s start date, and then iterates over six weeks and seven days to fill the grid.
const getTimeListByYearAndMonth = () => {
let selectDay = `${year.value}-${month.value}-01`;
const weekDay = dayjs(selectDay).day();
const firstDay = dayjs(selectDay).subtract(weekDay, 'day');
const dayList = [];
for (let i = 0; i < 6; i++) {
const childrenList = [];
for (let time = 0; time < 7; time++) {
let day = dayjs(firstDay).add(i * 7 + time, 'day');
const date = day.format('YYYY-MM-DD');
childrenList.push({
label: day.format('D'),
date,
active: Number(day.format('M')) == month.value,
isCurrent: date === dayjs().format('YYYY-MM-DD')
});
}
dayList.push({
time: dayjs(firstDay).add(i * 7, 'day').format('YYYY-MM-DD'),
children: childrenList
});
}
return dayList;
};Component Props and Events : The component accepts year , month , disabledFutureDay , and highlightCurrentDay as props, and emits yearChange , monthChange , and dateChange events with detailed time information.
interface Props {
year?: number;
month?: 1|2|3|4|5|6|7|8|9|10|11|12;
disabledFutureDay?: boolean;
highlightCurrentDay?: boolean;
}
const emit = defineEmits<{
yearChange: [params: TimeEmitInfo];
monthChange: [params: TimeEmitInfo];
dateChange: [params: TimeEmitInfo];
}>();Rendering Logic : The template renders navigation arrows, the month/year header, weekday labels, and the date grid. Conditional classes highlight the selected date, the current day, and optional dot markers.
<template>
<div class="schedule-calendar">
... (navigation UI) ...
<div class="week-day">
<span>日</span><span>一</span>...<span>六</span>
</div>
<div class="calendar-wrap">
<div class="day-list" v-for="time in dayList">
<div class="day-cell" v-for="item in time.children" :class="{select: item.date===selectDate}" @click="selectDay(item.date)">
<span class="day" :class="[ {active: item.active}, {current: item.isCurrent && props.highlightCurrentDay} ]">{{item.label}}</span>
<span :class="{dot: isShowDot(item.date)}"></span>
</div>
</div>
</div>
</div>
</template>Full Component Code : The article provides the complete <template> , <script lang="ts" setup> , and scoped <style lang="less" scoped> sections, ready to be copied into a Vue project.
Conclusion : By following the presented approach, developers can quickly integrate a fully functional, customizable calendar component into their applications, adapting styles and behavior to specific business needs.
Rare Earth Juejin Tech Community
Juejin, a tech community that helps developers grow.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.