0%

关于FL组件库日期组件制作思路

关于FL组件库日期组件制作思路

关于日期组件开发思路,首天的计算极为重要,要想知道一个月中第一天也就是1号在日历的第几个位置,就要计算当月1号为周几。得到之后就方便很多了😋

单页日历的数据

获取当前月第一天

getDate获取日期

假设我们要获取2022-07月的日期数据

  1. 先获取当月的1日是周几—date.getDay()得到周5

    • 所以开始遍历的起始为第5个,依次往后遍历累加直到当月最后一天
  2. 填充42个格子

    • 长度为42的数组,如果当月1日不为周一则前半部分为当月总日从后往前依次递减排列
  3. 一个月的日历涵盖了三部分,上月剩余日期,当月全部日期,下个月初始日期

    • 上月剩余日期可以通过当月计算上月有几天,从开始的星期开始依次递减往前填充
    • 当月日期只需要直到当月有几天从1到28/29/30/31填充
    • 因为一个月的展示固定为42个格子,前两部分算出来了剩下的只需要减一下从1开始累加到结束就好了
  4. 其实最重要的就只需要知道年份,月份,就可以获取到该日历页面的数据

  5. 剩下的就是其他因素了,比如:如果当月为1号上个月的日期数据该怎么办,如果是12号呢?还有就是点击日期的切换等等…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
useEffect(()=>{
// 监测月份和日期的变化
// 1.前本部分为上月最后几天
let frontArr = [];
// 2.中间为当月日期
let centerArr = [];
// 3.若长度不大于42,后半部分1-开始累加
let afterArr = [];
let day = new Date(`${year}-${math}`);
// console.log(day);
// console.log('当月第一天为周',day.getDay());//获取当月第一天周几
// console.log(day.getMonth());//获取上个月的月份(不需要加一)
// 上个月有dayMath[day.getMonth()-1]
let dayMath = [31,28,31,30,31,30,31,31,30,31,30,31];//一年每月的天数
// console.log(day.getFullYear());//年份
if (day.getFullYear()%4 === 0 && day.getFullYear()%100 !== 0) {
dayMath[1] = 29
}
// console.log('上个月有',dayMath[day.getMonth()-1]);
let forOne = dayMath[day.getMonth()-1]
if (day.getMonth()+1===1) {
forOne = 31
}
// console.log(forOne);//上月的天数
let shangMatch = day.getDay() - 1
if (day.getDay()===0) {
shangMatch = 6
}

// 如果这个月为1月,则上个月按照12月的31计算
while (frontArr.length < shangMatch) {
frontArr.unshift(forOne--)
}
// console.log('上个月的数组',frontArr);//上月的数组
// console.log('这个月',day.getMonth()+1);
let index = 1;
// while(centerArr.length < dayMath[day.getMonth()-1]){
// centerArr.push(index++)
// }
// console.log(dayMath[day.getMonth()]);
for (let i = 0; i < dayMath[day.getMonth()]; i++) {
// console.log(28);
if (index>31) {
break
}
centerArr.push(index++)
}
// console.log('这个月的数组',centerArr);//这个月的数组

let endFor = 42 - frontArr.length - centerArr.length;
// console.log(endFor);
for (let i = 1; i < endFor+1; i++) {
afterArr.push(i)
}
// console.log('最后数组',afterArr);
// 汇总数组 ---将一维数组转换为二维数组方便遍历数据渲染---有待改进算法
let countArr = [...frontArr,...centerArr,...afterArr];
// console.log('数组汇总',countArr);
let newArr = [];
newArr.push(countArr.slice(0,7))
newArr.push(countArr.slice(7,14))
newArr.push(countArr.slice(14,21))
newArr.push(countArr.slice(21,28))
newArr.push(countArr.slice(28,35))
newArr.push(countArr.slice(35,42))
// console.log(newArr);
setArr(newArr)
},[year,math])

页面渲染

image-20220903205537882

这里我是用的是<table>标签,所以我才需要将得到的数据重构成二维数组,方便渲染

日期的切换

点击下个月和上个月的交互其实很简单,因为上文我们页面的渲染有useEffect这个钩子监测。

