• Taro 封装日历组件


    Taro 封装日历组件

    API

    参数说明类型默认值必填
    lang星期的写法,中/英‘CN’/‘EN’‘CN’
    columnGap列间距number0
    rowGap行间距number0
    backgroundColor背景色string‘#ffffff’
    currentDayColor当前日期的背景色string‘#A399E2’
    planDayColor有计划的日期的背景色string‘#CEBAF9’
    planDayArr拥有计划的日期number[][]
    onClickDay点击日期的回调函数(year: number, month: number, day: number) => void-
    headComponent自定义日历头组件ReactNode-
    containerStyle整体日历的样式CSSProperties-
    className类名string-

    对外暴露的方法

    使用场景:父组件控制日历的组件的翻动

    方法名说明
    toNextMonth日历组件跳转到下一个月
    toPreMonth日历组件返回上一个月(目前设置,不能返回当月之前的月份)

    使用方法:

    1. 挂载ref
    // 父组件
    const controlRef = useRef(null)
    ....
    
    
    • 1
    • 2
    • 3
    • 4
    1. 使用
    controlRef.current.toNextMonth()
    controlRef.current.toPreMonth()
    
    • 1
    • 2

    使用案例

    }
        lang={'EN'}
        backgroundColor={'#ffffff'}
        currentDayColor={'#1f95af'}
        planDayColor={'#3e646d'}
        onClickDay={(year,month,day) => {
            console.log(year,month,day)
        }}
    />
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    为自定义日历头组件

    const HeadRender = () => {
        return (
            
                
                    Januari {(calCurTimeInfo && calCurTimeInfo[0]) || 2022}
                    
                         any }) => controlFunctions(e,'toPreMonth')} color={'#90959e'} fixedAngle={-135}>
                         any }) => controlFunctions(e,'toNextMonth')} color={'#90959e'} fixedAngle={45}>
                    
                
                
            
        )
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    工具类方法

    封装得比较差,可根据效果自行封装

    /**
     * 获取时间信息,返回数组 年、月、日、星期、原始的星期
     */
    const getTime = () => {
        const date = new Date()
        const year = date.getFullYear()
        const month = date.getMonth() + 1
        const day = date.getDate()
        const originalWeek = date.getDay()
        const week = '星期' + ['天','一', '二', '三', '四', '五', '六'][originalWeek] // 获取星期
        return [year, month, day, week,originalWeek] as const
    }
    
    /**
     * 返回指定年指定月的天数
     * @param year  年
     * @param month  月
     * @returns  天数
     */
    function getMonthDayNumber(year:number,month:number) {
        if (month === 2) {
            // 判断是平年还是闰年
            if ((!(year % 4) && year % 100) || !(year % 400)) {
                return 29
            } else {
                return 28
            }
        } else {
            return longMonth.indexOf(month as number) === -1 ? 30 : 31
        }
    }
    
    • 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
    /**
     * fields 节点选择,可添加返回的数据
     * selector:节点选择
     * extraAttribute:额外返回的数据
     *  */ 
    const selectorQueryFields = (selector: string, extraAttribute: string[] = []): Promise<TaroGeneral.IAnyObject> => {
        return new Promise(resolve => {
            const query = Taro.createSelectorQuery()
            query.select(selector).fields({
                dataset: true,
                size: true,
                scrollOffset: true,
                properties: ['scrollX', 'scrollY'],
                computedStyle: extraAttribute,
                context: true,
            }, res => resolve(res)).exec()
        })
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    使用到的自定义组件

    :用于Grid布局封装的组件

    详细请点击此处

    完整代码

    index.tsx

    import styles from "./index.module.css"
    import { View, Text } from "@tarojs/components"
    import HGrid from "../HGrid"
    import HGridItem from "../HGridItem"
    import { CSSProperties, ReactNode, useEffect, useImperativeHandle, useMemo, useState } from "react"
    import { getTime, getMonthDayNumber } from "../../utils/date"
    import { selectorQueryFields } from "../../utils/index"
    import React from "react"
    
    interface CalendarProps {
        lang?: 'CN' | 'EN', // 星期的写法,中/英
        columnGap?: number, // 列间距
        rowGap?: number, // 行间距
        backgroundColor?: string, // 背景色
        currentDayColor?: string, // 当前日期的颜色
        planDayColor?: string, /// 计划日期的颜色
        planDayArr?: number[], // 计划时间的列表
        onClickDay?: (year: number, month: number, day: number) => void, // 点击日期的回调函数
        headComponent?: ReactNode,
        containerStyle?: CSSProperties, // 样式
        className?: string // 类名
    }
    
    // 对外暴露方法的interface
    export interface ControlRefFunctionProps {
        toPreMonth: Function,
        toNextMonth: Function
    }
    /**
     * 创建日历数组
     * @param dayLength 一个月的天数
     * @param startWeek 一个月开始的星期
     */
    const createCalendarData = (dayLength: number, startWeek: number) => {
        let res: string[] = []
        // 下一个月所占的天数
        const remaining = 35 - dayLength - startWeek
        // 填充上一个月的位置
        while (startWeek) {
            res.push('')
            startWeek--
        }
        // 填充这个月的日子
        for (let i = 1; i <= dayLength; i++) {
            res.push(String(i))
        }
        // 填充下一个月的位置
        for (let j = 0; j < remaining; j++) {
            res.push('')
        }
        return res
    }
    
    const Calendar = React.forwardRef((props: CalendarProps, ref) => {
        let {
            lang = 'CN',
            rowGap = 0,
            columnGap = 0,
            containerStyle,
            backgroundColor = '#E3D8FC',
            currentDayColor = '#A399E2',
            planDayColor = '#CEBAF9',
            planDayArr = [],
            onClickDay,
            headComponent,
            className
        } = props
    
        const [calendarData, setCalendarData] = useState> | null>(null)
        const [point, setPoint] = useState(0) // 指针
        const [dayLength, setDayLength] = useState>([30]) // 记录每月的长度
        const [startWeek, setStartWeek] = useState>([0]) // 该月第一天的星期
        const [day, setDay] = useState>([2022, 1, 1]) // 当天的日子,第一个参数为年,第二个参数为月,第三个参数为日
        const [chosedDay, setChosedDay] = useState(33) // 当前选中的日期
        const [computedWidth, setComputedWidth] = useState(0) // 计算单个节点的宽度
    
        // 挂载完毕后创建数据
        useEffect(() => {
            const [year, month, day, week, originalWeek] = getTime()
            // 判断该月的天数
            let tempDayLength: number = getMonthDayNumber(year, month)
    
            // 计算该月第一天的星期 [0,1,2,3,4,5,6]
            const moveSteps = day % 7 - 1
            // console.log('originalWeek',originalWeek)
            const temp = originalWeek - moveSteps
            let realStartWeek: number
            if (temp >= 0) {
                realStartWeek = temp
            } else {
                realStartWeek = originalWeek + (7 - moveSteps)
            }
            setDay([year, month, day])
            setStartWeek([realStartWeek])
            setDayLength([tempDayLength])
            setChosedDay(day)
            // 创建数据
            setCalendarData(() => {
                return [createCalendarData(tempDayLength, realStartWeek)]
            })
        }, [])
    
        // 获取容器的宽度来实现背景圆圈的自适应
        useEffect(() => {
            // 此处选择innercontainer是因为如果外部使用flex布局,width就不受控制了
            selectorQueryFields(`.${styles.container}`).then(res => {
                // 计算单个节点的宽度
                setComputedWidth(() => Math.floor((res.width - 6 * columnGap) / 7))
            })
        }, [])
    
        const weekTitle = useMemo(() => {
            if (lang === 'CN') {
                return ['日', '一', '二', '三', '四', '五', '六']
            } else {
                return ['Sun', 'Mon', 'Tuse', 'Wed', 'Thur', 'Fri', 'Sat']
            }
        }, [])
    
        const computedCurInfo = (year, month, point: number): number[] => {
            return [
                year + Math.floor((month + point - 1) / 12),
                (month + point) % 12 || 12
            ]
        }
    
        // 对外暴露的方法
        const toNextMonth = () => {
            const tempPoint = point + 1
            const length = calendarData?.length || 0
            // 计算年与月
            let [year, month] = day
            let [newYear, newMonth] = computedCurInfo(year, month, tempPoint)
    
            // 下一个月未创建数据
            if (tempPoint === length) {
    
                const newStartWeek = (startWeek[point] + dayLength[point]) % 7
                const newDayLength = getMonthDayNumber(newYear, newMonth)
                const res = createCalendarData(newDayLength, newStartWeek)
                // 添加新数据
                setCalendarData(pre => {
                    pre && pre.push(res)
                    return pre
                })
                setPoint(tempPoint) // 修改指针
                setStartWeek(pre => [...pre, newStartWeek])
                setDayLength(pre => [...pre, newDayLength])
            } else {
                setPoint(pre => pre + 1)
            }
            return [newYear,newMonth]
        }
    
        const toPreMonth = () => {
            if (point > 0) {
                let [year, month] = day
                setPoint(pre => pre - 1)
                return computedCurInfo(year, month, point-1)
            }
        }
    
        useImperativeHandle(ref, () => {
            return {
                toNextMonth,
                toPreMonth
            }
        })
    
        return (
            { ...containerStyle, backgroundColor }} ref={ref}>
                {headComponent}
                
                    
                        {/* 星期 */}
                        {
                            weekTitle.map(value => {
                                return (
                                    
                                        {value}
                                    
                                )
                            })
                        }
                        {
                            calendarData && calendarData[point].map((value, index) => {
                                const selfDay = index + 1 - startWeek[point]
                                const isPlanDay = planDayArr.indexOf(selfDay) !== -1
                                const oneOfStartWeek = startWeek[point]
                                return (
                                    
                                         {
                                                if (selfDay > 0 && selfDay <= dayLength[point]) {
                                                    setChosedDay(selfDay)
                                                    onClickDay && onClickDay(day[0] + Math.floor((day[1] + point - 1) / 12), (day[1] + point) % 12 === 0 ? 12 : (day[1] + point) % 12, selfDay)
                                                }
                                            }}
                                            className="date"
                                            style={{
                                                boxSizing: 'border-box',
                                                fontWeight: '500',
                                                borderRadius: '50%',
                                                width: `${computedWidth}px`,
                                                height: `${computedWidth}px`,
                                                lineHeight: `${computedWidth}px`,
                                                backgroundColor: value ? (point === 0 && oneOfStartWeek + day[2] - 1 === index ? currentDayColor : (isPlanDay ? `${planDayColor}` : '')) : '',
                                                border: selfDay === chosedDay ? '2px solid' : `2px solid ${backgroundColor}`,
                                                color: isPlanDay ? '#ffffff' : (point === 0 && oneOfStartWeek + day[2] - 1 === index ? '' : '#2d4068')
                                            }}
                                        >{value}
                                    
                                )
                            })
                        }
                    
                
            
        )
    })
    
    export default Calendar
    
    • 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

    index.module.css

    .container {
        box-sizing: border-box;
        text-align: center;
        display: flex;
        flex-direction: column;
        justify-content: center;
        margin: 25px auto;
    }
    .weekTitle{
        color: #81848c;
        font-size: 28px;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
  • 相关阅读:
    Maven3.5.4下载和配置详细图文教程
    如何把在本地存储sessionStorage.setItem()上存的值渲染在输入框中js
    what is gopls
    javaee springMVC绑定复杂对象
    Java EE 用户删除和修改功能
    文件上传到oss上以及下载
    安装windows linux 双系统
    JAVA学习笔记和练习目录
    MongoDB索引原理及实践
    ts视频文件转为mp4(FFmpeg)
  • 原文地址:https://blog.csdn.net/qq_56303170/article/details/127963200