使用vue2 实现一个日历
时间: 2025-07-20 18:24:04 AIGC 浏览: 15
<think>我们需要实现一个日历组件,主要功能包括:
1. 显示当前月份的日历,包括上个月的部分日期和下个月的部分日期(以完整周显示)
2. 能够切换月份
3. 可以标记当前日期
设计思路:
- 使用Vue2的选项式API
- 数据:当前日期(用于确定当前显示的月份和年份)、选中的日期(可选)
- 计算属性:生成当前月份日历的数组(42个日期,因为6周*7天=42,包括上个月末和下个月初)
- 方法:切换上个月、下个月,回到今天
步骤:
1. 初始化:设置当前日期为今天,并生成日历数组
2. 生成日历数组的方法:
a. 获取当前月份的第一天和最后一天
b. 获取当前月份第一天是星期几(0代表星期日,1代表星期一,...,6代表星期六)
c. 生成一个长度为42的数组(6周)
d. 数组的前几天是上个月的最后几天,然后是当月的所有天,最后是下个月的前几天
3. 模板:使用表格展示,分为表头(星期几)和日期部分(6行7列)
4. 样式:对当前日期进行特殊标记,非本月日期用灰色显示
5. 添加切换按钮:上个月、下个月、今天
代码实现如下:</think># Vue2 日历组件实现
下面我将实现一个功能完整的日历组件,包含月份切换、日期选择、当前日期标记等功能。
## 解决方案
```html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue2 日历组件</title>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vue.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a6c, #b21f1f, #fdbb2d);
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.calendar-container {
max-width: 800px;
width: 100%;
background: rgba(255, 255, 255, 0.95);
border-radius: 20px;
box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3);
overflow: hidden;
}
.calendar-header {
background: linear-gradient(to right, #3498db, #2c3e50);
color: white;
padding: 25px;
text-align: center;
position: relative;
}
.calendar-title {
font-size: 2.2rem;
margin-bottom: 10px;
letter-spacing: 1px;
}
.nav-controls {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 15px;
}
.nav-btn {
background: rgba(255, 255, 255, 0.2);
border: none;
color: white;
padding: 12px 20px;
border-radius: 50px;
cursor: pointer;
font-size: 1.1rem;
font-weight: 600;
transition: all 0.3s ease;
display: flex;
align-items: center;
gap: 8px;
}
.nav-btn:hover {
background: rgba(255, 255, 255, 0.3);
transform: translateY(-2px);
}
.current-month {
font-size: 1.8rem;
font-weight: 600;
padding: 0 20px;
}
.weekdays {
display: grid;
grid-template-columns: repeat(7, 1fr);
background: #f5f5f5;
padding: 15px 0;
font-weight: bold;
color: #2c3e50;
border-bottom: 1px solid #eee;
}
.weekday {
text-align: center;
font-size: 1.2rem;
}
.days-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 8px;
padding: 20px;
}
.day {
height: 80px;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: flex-start;
padding: 10px;
border-radius: 10px;
background: #f8f9fa;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
overflow: hidden;
}
.day:hover {
background: #e3f2fd;
transform: translateY(-3px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.1);
}
.day.other-month {
color: #aaa;
background: #f0f0f0;
}
.day.today {
background: #e1f5fe;
border: 2px solid #29b6f6;
}
.day.selected {
background: #bbdefb;
border: 2px solid #2196f3;
}
.day-number {
font-size: 1.4rem;
font-weight: 600;
}
.day-events {
font-size: 0.8rem;
color: #2196f3;
width: 100%;
text-align: center;
}
.event-dot {
display: inline-block;
width: 8px;
height: 8px;
background: #2196f3;
border-radius: 50%;
margin: 0 1px;
}
.footer {
padding: 20px;
text-align: center;
background: #f8f9fa;
border-top: 1px solid #eee;
display: flex;
justify-content: center;
gap: 15px;
}
.today-btn {
background: linear-gradient(to right, #00c853, #009624);
color: white;
border: none;
padding: 12px 25px;
border-radius: 50px;
cursor: pointer;
font-size: 1.1rem;
font-weight: 600;
transition: all 0.3s ease;
}
.today-btn:hover {
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
}
.selected-date {
font-size: 1.2rem;
padding: 10px 20px;
background: #e3f2fd;
border-radius: 50px;
color: #0d47a1;
font-weight: 600;
}
@media (max-width: 768px) {
.day {
height: 60px;
padding: 5px;
}
.day-number {
font-size: 1.1rem;
}
.calendar-title {
font-size: 1.8rem;
}
.current-month {
font-size: 1.4rem;
}
}
@media (max-width: 480px) {
.day {
height: 50px;
}
.weekday {
font-size: 0.9rem;
}
.nav-btn {
padding: 8px 15px;
font-size: 0.9rem;
}
.today-btn {
padding: 10px 15px;
font-size: 0.9rem;
}
}
</style>
</head>
<body>
<div id="app">
<div class="calendar-container">
<div class="calendar-header">
<h1 class="calendar-title">Vue2 日历组件</h1>
<div class="nav-controls">
<button class="nav-btn" @click="prevYear">
<span>«</span> 去年
</button>
<button class="nav-btn" @click="prevMonth">
<span>‹</span> 上月
</button>
<div class="current-month">{{ displayMonth }}</div>
<button class="nav-btn" @click="nextMonth">
下月 <span>›</span>
</button>
<button class="nav-btn" @click="nextYear">
明年 <span>»</span>
</button>
</div>
</div>
<div class="weekdays">
<div class="weekday" v-for="day in weekdays" :key="day">{{ day }}</div>
</div>
<div class="days-grid">
<div
v-for="(day, index) in calendarDays"
:key="index"
class="day"
:class="{
'other-month': !day.isCurrentMonth,
'today': day.isToday,
'selected': day.isSelected
}"
@click="selectDate(day)"
>
<div class="day-number">{{ day.date }}</div>
<div class="day-events" v-if="day.hasEvents">
<span class="event-dot"></span>
<span class="event-dot"></span>
<span class="event-dot"></span>
</div>
</div>
</div>
<div class="footer">
<button class="today-btn" @click="goToToday">返回今天</button>
<div class="selected-date" v-if="selectedDate">
已选日期: {{ selectedDate.toLocaleDateString() }}
</div>
</div>
</div>
</div>
<script>
new Vue({
el: '#app',
data: {
currentDate: new Date(),
selectedDate: null,
weekdays: ['周日', '周一', '周二', '周三', '周四', '周五', '周六']
},
computed: {
// 当前显示的年份和月份
displayMonth() {
return this.currentDate.toLocaleDateString('zh-CN', {
year: 'numeric',
month: 'long'
});
},
// 生成日历数组
calendarDays() {
const year = this.currentDate.getFullYear();
const month = this.currentDate.getMonth();
// 获取当月第一天
const firstDay = new Date(year, month, 1);
// 获取当月最后一天
const lastDay = new Date(year, month + 1, 0);
// 获取当月第一天是星期几(0是周日)
const firstDayOfWeek = firstDay.getDay();
// 获取当月总天数
const daysInMonth = lastDay.getDate();
// 获取上个月最后一天
const prevMonthLastDay = new Date(year, month, 0).getDate();
// 生成日历数组
const days = [];
// 添加上个月末尾的几天
for (let i = firstDayOfWeek - 1; i >= 0; i--) {
const date = prevMonthLastDay - i;
days.push(this.createDayObject(year, month - 1, date, false));
}
// 添加当月的所有天
for (let i = 1; i <= daysInMonth; i++) {
days.push(this.createDayObject(year, month, i, true));
}
// 添加下个月开始的几天(补齐42个格子)
const totalCells = 42; // 6行 * 7列
const nextDaysCount = totalCells - days.length;
for (let i = 1; i <= nextDaysCount; i++) {
days.push(this.createDayObject(year, month + 1, i, false));
}
return days;
}
},
methods: {
// 创建日期对象
createDayObject(year, month, date, isCurrentMonth) {
const dayDate = new Date(year, month, date);
const today = new Date();
// 检查是否是今天
const isToday = today.getFullYear() === year &&
today.getMonth() === month &&
today.getDate() === date;
// 检查是否是选中的日期
const isSelected = this.selectedDate &&
this.selectedDate.getFullYear() === year &&
this.selectedDate.getMonth() === month &&
this.selectedDate.getDate() === date;
// 随机生成一些事件标记
const hasEvents = Math.random() > 0.7;
return {
date,
dayDate,
isCurrentMonth,
isToday,
isSelected,
hasEvents
};
},
// 选择日期
selectDate(day) {
this.selectedDate = day.dayDate;
},
// 返回今天
goToToday() {
this.currentDate = new Date();
this.selectedDate = new Date();
},
// 上个月
prevMonth() {
this.currentDate = new Date(
this.currentDate.getFullYear(),
this.currentDate.getMonth() - 1,
1
);
},
// 下个月
nextMonth() {
this.currentDate = new Date(
this.currentDate.getFullYear(),
this.currentDate.getMonth() + 1,
1
);
},
// 去年
prevYear() {
this.currentDate = new Date(
this.currentDate.getFullYear() - 1,
this.currentDate.getMonth(),
1
);
},
// 明年
nextYear() {
this.currentDate = new Date(
this.currentDate.getFullYear() + 1,
this.currentDate.getMonth(),
1
);
}
},
mounted() {
// 初始化时选中今天
this.selectedDate = new Date();
}
});
</script>
</body>
</html>
```
## 代码解释
### 核心功能实现
1. **数据模型**:
- `currentDate`: 控制当前显示的月份
- `selectedDate`: 存储用户选择的日期
- `weekdays`: 周几的显示文本数组
2. **计算属性**:
- `displayMonth`: 格式化为"年 月"的显示文本
- `calendarDays`: 生成日历数组(包括上个月末、本月和下个月初的日期)
3. **核心方法**:
- `createDayObject()`: 创建日期对象,包含日期信息、是否属于当前月、是否是今天、是否有事件等属性
- `selectDate()`: 处理日期选择逻辑
- `goToToday()`: 返回今天
- 月份/年份切换方法:`prevMonth()`, `nextMonth()`, `prevYear()`, `nextYear()`
### 日历生成算法
1. 计算当前月份的第一天和最后一天
2. 确定第一天是周几,从而计算需要显示的上个月末尾天数
3. 生成当月所有日期
4. 补齐下个月初的日期,使日历表格完整(6行×7列=42天)
### 界面设计特点
1. **响应式布局**:适配不同屏幕尺寸
2. **视觉层次**:使用颜色区分不同状态的日期
- 当前月:正常显示
- 非当前月:灰色显示
- 今天:蓝色边框标记
- 选中日期:蓝色背景
3. **交互反馈**:悬停动画、点击效果
4. **事件标记**:随机生成的事件点标记
### 使用说明
1. 点击日期可以选中该日期
2. 使用导航按钮切换月份和年份
3. 点击"返回今天"按钮回到当前日期
4. 带点的日期表示有事件安排
阅读全文
相关推荐
















