From 50525f82a1d4f97b795f509529ebcd9f157e89a0 Mon Sep 17 00:00:00 2001 From: richarjiang Date: Tue, 11 Nov 2025 10:02:37 +0800 Subject: [PATCH] =?UTF-8?q?feat(medications):=20=E4=BC=98=E5=8C=96?= =?UTF-8?q?=E8=8D=AF=E5=93=81=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=E5=92=8C?= =?UTF-8?q?=E7=99=BB=E5=BD=95=E6=B5=81=E7=A8=8B?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 更新默认药品图片为专用图标 - 移除未使用的 loading 状态选择器 - 优化 Apple 登录按钮样式,支持毛玻璃效果和加载状态 - 添加登录成功后返回功能(shouldBack 参数) - 药品详情页添加信息卡片点击交互 - 添加药品添加页面的登录状态检查 - 增强时间选择器错误处理和数据验证 - 修复药品图片显示逻辑,支持网络图片 - 优化药品卡片样式和布局 - 添加图片加载错误处理 --- app/(tabs)/medications.tsx | 7 +- app/auth/login.tsx | 89 +++++++++++---- app/medications/[medicationId].tsx | 66 +++++++++++- app/medications/add-medication.tsx | 125 +++++++++++++++++----- assets/images/medicine/image-medicine.png | Bin 0 -> 39963 bytes components/medication/MedicationCard.tsx | 40 ++++--- hooks/useAuthGuard.ts | 3 + store/medicationsSlice.ts | 3 +- 8 files changed, 263 insertions(+), 70 deletions(-) create mode 100644 assets/images/medicine/image-medicine.png diff --git a/app/(tabs)/medications.tsx b/app/(tabs)/medications.tsx index 14a5d94..98c1cb2 100644 --- a/app/(tabs)/medications.tsx +++ b/app/(tabs)/medications.tsx @@ -5,7 +5,7 @@ import { IconSymbol } from '@/components/ui/IconSymbol'; import { Colors } from '@/constants/Colors'; import { useAppDispatch, useAppSelector } from '@/hooks/redux'; import { useColorScheme } from '@/hooks/useColorScheme'; -import { fetchMedicationRecords, fetchMedications, selectMedicationDisplayItemsByDate, selectMedicationsLoading } from '@/store/medicationsSlice'; +import { fetchMedicationRecords, fetchMedications, selectMedicationDisplayItemsByDate } from '@/store/medicationsSlice'; import { DEFAULT_MEMBER_NAME } from '@/store/userSlice'; import { useFocusEffect } from '@react-navigation/native'; import dayjs, { Dayjs } from 'dayjs'; @@ -42,7 +42,6 @@ export default function MedicationsScreen() { // 从 Redux 获取数据 const selectedKey = selectedDate.format('YYYY-MM-DD'); const medicationsForDay = useAppSelector((state) => selectMedicationDisplayItemsByDate(selectedKey)(state)); - const loading = useAppSelector(selectMedicationsLoading); const handleOpenAddMedication = useCallback(() => { router.push('/medications/add-medication'); @@ -79,9 +78,11 @@ export default function MedicationsScreen() { // 为每个药物添加默认图片(如果没有图片) const medicationsWithImages = useMemo(() => { + console.log('medicationsForDay', medicationsForDay); + return medicationsForDay.map((med: any) => ({ ...med, - image: med.image || require('@/assets/images/icons/icon-healthy-diet.png'), // 默认使用瓶子图标 + image: med.image || require('@/assets/images/medicine/image-medicine.png'), // 默认使用瓶子图标 })); }, [medicationsForDay]); diff --git a/app/auth/login.tsx b/app/auth/login.tsx index baa655f..5ca84f6 100644 --- a/app/auth/login.tsx +++ b/app/auth/login.tsx @@ -4,7 +4,7 @@ import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; import { LinearGradient } from 'expo-linear-gradient'; import { useLocalSearchParams, useRouter } from 'expo-router'; import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import { Alert, Animated, Linking, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; +import { ActivityIndicator, Alert, Animated, Linking, Pressable, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; import { SafeAreaView } from 'react-native-safe-area-context'; import { ThemedText } from '@/components/ThemedText'; @@ -18,7 +18,7 @@ import Toast from 'react-native-toast-message'; export default function LoginScreen() { const router = useRouter(); - const searchParams = useLocalSearchParams<{ redirectTo?: string; redirectParams?: string }>(); + const searchParams = useLocalSearchParams<{ redirectTo?: string; redirectParams?: string; shouldBack?: string }>(); const scheme = (useColorScheme() ?? 'light') as 'light' | 'dark'; const color = Colors[scheme]; const pageBackground = scheme === 'light' ? color.pageBackgroundEmphasis : color.background; @@ -122,16 +122,24 @@ export default function LoginScreen() { type: 'success', }); // 登录成功后处理重定向 - const to = searchParams?.redirectTo as string | undefined; - const paramsJson = searchParams?.redirectParams as string | undefined; - let parsedParams: Record | undefined; - if (paramsJson) { - try { parsedParams = JSON.parse(paramsJson); } catch { } - } - if (to) { - router.replace({ pathname: to, params: parsedParams } as any); - } else { + const shouldBack = searchParams?.shouldBack === 'true'; + + if (shouldBack) { + // 如果设置了 shouldBack,直接返回上一页 router.back(); + } else { + // 否则按照原有逻辑进行重定向 + const to = searchParams?.redirectTo as string | undefined; + const paramsJson = searchParams?.redirectParams as string | undefined; + let parsedParams: Record | undefined; + if (paramsJson) { + try { parsedParams = JSON.parse(paramsJson); } catch { } + } + if (to) { + router.replace({ pathname: to, params: parsedParams } as any); + } else { + router.back(); + } } } catch (err: any) { console.log('err.code', err.code); @@ -248,20 +256,55 @@ export default function LoginScreen() { {/* Apple 登录 */} {appleAvailable && ( - guardAgreement(onAppleLogin)} disabled={loading} - style={({ pressed }) => [ - styles.appleButton, - { backgroundColor: '#000000' }, - loading && { opacity: 0.7 }, - pressed && { transform: [{ scale: 0.98 }] }, - ]} + activeOpacity={0.7} > - - 使用 Apple 登录 - + {isLiquidGlassAvailable() ? ( + + {loading ? ( + <> + + 登录中... + + ) : ( + <> + + 使用 Apple 登录 + + )} + + ) : ( + + {loading ? ( + <> + + 登录中... + + ) : ( + <> + + 使用 Apple 登录 + + )} + + )} + )} {/* 协议勾选 */} @@ -343,12 +386,16 @@ const styles = StyleSheet.create({ justifyContent: 'center', flexDirection: 'row', marginBottom: 16, + overflow: 'hidden', shadowColor: '#000', shadowOffset: { width: 0, height: 8 }, shadowOpacity: 0.15, shadowRadius: 12, elevation: 2, }, + appleButtonFallback: { + backgroundColor: '#000000', + }, appleText: { fontSize: 16, color: '#FFFFFF', diff --git a/app/medications/[medicationId].tsx b/app/medications/[medicationId].tsx index 78be332..e504210 100644 --- a/app/medications/[medicationId].tsx +++ b/app/medications/[medicationId].tsx @@ -44,7 +44,7 @@ const FORM_LABELS: Record = { other: '其他', }; -const DEFAULT_IMAGE = require('@/assets/images/icons/icon-healthy-diet.png'); +const DEFAULT_IMAGE = require('@/assets/images/medicine/image-medicine.png'); type RecordsSummary = { takenCount: number; @@ -413,6 +413,22 @@ export default function MedicationDetailScreen() { } }, [deleteLoading, dispatch, medication, router]); + const handleStartDatePress = useCallback(() => { + Alert.alert('开始日期', `开始服药日期:${startDateLabel}`); + }, [startDateLabel]); + + const handleTimePress = useCallback(() => { + Alert.alert('服药时间', `设置的时间:${reminderTimes}`); + }, [reminderTimes]); + + const handleDosagePress = useCallback(() => { + Alert.alert('每次剂量', `单次服用剂量:${dosageLabel}`); + }, [dosageLabel]); + + const handleFormPress = useCallback(() => { + Alert.alert('剂型', `药品剂型:${formLabel}`); + }, [formLabel]); + if (!medicationId) { return ( @@ -489,12 +505,16 @@ export default function MedicationDetailScreen() { value={startDateLabel} icon="calendar-outline" colors={colors} + clickable={true} + onPress={handleStartDatePress} /> @@ -511,8 +531,22 @@ export default function MedicationDetailScreen() {
- - + +
@@ -712,20 +746,35 @@ const InfoCard = ({ value, icon, colors, + onPress, + clickable = false, }: { label: string; value: string; icon: keyof typeof Ionicons.glyphMap; colors: (typeof Colors)[keyof typeof Colors]; + onPress?: () => void; + clickable?: boolean; }) => { + const CardWrapper = clickable ? TouchableOpacity : View; + return ( - + + {clickable && ( + + + + )} {label} {value} - + ); }; @@ -823,6 +872,13 @@ const styles = StyleSheet.create({ shadowRadius: 8, shadowOffset: { width: 0, height: 4 }, elevation: 2, + position: 'relative', + }, + infoCardArrow: { + position: 'absolute', + top: 12, + right: 12, + zIndex: 1, }, infoCardIcon: { width: 28, diff --git a/app/medications/add-medication.tsx b/app/medications/add-medication.tsx index 4207cb9..146212e 100644 --- a/app/medications/add-medication.tsx +++ b/app/medications/add-medication.tsx @@ -3,6 +3,7 @@ import { HeaderBar } from '@/components/ui/HeaderBar'; import { IconSymbol } from '@/components/ui/IconSymbol'; import { Colors } from '@/constants/Colors'; import { useAppDispatch } from '@/hooks/redux'; +import { useAuthGuard } from '@/hooks/useAuthGuard'; import { useColorScheme } from '@/hooks/useColorScheme'; import { useCosUpload } from '@/hooks/useCosUpload'; import { createMedicationAction, fetchMedicationRecords, fetchMedications } from '@/store/medicationsSlice'; @@ -86,10 +87,40 @@ const DEFAULT_TIME_PRESETS = ['08:00', '12:00', '18:00', '22:00']; const formatTime = (date: Date) => dayjs(date).format('HH:mm'); const getDefaultTimeByIndex = (index: number) => DEFAULT_TIME_PRESETS[index % DEFAULT_TIME_PRESETS.length]; const createDateFromTime = (time: string) => { - const [hour, minute] = time.split(':').map((val) => parseInt(val, 10)); - const next = new Date(); - next.setHours(hour || 0, minute || 0, 0, 0); - return next; + try { + if (!time || typeof time !== 'string') { + console.warn('[MEDICATION] Invalid time string provided:', time); + return new Date(); + } + + const parts = time.split(':'); + if (parts.length !== 2) { + console.warn('[MEDICATION] Invalid time format:', time); + return new Date(); + } + + const hour = parseInt(parts[0], 10); + const minute = parseInt(parts[1], 10); + + if (isNaN(hour) || isNaN(minute) || hour < 0 || hour > 23 || minute < 0 || minute > 59) { + console.warn('[MEDICATION] Invalid time values:', { hour, minute }); + return new Date(); + } + + const next = new Date(); + next.setHours(hour, minute, 0, 0); + + // 验证日期是否有效 + if (isNaN(next.getTime())) { + console.error('[MEDICATION] Failed to create valid date'); + return new Date(); + } + + return next; + } catch (error) { + console.error('[MEDICATION] Error in createDateFromTime:', error); + return new Date(); + } }; export default function AddMedicationScreen() { @@ -103,6 +134,8 @@ export default function AddMedicationScreen() { const [medicationName, setMedicationName] = useState(''); const [isSubmitting, setIsSubmitting] = useState(false); const { upload, uploading } = useCosUpload({ prefix: 'images/medications' }); + // 获取登录验证相关的功能 + const { ensureLoggedIn } = useAuthGuard(); const softBorderColor = useMemo(() => withAlpha(colors.border, 0.45), [colors.border]); const fadedBorderFill = useMemo(() => withAlpha(colors.border, 0.2), [colors.border]); const glassPrimaryTint = useMemo(() => withAlpha(colors.primary, theme === 'dark' ? 0.55 : 0.45), [colors.primary, theme]); @@ -231,6 +264,16 @@ export default function AddMedicationScreen() { setIsSubmitting(true); try { + // 先检查用户是否已登录,如果未登录则跳转到登录页面 + const isLoggedIn = await ensureLoggedIn({ + shouldBack: true + }); + if (!isLoggedIn) { + // 未登录,ensureLoggedIn 已处理跳转,直接返回 + setIsSubmitting(false); + return; + } + // 构建药物数据,符合 CreateMedicationDto 接口 const medicationData = { name: medicationName.trim(), @@ -289,6 +332,7 @@ export default function AddMedicationScreen() { startDate, note, dispatch, + ensureLoggedIn, ]); const handlePrev = useCallback(() => { @@ -378,29 +422,51 @@ export default function AddMedicationScreen() { const openTimePicker = useCallback( (index?: number) => { - if (typeof index === 'number') { - setEditingTimeIndex(index); - setTimePickerDate(createDateFromTime(medicationTimes[index])); - } else { - setEditingTimeIndex(null); - setTimePickerDate(createDateFromTime(getDefaultTimeByIndex(medicationTimes.length))); + try { + if (typeof index === 'number') { + if (index >= 0 && index < medicationTimes.length) { + setEditingTimeIndex(index); + setTimePickerDate(createDateFromTime(medicationTimes[index])); + } else { + console.error('[MEDICATION] Invalid time index:', index); + return; + } + } else { + setEditingTimeIndex(null); + setTimePickerDate(createDateFromTime(getDefaultTimeByIndex(medicationTimes.length))); + } + setTimePickerVisible(true); + } catch (error) { + console.error('[MEDICATION] Error in openTimePicker:', error); } - setTimePickerVisible(true); }, [medicationTimes] ); const confirmTime = useCallback( (date: Date) => { - const nextValue = formatTime(date); - setMedicationTimes((prev) => { - if (editingTimeIndex == null) { - return [...prev, nextValue]; + try { + if (!date || isNaN(date.getTime())) { + console.error('[MEDICATION] Invalid date provided to confirmTime'); + setTimePickerVisible(false); + setEditingTimeIndex(null); + return; } - return prev.map((time, idx) => (idx === editingTimeIndex ? nextValue : time)); - }); - setTimePickerVisible(false); - setEditingTimeIndex(null); + + const nextValue = formatTime(date); + setMedicationTimes((prev) => { + if (editingTimeIndex == null) { + return [...prev, nextValue]; + } + return prev.map((time, idx) => (idx === editingTimeIndex ? nextValue : time)); + }); + setTimePickerVisible(false); + setEditingTimeIndex(null); + } catch (error) { + console.error('[MEDICATION] Error in confirmTime:', error); + setTimePickerVisible(false); + setEditingTimeIndex(null); + } }, [editingTimeIndex] ); @@ -915,15 +981,26 @@ export default function AddMedicationScreen() { visible={timePickerVisible} transparent animationType="fade" - onRequestClose={() => setTimePickerVisible(false)} + onRequestClose={() => { + setTimePickerVisible(false); + setEditingTimeIndex(null); + }} > - setTimePickerVisible(false)} /> - + { + setTimePickerVisible(false); + setEditingTimeIndex(null); + }} + /> + + + {editingTimeIndex !== null ? '修改提醒时间' : '添加提醒时间'} + { if (Platform.OS === 'ios') { if (date) setTimePickerDate(date); diff --git a/assets/images/medicine/image-medicine.png b/assets/images/medicine/image-medicine.png new file mode 100644 index 0000000000000000000000000000000000000000..9d665b5ad2c010f73163533e8c12165ef1b6eb97 GIT binary patch literal 39963 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 bJcK0z$+-QoZ`8v+z>Sugo@%|aP2~Ro-fh9g literal 0 HcmV?d00001 diff --git a/components/medication/MedicationCard.tsx b/components/medication/MedicationCard.tsx index f1d507a..add0be5 100644 --- a/components/medication/MedicationCard.tsx +++ b/components/medication/MedicationCard.tsx @@ -6,7 +6,7 @@ import { Ionicons } from '@expo/vector-icons'; import dayjs, { Dayjs } from 'dayjs'; import { GlassView, isLiquidGlassAvailable } from 'expo-glass-effect'; import { Image } from 'expo-image'; -import React, { useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { Alert, StyleSheet, TouchableOpacity, View } from 'react-native'; export type MedicationCardProps = { @@ -19,10 +19,16 @@ export type MedicationCardProps = { export function MedicationCard({ medication, colors, selectedDate, onOpenDetails }: MedicationCardProps) { const dispatch = useAppDispatch(); const [isSubmitting, setIsSubmitting] = useState(false); + const [imageError, setImageError] = useState(false); const scheduledDate = dayjs(`${selectedDate.format('YYYY-MM-DD')} ${medication.scheduledTime}`); const timeDiffMinutes = scheduledDate.diff(dayjs(), 'minute'); + // 当药品变化时重置图片错误状态 + useEffect(() => { + setImageError(false); + }, [medication.id]); + /** * 处理服药操作 */ @@ -166,6 +172,7 @@ export function MedicationCard({ medication, colors, selectedDate, onOpenDetails const statusChip = renderStatusBadge(); + return ( - + setImageError(true)} + key={medication.id} // 重新渲染时重置状态 + /> @@ -211,21 +223,17 @@ export function MedicationCard({ medication, colors, selectedDate, onOpenDetails const styles = StyleSheet.create({ card: { - borderRadius: 26, - shadowOpacity: 0.08, - shadowOffset: { width: 0, height: 12 }, - shadowRadius: 24, - elevation: 2, + borderRadius: 18, position: 'relative', }, cardSurface: { - borderRadius: 26, + borderRadius: 18, overflow: 'hidden', }, cardBody: { - paddingHorizontal: 20, - paddingBottom: 20, - paddingTop: 28, + paddingHorizontal: 10, + paddingBottom: 10, + paddingTop: 10, }, cardContent: { flexDirection: 'row', @@ -233,20 +241,20 @@ const styles = StyleSheet.create({ gap: 20, }, thumbnailWrapper: { - width: 126, + width: 148, height: 110, }, thumbnailSurface: { flex: 1, - borderRadius: 22, backgroundColor: '#F1F4FF', alignItems: 'center', justifyContent: 'center', overflow: 'hidden', + borderRadius: 18, }, thumbnailImage: { - width: '80%', - height: '80%', + width: '70%', + height: '70%', resizeMode: 'contain', }, infoSection: { @@ -281,7 +289,7 @@ const styles = StyleSheet.create({ gap: 6, justifyContent: 'center', height: 38, - borderRadius: 24, + borderRadius: 10, overflow: 'hidden', }, actionButtonUpcoming: { diff --git a/hooks/useAuthGuard.ts b/hooks/useAuthGuard.ts index bbff00c..620e9b3 100644 --- a/hooks/useAuthGuard.ts +++ b/hooks/useAuthGuard.ts @@ -13,6 +13,7 @@ type RedirectParams = Record; type EnsureOptions = { redirectTo?: string; redirectParams?: RedirectParams; + shouldBack?: boolean; // 登录成功后是否返回上一页而不是重定向 }; export function useAuthGuard() { @@ -28,12 +29,14 @@ export function useAuthGuard() { const redirectTo = options?.redirectTo ?? currentPath ?? ROUTES.TAB_STATISTICS; const paramsJson = options?.redirectParams ? JSON.stringify(options.redirectParams) : undefined; + const shouldBack = options?.shouldBack; router.push({ pathname: '/auth/login', params: { redirectTo, ...(paramsJson ? { redirectParams: paramsJson } : {}), + ...(shouldBack ? { shouldBack: 'true' } : {}), }, } as any); return false; diff --git a/store/medicationsSlice.ts b/store/medicationsSlice.ts index af9a326..8340797 100644 --- a/store/medicationsSlice.ts +++ b/store/medicationsSlice.ts @@ -698,7 +698,7 @@ export const selectMedicationDisplayItemsByDate = (date: string) => (state: Root // 转换为展示项 return records .map((record) => { - const medication = record.medication || medicationMap.get(record.medicationId); + const medication = medicationMap.get(record.medicationId); if (!medication) return null; // 格式化剂量 @@ -721,6 +721,7 @@ export const selectMedicationDisplayItemsByDate = (date: string) => (state: Root status: record.status, recordId: record.id, medicationId: medication.id, + image: medication.photoUrl ? { uri: medication.photoUrl } : undefined } as import('@/types/medication').MedicationDisplayItem; }) .filter((item): item is import('@/types/medication').MedicationDisplayItem => item !== null);