Vue【原创】日历组件Calendar
最近项目中封装了一个日历组件,用于节假日管理,支持输入默认选中的日期,选择管理日期。
效果图:
calendar组件:
1 <template> 2 <div class="calendar"> 3 <slot name="title"> 4 <div class="calendar-title">{{ curYearMonth }}</div> 5 </slot> 6 7 <table class="calendar-table"> 8 <thead> 9 <tr> 10 <th v-for="(item, i) in weeks" :key="i">{{ item }}</th> 11 </tr> 12 </thead> 13 <tbody> 14 <tr v-for="(dates, i) in res" :key="i" :style="{ height: cellHeight }"> 15 <td v-for="(item, index) in dates" :key="index" :class="{ 16 notCurMonth: !item.isCurMonth, 17 currentDay: item.date === curDate, 18 selectDay: item.isSelected, 19 rangeSelectd: item.isRangeSelected, 20 weekend: item.isWeekend 21 }" @click="handleItemClick(item, i, index)" @mouseover="handleItemMove(item, i, index)"> 22 <!-- <span>{{ item.date.split('-').slice(1).join('-') }}</span> --> 23 <span>{{ item.date | cellDate }}</span> 24 <slot :data="item" /> 25 </td> 26 </tr> 27 </tbody> 28 </table> 29 </div> 30 </template> 31 32 <script> 33 import { 34 getDaysInMonth, 35 handleCreateDate, 36 handleCreateDatePicker, 37 parseTime 38 } from '../../../src/utils/dateUtils.js'; 39 40 const SELECT_MODE = { 41 SINGLE: 'single', 42 RANGE: 'range' 43 } 44 45 export default { 46 name: 'LiloCalendar', 47 components: {}, 48 filters: { 49 cellDate(value) { 50 // value.split('-')[1] + '-' + value.split('-')[2] 51 return value.split('-')[2] 52 } 53 }, 54 props: { 55 selectMode: { 56 type: String, 57 default: SELECT_MODE.SINGLE //'single,range' 58 }, 59 startOfWeek: { 60 type: Number, 61 default: 1 62 }, 63 canSelect: { 64 type: Boolean, 65 default: false 66 }, 67 cellHeight: { 68 type: String, 69 default: '60px' 70 }, 71 currentDate: { 72 type: String, 73 default: new Date().getFullYear() + '-' + (new Date().getMonth() + 1) 74 }, 75 defaultSelectedDates: { 76 type: Array, 77 default () { 78 return [] 79 } 80 }, 81 }, 82 data() { 83 return { 84 monthOptions: [], 85 yearOptions: [], 86 weeks: ['一', '二', '三', '四', '五', '六', '日'], 87 curYear: 0, // 当前年 88 curMonth: 0, // 当前月 89 days: 0, // 当前月总共天数 90 curDate: parseTime(new Date().getTime()), // 当前日期 yyyy-MM-dd 格式,用来匹配是否是当前日期 91 prevDays: [], // 非当前月的上一月展示的日期 92 rearDays: [], // 非当前月的下一月展示的日期 93 curDays: [], // 当前月的日期 94 showDays: [], // 总共展示的42个日期 95 res: [], // 二维数组 96 selectedDates: [], // 选中的日期 97 selectedMode: false, // true表示点击, false表示滑动 98 moveIndex: [], // 两个,第一个是起始,第二个是结束 99 canMove: false // 当moveIndex数组有一个值时,可以触发滑动 100 }; 101 }, 102 computed: { 103 curYearMonth() { 104 const temp = parseInt(this.curMonth) + 1; 105 return this.curYear + '-' + (temp < 10 ? `0${temp}` : temp); 106 } 107 }, 108 watch: { 109 curMonth: { 110 handler(val) { 111 this.handleGetDays(this.curYear, val, this.startOfWeek); 112 } 113 }, 114 curYear: { 115 handler(val) { 116 this.handleGetDays(val, this.curMonth, this.startOfWeek); 117 } 118 }, 119 currentDate: { 120 handler(val) { 121 this.setup(); 122 } 123 }, 124 defaultSelectedDates: { 125 handler(val) { 126 this.setup(); 127 } 128 } 129 }, 130 created() {}, 131 mounted() { 132 this.setup(); 133 }, 134 methods: { 135 setup() { 136 this.weeks.unshift(...this.weeks.splice(this.startOfWeek - 1)); 137 this.handleGetDays(this.curYear, this.curMonth, this.startOfWeek); 138 this.selectedMode = this.selectMode === SELECT_MODE.SINGLE; 139 140 const temp = this.currentDate.split('-'); 141 this.curYear = parseInt(temp[0]); 142 this.curMonth = parseInt(temp[1]) - 1; 143 144 this.monthOptions = handleCreateDatePicker().months; 145 this.yearOptions = handleCreateDatePicker().years; 146 if (localStorage.selectedDates) this.selectedDates = JSON.parse(localStorage.selectedDates); 147 }, 148 handleGetDays(year, month, startOfWeek) { 149 this.showDays = []; 150 this.days = getDaysInMonth(year, month); 151 let firstDayOfWeek = new Date(`${year}-${month + 1}-01`).getDay(); 152 153 // 处理周起始日 154 const obj = { 155 1: '一', 156 2: '二', 157 3: '三', 158 4: '四', 159 5: '五', 160 6: '六', 161 0: '日' 162 }; 163 const firstDayInCN = obj[firstDayOfWeek]; 164 const index = this.weeks.indexOf(firstDayInCN); 165 // console.log(firstDayOfWeek, index); 166 167 if (firstDayOfWeek === 0) { 168 // 星期天为0 星期一为1 ,以此类推 169 firstDayOfWeek = 7; 170 } 171 172 this.prevDays = handleCreateDate(year, month, 1, index + 1, 'prev'); 173 this.rearDays = handleCreateDate(year, month, 1, 42 - this.days - index, 'rear'); 174 175 this.curDays = handleCreateDate(year, month, 1, this.days, 'cur', this.defaultSelectedDates); 176 this.showDays.unshift(...this.prevDays); 177 this.showDays.push(...this.curDays); 178 this.showDays.push(...this.rearDays); 179 this.res = this.handleFormatDates(this.showDays); 180 }, 181 handleFormatDates(arr, size = 7) { 182 // 传入长度42的原数组,最终转换成二维数组 183 const arr2 = []; 184 for (let i = 0; i < size - 1; i++) { 185 const temp = arr.slice(i * size, i * size + size); 186 arr2.push(temp); 187 } 188 // console.log(arr2) 189 return arr2; 190 }, 191 handleTableHead(start) { 192 const sliceDates = this.weeks.splice(start - 1); 193 this.weeks.unshift(...sliceDates); 194 }, 195 handleItemClick(item, i, j) { 196 if (!this.canSelect) return; 197 if (!item.isCurMonth) return; 198 if (this.selectedMode) { 199 this.$nextTick(() => { 200 // this.$set(this.res[i][j], 'isSelected', ) 201 this.res[i][j].isSelected = !this.res[i][j].isSelected; 202 if (this.res[i][j].isSelected) { 203 this.selectedDates.push(this.res[i][j].date); 204 this.selectedDates = Array.from(new Set(this.selectedDates)); 205 this.$emit('date-selected', { 206 selectedDates: this.selectedDates, 207 removeDate: '', 208 addDate: item.date 209 }); 210 } else { 211 this.selectedDates.splice(this.selectedDates.indexOf(item.date), 1); 212 this.$emit('date-selected', { 213 selectedDates: this.selectedDates, 214 removeDate: item.date, 215 addDate: '' 216 }); 217 } 218 }); 219 } else { 220 // 滑动模式下,第一次点击是起始,第二次点击是结束 221 const index = i * 7 + j; 222 this.canMove = true; 223 if (this.moveIndex.length === 1) { 224 this.canMove = false; 225 } 226 if (this.moveIndex.length === 2) { 227 this.showDays.forEach(item => { 228 item.isSelected = false; 229 item.isRangeSelected = false; 230 }); 231 this.canMove = true; 232 this.moveIndex.length = 0; 233 } 234 this.moveIndex.push(index); 235 this.moveIndex.sort((a, b) => a - b); 236 this.selectedDates = this.showDays.slice(this.moveIndex[0], this.moveIndex[1] + 1); 237 this.selectedDates.length !== 0 && this.$emit('date-selected', this.selectedDates); 238 } 239 }, 240 handleItemMove(data, i, j) { 241 if (this.canMove && !this.selectedMode) { 242 const index = i * 7 + j; 243 this.showDays.forEach(item => { 244 item.isSelected = false; 245 item.isRangeSelected = false; 246 }); 247 // 让第一个日期和最后一个日期显示蓝色高亮 248 this.showDays[index].isSelected = true; 249 this.showDays[this.moveIndex[0]].isSelected = true; 250 251 // 不同情况的判断,当用户的鼠标滑动进日期的索引小于起始日期的索引,要做if else处理 252 if (this.moveIndex[0] < index) { 253 for (let i = this.moveIndex[0] + 1; i < index; i++) { 254 this.showDays[i].isRangeSelected = true; 255 } 256 } else { 257 for (let i = index + 1; i < this.moveIndex[0]; i++) { 258 this.showDays[i].isRangeSelected = true; 259 } 260 } 261 } 262 }, 263 handleQuickChange(type) { 264 if (type === 'prev') { 265 this.curMonth--; 266 // console.log(this.curMonth); 267 if (this.curMonth === -1) { 268 this.curMonth = 11; 269 this.curYear -= 1; 270 } 271 } else if (type === 'next') { 272 this.curMonth++; 273 if (this.curMonth === 12) { 274 this.curMonth = 0; 275 this.curYear += 1; 276 } 277 } 278 } 279 } 280 }; 281 </script> 282 283 <style scoped lang="scss"> 284 .calendar { 285 display: flex; 286 align-items: center; 287 justify-content: center; 288 flex-direction: column; 289 } 290 291 .calendar-title { 292 width: 100%; 293 padding-top: 8px; 294 padding-bottom: 5px; 295 font-weight: bold; 296 border-bottom: 1px solid rgba($color: #000000, $alpha: .1); 297 } 298 299 .calendar-table { 300 width: 100%; 301 table-layout: fixed; 302 border-collapse: collapse; 303 transition: 0.3s; 304 305 thead tr { 306 height: 50px; 307 } 308 309 tbody tr { 310 &:first-child td { 311 border-top: 1px solid rgba($color: #000000, $alpha: .1); 312 } 313 314 td { 315 cursor: pointer; 316 border-right: 1px solid rgba($color: #000000, $alpha: .1); 317 border-bottom: 1px solid rgba($color: #000000, $alpha: .1); 318 text-align: center; 319 320 &:first-child { 321 border-left: 1px solid rgba($color: #000000, $alpha: .1); 322 } 323 } 324 } 325 } 326 327 .notCurMonth { 328 transition: all .25s ease-out; 329 color: #c0c4cc; 330 } 331 332 .currentDay { 333 transition: all .25s ease-out; 334 color: #fff; 335 background-color: #409eff; 336 } 337 338 .selectDay { 339 transition: all .25s ease-out; 340 color: #fff; 341 background-color: #08a8a0; 342 } 343 344 .rangeSelectd { 345 transition: all .25s ease-out; 346 color: #606266; 347 background-color: #dee2e9; 348 } 349 350 .weekend { 351 transition: all .25s ease-out; 352 color: #f56c6c; 353 } 354 </style>
调用案例和参数说明(我这里说全局插件引入,单独使用需要自行import导入):
1 <template> 2 <div class="calendar-container"> 3 <lilo-calendar 4 :default-selected-dates="defaultSelectedDates" 5 :current-date="currentDate" 6 :start-of-week="startOfWeek" 7 :cell-height="cellHeight" 8 :can-select="canSelect" 9 @date-selected="dateSelected"> 10 <!-- <template #title> --> 11 <!-- 标题栏可以设置插槽 --> 12 <!-- <div class="custom-title">2023-08</div> --> 13 <!-- </template> --> 14 </lilo-calendar> 15 </div> 16 </template> 17 18 <script> 19 export default { 20 data() { 21 return { 22 defaultSelectedDates: [ '2023-08-01', '2023-08-03' ], //默认选中的日期 23 currentDate: '2023-08', //当前月份 24 startOfWeek: 1, //从星期几开始, 25 // 1: '一', 26 // 2: '二', 27 // 3: '三', 28 // 4: '四', 29 // 5: '五', 30 // 6: '六', 31 // 0: '日' 32 cellHeight: '120px', //日期单元的高度 33 canSelect: true //是否可以选中,选中之后触发date-selected事件 34 } 35 }, 36 methods: { 37 dateSelected(val) { 38 console.log(val) 39 } 40 } 41 } 42 </script> 43 44 <style lang="scss" scoped> 45 .calendar-container { 46 padding: 20px; 47 .custom-title { 48 width: 100%; 49 padding: 8px; 50 color: #409eff; 51 font-weight: bold; 52 font-size: 1.1rem; 53 border-bottom: 1px solid #0000001f; 54 } 55 } 56 </style>
查看基于【日历组件】的【节假日管理】功能整个请移步:https://www.cnblogs.com/loveFlex/p/17662512.html
作者: Binyy
出处: https://www.cnblogs.com/loveFlex
城市:wuhan
微信:momakeyy
详细源码请移步,记得点个星星噢~ https://gitee.com/binyylovesino/lilo-ui 欢迎各路大佬指导、提问~
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文链接 如有问题, 可邮件(408460486@qq.com)或者微信咨询.
posted on 2023-08-28 14:40 Binyy_Wuhan 阅读(1554) 评论(1) 编辑 收藏 举报