所以当我们改变传入的初始日期就可以自动渲染页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 事件
function nextMath() {
// 点击下一月份
setMath(title=>{
return title+1
})
if (math >= 12) {
setyear(title=>{
return title+1
})
setMath(title=>{
return 1
})
}
// console.log(myRefipt);
// myRefipt.current.focus();
}
function upMath() {
// 点击上一月份
setMath(title=>{
return title-1
})
if (math <= 1) {
setyear(title=>{
return title-1
})
setMath(title=>{
return 12
})
}
}

// 点击下一年
function nextYear() {
setyear(title=>{
return title+1
})
}
// 点击上一年
function upYear() {
setyear(title=>{
return title - 1
})
}
// 点击今天跳转
function teday(){
let data = new Date();
setyear(data.getFullYear());
setMath(data.getMonth()+1)
setDatas(data.getDate())
}

源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
// index.jsx
import React,{useEffect, useRef, useState} from 'react'
import style from './index.module.scss'

function DatePicker() {
// 获取焦点后的样式
let [focus,setFocus] = useState(false);
// main主体数据
let [show,setShow] = useState(false)
// 1. 初始日期
// let [day,setday] = useState(new Date());
// 2. 42格子遍历数据
let [Arr,setArr] = useState([]);
// 3. 年份 // 控制这俩就可以控制日历
let [year,setyear] = useState(2022);
// 4. 月份
let [math,setMath] = useState(1);
// 5. 日期
let [datas,setDatas] = useState(0);
// 6. 输入框
let myRefipt = useRef();
let mySection = useRef();
// 7. 输入框内容
let [iptValue,setiptValue] = useState("");

// 获取当前月第一天
// getDate获取日期
// 假设我们要获取2022-07月的日期数据
// 1. 先获取当月的1日是周几---date.getDay()得到周5
// - 所以开始遍历的起始为第5个,依次往后遍历累加直到当月最后一天
// 2. 填充42个格子
// - 长度为42的数组,如果当月1日不为周一则前半部分为当月总日从后往前依次递减排列
useEffect(()=>{
// 监测月份和日期的变化
// 1.前本部分为上月最后几天
let frontArr = [];
// 2.中间为当月日期
let centerArr = [];
// 3.若长度不大于42,后半部分1-开始累加
let afterArr = [];
let day = new Date(`${year}-${math}`);
// console.log(day);
// console.log('当月第一天为周',day.getDay());//获取当月第一天周几
// console.log(day.getMonth());//获取上个月的月份(不需要加一)
// 上个月有dayMath[day.getMonth()-1]
let dayMath = [31,28,31,30,31,30,31,31,30,31,30,31];//一年每月的天数
// console.log(day.getFullYear());//年份
if (day.getFullYear()%4 === 0 && day.getFullYear()%100 !== 0) {
dayMath[1] = 29
}
// console.log('上个月有',dayMath[day.getMonth()-1]);
let forOne = dayMath[day.getMonth()-1]
if (day.getMonth()+1===1) {
forOne = 31
}
// console.log(forOne);//上月的天数
let shangMatch = day.getDay() - 1
if (day.getDay()===0) {
shangMatch = 6
}

// 如果这个月为1月,则上个月按照12月的31计算
while (frontArr.length < shangMatch) {
frontArr.unshift(forOne--)
}
// console.log('上个月的数组',frontArr);//上月的数组
// console.log('这个月',day.getMonth()+1);
let index = 1;
// while(centerArr.length < dayMath[day.getMonth()-1]){
// centerArr.push(index++)
// }
// console.log(dayMath[day.getMonth()]);
for (let i = 0; i < dayMath[day.getMonth()]; i++) {
// console.log(28);
if (index>31) {
break
}
centerArr.push(index++)
}
// console.log('这个月的数组',centerArr);//这个月的数组

let endFor = 42 - frontArr.length - centerArr.length;
// console.log(endFor);
for (let i = 1; i < endFor+1; i++) {
afterArr.push(i)
}
// console.log('最后数组',afterArr);

// 汇总数组
let countArr = [...frontArr,...centerArr,...afterArr];
// console.log('数组汇总',countArr);
let newArr = [];
newArr.push(countArr.slice(0,7))
newArr.push(countArr.slice(7,14))
newArr.push(countArr.slice(14,21))
newArr.push(countArr.slice(21,28))
newArr.push(countArr.slice(28,35))
newArr.push(countArr.slice(35,42))
// console.log(newArr);
setArr(newArr)


},[year,math])

// 第一次进入页面当前日期
useEffect(()=>{
let data = new Date();
// console.log(data.getFullYear());
// console.log(data.getMonth());
setyear(data.getFullYear());
setMath(data.getMonth()+1)
// console.log(data.getDate());
setDatas(data.getDate())
},[])

useEffect(()=>{
// 点击除去日历以外的部分隐藏日历
document.onclick = ()=>{
setShow(false)
setFocus(false)
}
},[])

// div获取焦点
function divFocus(e) {
e.nativeEvent.stopImmediatePropagation();
// console.log('div获取了焦点');
setFocus(true)
setShow(true)
}

// 事件
function nextMath() {
// 点击下一月份
setMath(title=>{
return title+1
})
if (math >= 12) {
setyear(title=>{
return title+1
})
setMath(title=>{
return 1
})
}
// console.log(myRefipt);
// myRefipt.current.focus();
}
function upMath() {
// 点击上一月份
setMath(title=>{
return title-1
})
if (math <= 1) {
setyear(title=>{
return title-1
})
setMath(title=>{
return 12
})
}
}

// 点击下一年
function nextYear() {
setyear(title=>{
return title+1
})
}
// 点击上一年
function upYear() {
setyear(title=>{
return title - 1
})
}
// 点击今天跳转
function teday(){
let data = new Date();
setyear(data.getFullYear());
setMath(data.getMonth()+1)
setDatas(data.getDate())
}

// 鼠标移入改变输入框内容
function mouseOver(tit,index) {
// 获取到内容
myRefipt.current.value = year+'-'+math+'-'+tit;
if (index === 0) {
if (tit > 7) {
if (math > 1) {
myRefipt.current.value = year+'-'+(math-1)+'-'+tit;
}else{
myRefipt.current.value = (year-1)+'-'+ 12 +'-'+tit;
}
}
}else if(index === 4){
if (tit < 7) {
if (math < 12) {
myRefipt.current.value = year+'-'+(math+1)+'-'+tit;
}else{
myRefipt.current.value = (year+1)+'-'+1+'-'+tit;
}
}
}else if(index === 5){
if (tit <= 14) {
if (math < 12) {
myRefipt.current.value = year+'-'+(math+1)+'-'+tit;
}else{
myRefipt.current.value = (year+1)+'-'+1+'-'+tit;
}
}
}

}

// 鼠标点击改变ipt值,并且隐藏日历
function clickIpt(tit,index) {
if (index === 0) {
if (tit > 7) {
if (math > 1) {
iptValue = year+'-'+(math-1)+'-'+tit;
setiptValue(year+'-'+(math-1)+'-'+tit)
}else{
iptValue = (year-1)+'-'+12+'-'+tit;
setiptValue((year-1)+'-'+12+'-'+tit)
}
}else{
iptValue = year+'-'+math+'-'+tit;
setiptValue(year+'-'+math+'-'+tit)
}
}else if(index === 4){
if (tit < 7) {
if (math<12) {
iptValue = year+'-'+(math+1)+'-'+tit;
setiptValue(year+'-'+(math+1)+'-'+tit)
}else{
iptValue = (year+1)+'-'+1+'-'+tit;
setiptValue((year+1)+'-'+1+'-'+tit)
}
}else{
iptValue = year+'-'+math+'-'+tit;
setiptValue(year+'-'+math+'-'+tit)
}
}else if(index === 5){
if (tit <= 14) {
if (math<12) {
iptValue = year+'-'+(math+1)+'-'+tit;
setiptValue(year+'-'+(math+1)+'-'+tit)
}else{
iptValue = (year+1)+'-'+1+'-'+tit;
setiptValue((year+1)+'-'+1+'-'+tit)
}
}else{
iptValue = year+'-'+math+'-'+tit;
setiptValue(year+'-'+math+'-'+tit)
}
}else{
iptValue = year+'-'+math+'-'+tit;
setiptValue(year+'-'+math+'-'+tit)
}
// console.log(iptValue);
// console.log(iptValue);
myRefipt.current.value = iptValue
// console.log(myRefipt.current.value);
setTimeout(() => {
setShow(false)
}, 0);
}

// 没有点击鼠标移出清除input
function mouseOut() {
myRefipt.current.value = iptValue;
}


return (
<div className={style.Box}
onClick={(ev)=>divFocus(ev)}
// onBlur={divBlur}

>
<div
className={focus?[style.iptBox,style.iptBoxFocu].join(' '):style.iptBox}>
<input type="text"
placeholder="请选择日期"
ref={myRefipt}
/>
<span>icon</span>
</div>

<section
ref={mySection}
tabIndex="-1"
className={show?`${style.dropChangeUp}`:`${style.dropChangeDown}`}
>
<div className={style.sectionTop}>
<header>
<button
className={style.leftBtnOne}
onClick={upYear}
>
<span></span>
</button>
<button
className={style.leftBtnTwo}
onClick={upMath}
>
<span></span>
</button>
<div>
<button
className={style.yearBtn}
style={{marginRight:5}}
>
{year}年
</button>
<button className={style.monthBth}>
{math}月
</button>
</div>
<button
className={style.rightBtnTwo}
onClick={nextMath}
>
<span></span>
</button>
<button
className={style.rightBtnOne}
onClick={nextYear}
>
<span></span>
</button>
</header>
<main>
<table>
<thead>
<tr>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
</tr>
</thead>
<tbody
onMouseOut={mouseOut}
>
{
Arr.map((title,index)=>{
return (
<tr key={index}>
{
title.map((tit,ind)=>{
return (
<td
key={ind}
className={
[index === 0 ? (tit > 7 ? `${style.lightColour}` : ""):
index === 5 ? (tit<=20 ? `${style.lightColour}` : ""):
index === 4 ? (tit<=7 ? `${style.lightColour}` : ""):
"",
tit === datas&&year===new Date().getFullYear()&&math===new Date().getMonth()+1 ? `${style.choice}`:""
].join('')
}
onMouseOver={()=>mouseOver(tit,index)}

onClick={()=>clickIpt(tit,index)}
>{tit}</td>
)
})
}
</tr>
)
})
}
</tbody>
</table>
</main>
</div>
<footer className={style.footer}>
<a href="javascript:void(0)" onClick={teday}>今天</a>
</footer>
</section>
</div>
)
}

