Taro 封装日历组件
Taro 封装日历组件
·
Taro 封装日历组件
API
参数 | 说明 | 类型 | 默认值 | 必填 |
---|---|---|---|---|
lang | 星期的写法,中/英 | ‘CN’/‘EN’ | ‘CN’ | 否 |
columnGap | 列间距 | number | 0 | 否 |
rowGap | 行间距 | number | 0 | 否 |
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 | 日历组件返回上一个月(目前设置,不能返回当月之前的月份) |
使用方法:
- 挂载ref
// 父组件
const controlRef = useRef<null | ControlRefFunctionProps>(null)
....
<HCalendar ref={controlRef}/>
- 使用
controlRef.current.toNextMonth()
controlRef.current.toPreMonth()
使用案例
<HCalendar
ref={controlRef}
className={styles['calendar']}
headComponent={<HeadRender/>}
lang={'EN'}
backgroundColor={'#ffffff'}
currentDayColor={'#1f95af'}
planDayColor={'#3e646d'}
onClickDay={(year,month,day) => {
console.log(year,month,day)
}}
/>
<HeadRender/>
为自定义日历头组件
const HeadRender = () => {
return (
<View className={styles['calendar-header']}>
<View className={styles['calendar-header-controll']}>
<Text className={styles['mounth-text']}>Januari {(calCurTimeInfo && calCurTimeInfo[0]) || 2022}</Text>
<View className={styles['calendar-header-arrows']}>
<HArrow onClick={(e: { stopPropagation: () => any }) => controlFunctions(e,'toPreMonth')} color={'#90959e'} fixedAngle={-135}></HArrow>
<HArrow onClick={(e: { stopPropagation: () => any }) => controlFunctions(e,'toNextMonth')} color={'#90959e'} fixedAngle={45}></HArrow>
</View>
</View>
<View className={styles['division']}></View>
</View>
)
}
工具类方法
封装得比较差,可根据效果自行封装
/**
* 获取时间信息,返回数组 年、月、日、星期、原始的星期
*/
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
}
}
/**
* 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()
})
}
使用到的自定义组件
<HGrid>
:用于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<Array<Array<string>> | null>(null)
const [point, setPoint] = useState<number>(0) // 指针
const [dayLength, setDayLength] = useState<Array<number>>([30]) // 记录每月的长度
const [startWeek, setStartWeek] = useState<Array<number>>([0]) // 该月第一天的星期
const [day, setDay] = useState<Array<number>>([2022, 1, 1]) // 当天的日子,第一个参数为年,第二个参数为月,第三个参数为日
const [chosedDay, setChosedDay] = useState(33) // 当前选中的日期
const [computedWidth, setComputedWidth] = useState<number>(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 (
<View className={`${styles.container} ${className}`} style={{ ...containerStyle, backgroundColor }} ref={ref}>
{headComponent}
<View className={styles.innercontainer} >
<HGrid column={7} columnGap={columnGap} rowGap={rowGap}>
{/* 星期 */}
{
weekTitle.map(value => {
return (
<HGridItem key={value}>
<Text className={styles.weekTitle}>{value}</Text>
</HGridItem>
)
})
}
{
calendarData && calendarData[point].map((value, index) => {
const selfDay = index + 1 - startWeek[point]
const isPlanDay = planDayArr.indexOf(selfDay) !== -1
const oneOfStartWeek = startWeek[point]
return (
<HGridItem key={index}>
<Text
onClick={() => {
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}</Text>
</HGridItem>
)
})
}
</HGrid>
</View>
</View>
)
})
export default Calendar
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;
}
更多推荐
所有评论(0)