- Introduced new translation files for medication, personal, and weight management in Chinese. - Updated the main index file to include the new translation modules. - Enhanced the medication type definitions to include 'ointment'. - Refactored workout type labels to utilize i18n for better localization support. - Improved sleep quality descriptions and recommendations with i18n integration.
623 lines
19 KiB
TypeScript
623 lines
19 KiB
TypeScript
import { CalorieRingChart } from '@/components/CalorieRingChart';
|
||
import { DateSelector } from '@/components/DateSelector';
|
||
import { FloatingFoodOverlay } from '@/components/FloatingFoodOverlay';
|
||
import { NutritionRecordCard } from '@/components/NutritionRecordCard';
|
||
import { HeaderBar } from '@/components/ui/HeaderBar';
|
||
import { Colors } from '@/constants/Colors';
|
||
import { useAppDispatch, useAppSelector } from '@/hooks/redux';
|
||
import { useAuthGuard } from '@/hooks/useAuthGuard';
|
||
import { useColorScheme } from '@/hooks/useColorScheme';
|
||
import { useI18n } from '@/hooks/useI18n';
|
||
import { useSafeAreaTop } from '@/hooks/useSafeAreaWithPadding';
|
||
import { DietRecord } from '@/services/dietRecords';
|
||
import { type FoodRecognitionResponse } from '@/services/foodRecognition';
|
||
import { saveRecognitionResult } from '@/store/foodRecognitionSlice';
|
||
import { selectHealthDataByDate } from '@/store/healthSlice';
|
||
import {
|
||
deleteNutritionRecord,
|
||
fetchDailyNutritionData,
|
||
fetchNutritionRecords,
|
||
selectNutritionLoading,
|
||
selectNutritionRecordsByDate,
|
||
selectNutritionSummaryByDate
|
||
} from '@/store/nutritionSlice';
|
||
import { getTodayIndexInMonth } from '@/utils/date';
|
||
import { fetchBasalEnergyBurned } from '@/utils/health';
|
||
import { Ionicons } from '@expo/vector-icons';
|
||
import { useFocusEffect } from '@react-navigation/native';
|
||
import dayjs from 'dayjs';
|
||
import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect';
|
||
import { Image } from 'expo-image';
|
||
import { LinearGradient } from 'expo-linear-gradient';
|
||
import { router } from 'expo-router';
|
||
import React, { useCallback, useEffect, useState } from 'react';
|
||
import {
|
||
FlatList,
|
||
RefreshControl,
|
||
StatusBar,
|
||
StyleSheet,
|
||
Text,
|
||
TouchableOpacity,
|
||
View
|
||
} from 'react-native';
|
||
|
||
type ViewMode = 'daily' | 'all';
|
||
|
||
export default function NutritionRecordsScreen() {
|
||
const { t } = useI18n();
|
||
const safeAreaTop = useSafeAreaTop();
|
||
const theme = (useColorScheme() ?? 'light') as 'light' | 'dark';
|
||
const colorTokens = Colors[theme];
|
||
const dispatch = useAppDispatch();
|
||
const isGlassAvailable = isLiquidGlassAvailable();
|
||
|
||
const { isLoggedIn } = useAuthGuard();
|
||
|
||
// 日期相关状态
|
||
const [selectedIndex, setSelectedIndex] = useState(getTodayIndexInMonth());
|
||
// 直接使用 state 管理当前选中日期,而不是从 days 数组派生,以支持 DateSelector 内部月份切换
|
||
const [currentSelectedDate, setCurrentSelectedDate] = useState<Date>(new Date());
|
||
|
||
const currentSelectedDateString = dayjs(currentSelectedDate).format('YYYY-MM-DD');
|
||
|
||
// 从 Redux 获取数据
|
||
const healthData = useAppSelector(selectHealthDataByDate(currentSelectedDateString));
|
||
const nutritionSummary = useAppSelector(selectNutritionSummaryByDate(currentSelectedDateString));
|
||
const userProfile = useAppSelector((state) => state.user.profile);
|
||
|
||
// 从 Redux 获取营养记录数据
|
||
const nutritionRecords = useAppSelector(selectNutritionRecordsByDate(currentSelectedDateString));
|
||
const nutritionLoading = useAppSelector(selectNutritionLoading);
|
||
|
||
// 视图模式:按天查看 vs 全部查看
|
||
const [viewMode, setViewMode] = useState<ViewMode>('daily');
|
||
|
||
// 全部记录模式的本地状态
|
||
const [allRecords, setAllRecords] = useState<DietRecord[]>([]);
|
||
const [allRecordsLoading, setAllRecordsLoading] = useState(false);
|
||
const [refreshing, setRefreshing] = useState(false);
|
||
const [hasMoreData, setHasMoreData] = useState(true);
|
||
const [page, setPage] = useState(1);
|
||
|
||
// 基础代谢数据状态
|
||
const [basalMetabolism, setBasalMetabolism] = useState<number>(1482);
|
||
|
||
// 食物添加弹窗状态
|
||
const [showFoodOverlay, setShowFoodOverlay] = useState(false);
|
||
|
||
// 根据视图模式选择使用的数据
|
||
const displayRecords = viewMode === 'daily' ? nutritionRecords : allRecords;
|
||
const loading = viewMode === 'daily' ? nutritionLoading.records : allRecordsLoading;
|
||
|
||
// 页面聚焦时自动刷新数据
|
||
useFocusEffect(
|
||
useCallback(() => {
|
||
if (!isLoggedIn) return;
|
||
|
||
if (viewMode === 'daily') {
|
||
dispatch(fetchDailyNutritionData(currentSelectedDate));
|
||
} else {
|
||
// 全部记录模式:重新加载数据
|
||
const loadAllRecords = async () => {
|
||
try {
|
||
setAllRecordsLoading(true);
|
||
const response = await dispatch(fetchNutritionRecords({
|
||
page: 1,
|
||
limit: 10,
|
||
append: false,
|
||
}));
|
||
|
||
if (fetchNutritionRecords.fulfilled.match(response)) {
|
||
const { records } = response.payload;
|
||
setAllRecords(records);
|
||
setHasMoreData(records.length === 10);
|
||
setPage(1);
|
||
}
|
||
setAllRecordsLoading(false);
|
||
} catch (error) {
|
||
console.error('加载全部记录失败:', error);
|
||
setAllRecordsLoading(false);
|
||
}
|
||
};
|
||
|
||
loadAllRecords();
|
||
}
|
||
}, [viewMode, currentSelectedDateString, dispatch, isLoggedIn])
|
||
);
|
||
|
||
// 当选中日期或视图模式变化时重新加载数据
|
||
useEffect(() => {
|
||
fetchBasalMetabolismData();
|
||
if (viewMode === 'daily') {
|
||
dispatch(fetchDailyNutritionData(currentSelectedDate));
|
||
} else {
|
||
setPage(1); // 重置分页
|
||
setAllRecords([]); // 清空记录
|
||
|
||
// 全部记录模式:加载数据
|
||
const loadAllRecords = async () => {
|
||
try {
|
||
setAllRecordsLoading(true);
|
||
const response = await dispatch(fetchNutritionRecords({
|
||
page: 1,
|
||
limit: 10,
|
||
append: false,
|
||
}));
|
||
|
||
if (fetchNutritionRecords.fulfilled.match(response)) {
|
||
const { records } = response.payload;
|
||
setAllRecords(records);
|
||
setHasMoreData(records.length === 10);
|
||
}
|
||
setAllRecordsLoading(false);
|
||
} catch (error) {
|
||
console.error('加载全部记录失败:', error);
|
||
setAllRecordsLoading(false);
|
||
}
|
||
};
|
||
|
||
loadAllRecords();
|
||
}
|
||
}, [viewMode, currentSelectedDateString, dispatch]);
|
||
|
||
// 获取基础代谢数据
|
||
const fetchBasalMetabolismData = useCallback(async () => {
|
||
try {
|
||
const options = {
|
||
startDate: dayjs(currentSelectedDate).startOf('day').toDate().toISOString(),
|
||
endDate: dayjs(currentSelectedDate).endOf('day').toDate().toISOString()
|
||
};
|
||
|
||
const basalEnergy = await fetchBasalEnergyBurned(options);
|
||
setBasalMetabolism(basalEnergy || 1482);
|
||
} catch (error) {
|
||
console.error('获取基础代谢数据失败:', error);
|
||
setBasalMetabolism(1482); // 失败时使用默认值
|
||
}
|
||
}, [currentSelectedDate]);
|
||
|
||
const onRefresh = useCallback(async () => {
|
||
try {
|
||
setRefreshing(true);
|
||
|
||
if (viewMode === 'daily') {
|
||
await dispatch(fetchDailyNutritionData(currentSelectedDate));
|
||
} else {
|
||
// 全部记录模式:刷新数据
|
||
setPage(1);
|
||
const response = await dispatch(fetchNutritionRecords({
|
||
page: 1,
|
||
limit: 10,
|
||
append: false,
|
||
}));
|
||
|
||
if (fetchNutritionRecords.fulfilled.match(response)) {
|
||
const { records } = response.payload;
|
||
setAllRecords(records);
|
||
setHasMoreData(records.length === 10);
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('刷新数据失败:', error);
|
||
} finally {
|
||
setRefreshing(false);
|
||
}
|
||
}, [viewMode, currentSelectedDateString, dispatch]);
|
||
|
||
// 计算营养目标
|
||
const calculateNutritionGoals = () => {
|
||
const weight = parseFloat(userProfile?.weight || '70'); // 默认70kg
|
||
const height = parseFloat(userProfile?.height || '170'); // 默认170cm
|
||
const age = userProfile?.birthDate ?
|
||
dayjs().diff(dayjs(userProfile.birthDate), 'year') : 25; // 默认25岁
|
||
const isWoman = userProfile?.gender === 'female';
|
||
|
||
// 基础代谢率计算(Mifflin-St Jeor Equation)
|
||
let bmr;
|
||
if (isWoman) {
|
||
bmr = 10 * weight + 6.25 * height - 5 * age - 161;
|
||
} else {
|
||
bmr = 10 * weight + 6.25 * height - 5 * age + 5;
|
||
}
|
||
|
||
// 总热量需求(假设轻度活动)
|
||
const totalCalories = bmr * 1.375;
|
||
|
||
// 计算营养素目标
|
||
const proteinGoal = weight * 1.6; // 1.6g/kg
|
||
const fatGoal = totalCalories * 0.25 / 9; // 25%来自脂肪,9卡/克
|
||
const carbsGoal = (totalCalories - proteinGoal * 4 - fatGoal * 9) / 4; // 剩余来自碳水
|
||
|
||
return {
|
||
proteinGoal: Math.round(proteinGoal * 10) / 10,
|
||
fatGoal: Math.round(fatGoal * 10) / 10,
|
||
carbsGoal: Math.round(carbsGoal * 10) / 10,
|
||
};
|
||
};
|
||
|
||
const nutritionGoals = calculateNutritionGoals();
|
||
|
||
const loadMoreRecords = useCallback(async () => {
|
||
if (hasMoreData && !loading && !refreshing && viewMode === 'all') {
|
||
try {
|
||
const nextPage = page + 1;
|
||
const response = await dispatch(fetchNutritionRecords({
|
||
page: nextPage,
|
||
limit: 10,
|
||
append: true,
|
||
}));
|
||
|
||
if (fetchNutritionRecords.fulfilled.match(response)) {
|
||
const { records } = response.payload;
|
||
setAllRecords(prev => [...prev, ...records]);
|
||
setHasMoreData(records.length === 10);
|
||
setPage(nextPage);
|
||
}
|
||
} catch (error) {
|
||
console.error('加载更多记录失败:', error);
|
||
}
|
||
}
|
||
}, [hasMoreData, loading, refreshing, viewMode, page, dispatch]);
|
||
|
||
// 删除记录
|
||
const handleDeleteRecord = async (recordId: number) => {
|
||
try {
|
||
if (viewMode === 'daily') {
|
||
// 按天查看模式,使用 Redux 删除
|
||
await dispatch(deleteNutritionRecord({
|
||
recordId,
|
||
dateKey: currentSelectedDateString
|
||
}));
|
||
} else {
|
||
// 全部记录模式,从本地状态中移除
|
||
await dispatch(deleteNutritionRecord({
|
||
recordId,
|
||
dateKey: currentSelectedDateString
|
||
}));
|
||
setAllRecords(prev => prev.filter(record => record.id !== recordId));
|
||
}
|
||
} catch (error) {
|
||
console.error('删除营养记录失败:', error);
|
||
}
|
||
};
|
||
|
||
// 处理营养记录卡片点击
|
||
const handleRecordPress = (record: DietRecord) => {
|
||
// 将 DietRecord 转换为 FoodRecognitionResponse 格式
|
||
const recognitionResult: FoodRecognitionResponse = {
|
||
items: [{
|
||
id: record.id.toString(),
|
||
label: record.foodName,
|
||
foodName: record.foodName,
|
||
portion: record.portionDescription || `${record.estimatedCalories || 0}g`,
|
||
calories: record.estimatedCalories || 0,
|
||
mealType: record.mealType,
|
||
nutritionData: {
|
||
proteinGrams: record.proteinGrams || 0,
|
||
carbohydrateGrams: record.carbohydrateGrams || 0,
|
||
fatGrams: record.fatGrams || 0,
|
||
fiberGrams: 0, // DietRecord 中没有纤维数据,设为0
|
||
}
|
||
}],
|
||
analysisText: record.foodDescription || `${record.foodName} - ${record.portionDescription}`,
|
||
confidence: 95, // 设置一个默认置信度
|
||
isFoodDetected: true,
|
||
nonFoodMessage: undefined
|
||
};
|
||
|
||
// 生成唯一的识别ID
|
||
const recognitionId = `record-${record.id}-${Date.now()}`;
|
||
|
||
// 保存到 Redux
|
||
dispatch(saveRecognitionResult({
|
||
id: recognitionId,
|
||
result: recognitionResult
|
||
}));
|
||
|
||
// 跳转到分析结果页面
|
||
router.push({
|
||
pathname: '/food/analysis-result',
|
||
params: {
|
||
imageUri: record.imageUrl || '',
|
||
mealType: record.mealType,
|
||
recognitionId: recognitionId,
|
||
hideRecordBar: 'true'
|
||
}
|
||
});
|
||
};
|
||
|
||
// 根据当前时间智能判断餐次类型
|
||
const getCurrentMealType = (): 'breakfast' | 'lunch' | 'dinner' | 'snack' => {
|
||
const hour = new Date().getHours();
|
||
|
||
if (hour >= 5 && hour < 11) {
|
||
return 'breakfast'; // 5:00-10:59 早餐
|
||
} else if (hour >= 11 && hour < 14) {
|
||
return 'lunch'; // 11:00-13:59 午餐
|
||
} else if (hour >= 17 && hour < 21) {
|
||
return 'dinner'; // 17:00-20:59 晚餐
|
||
} else {
|
||
return 'snack'; // 其他时间默认为零食
|
||
}
|
||
};
|
||
|
||
// 添加食物的处理函数
|
||
const handleAddFood = () => {
|
||
setShowFoodOverlay(true);
|
||
};
|
||
|
||
// 渲染右侧添加按钮
|
||
const renderRightButton = () => (
|
||
<TouchableOpacity
|
||
onPress={handleAddFood}
|
||
activeOpacity={0.7}
|
||
>
|
||
{isGlassAvailable ? (
|
||
<GlassView
|
||
style={styles.glassAddButton}
|
||
glassEffectStyle="regular"
|
||
tintColor="rgba(255, 255, 255, 0.4)"
|
||
isInteractive={true}
|
||
>
|
||
<Ionicons name="add" size={24} color={colorTokens.primary} />
|
||
</GlassView>
|
||
) : (
|
||
<View style={[styles.fallbackAddButton, { backgroundColor: 'rgba(255,255,255,0.8)' }]}>
|
||
<Ionicons name="add" size={24} color={colorTokens.primary} />
|
||
</View>
|
||
)}
|
||
</TouchableOpacity>
|
||
);
|
||
|
||
const renderEmptyState = () => (
|
||
<View style={styles.emptySimpleContainer}>
|
||
<Image
|
||
source={require('@/assets/images/icons/icon-yingyang.png')}
|
||
style={styles.emptySimpleImage}
|
||
contentFit="contain"
|
||
/>
|
||
<Text style={styles.emptySimpleText}>
|
||
{t('nutritionRecords.empty.title')}
|
||
</Text>
|
||
<TouchableOpacity onPress={handleAddFood}>
|
||
<Text style={[styles.emptyActionText, { color: colorTokens.primary }]}>
|
||
{t('nutritionRecords.empty.action')}
|
||
</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
);
|
||
|
||
const renderRecord = ({ item }: { item: DietRecord }) => (
|
||
<NutritionRecordCard
|
||
record={item}
|
||
onPress={() => handleRecordPress(item)}
|
||
onDelete={() => handleDeleteRecord(item.id)}
|
||
/>
|
||
);
|
||
|
||
const renderFooter = () => {
|
||
if (!hasMoreData) {
|
||
if (displayRecords.length === 0) return null;
|
||
return (
|
||
<View style={styles.footerContainer}>
|
||
<Text style={[styles.footerText, { color: colorTokens.textSecondary }]}>
|
||
{t('nutritionRecords.footer.end')}
|
||
</Text>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
if (viewMode === 'all' && displayRecords.length > 0) {
|
||
return (
|
||
<TouchableOpacity style={styles.loadMoreButton} onPress={loadMoreRecords}>
|
||
<Text style={[styles.loadMoreText, { color: colorTokens.primary }]}>
|
||
{t('nutritionRecords.footer.loadMore')}
|
||
</Text>
|
||
</TouchableOpacity>
|
||
);
|
||
}
|
||
|
||
return null;
|
||
};
|
||
|
||
const ListHeader = () => (
|
||
<View>
|
||
<View style={styles.headerContent}>
|
||
{viewMode === 'daily' && (
|
||
<DateSelector
|
||
selectedIndex={selectedIndex}
|
||
onDateSelect={(index, date) => {
|
||
setSelectedIndex(index);
|
||
setCurrentSelectedDate(date);
|
||
}}
|
||
showMonthTitle={true}
|
||
disableFutureDates={true}
|
||
showCalendarIcon={true}
|
||
containerStyle={styles.dateSelectorContainer}
|
||
/>
|
||
)}
|
||
|
||
<View style={styles.chartWrapper}>
|
||
<CalorieRingChart
|
||
metabolism={basalMetabolism}
|
||
exercise={healthData?.activeEnergyBurned || 0}
|
||
consumed={nutritionSummary?.totalCalories || 0}
|
||
protein={nutritionSummary?.totalProtein || 0}
|
||
fat={nutritionSummary?.totalFat || 0}
|
||
carbs={nutritionSummary?.totalCarbohydrate || 0}
|
||
proteinGoal={nutritionGoals.proteinGoal}
|
||
fatGoal={nutritionGoals.fatGoal}
|
||
carbsGoal={nutritionGoals.carbsGoal}
|
||
/>
|
||
</View>
|
||
|
||
<View style={styles.listTitleContainer}>
|
||
<Text style={styles.listTitle}>{t('nutritionRecords.listTitle')}</Text>
|
||
{displayRecords.length > 0 && (
|
||
<Text style={styles.listSubtitle}>{t('nutritionRecords.recordCount', { count: displayRecords.length })}</Text>
|
||
)}
|
||
</View>
|
||
</View>
|
||
</View>
|
||
);
|
||
|
||
return (
|
||
<View style={[styles.container, { backgroundColor: '#f3f4fb' }]}>
|
||
<StatusBar barStyle="dark-content" />
|
||
|
||
{/* 顶部柔和渐变背景 */}
|
||
<LinearGradient
|
||
colors={['rgba(255, 243, 224, 0.8)', 'rgba(243, 244, 251, 0)']}
|
||
style={styles.topGradient}
|
||
start={{ x: 0.5, y: 0 }}
|
||
end={{ x: 0.5, y: 1 }}
|
||
/>
|
||
|
||
<HeaderBar
|
||
title={t('nutritionRecords.title')}
|
||
onBack={() => router.back()}
|
||
right={renderRightButton()}
|
||
transparent={true}
|
||
/>
|
||
|
||
<FlatList
|
||
data={displayRecords}
|
||
renderItem={renderRecord}
|
||
keyExtractor={(item) => item.id.toString()}
|
||
contentContainerStyle={[
|
||
styles.listContainer,
|
||
{ paddingTop: safeAreaTop }
|
||
]}
|
||
showsVerticalScrollIndicator={false}
|
||
refreshControl={
|
||
<RefreshControl
|
||
refreshing={refreshing}
|
||
onRefresh={onRefresh}
|
||
tintColor={colorTokens.primary}
|
||
colors={[colorTokens.primary]}
|
||
/>
|
||
}
|
||
ListHeaderComponent={ListHeader}
|
||
ListEmptyComponent={renderEmptyState}
|
||
ListFooterComponent={renderFooter}
|
||
onEndReached={viewMode === 'all' ? loadMoreRecords : undefined}
|
||
onEndReachedThreshold={0.1}
|
||
/>
|
||
|
||
{/* 食物添加悬浮窗 */}
|
||
<FloatingFoodOverlay
|
||
visible={showFoodOverlay}
|
||
onClose={() => setShowFoodOverlay(false)}
|
||
mealType={getCurrentMealType()}
|
||
/>
|
||
</View>
|
||
);
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
container: {
|
||
flex: 1,
|
||
},
|
||
topGradient: {
|
||
position: 'absolute',
|
||
left: 0,
|
||
right: 0,
|
||
top: 0,
|
||
height: 320,
|
||
},
|
||
listContainer: {
|
||
paddingBottom: 100, // 留出底部空间防止遮挡
|
||
},
|
||
headerContent: {
|
||
marginBottom: 16,
|
||
},
|
||
dateSelectorContainer: {
|
||
paddingHorizontal: 16,
|
||
marginBottom: 16,
|
||
},
|
||
chartWrapper: {
|
||
marginBottom: 24,
|
||
shadowColor: 'rgba(30, 41, 59, 0.05)',
|
||
shadowOffset: { width: 0, height: 8 },
|
||
shadowOpacity: 0.1,
|
||
shadowRadius: 12,
|
||
elevation: 4,
|
||
},
|
||
listTitleContainer: {
|
||
flexDirection: 'row',
|
||
alignItems: 'baseline',
|
||
paddingHorizontal: 24,
|
||
marginBottom: 12,
|
||
gap: 8,
|
||
},
|
||
listTitle: {
|
||
fontSize: 18,
|
||
fontWeight: '700',
|
||
color: '#1c1f3a',
|
||
fontFamily: 'AliBold',
|
||
},
|
||
listSubtitle: {
|
||
fontSize: 13,
|
||
color: '#6f7ba7',
|
||
fontFamily: 'AliRegular',
|
||
},
|
||
glassAddButton: {
|
||
width: 40,
|
||
height: 40,
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
borderRadius: 20,
|
||
overflow: 'hidden',
|
||
},
|
||
fallbackAddButton: {
|
||
width: 40,
|
||
height: 40,
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
borderRadius: 20,
|
||
borderWidth: 1,
|
||
borderColor: 'rgba(0,0,0,0.05)',
|
||
},
|
||
emptySimpleContainer: {
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
paddingVertical: 60,
|
||
},
|
||
emptySimpleImage: {
|
||
width: 48,
|
||
height: 48,
|
||
opacity: 0.4,
|
||
marginBottom: 12,
|
||
},
|
||
emptySimpleText: {
|
||
fontSize: 14,
|
||
color: '#94A3B8',
|
||
fontFamily: 'AliRegular',
|
||
marginBottom: 8,
|
||
},
|
||
emptyActionText: {
|
||
fontSize: 14,
|
||
fontWeight: '600',
|
||
fontFamily: 'AliBold',
|
||
},
|
||
footerContainer: {
|
||
paddingVertical: 24,
|
||
alignItems: 'center',
|
||
},
|
||
footerText: {
|
||
fontSize: 12,
|
||
fontWeight: '500',
|
||
opacity: 0.6,
|
||
fontFamily: 'AliRegular',
|
||
},
|
||
loadMoreButton: {
|
||
paddingVertical: 16,
|
||
alignItems: 'center',
|
||
},
|
||
loadMoreText: {
|
||
fontSize: 14,
|
||
fontWeight: '600',
|
||
fontFamily: 'AliBold',
|
||
},
|
||
});
|