vue3实现日历
Vue3 实现日历组件
使用 Vue3 的 Composition API 和 <script setup> 语法实现一个基础日历组件。以下代码展示核心功能:日期渲染、月份切换和日期选择。
模板部分

<template>
<div class="calendar">
<div class="header">
<button @click="prevMonth">←</button>
<h2>{{ currentMonth }}</h2>
<button @click="nextMonth">→</button>
</div>
<div class="weekdays">
<div v-for="day in weekdays" :key="day" class="weekday">{{ day }}</div>
</div>
<div class="days">
<div
v-for="day in daysInMonth"
:key="day.date"
:class="{
'day': true,
'selected': day.date === selectedDate,
'other-month': !day.isCurrentMonth
}"
@click="selectDate(day.date)"
>
{{ day.day }}
</div>
</div>
</div>
</template>
逻辑部分
<script setup>
import { ref, computed } from 'vue';
const currentDate = ref(new Date());
const selectedDate = ref(null);
const weekdays = ['日', '一', '二', '三', '四', '五', '六'];
const currentMonth = computed(() => {
return currentDate.value.toLocaleString('zh-CN', {
year: 'numeric',
month: 'long'
});
});
const daysInMonth = computed(() => {
const year = currentDate.value.getFullYear();
const month = currentDate.value.getMonth();
// 获取当月第一天和最后一天
const firstDay = new Date(year, month, 1);
const lastDay = new Date(year, month + 1, 0);
// 获取当月天数
const days = [];
for (let i = 1; i <= lastDay.getDate(); i++) {
days.push({
day: i,
date: new Date(year, month, i).toDateString(),
isCurrentMonth: true
});
}
// 补充上个月末尾几天
const prevMonthDays = firstDay.getDay();
const prevMonthLastDay = new Date(year, month, 0).getDate();
for (let i = 0; i < prevMonthDays; i++) {
days.unshift({
day: prevMonthLastDay - i,
date: new Date(year, month - 1, prevMonthLastDay - i).toDateString(),
isCurrentMonth: false
});
}
// 补充下个月开头几天
const nextMonthDays = 6 - lastDay.getDay();
for (let i = 1; i <= nextMonthDays; i++) {
days.push({
day: i,
date: new Date(year, month + 1, i).toDateString(),
isCurrentMonth: false
});
}
return days;
});
function prevMonth() {
currentDate.value = new Date(
currentDate.value.getFullYear(),
currentDate.value.getMonth() - 1,
1
);
}
function nextMonth() {
currentDate.value = new Date(
currentDate.value.getFullYear(),
currentDate.value.getMonth() + 1,
1
);
}
function selectDate(date) {
selectedDate.value = date;
}
</script>
样式部分

<style scoped>
.calendar {
width: 300px;
border: 1px solid #eee;
border-radius: 8px;
padding: 10px;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.weekdays {
display: grid;
grid-template-columns: repeat(7, 1fr);
text-align: center;
font-weight: bold;
margin-bottom: 5px;
}
.days {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 5px;
}
.day {
height: 30px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
border-radius: 4px;
}
.day:hover {
background-color: #f0f0f0;
}
.selected {
background-color: #1890ff;
color: white;
}
.other-month {
color: #ccc;
}
</style>
功能扩展建议
- 日期范围选择
const rangeStart = ref(null); const rangeEnd = ref(null);
function selectDate(date) { if (!rangeStart.value || rangeEnd.value) { rangeStart.value = date; rangeEnd.value = null; } else if (date > rangeStart.value) { rangeEnd.value = date; } else { rangeEnd.value = rangeStart.value; rangeStart.value = date; } }
2. 添加事件标记
```vue
const events = ref({
'Wed May 15 2024': '会议',
'Fri May 17 2024': '生日'
});
// 在模板中添加事件显示
<div v-if="events[day.date]" class="event-marker"></div>
- 国际化支持
import { useI18n } from 'vue-i18n'; // 在setup中 const { t } = useI18n(); // 替换weekdays为翻译后的版本
性能优化
对于大型日历应用,考虑使用虚拟滚动技术处理大量日期渲染:
import { useVirtualList } from '@vueuse/core';
const { list, containerProps, wrapperProps } = useVirtualList(
daysInMonth,
{ itemHeight: 30 }
);
以上实现提供了日历的核心功能,可根据实际需求进一步扩展和完善。