export default DatePicker

scss

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325

$allClor:#64abfb;

a{
text-decoration: none;
}
.iptBox{
width: 170px;
height: 30px;
border: 1px solid #afafaf;
display: flex;
justify-content: space-between;
align-items: center;
transition: all 0.3s ease;
border-radius: 2px;

&:hover{
border: 1px solid $allClor;
}

input{
width: 80%;
border: none;
box-sizing: border-box;
outline: none;
height: 100%;
padding-left: 8px;
font-size: 16px;
}
span{
display: inline-block;
flex: 1;
height: 100%;
line-height: 30px;
user-select: none;
}
}

.Box{
position: relative;
}


section{
display: flex;
flex-direction: column;
width: 280px;
height: 308px;
// border: 1px solid #afafaf;
box-shadow: 0px 0px 20px -5px #afafaf;
overflow: hidden;
position: absolute;
background-color: #fff;
z-index: 100;

.sectionTop{
display:flex;
flex: 1;
flex-direction: column;
height: 32px;

header{
display: flex;
justify-content: center;
padding: 0px 20px;
align-items: center;
height: 40px;
border-bottom: 1px solid #f0f0f0;
div{
height: 100%;
flex: 1;
display: flex;
justify-content: center;
button{
height: 100%;
text-align: center;
color: #808080;
font-weight: 600;
transition: all 0.3s ease;
cursor: pointer;
&:hover{
color: $allClor;
}
}
}
button{
height: 100%;
border: none;
background-color: #fff;
}

.rightBtnOne span,
.rightBtnTwo span,
.leftBtnTwo span,
.leftBtnOne span{
width: 7px;
height: 7px;
cursor: pointer;
position: relative;
}


.leftBtnOne span::after{
top: 3px;
left: -4px;
position: absolute;
content: "";
display: inline-block;
width: 7px;
height: 7px;
border-top: 2px solid #afafaf;
border-left: 2px solid #afafaf;
transform: rotate(-45deg);
margin-left: -2px;
transition: all 0.3s ease;
}
.rightBtnOne span::after{
left: -4px;
top: 3px;
position: absolute;
content: "";
display: inline-block;
width: 7px;
height: 7px;
border-top: 2px solid #afafaf;
border-left: 2px solid #afafaf;
transform: rotate(135deg);
margin-left: -2px;
transition: all 0.3s ease;
}
.leftBtnOne span::before{
content: "";
display: inline-block;
width: 7px;
height: 7px;
border-top: 2px solid #afafaf;
border-left: 2px solid #afafaf;
transform: rotate(-45deg);
transition: all 0.3s ease;
}
.rightBtnOne span::before{
content: "";
display: inline-block;
width: 7px;
height: 7px;
border-top: 2px solid #afafaf;
border-left: 2px solid #afafaf;
transform: rotate(135deg);
transition: all 0.3s ease;
}

// 鼠标移入
.leftBtnTwo{
width: 30px;
// 鼠标移入
&:hover span::after{
border-top: 2px solid #797979;
border-left: 2px solid #797979;
}
}
.rightBtnTwo{
width: 30px;
// 鼠标移入
&:hover span::after{
border-top: 2px solid #797979;
border-left: 2px solid #797979;
}
}
.rightBtnOne{
width: 20px;
// 鼠标移入
&:hover span::after,
&:hover span::before{
border-top: 2px solid #797979;
border-left: 2px solid #797979;
}
}
.leftBtnOne{
width: 20px;
// 鼠标移入
&:hover span::after,
&:hover span::before{
border-top: 2px solid #797979;
border-left: 2px solid #797979;
}
}

.leftBtnTwo span::after{
content: "";
display: inline-block;
width: 7px;
height: 7px;
border-top: 2px solid #afafaf;
border-left: 2px solid #afafaf;
transform: rotate(-45deg);
margin-left: -2px;
transition: all 0.3s ease;
}
.rightBtnTwo span::after{
content: "";
display: inline-block;
width: 7px;
height: 7px;
border-top: 2px solid #afafaf;
border-left: 2px solid #afafaf;
transform: rotate(135deg);
margin-left: -2px;
transition: all 0.3s ease;
}



}
main{
flex: 1;
}
}
.footer{
display: flex;
height: 35px;
border-top: 1px solid #f0f0f0;
a{
margin: auto;
color: $allClor;
}
}


main{
padding: 10px;

table{
height: 100%;
display: flex;
flex-direction: column;
width: 100%;
color: #363636;
thead{
height: 30px;
tr{
display: flex;
th{
flex: 1;
}
}
}
tbody{
display: flex;
flex-direction: column;
flex: 1;

tr{
display: flex;
flex: 1;
td{
flex: 1;
text-align: center;
line-height: 29px;
cursor: pointer;
font-weight: 400;
transition: all 0.3s ease;
user-select: none;
&:hover{
background-color: #f0f0f0;
}
}
}

.lightColour{
color: #afafaf;
}

.choice{
background-color: #64abfb;
color: #fff;
}

}
}
}

}


.iptBoxFocu{
border: 1px solid $allClor;
box-shadow: 0px 0px 3px 0.5px $allClor;
}



.dropChangeUp{
animation: changeUp 0.2s ease 1 normal forwards
}
.dropChangeDown{
animation: changeDown 0.2s ease 1 normal forwards
}

/* 动画函数 */
@keyframes changeUp {
/*以一种写法*/
0% {
top: 0px;
opacity: 0;
height: 0px;
}
100%{
top: 40px;
opacity: 1;
height: 308px;
}
}
@keyframes changeDown {
/*以一种写法*/
from {
top: 40px;
opacity: 1;
height: 308px;
}
to {
top: 0px;
opacity: 0;
height: 0px;
}
}