- 在项目中引入expo-notifications库,支持本地推送通知功能 - 实现通知权限管理,用户可选择开启或关闭通知 - 新增通知发送、定时通知和重复通知功能 - 更新个人页面,集成通知开关和权限请求逻辑 - 编写推送通知功能实现文档,详细描述功能和使用方法 - 优化心情日历页面,确保数据实时刷新
655 lines
19 KiB
TypeScript
655 lines
19 KiB
TypeScript
import { HeaderBar } from '@/components/ui/HeaderBar';
|
||
import { Colors } from '@/constants/Colors';
|
||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||
import { useMoodData } from '@/hooks/useMoodData';
|
||
import { getMoodOptions } from '@/services/moodCheckins';
|
||
import { selectLatestMoodRecordByDate } from '@/store/moodSlice';
|
||
import dayjs from 'dayjs';
|
||
import { LinearGradient } from 'expo-linear-gradient';
|
||
import { router, useFocusEffect, useLocalSearchParams } from 'expo-router';
|
||
import React, { useCallback, useEffect, useState } from 'react';
|
||
import {
|
||
Dimensions,
|
||
SafeAreaView,
|
||
ScrollView,
|
||
StyleSheet,
|
||
Text,
|
||
TouchableOpacity,
|
||
View,
|
||
} from 'react-native';
|
||
|
||
const { width } = Dimensions.get('window');
|
||
|
||
// 心情日历数据生成函数
|
||
const generateCalendarData = (targetDate: Date) => {
|
||
const year = targetDate.getFullYear();
|
||
const month = targetDate.getMonth();
|
||
const daysInMonth = new Date(year, month + 1, 0).getDate();
|
||
const firstDayOfWeek = new Date(year, month, 1).getDay();
|
||
|
||
const calendar = [];
|
||
const weeks = [];
|
||
|
||
// 添加空白日期
|
||
for (let i = 0; i < firstDayOfWeek; i++) {
|
||
weeks.push(null);
|
||
}
|
||
|
||
// 添加实际日期
|
||
for (let day = 1; day <= daysInMonth; day++) {
|
||
weeks.push(day);
|
||
}
|
||
|
||
// 按周分组
|
||
for (let i = 0; i < weeks.length; i += 7) {
|
||
calendar.push(weeks.slice(i, i + 7));
|
||
}
|
||
|
||
return { calendar, today: new Date().getDate(), month: month + 1, year };
|
||
};
|
||
|
||
export default function MoodCalendarScreen() {
|
||
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
|
||
const colorTokens = Colors[theme];
|
||
const params = useLocalSearchParams();
|
||
const dispatch = useAppDispatch();
|
||
const { fetchMoodRecords, fetchMoodHistoryRecords } = useMoodData();
|
||
|
||
const { selectedDate } = params;
|
||
const initialDate = selectedDate ? dayjs(selectedDate as string).toDate() : new Date();
|
||
|
||
const [currentMonth, setCurrentMonth] = useState(initialDate);
|
||
const [selectedDay, setSelectedDay] = useState<number | null>(null);
|
||
|
||
// 使用 Redux store 中的数据
|
||
const moodRecords = useAppSelector(state => state.mood.moodRecords);
|
||
|
||
// 获取选中日期的数据
|
||
const selectedDateString = selectedDay ? dayjs(currentMonth).date(selectedDay).format('YYYY-MM-DD') : null;
|
||
const selectedDateMood = useAppSelector(state => {
|
||
if (!selectedDateString) return null;
|
||
return selectLatestMoodRecordByDate(selectedDateString)(state);
|
||
});
|
||
|
||
const moodOptions = getMoodOptions();
|
||
const weekDays = ['周一', '周二', '周三', '周四', '周五', '周六', '周日'];
|
||
const monthNames = ['1月', '2月', '3月', '4月', '5月', '6月', '7月', '8月', '9月', '10月', '11月', '12月'];
|
||
|
||
// 生成当前月份的日历数据
|
||
const { calendar, today, month, year } = generateCalendarData(currentMonth);
|
||
|
||
// 加载整个月份的心情数据
|
||
const loadMonthMoodData = async (targetMonth: Date) => {
|
||
try {
|
||
const startDate = dayjs(targetMonth).startOf('month').format('YYYY-MM-DD');
|
||
const endDate = dayjs(targetMonth).endOf('month').format('YYYY-MM-DD');
|
||
|
||
const historyData = await fetchMoodHistoryRecords({ startDate, endDate });
|
||
|
||
// 历史记录已经通过 fetchMoodHistoryRecords 自动存储到 Redux store 中
|
||
// 不需要额外的处理
|
||
} catch (error) {
|
||
console.error('加载月份心情数据失败:', error);
|
||
}
|
||
};
|
||
|
||
// 加载选中日期的心情记录
|
||
const loadDailyMoodCheckins = async (dateString: string) => {
|
||
try {
|
||
await fetchMoodRecords(dateString);
|
||
// 不需要手动设置 selectedDateMood,因为它现在从 Redux store 中获取
|
||
} catch (error) {
|
||
console.error('加载心情记录失败:', error);
|
||
}
|
||
};
|
||
|
||
// 初始化选中日期
|
||
useEffect(() => {
|
||
if (selectedDate) {
|
||
const date = dayjs(selectedDate as string);
|
||
setCurrentMonth(date.toDate());
|
||
setSelectedDay(date.date());
|
||
const dateString = date.format('YYYY-MM-DD');
|
||
loadDailyMoodCheckins(dateString);
|
||
} else {
|
||
const today = new Date();
|
||
setCurrentMonth(today);
|
||
setSelectedDay(today.getDate());
|
||
const dateString = dayjs().format('YYYY-MM-DD');
|
||
loadDailyMoodCheckins(dateString);
|
||
}
|
||
loadMonthMoodData(currentMonth);
|
||
}, [selectedDate]);
|
||
|
||
// 监听页面焦点变化,当从编辑页面返回时刷新数据
|
||
useFocusEffect(
|
||
useCallback(() => {
|
||
// 当页面获得焦点时,刷新当前月份的数据和选中日期的数据
|
||
const refreshData = async () => {
|
||
if (selectedDay) {
|
||
const selectedDateString = dayjs(currentMonth).date(selectedDay).format('YYYY-MM-DD');
|
||
await fetchMoodRecords(selectedDateString);
|
||
}
|
||
const startDate = dayjs(currentMonth).startOf('month').format('YYYY-MM-DD');
|
||
const endDate = dayjs(currentMonth).endOf('month').format('YYYY-MM-DD');
|
||
await fetchMoodHistoryRecords({ startDate, endDate });
|
||
};
|
||
refreshData();
|
||
}, [currentMonth, selectedDay, fetchMoodRecords, fetchMoodHistoryRecords])
|
||
);
|
||
|
||
// 月份切换函数
|
||
const goToPreviousMonth = () => {
|
||
const newMonth = new Date(currentMonth);
|
||
newMonth.setMonth(newMonth.getMonth() - 1);
|
||
setCurrentMonth(newMonth);
|
||
setSelectedDay(null);
|
||
loadMonthMoodData(newMonth);
|
||
};
|
||
|
||
const goToNextMonth = () => {
|
||
const newMonth = new Date(currentMonth);
|
||
newMonth.setMonth(newMonth.getMonth() + 1);
|
||
setCurrentMonth(newMonth);
|
||
setSelectedDay(null);
|
||
loadMonthMoodData(newMonth);
|
||
};
|
||
|
||
// 日期选择函数
|
||
const onSelectDate = (day: number) => {
|
||
setSelectedDay(day);
|
||
const selectedDateString = dayjs(currentMonth).date(day).format('YYYY-MM-DD');
|
||
loadDailyMoodCheckins(selectedDateString);
|
||
};
|
||
|
||
// 跳转到心情编辑页面
|
||
const openMoodEdit = () => {
|
||
const selectedDateString = selectedDay ? dayjs(currentMonth).date(selectedDay).format('YYYY-MM-DD') : dayjs().format('YYYY-MM-DD');
|
||
const moodId = selectedDateMood?.id;
|
||
|
||
router.push({
|
||
pathname: '/mood/edit',
|
||
params: {
|
||
date: selectedDateString,
|
||
...(moodId && { moodId })
|
||
}
|
||
});
|
||
};
|
||
|
||
const renderMoodIcon = (day: number | null, isSelected: boolean) => {
|
||
if (!day) return null;
|
||
|
||
// 检查该日期是否有心情记录 - 现在从 Redux store 中获取
|
||
const dayDateString = dayjs(currentMonth).date(day).format('YYYY-MM-DD');
|
||
const dayRecords = moodRecords[dayDateString] || [];
|
||
const moodRecord = dayRecords.length > 0 ? dayRecords[0] : null;
|
||
|
||
if (moodRecord) {
|
||
const mood = moodOptions.find(m => m.type === moodRecord.moodType);
|
||
return (
|
||
<View style={[styles.moodIconContainer, { backgroundColor: mood?.color }]}>
|
||
<View style={styles.moodIcon}>
|
||
<Text style={styles.moodEmoji}>{mood?.emoji || '😊'}</Text>
|
||
</View>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
return (
|
||
<View style={styles.defaultMoodIcon}>
|
||
<Text style={styles.defaultMoodEmoji}>😊</Text>
|
||
</View>
|
||
);
|
||
};
|
||
|
||
// 使用统一的渐变背景色
|
||
const backgroundGradientColors = [colorTokens.backgroundGradientStart, colorTokens.backgroundGradientEnd] as const;
|
||
|
||
return (
|
||
<View style={styles.container}>
|
||
<LinearGradient
|
||
colors={['#fafaff', '#f4f3ff']} // 使用紫色主题的浅色渐变
|
||
style={styles.gradientBackground}
|
||
start={{ x: 0, y: 0 }}
|
||
end={{ x: 0, y: 1 }}
|
||
/>
|
||
{/* 装饰性圆圈 */}
|
||
<View style={styles.decorativeCircle1} />
|
||
<View style={styles.decorativeCircle2} />
|
||
|
||
<SafeAreaView style={styles.safeArea}>
|
||
<HeaderBar
|
||
title="心情日历"
|
||
onBack={() => router.back()}
|
||
withSafeTop={false}
|
||
transparent={true}
|
||
tone="light"
|
||
/>
|
||
|
||
<ScrollView style={styles.content}>
|
||
{/* 日历视图 */}
|
||
<View style={styles.calendar}>
|
||
{/* 月份导航 */}
|
||
<View style={styles.monthNavigation}>
|
||
<TouchableOpacity
|
||
style={styles.navButton}
|
||
onPress={goToPreviousMonth}
|
||
>
|
||
<Text style={styles.navButtonText}>‹</Text>
|
||
</TouchableOpacity>
|
||
<Text style={styles.monthTitle}>{year}年{monthNames[month - 1]}</Text>
|
||
<TouchableOpacity
|
||
style={styles.navButton}
|
||
onPress={goToNextMonth}
|
||
>
|
||
<Text style={styles.navButtonText}>›</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
|
||
<View style={styles.weekHeader}>
|
||
{weekDays.map((day, index) => (
|
||
<Text key={index} style={styles.weekDay}>{day}</Text>
|
||
))}
|
||
</View>
|
||
|
||
{calendar.map((week, weekIndex) => (
|
||
<View key={weekIndex} style={styles.weekRow}>
|
||
{week.map((day, dayIndex) => {
|
||
const isSelected = day === selectedDay;
|
||
const isToday = day === today && month === new Date().getMonth() + 1 && year === new Date().getFullYear();
|
||
const isFutureDate = Boolean(day && dayjs(currentMonth).date(day).isAfter(dayjs(), 'day'));
|
||
|
||
return (
|
||
<View key={dayIndex} style={styles.dayContainer}>
|
||
{day && (
|
||
<TouchableOpacity
|
||
style={[
|
||
styles.dayButton,
|
||
isSelected && styles.dayButtonSelected,
|
||
isToday && styles.dayButtonToday
|
||
]}
|
||
onPress={() => !isFutureDate && day && onSelectDate(day)}
|
||
disabled={isFutureDate}
|
||
>
|
||
<View style={styles.dayContent}>
|
||
<Text style={[
|
||
styles.dayNumber,
|
||
isSelected && styles.dayNumberSelected,
|
||
isToday && styles.dayNumberToday,
|
||
isFutureDate && styles.dayNumberDisabled
|
||
]}>
|
||
{day.toString().padStart(2, '0')}
|
||
</Text>
|
||
{renderMoodIcon(day, isSelected)}
|
||
</View>
|
||
</TouchableOpacity>
|
||
)}
|
||
</View>
|
||
);
|
||
})}
|
||
</View>
|
||
))}
|
||
</View>
|
||
|
||
{/* 选中日期的记录 */}
|
||
<View style={styles.selectedDateSection}>
|
||
<View style={styles.selectedDateHeader}>
|
||
<Text style={styles.selectedDateTitle}>
|
||
{selectedDay ? dayjs(currentMonth).date(selectedDay).format('YYYY年M月D日') : '请选择日期'}
|
||
</Text>
|
||
<TouchableOpacity
|
||
style={styles.addMoodButton}
|
||
onPress={openMoodEdit}
|
||
>
|
||
<Text style={styles.addMoodButtonText}>记录</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
|
||
{selectedDay ? (
|
||
selectedDateMood ? (
|
||
<TouchableOpacity
|
||
style={styles.moodRecord}
|
||
onPress={openMoodEdit}
|
||
>
|
||
<View style={styles.recordIcon}>
|
||
<View style={styles.moodIcon}>
|
||
<Text style={styles.moodEmoji}>
|
||
{moodOptions.find(m => m.type === selectedDateMood.moodType)?.emoji || '😊'}
|
||
</Text>
|
||
</View>
|
||
</View>
|
||
<View style={styles.recordContent}>
|
||
<Text style={styles.recordMood}>
|
||
{moodOptions.find(m => m.type === selectedDateMood.moodType)?.label}
|
||
</Text>
|
||
<Text style={styles.recordIntensity}>强度: {selectedDateMood.intensity}</Text>
|
||
{selectedDateMood.description && (
|
||
<Text style={styles.recordDescription}>{selectedDateMood.description}</Text>
|
||
)}
|
||
</View>
|
||
<View style={styles.spacer} />
|
||
<Text style={styles.recordTime}>
|
||
{dayjs(selectedDateMood.createdAt).format('HH:mm')}
|
||
</Text>
|
||
</TouchableOpacity>
|
||
) : (
|
||
<View style={styles.emptyRecord}>
|
||
<Text style={styles.emptyRecordText}>暂无心情记录</Text>
|
||
<Text style={styles.emptyRecordSubtext}>点击右上角"记录"按钮添加心情</Text>
|
||
</View>
|
||
)
|
||
) : (
|
||
<View style={styles.emptyRecord}>
|
||
<Text style={styles.emptyRecordText}>请先选择一个日期</Text>
|
||
<Text style={styles.emptyRecordSubtext}>点击日历中的日期,然后点击"记录"按钮添加心情</Text>
|
||
</View>
|
||
)}
|
||
</View>
|
||
</ScrollView>
|
||
</SafeAreaView>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
container: {
|
||
flex: 1,
|
||
},
|
||
gradientBackground: {
|
||
position: 'absolute',
|
||
left: 0,
|
||
right: 0,
|
||
top: 0,
|
||
bottom: 0,
|
||
},
|
||
decorativeCircle1: {
|
||
position: 'absolute',
|
||
top: 40,
|
||
right: 20,
|
||
width: 60,
|
||
height: 60,
|
||
borderRadius: 30,
|
||
backgroundColor: '#7a5af8',
|
||
opacity: 0.08,
|
||
},
|
||
decorativeCircle2: {
|
||
position: 'absolute',
|
||
bottom: -15,
|
||
left: -15,
|
||
width: 40,
|
||
height: 40,
|
||
borderRadius: 20,
|
||
backgroundColor: '#7a5af8',
|
||
opacity: 0.04,
|
||
},
|
||
safeArea: {
|
||
flex: 1,
|
||
},
|
||
|
||
content: {
|
||
flex: 1,
|
||
},
|
||
calendar: {
|
||
backgroundColor: 'rgba(255,255,255,0.95)',
|
||
margin: 16,
|
||
borderRadius: 20,
|
||
padding: 20,
|
||
shadowColor: '#7a5af8',
|
||
shadowOffset: { width: 0, height: 4 },
|
||
shadowOpacity: 0.1,
|
||
shadowRadius: 12,
|
||
elevation: 6,
|
||
},
|
||
monthNavigation: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
marginBottom: 24,
|
||
},
|
||
navButton: {
|
||
width: 44,
|
||
height: 44,
|
||
borderRadius: 22,
|
||
backgroundColor: 'rgba(122,90,248,0.1)',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
shadowColor: '#7a5af8',
|
||
shadowOffset: { width: 0, height: 2 },
|
||
shadowOpacity: 0.1,
|
||
shadowRadius: 4,
|
||
elevation: 2,
|
||
},
|
||
navButtonText: {
|
||
fontSize: 24,
|
||
color: '#7a5af8',
|
||
fontWeight: '700',
|
||
},
|
||
monthTitle: {
|
||
fontSize: 20,
|
||
fontWeight: '800',
|
||
color: '#192126',
|
||
},
|
||
weekHeader: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-around',
|
||
marginBottom: 20,
|
||
},
|
||
weekDay: {
|
||
fontSize: 13,
|
||
color: '#5d6676',
|
||
textAlign: 'center',
|
||
width: (width - 96) / 7,
|
||
fontWeight: '600',
|
||
},
|
||
weekRow: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-around',
|
||
marginBottom: 16,
|
||
},
|
||
dayContainer: {
|
||
width: (width - 96) / 7,
|
||
alignItems: 'center',
|
||
},
|
||
dayButton: {
|
||
width: 44,
|
||
height: 44,
|
||
borderRadius: 22,
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
marginBottom: 8,
|
||
backgroundColor: 'transparent',
|
||
},
|
||
dayButtonSelected: {
|
||
backgroundColor: '#FFFFFF',
|
||
shadowColor: '#7a5af8',
|
||
shadowOffset: { width: 0, height: 2 },
|
||
shadowOpacity: 0.15,
|
||
shadowRadius: 6,
|
||
elevation: 4,
|
||
},
|
||
dayButtonToday: {
|
||
borderWidth: 2,
|
||
borderColor: '#7a5af8',
|
||
},
|
||
dayContent: {
|
||
position: 'relative',
|
||
width: '100%',
|
||
height: '100%',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
},
|
||
dayNumber: {
|
||
fontSize: 14,
|
||
color: '#777f8c',
|
||
fontWeight: '600',
|
||
position: 'absolute',
|
||
top: 2,
|
||
zIndex: 1,
|
||
},
|
||
dayNumberSelected: {
|
||
color: '#192126',
|
||
fontWeight: '700',
|
||
},
|
||
dayNumberToday: {
|
||
color: '#7a5af8',
|
||
fontWeight: '700',
|
||
},
|
||
dayNumberDisabled: {
|
||
color: '#c0c4ca',
|
||
},
|
||
moodIconContainer: {
|
||
position: 'absolute',
|
||
bottom: 2,
|
||
width: 22,
|
||
height: 22,
|
||
borderRadius: 11,
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
shadowColor: '#000',
|
||
shadowOffset: { width: 0, height: 1 },
|
||
shadowOpacity: 0.1,
|
||
shadowRadius: 2,
|
||
elevation: 1,
|
||
},
|
||
moodIcon: {
|
||
width: 18,
|
||
height: 18,
|
||
borderRadius: 9,
|
||
backgroundColor: 'rgba(255,255,255,0.95)',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
},
|
||
moodEmoji: {
|
||
fontSize: 11,
|
||
},
|
||
defaultMoodIcon: {
|
||
position: 'absolute',
|
||
bottom: 2,
|
||
width: 22,
|
||
height: 22,
|
||
borderRadius: 11,
|
||
borderWidth: 1.5,
|
||
borderColor: 'rgba(122,90,248,0.3)',
|
||
borderStyle: 'dashed',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
backgroundColor: 'rgba(122,90,248,0.05)',
|
||
},
|
||
defaultMoodEmoji: {
|
||
fontSize: 10,
|
||
opacity: 0.4,
|
||
color: '#7a5af8',
|
||
},
|
||
selectedDateSection: {
|
||
backgroundColor: 'rgba(255,255,255,0.95)',
|
||
margin: 16,
|
||
marginTop: 0,
|
||
borderRadius: 20,
|
||
padding: 20,
|
||
shadowColor: '#7a5af8',
|
||
shadowOffset: { width: 0, height: 4 },
|
||
shadowOpacity: 0.1,
|
||
shadowRadius: 12,
|
||
elevation: 6,
|
||
},
|
||
selectedDateHeader: {
|
||
flexDirection: 'row',
|
||
justifyContent: 'space-between',
|
||
alignItems: 'center',
|
||
marginBottom: 20,
|
||
},
|
||
selectedDateTitle: {
|
||
fontSize: 22,
|
||
fontWeight: '800',
|
||
color: '#192126',
|
||
},
|
||
addMoodButton: {
|
||
paddingHorizontal: 20,
|
||
height: 36,
|
||
borderRadius: 18,
|
||
backgroundColor: '#7a5af8',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
shadowColor: '#7a5af8',
|
||
shadowOffset: { width: 0, height: 2 },
|
||
shadowOpacity: 0.2,
|
||
shadowRadius: 4,
|
||
elevation: 3,
|
||
},
|
||
addMoodButtonText: {
|
||
color: '#fff',
|
||
fontSize: 14,
|
||
fontWeight: '700',
|
||
},
|
||
moodRecord: {
|
||
flexDirection: 'row',
|
||
alignItems: 'flex-start',
|
||
paddingVertical: 16,
|
||
backgroundColor: 'rgba(122,90,248,0.05)',
|
||
borderRadius: 16,
|
||
paddingHorizontal: 16,
|
||
},
|
||
recordIcon: {
|
||
width: 52,
|
||
height: 52,
|
||
borderRadius: 26,
|
||
backgroundColor: '#7a5af8',
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
marginRight: 16,
|
||
shadowColor: '#7a5af8',
|
||
shadowOffset: { width: 0, height: 2 },
|
||
shadowOpacity: 0.15,
|
||
shadowRadius: 4,
|
||
elevation: 2,
|
||
},
|
||
recordContent: {
|
||
flex: 1,
|
||
},
|
||
recordMood: {
|
||
fontSize: 18,
|
||
color: '#192126',
|
||
fontWeight: '700',
|
||
marginBottom: 4,
|
||
},
|
||
recordIntensity: {
|
||
fontSize: 14,
|
||
color: '#5d6676',
|
||
marginTop: 2,
|
||
fontWeight: '500',
|
||
},
|
||
recordDescription: {
|
||
fontSize: 14,
|
||
color: '#5d6676',
|
||
marginTop: 6,
|
||
fontStyle: 'italic',
|
||
lineHeight: 20,
|
||
},
|
||
spacer: {
|
||
flex: 1,
|
||
},
|
||
recordTime: {
|
||
fontSize: 14,
|
||
color: '#777f8c',
|
||
fontWeight: '500',
|
||
},
|
||
emptyRecord: {
|
||
alignItems: 'center',
|
||
paddingVertical: 32,
|
||
},
|
||
emptyRecordText: {
|
||
fontSize: 16,
|
||
color: '#5d6676',
|
||
marginBottom: 8,
|
||
fontWeight: '600',
|
||
},
|
||
emptyRecordSubtext: {
|
||
fontSize: 13,
|
||
color: '#777f8c',
|
||
textAlign: 'center',
|
||
lineHeight: 18,
|
||
},
|
||
|
||
});
|