const {width, height} = Dimensions.get('window');
const isLargeScreenIphone = () => {
if (Platform.OS !== 'ios') return false;
// 主流大屏分辨率:宽度 >= 414,或高度 >= 812 (iPhone X以上)
return width >= 414 && height >= 896;
}
// 设置中文本地化配置
LocaleConfig.locales['zh'] = {
monthNames: ['一月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '十一月', '十二月'],
monthNamesShort: ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'],
dayNames: ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'],
dayNamesShort: ['日', '一', '二', '三', '四', '五', '六'],
today: "今天"
};
// 设置默认的本地化配置为中文
LocaleConfig.defaultLocale = 'zh';
const tabMapping = {
'allCalendar': <AllPage/>, //全部日历
'appJobCalendar': <OutLookPage/>, //日程日历
'appFeishuCalendar': <FeiShuPage/>, //飞书日历
'alternative_investment_calendar': <AiPage/>, //另类投资日历
'ir_calendar_index': <IrPage/>, //投研日历
'ims_calendar_index': <ImsPage/>,//ims日历
};
const ExpandableCalendarScreen = observer((props) => {
const storeApi = CalendarStore;
const [reload, setReload] = useState(0)
const [routeData, setRouteData] = useState([]);
const routeDataRef = useRef([])
const tabIndexRef = useRef(0)
const pageidRef = useRef('allCalendar')
const [datePickerFlag, setDatePickerFlag] = useState(false)
const [defaultDate, setDefaultDate] = useState("")
const curDate = new Date()
const monthDebounceRef = useRef();
const dateChangeDebounceRef = useRef();
const [selectedDate, setSelectedDate] = useState(moment().format('YYYY-MM-DD'));
const showChangeMonthRef = useRef(false);//false是周历,true是月历
//卸载计时器
useEffect(() => {
return () => {
if (monthDebounceRef.current) {
clearTimeout(monthDebounceRef.current);
}
if (dateChangeDebounceRef.current) {
clearTimeout(dateChangeDebounceRef.current);
}
};
}, []);
//初始化数据
const getRoute = async () => {
let routeList = []
if (typeof storeApi.tabData != 'undefined' && storeApi.tabData.length > 0) {
storeApi.tabData.map((item) => {
let route = {key: '', title: '', pageid: '', myAssembly: ''}
route.key = item.category
route.title = item.title
route.pageid = item.pageid
route.myAssembly = tabMapping[item.pageid]
routeList.push(route)
})
}
routeDataRef.current = routeList
await getPageId(0)
await setRouteData(routeList)
}
useEffect(() => {
getRoute()
setTheme(getTheme())
setDefaultDate(moment().format('YYYY-MM'))
}, []);
useEffect(() => {
let marked = storeApi.markedData
if (pageidRef.current != calendarTypeMapping.all.pageid) {//非全部日历tab要过滤marked数据
let typeArr = Object.keys(calendarTypeMapping).filter((typeKey) => pageidRef.current == calendarTypeMapping[typeKey]['pageid']).map((typeKey) => calendarTypeMapping[typeKey])
let type = typeArr.length > 0 ? typeArr[0]['type'] : ''
marked = Object.keys(storeApi.markedData).reduce((result, key) => {
if (!result[key]?.dots) {
result[key] = {'dots': []}
}
result[key]['dots'].push(...storeApi.markedData[key]['dots'].filter((item) => item.key == type));
return result
}, {})
} else {//全部数据过期时只保留一条过期的数据
marked = Object.keys(storeApi.markedData).reduce((result, key) => {
let date = key
if (!result[key]?.dots) {
result[key] = {'dots': []}
}
if (moment(date).format('YYYYMMDD') < moment().format('YYYYMMDD')) {//过期
result[key]['dots'] = storeApi.markedData[key]['dots'].length > 0 ? [storeApi.markedData[key]['dots'][0]] : []
} else {
result[key]['dots'].push(...storeApi.markedData[key]['dots'])
}
return result
}, {})
}
// 保证selectedDate有key
if (!marked[selectedDate]) {
marked[selectedDate] = {dots: []};
}
// 所有key,只让selectedDate为true,其他为false
Object.keys(marked).forEach(key => {
// console.log('key', key);
// console.log('selectedDate', selectedDate);
marked[key].selected = (key === selectedDate);
});
setMarked({...marked}); // 创建一个新的对象,
}, [storeApi.markedData, tabIndexRef.current, selectedDate]);
const [theme, setTheme] = useState()
const todayBtnTheme = useRef({
todayButtonTextColor: themeColor
});
// const [currentDate, setCurrentDate] = useState(moment().format('YYYY-MM-DD'));
const currentDate=useRef(moment().format('YYYY-MM-DD'))
const [showToday, setShowToday] = useState(false);// 是否显示今天
const [marked, setMarked] = useState(storeApi.markedData);
const clickFlag=useRef(false)
// 使用 useCallback 优化事件处理
const onDateChanged = useCallback((date) => {
let cflag=false//用来标记修改标志的
if (dateChangeDebounceRef.current) {
clearTimeout(dateChangeDebounceRef.current);
}
let checkDate=date
const todayDate = new Date();
const today=moment().format('YYYY-MM-DD')
const selectedDateObj = new Date(selectedDate);
const callbackDateObj = new Date(date);
const isSameMonth1 =
todayDate.getFullYear() === callbackDateObj.getFullYear() && todayDate.getMonth() === callbackDateObj.getMonth();
const isSameMonth2 =
selectedDateObj.getFullYear() === callbackDateObj.getFullYear() &&
selectedDateObj.getMonth() === callbackDateObj.getMonth();
setTimeout(() => {//延迟保证点击时间修改了clickFlag后再执行
if ((isSameMonth1 || isSameMonth2) && showChangeMonthRef.current) {
if (clickFlag.current) {
checkDate = today
clickFlag.current = false
cflag = true
} else {
checkDate = date
clickFlag.current = false
cflag = true
}
}
dateChangeDebounceRef.current = setTimeout(() => {
if (!cflag) {
clickFlag.current = true
}
storeApi.curSelectDate = moment(checkDate).format('YYYYMMDD');
storeApi.getCurrentDate(checkDate);
// setCurrentDate(moment(date).format('YYYY-MM-DD'));
currentDate.current = moment(checkDate).format('YYYY-MM-DD')
console.log('onDateChanged set selectedDate2222', moment(checkDate).format('YYYY-MM-DD'), selectedDate, currentDate);
setSelectedDate(moment(checkDate).format('YYYY-MM-DD'));
if (isToday(moment(checkDate).format('YYYY-MM-DD'))) {
setShowToday(false);
} else {
setShowToday(true);
}
}, 400);
}, 10);
}, []);
// 回到今天
const goToToday = () => {
const today = moment().format('YYYY-MM-DD');
storeApi.curSelectDate = moment(today).format('YYYYMMDD')
storeApi.getCurrentDate(today);
// setCurrentDate(today)
currentDate.current=today
setDefaultDate(moment(today).format('YYYY-MM-DD'))
setSelectedDate(moment(today).format('YYYY-MM-DD'))
};
//关闭年月选择器
const hideView = () => {
setDatePickerFlag(false);
}
const getDate = (date: any) => {
setDefaultDate(moment(date).format('YYYY-MM-DD'))
// setCurrentDate(moment(date).format('YYYY-MM-DD'));
currentDate.current=moment(date).format('YYYY-MM-DD')
setSelectedDate(moment(date).format('YYYY-MM-DD'))
setDatePickerFlag(false)
}
//打开选择器按钮
const openPicker = () => {
if (datePickerFlag) {
setDatePickerFlag(false)
} else {
setDatePickerFlag(true)
}
}
// 折叠效果 显示月度日历和周日历
const positionsStatusRef = useRef(ExpandableCalendar.positions.CLOSED)
// 日历展开折叠
const onCalendarToggled = (calendarOpened) => {
// setShowChangeMonth(calendarOpened)
showChangeMonthRef.current = calendarOpened
};
//根据index获取pageId
const getPageId = async (index) => {
let routeArr = routeDataRef.current.filter((item, key) => key == index)
let pageId = routeArr.length > 0 ? routeArr[0]['pageid'] : ''
pageidRef.current = pageId
storeApi.tabType = pageId
}
const handleSelectDay = useCallback((dayObj: { dateString: string }) => {
const currentMonth = moment().format('YYYY-MM');
const changedMonth = moment(dayObj.dateString).format('YYYY-MM');
if(currentMonth!=changedMonth){
clickFlag.current=true
}else{
clickFlag.current=false
}
const formatted = dayjs(dayObj.dateString).format('YYYY-MM-DD');
setSelectedDate(formatted);
storeApi.curSelectDate = formatted
// setCurrentDate(formatted);
currentDate.current=formatted
let isToday = storeApi.curSelectDate == moment().format('YYYYMMDD')
let theme = getTheme(pageidRef.current, isToday)
setTheme(prev => ({...theme}))
setReload(1);
}, []);
//tab切换事件
const tabSelect = async (index) => {
tabIndexRef.current = index;
storeApi.tabIndex = index
await getPageId(index);
let isToday = storeApi.curSelectDate == moment().format('YYYYMMDD');
let theme = getTheme(pageidRef.current, isToday);
await setTheme(prev => ({...theme}));
await setReload(1);
};
// Header 渲染函数 date.toString('yyyy年MM月'
const renderHeader = useCallback(
(date: XDate) => (
<View style={styles.header}>
<View style={styles.headerLeft}>
<TouchableOpacity style={styles.ymdSty} onPress={() => {
openPicker()
}}><Text style={styles.headerText}>{date?date.toString('yyyy年MM月'):moment(selectedDate).format('yyyy年MM月')}</Text>
<Image
style={styles.upImage}
source={{uri: datePickerFlag ? (cdnHost + '?mFileName=5a4c0cf34d8efcc043b69d2a6a8f04a3') : (cdnHost + '?mFileName=ca8f744f33202c452c7c5ec4c050ba72')}}
/>
</TouchableOpacity>
{
showToday && <TouchableOpacity onPress={goToToday}>
<Image
style={styles.todayImage}
source={{uri: cdnHost + '?mFileName=e97e6d1f124f6aabda71ca38c102ff18'}}
/>
</TouchableOpacity>
}
</View>
</View>
)
);
return (
<View style={{flex: 1}}>
<CalendarProvider
date={currentDate.current}
onDateChanged={(dateString) => {
onDateChanged(dateString)
}}
// onMonthChange={(dateString) => {
// onDateChanged(dateString.dateString)
// }}
disableAutoDaySelection={[CalendarNavigationTypes.MONTH_SCROLL]}
showTodayButton={false}
theme={todayBtnTheme.current}
>
<LinearGradient
colors={['rgba(255,255,255,0)', '#fff']}
start={{x: 0, y: 0}} // 起点
end={{x: 0, y: Platform.OS == "ios" ? (isLargeScreenIphone() ? 0.8 : 1) : 1}} // 终点,横向渐变
style={{
position: "absolute",
width: "100%",
height: rem(20),
zIndex: 10,
top: Platform.OS == "ios" ? (isLargeScreenIphone() ? -20 : -12) : -20
}}
>
{/* 内部内容 */}
</LinearGradient>
<ExpandableCalendar
key={reload}
hideArrows
hideKnob={false}
onCalendarToggled={onCalendarToggled}
initialPosition={positionsStatusRef.current}
// 禁用滑动和拖动手势
disableWeekScroll={false}
theme={theme}
calendarHeight={400}
firstDay={0}
current={currentDate.current}
markingType={'multi-dot'}
markedDates={marked}
renderHeader={renderHeader}
onDayPress={handleSelectDay}
closeOnDayPress={false}
dayComponent={({ date, state, marking }) => {
const isCurrentSelected = date.dateString === selectedDate;
const isToday = moment(date.dateString).isSame(moment(), 'day');
let textColor = '#2F3033'; // 默认颜色
if (state === 'disabled') {
textColor = '#ccc'; // 非本月日期 → 灰色
} else if (isCurrentSelected) {
textColor = '#fff'; // 选中日期 → 白色文字
} else if (isToday) {
textColor = themeColor; // 今天 → 主题色
} else {
textColor = '#2F3033'; // 正常日期
}
return (
<TouchableOpacity
onPress={() => handleSelectDay({ dateString: date.dateString })}
style={[
styles.dayContainer,
isCurrentSelected && styles.selectedDay,
state === 'disabled' && styles.disabledDay, // 可选:加背景透明
]}
activeOpacity={0.7}
>
<Text style={[styles.dayText, { color: textColor }]}>
{date.day}
</Text>
{/* 渲染 multi-dot 标记 */}
{marking?.dots?.length > 0 ? (
<View style={styles.dotsContainer}>
{marking.dots.map((dot, i) => (
<View
key={i}
style={[styles.dot, { backgroundColor: dot.color }]}
/>
))}
</View>
):<View style={styles.dotsContainer}>
</View>}
</TouchableOpacity>
);
}}
/>
<View style={{
position: "relative",
bottom: 10,
backgroundColor: "#fff",
width: "100%",
height: rem(15),
borderBottomLeftRadius: rem(8),
borderBottomRightRadius: rem(8),
}}>
</View>
<View style={{
flex: 1,
width: viewBaseInfo.S_WIDTH,
height: rem(100),
backgroundColor: '#F5F6F9',
top: rem(-10)
}}>
<TabEllipseAuto
textFontSize={rem(14)}
tabSpacing={rem(6)}
tabBarContainer={{borderTopLeftRadius: 8, borderTopRightRadius: 8}}
routeData={routeData}
getSelectIndex={tabSelect}
/>
</View>
<DatePickerViewCustom
defaultValue={defaultDate}
show={datePickerFlag}
onClose={hideView}
title="请选择"
precision='month'
onDateChange={getDate}
maxDate={curDate}
minDate={new Date(curDate.getFullYear(), 0, 1)}
/>
</CalendarProvider>
</View>
);
});
export default ExpandableCalendarScreen;
const styles = StyleSheet.create({
calendar: {
paddingLeft: 20,
paddingRight: 20
},
section: {
backgroundColor: lightThemeColor,
color: 'grey',
textTransform: 'capitalize'
},
header: {
flex: 1,
// marginTop: 6,
flexDirection: 'row',
justifyContent: 'space-between', // 在主轴(水平轴)上均匀分布空间
alignItems: 'center', // 在交叉轴(垂直轴)上居中对齐
height: 30,
},
headerLeft: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
marginLeft: rem(8)
},
headerRight: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-end',
marginRight: rem(6)
},
headerText: {
fontSize: 16,
color: '#2F3033',
fontWeight: "500",
fontFamily: 'PingFangSC-Medium', // 替换为你要使用的自定义字体
},
upImage: {
width: rem(8),
height: rem(6),
marginLeft: rem(3)
},
todayImage: {
width: rem(20),
height: rem(20),
marginLeft: rem(8)
},
leftImage: {
width: rem(20),
height: rem(20),
marginRight: rem(8)
},
rightImage: {
width: rem(20),
height: rem(20),
},
touchSty: {width: rem(16), height: rem(16), alignItems: 'center', justifyContent: 'center',},
ymdSty: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'flex-start',
},
dayContainer: {
alignItems: 'center',
justifyContent: 'center',
width: 25,
height: 30,
borderRadius: 30,
},
disabledDay: {
opacity: 0.5, // 可选:让非本月整体变淡
},
dayText: {
fontSize: 16,
fontWeight: '500',
},
selectedDay: {
backgroundColor: themeColor,
},
dotsContainer: {
flexDirection: 'row',
marginTop: 4,
},
dot: {
width: 4,
height: 4,
borderRadius: 2,
marginHorizontal: 1,
},
});初始化的时候日历不渲染
最新发布