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

效果图:

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

Vue【原创】日历组件Calendar的更多相关文章

  1. Vue自定义日历组件

    今天给大家介绍Vue的日历组件,可自定义样式.日历类型及支持扩展,可自定义事件回调.Props数据传输. 线上demo效果 示例 Template: <Calendar :sundayStart ...

  2. 使用ant design vue的日历组件,实现一个简单交易日与非交易日的切换

    使用ant design vue的日历组件,实现一个简单交易日与非交易日的切换 需求: 日历区分交易日.非交易日 可以切换面板查看整年交易日信息 可以在手动调整交易日.非交易日 演示实例 序--使用软 ...

  3. vue初学实践之路——vue简单日历组件(1)

    ---恢复内容开始--- 最近做的项目有一个需求,需要有一个日历组件供预定功能使用,之前的代码过于繁琐复杂,所以我采用vue重写了这个组件. npm.vue等等安装. 只是一个简单的日历组件,所以并不 ...

  4. 一个vue的日历组件

    说明: 1.基于element-ui开发的vue日历组件. 地址 更新: 1.增加value-format指定返回值的格式2.增加头部插槽自定义头部 <ele-calendar > < ...

  5. vue初学实践之路——vue简单日历组件(3)

    这一篇我们来实现管理员修改每一天剩余数量的功能. <div id="calendar"> <div id="left"> <spa ...

  6. vue初学实践之路——vue简单日历组件(2)

    上一篇我们已经实现了基本的日历显示功能,这一次我们要加上预定的功能 废话不多说,上代码 <div id="calendar"> <!-- 年份 月份 --> ...

  7. vue自定义日历组件的实现

    实现一个日期组件,如图: components.js代码如下: Vue.component('sc-calendar',{ template:'<div class="scCalend ...

  8. vue 自定义日历组件

    <template> <div class=""> <div class="calendarTraffic" name=" ...

  9. 基于Vue的日历组件

    可以标注重要日子 自己写的,可能不是特别很好,大家多提意见!!! 地址:https://github.com/jsLWQ/calendar

  10. Vue文件封装日历组件

    封装就是要具有灵活性,样式自适应,调用的时候传入props就可以变成自己想要的样式. 效果展示网址:https://1963331542.github.io/ 源代码: <template> ...

随机推荐

  1. 这10个Lambda表达式必须掌握,简化你的代码,提高生产力

    Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的λ演算得名,直接对应于其中的lambda抽象(lambda abstraction),是一个匿名 ...

  2. spring之AOP的概念及简单案例

    AOP概念 AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善.OOP ...

  3. Kotlin难点

    目录 高阶函数 双冒号 函数引用 类引用 属性引用 匿名函数 Lambda 表达式 例子 作用域函数 高阶函数 高阶函数是将函数用作参数或返回值的函数,还可以把函数赋值给一个变量. 所有函数类型都有一 ...

  4. EF Core + MySQL 基本增删改查

    前言 基于EF Core + MySQL的基本增删改查,示例是基于.NET6 + EF Core + MySQL 创建实体和数据库.EFCore 数据迁移项目基础上的内容增加.同时也是对基于Canal ...

  5. 【python基础】复杂数据类型-字典(增删改查)

    1.初识字典 字典,是另外一种复杂的数据类型,相较于列表,字典可以将相关信息关联起来.比如说一个人的信息有名字.年龄.性别等,如果用列表存储的话,不能表示他们之间是相关联的,而字典可以,字典是一个或多 ...

  6. rust随笔

    # 第二章 语言精要 ​ 好读书,不求甚解:每有会意,便欣然忘食. **动手,动手,动手!!!** ## 语句与表达式 Rust 中语法可以分成两大类:语句 statement 和表达式 expres ...

  7. 【LGR-142-Div.4】洛谷入门赛 #13 赛后总结

    A.魔方 目测入门 -,就是需要开long long //1 #include<bits/stdc++.h> typedef long long valueType; int main() ...

  8. 批量生成,本地推理,人工智能声音克隆框架PaddleSpeech本地批量克隆实践(Python3.10)

    云端炼丹固然是极好的,但不能否认的是,成本要比本地高得多,同时考虑到深度学习的训练相对于推理来说成本也更高,这主要是因为它需要大量的数据.计算资源和时间等资源,并且对超参数的调整也要求较高,更适合在云 ...

  9. JPA在事务结束时自动更新查询数据

    目录 现象 产生的原因 解决方法 现象 最近解决了一个困惑几天的bug,数据库里的某一些记录莫名其妙的被刷新了,排查过代码跟应用日志,可以确定不是代码执行的更新.直到今天看到了一条日志,在事务提交时报 ...

  10. ARC118E Avoid Permutations

    题意 给定一个长度为 \(n\) 的排列 \(p\),在一个 \((n + 2)\times(n + 2)\) 的网格上,禁止通过 \((i, p_i)\) 这些点,每次只能向上或右走一格,从 \(( ...