当前位置: 首页 > ops >正文

vue封装移动端日历,可折叠展开,以及考勤

效果图:
在这里插入图片描述
注意:日历中小圆点可代表考勤
可收起
在这里插入图片描述
代码:

<template><div class="calendar-container"><!-- 头部导航 --><div class="calendar-header"><!-- <button @click="prevMonth" class="calendar-nav-btn">上月</button> --><van-icon name="arrow-left" @click="prevMonth" class="calendar-nav-btn" /><h3>{{ currentYear }}{{ currentMonth }}</h3><van-icon name="arrow" @click="nextMonth" class="calendar-nav-btn" /><!-- <button @click="nextMonth" class="calendar-nav-btn">下月</button> --></div><!-- 星期标题 --><div class="calendar-weekdays"><span v-for="day in weekdays" :key="day" class="weekday">{{ day }}</span></div><!-- 日历收起部分 --><div class="calendar-days" v-if="!isExpand"><!-- 当月日期 --><span v-for="day in weekDates" :key="day.id" class="calendar-day" :class="{'today': day.isToday,'selected': isSelectedZD(day),'disabled': isDisabled(day.date)}" @click="!isDisabled(day.date) && selectDate(day.date)">{{ formatDatess(day) }}<span v-if="isTrueEheck(day.date)" :style="{ backgroundColor: bgcColor(day.date) }"class="cirel"></span></span></div><!-- 收齐按钮 --><div class="open-btn" v-if="!isExpand" @click="() => {isExpand = true}"><van-icon name="arrow-down" /></div><!-- 日期网格展开部分 --><div class="calendar-days" v-if="isExpand"><!-- 上个月的日期(灰色显示) --><span v-for="prevDay in prevMonthDays" :key="prevDay.id" class="calendar-day prev-month-day">{{ prevDay.day }}</span><!-- 当月日期 --><span v-for="day in currentMonthDays" :key="day.id" class="calendar-day" :class="{'today': day.isToday,'selected': isSelected(day.date),'disabled': isDisabled(day.date)}" @click="!isDisabled(day.date) && selectDate(day.date)">{{ day.day }}<span v-if="isTrueEheck(day.date)" :style="{ backgroundColor: bgcColor(day.date) }"class="cirel"></span></span><!-- 下个月的日期(灰色显示) --><span v-for="nextDay in nextMonthDays" :key="nextDay.id" class="calendar-day next-month-day">{{ nextDay.day }}</span></div><!-- 收齐按钮 --><div class="open-btn" v-if="isExpand" @click="() => {isExpand = false}"><van-icon name="arrow-up" /></div></div>
</template><script>
export default {name: 'Calendar',props: {// 默认选中日期,格式:YYYY-MM-DDdefaultValue: {type: String,default: ''},// 最小可选日期,格式:YYYY-MM-DDminDate: {type: String,default: ''},// 最大可选日期,格式:YYYY-MM-DDmaxDate: {type: String,default: ''},// 禁用的日期数组,格式:['YYYY-MM-DD', 'YYYY-MM-DD']disabledDates: {type: Array,default: () => []},// 禁用的日期数组,格式:['YYYY-MM-DD', 'YYYY-MM-DD']checkList: {type: Array,default: () => []}},data() {return {currentDate: new Date(),weekdays: ['周日', '周一', '周二', '周三', '周四', '周五', '周六'],selectedDate: null,isExpand: true,todatL: new Date(),jintian: new Date()}},computed: {currentYear() {return this.currentDate.getFullYear()},currentMonth() {return this.currentDate.getMonth() + 1},// 当月第一天是星期几(0-6)firstDayOfMonth() {const firstDay = new Date(this.currentYear, this.currentMonth - 1, 1)return firstDay.getDay()},// 当月的总天数daysInMonth() {return new Date(this.currentYear, this.currentMonth, 0).getDate()},// 上个月的总天数daysInPrevMonth() {return new Date(this.currentYear, this.currentMonth - 1, 0).getDate()},// 当月日期数据currentMonthDays() {const days = []const today = new Date()const isCurrentMonth =today.getFullYear() === this.currentYear &&today.getMonth() === this.currentMonth - 1for (let i = 1; i <= this.daysInMonth; i++) {const date = new Date(this.currentYear, this.currentMonth - 1, i)const dateStr = this.formatDate(date)days.push({id: `current-${i}`,day: i,date: dateStr,isToday: isCurrentMonth && today.getDate() === i})}return days},// 上个月需要显示的日期数据prevMonthDays() {const days = []// 上个月需要显示的天数(根据当月第一天是星期几来确定)const displayCount = this.firstDayOfMonthfor (let i = displayCount; i > 0; i--) {const day = this.daysInPrevMonth - i + 1const date = new Date(this.currentYear, this.currentMonth - 2, day)const dateStr = this.formatDate(date)days.push({id: `prev-${day}`,day,date: dateStr})}return days},// 下个月需要显示的日期数据nextMonthDays() {const days = []// 当月最后一天是星期几const lastDayOfMonth = new Date(this.currentYear, this.currentMonth - 1, this.daysInMonth).getDay()// 下个月需要显示的天数(凑够42个格子,6行7列)const displayCount = 42 - (this.prevMonthDays.length + this.currentMonthDays.length)for (let i = 1; i <= displayCount; i++) {const date = new Date(this.currentYear, this.currentMonth, i)const dateStr = this.formatDate(date)days.push({id: `next-${i}`,day: i,date: dateStr})}return days},weekDates() {if (!this.todatL) return [];// 转换为 Date 对象const baseDate = new Date(this.todatL);// 获取选中日期的星期(周日=0,周一=1,...,周六=6)const dayOfWeek = baseDate.getDay();const dates = [];// 计算从周日到周六的偏移量:周日为 -dayOfWeek,依次递增到周六为 6 - dayOfWeekfor (let i = -dayOfWeek; i <= 6 - dayOfWeek; i++) {const newDate = new Date(baseDate);newDate.setDate(baseDate.getDate() + i); // 计算偏移后的日期dates.push(newDate);}return dates;}},mounted() {// 如果有默认值,设置选中日期if (this.defaultValue) {this.selectedDate = this.defaultValue}},methods: {// 格式化日期为 YYYY-MM-DDformatDate(date) {const year = date.getFullYear()const month = String(date.getMonth() + 1).padStart(2, '0')const day = String(date.getDate()).padStart(2, '0')return `${year}-${month}-${day}`},// 切换到上个月prevMonth() {this.currentDate = new Date(this.currentYear, this.currentMonth - 2, 1)},// 切换到下个月nextMonth() {this.currentDate = new Date(this.currentYear, this.currentMonth, 1)},// 判断日期是否被选中isSelected(date) {return this.selectedDate === date},// 判断是否存在考勤isTrueEheck(date) {let isTru = falsethis.checkList.forEach(item => {if (date == item.rq) {isTru = true}})return isTru},// 判断显示颜色bgcColor(date) {let color = ''this.checkList.forEach(item => {if (date == item.rq) {color = item.color}})return color},// 判断日期是否被禁用isDisabled(date) {// 检查是否在最小日期之前if (this.minDate && date < this.minDate) {return true}// 检查是否在最大日期之后if (this.maxDate && date > this.maxDate) {return true}// 检查是否在禁用日期列表中if (this.disabledDates.includes(date)) {return true}return false},// 选择日期selectDate(date) {this.todatL = datethis.jintian = datethis.selectedDate = datethis.$emit('select', date)},// 折叠数据处理formatDatess(date) {const day = String(date.getDate()).padStart(2, '0'); // 日期补零return day;},// 判断折叠是否选中isSelectedZD(date) {console.log(this.jintian, '111111111')const date2 = new Date(this.jintian)const date1 = new Date(date)return date2.getDate() == date1.getDate();}}
}
</script><style scoped lang="scss">
.calendar-container {font-family: Arial, sans-serif;max-width: 400px;margin: 0 auto;padding: 0 15px;border: 1px solid #e0e0e0;/* border-radius: 8px; */background-color: #fff;box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
}.calendar-header {display: flex;justify-content: center;align-items: center;/* margin-bottom: 15px; */
}.calendar-nav-btn {/* background-color: #f0f0f0; */border: none;padding: 8px 12px;border-radius: 4px;cursor: pointer;transition: background-color 0.2s;
}/* .calendar-nav-btn:hover {background-color: #e0e0e0;
} */.calendar-weekdays {display: grid;grid-template-columns: repeat(7, 1fr);margin-bottom: 10px;
}.weekday {text-align: center;font-weight: bold;color: #666;padding: 5px;
}.calendar-days {display: grid;grid-template-columns: repeat(7, 1fr);gap: 5px;
}.calendar-day {position: relative;display: flex;justify-content: center;align-items: center;height: 40px;border-radius: 4px;cursor: pointer;transition: background-color 0.2s;
}.calendar-day:hover:not(.disabled) {background-color: #f0f0f0;
}.prev-month-day,
.next-month-day {color: #ccc;cursor: default;
}.today {background-color: #e7f3ff;font-weight: bold;
}.selected {background-color: #2196f3 !important;color: white;
}.disabled {color: #ddd;cursor: not-allowed;background-color: #f9f9f9;
}.cirel {position: absolute;top: 6px;right: 6px;width: 5px;height: 5px;border-radius: 50%;background-color: red;
}.open-btn {width: 100%;text-align: center;
}
</style>

使用

<template><div class="work-home-main"><calendar :checkList="checkList" @select="handleDateSelect" /></div>
</template><script>
import calendar from './components/calendar.vue'
export default {components: { calendar },props: {},data() {return {// 考勤时间,以及小圆点颜色数组,入不需要考勤则可不传checkList: [{rq: '2025-05-09',color: '#13891f'},{rq: '2025-06-09',color: '#13891f'},{rq: '2025-06-08',color: '#f59a23'},{rq: '2025-06-07',color: '#f59a23'}]}},computed: {},created() { },mounted() {},watch: {},methods: {// 点击日历获取日期handleDateSelect(val) {this.listParam.rq = valconsole.log(val)}}
}
</script>
<style scoped lang="scss">
</style>
http://www.xdnf.cn/news/13435.html

相关文章:

  • 大模型在输尿管下段积水预测及临床应用的研究
  • 计算机网络第3章(上):数据链路层全解析——组帧、差错控制与信道效率
  • 【期末速成】软件项目管理
  • 最长和谐子序列
  • A. Dr. TC
  • 产品升级 | 新一代高性能数据采集平台BRICK2 X11,助力ADAS与自动驾驶开发
  • MatAnyone本地部署,视频分割处理,绿幕抠像(WIN/MAC)
  • YOLOv1 技术详解:正负样本划分与置信度设计
  • 【unitrix】1.1 密封模式(srcsealed.rs)
  • SQL29 验证刷题效果,输出题目真实通过率
  • Spring声明式事务生效是有条件滴!
  • 达梦数据库单机部署dmhs同步复制(dm8->kafka)
  • MFC 第1章:适配 WIndows 编程的软件界面调整
  • 如何诊断服务器硬盘故障?出现硬盘故障如何处理比较好?
  • 【沉浸式解决问题】Idea运行Junit测试中scanner无法获取控制台的输入内容
  • 山东大学软件学院项目实训-基于大模型的模拟面试系统-面试对话标题自动总结
  • 看安科瑞分布式光伏解决方案如何破解光伏痛点?
  • Python实战案例详解:基于计算器项目的扩展应用
  • 前端框架vue3的变化之处
  • API:解锁数字化协作的钥匙及开放实现路径深度剖析
  • HakcMyVM-Blackhat
  • Ubuntu 24.04 上安装与 Docker 部署 Sentinel
  • UE5 学习系列(六)导入资产包
  • BW非法字符处理
  • 智能空气流向控制系统SKLX的优化与实践
  • Hi3519DV500开发板适配新sensor的详细流程(最新版)
  • 96页PPT华为销售战略规划市场洞察品牌策略
  • Squid 代理服务器实战:解决动态 IP 访问第三方接口的生产级方案
  • 多系统合路器(POI)详解
  • 【数据结构】 优先级队列 —— 堆