首页 > 其他分享 >Vue【原创】日历组件

Vue【原创】日历组件

时间:2023-08-28 14:58:08浏览次数:31  
标签:Vue color 日历 moveIndex item curMonth 组件 showDays selectedDates

最近项目中封装了一个日历组件,用于节假日管理,支持输入默认选中的日期,选择管理日期。

效果图:

 

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>
View Code

 

调用案例和参数说明(我这里说全局插件引入,单独使用需要自行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>

 

标签:Vue,color,日历,moveIndex,item,curMonth,组件,showDays,selectedDates
From: https://www.cnblogs.com/loveFlex/p/17662243.html

相关文章

  • Vue3 使用Vuex与Vuex-persistedstate
    Vuex与vuex-persistedstateVuex是什么?Vuex是一个用于Vue.js应用程序的状态管理模式。它使得在应用程序中的所有组件之间共享和访问状态变得非常简单。Vuex将应用程序的状态存储在一个单一的存储库中,并且提供了一组用于更改状态的API。这使得状态管理变得更加可预测和易于调试。......
  • vue element 多个Form 表单同时验证
     多个Form内容统一提交验证1<el-formref="form1"></el-form>2<el-formref="form2"></el-form>3<el-formref="form3"></el-form>4<el-formref="form4"></el-form>......
  • vue3同一页面内重复引用同一操作dom的组件产生的问题
    [2023年8月28日12:39:40]vue3同一页面内标签<component>重复引用同一组件,且该组件内使用css选择器进行dom操作导致页面内相同组件发生变化的问题解决记录组件内进行dom操作,需要通过js方法进行选择器的元素获取,但当vue3全部渲染完毕后,页面内有多个id为test9的相同元素,元素选择将......
  • normalizeKey is not a function #element #vue #疑难杂症
    normalizeKeyisnotafunction#element#vue#疑难杂症原因是组件中使用了import{Search}from'@element-plus/icons-vue'解决方案是,在main.ts中,将Vue的引入置顶。 //TOP import{createApp}from'vue' importAppfrom'./App.vue&......
  • Vue element 表单验证不通过时,滚动到校验未通过位置
    我们在使用elementIUI实现表单验证,内容比较多的时候,提示内容会被遮挡,这时候用户不清楚什么情况,还会连续点击提交按钮。这个时候需求来啦:我们需要在表单验证不通过的时候,页面滚动到对应校验不通过的位置。大致思路在表单验证方法validate中,提供了两个参数:是否校验成功,和未通过校......
  • 使用 Vitest 进行组件测试
    原文:试试使用Vitest进行组件测试,确实很香。参考:Vitest:https://cn.vitest.dev/guide/ui.html什么是Vitest?自从尤大的构建工具Vite获得了巨大的人气,现在有了一个由它驱动的极快的单元测试框架。Vitest。Vitest与Jest兼容,具有开箱即用的ESM、Typescript和JSX支持,并且......
  • vue使用swiper调到指定页(非initialSlide)
    使用swiper的Methods方法watch:{showIndx(n){this.$nextTick(function(){this.$refs.mySwiper.swiper.slideTo(n,1000,true)})},}完整模板代码<template><swiper:options='swiperOption'ref="mySwiper"cl......
  • 鼠标任意拖动元素排列顺序(vue)
    参考地址:https://codesandbox.io/s/condescending-butterfly-enjqpr?file=/src/App.vue <template><div><transition-groupname="drag"class="list"tag="ul"><li@dragstart="dragStart(......
  • 异步动态引入组件
    //vue3写法<component:is="componentParam[active]"/>//引入apiimport{defineAsyncComponent}from'vue'constactive=ref('b')constcomponentParam={card:defineAsyncComponent(()=>import('./a.vue�......
  • React加载组件后自动触发某函数,而不需要点击按钮
    问题在项目中使用了ReactToPrint来实现打印,但是trigger属性中的按钮需要点击才能执行打印。期望能在组件加载完成时自动执行打印方法。解决可在组件加载完成时,通过id获取组件,并执行该组件的click方法。代码如下:importReact,{useRef,useEffect}from'react';//.........