基于vue实现日历
实现基础日历框架
使用Vue的v-for指令循环生成日历格子,结合日期计算库(如date-fns)处理月份切换逻辑。初始化当前月份数据,计算该月天数及起始星期位置。
<template>
<div class="calendar">
<div class="header">
<button @click="prevMonth">上个月</button>
<h3>{{ currentMonth }}</h3>
<button @click="nextMonth">下个月</button>
</div>
<div class="weekdays">
<div v-for="day in weekdays" :key="day">{{ day }}</div>
</div>
<div class="days">
<div
v-for="(day, index) in days"
:key="index"
:class="{ 'other-month': !day.isCurrentMonth }"
>
{{ day.date }}
</div>
</div>
</div>
</template>
<script>
import { format, addMonths, startOfMonth, endOfMonth, eachDayOfInterval, getDay } from 'date-fns'
export default {
data() {
return {
currentDate: new Date(),
weekdays: ['日', '一', '二', '三', '四', '五', '六']
}
},
computed: {
currentMonth() {
return format(this.currentDate, 'yyyy年MM月')
},
days() {
const start = startOfMonth(this.currentDate)
const end = endOfMonth(this.currentDate)
const daysInMonth = eachDayOfInterval({ start, end })
// 补齐前后空白日期
const startDay = getDay(start)
const prevMonthDays = Array(startDay).fill(null)
.map((_, i) => ({
date: '',
isCurrentMonth: false
}))
const currentMonthDays = daysInMonth.map(date => ({
date: format(date, 'd'),
isCurrentMonth: true,
fullDate: date
}))
return [...prevMonthDays, ...currentMonthDays]
}
},
methods: {
prevMonth() {
this.currentDate = addMonths(this.currentDate, -1)
},
nextMonth() {
this.currentDate = addMonths(this.currentDate, 1)
}
}
}
</script>
<style>
.calendar {
width: 350px;
font-family: Arial;
}
.header {
display: flex;
justify-content: space-between;
align-items: center;
}
.weekdays, .days {
display: grid;
grid-template-columns: repeat(7, 1fr);
}
.days div {
height: 40px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid #eee;
}
.other-month {
background: #f9f9f9;
color: #ccc;
}
</style>
添加日期选择功能
扩展基础日历,增加日期选中状态和点击事件处理。使用v-model实现双向数据绑定,支持外部控制选中日期。
<script>
export default {
props: {
value: Date
},
data() {
return {
selectedDate: this.value || null
}
},
watch: {
value(newVal) {
this.selectedDate = newVal
}
},
methods: {
selectDate(day) {
if (!day.isCurrentMonth) return
this.selectedDate = day.fullDate
this.$emit('input', day.fullDate)
}
}
}
</script>
<template>
<!-- 在days的div中添加 -->
<div
@click="selectDate(day)"
:class="{
'selected': selectedDate && day.fullDate &&
format(selectedDate, 'yyyy-MM-dd') === format(day.fullDate, 'yyyy-MM-dd')
}"
>
{{ day.date }}
</div>
</template>
<style>
.selected {
background: #42b983;
color: white;
}
</style>
实现事件标记功能
添加事件数据支持,在特定日期显示标记点。通过计算属性筛选当前月份的事件,按日期分组渲染。
<script>
export default {
props: {
events: {
type: Array,
default: () => []
}
},
computed: {
eventMap() {
return this.events.reduce((map, event) => {
const dateKey = format(event.date, 'yyyy-MM-dd')
map[dateKey] = event
return map
}, {})
}
}
}
</script>
<template>
<div>
{{ day.date }}
<div v-if="day.fullDate" class="event-dot"
:style="{ backgroundColor: eventMap[format(day.fullDate, 'yyyy-MM-dd')]?.color }">
</div>
</div>
</template>
<style>
.event-dot {
width: 6px;
height: 6px;
border-radius: 50%;
margin: 2px auto 0;
}
</style>
支持多语言和自定义样式
通过props接受自定义配置,使组件更具扩展性。添加i18n支持,允许覆盖默认的星期显示和日期格式。
<script>
export default {
props: {
locale: {
type: Object,
default: () => ({
weekdays: ['日', '一', '二', '三', '四', '五', '六'],
monthFormat: 'yyyy年MM月'
})
},
theme: {
type: Object,
default: () => ({
selectedColor: '#42b983',
todayColor: '#ffeb3b'
})
}
},
computed: {
currentMonth() {
return format(this.currentDate, this.locale.monthFormat)
},
weekdays() {
return this.locale.weekdays
}
}
}
</script>
完整组件集成示例
将上述功能整合为可直接使用的组件,暴露必要的事件和props接口。
<template>
<div class="calendar" :style="{ '--selected-color': theme.selectedColor }">
<!-- 组合所有模板部分 -->
</div>
</template>
<script>
export default {
name: 'VueCalendar',
props: {
value: Date,
events: Array,
locale: Object,
theme: Object
},
// 组合所有脚本部分
}
</script>
<style scoped>
/* 组合所有样式部分 */
.selected {
background: var(--selected-color);
}
</style>
使用时通过props传递配置和事件:
<vue-calendar
v-model="selectedDate"
:events="calendarEvents"
:locale="{ weekdays: ['Sun', 'Mon', ...], monthFormat: 'MMM yyyy' }"
@date-click="handleDateClick"
/>






