Add Chinese translations for medication management and personal settings
- 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.
This commit is contained in:
@@ -559,19 +559,17 @@ export default function MedicationDetailScreen() {
|
||||
|
||||
const formLabel = medication ? t(`medications.manage.formLabels.${medication.form}`) : '';
|
||||
const dosageLabel = medication ? `${medication.dosageValue} ${medication.dosageUnit}` : '--';
|
||||
const startDateLabel = medication
|
||||
? dayjs(medication.startDate).format('YYYY年M月D日')
|
||||
: '--';
|
||||
|
||||
|
||||
// 计算服药周期显示
|
||||
const medicationPeriodLabel = useMemo(() => {
|
||||
if (!medication) return '--';
|
||||
|
||||
const startDate = dayjs(medication.startDate).format('YYYY年M月D日');
|
||||
const format = t('medications.detail.plan.dateFormat', { defaultValue: 'YYYY年M月D日' });
|
||||
const startDate = dayjs(medication.startDate).format(format);
|
||||
|
||||
if (medication.endDate) {
|
||||
// 有结束日期,显示开始日期到结束日期
|
||||
const endDate = dayjs(medication.endDate).format('YYYY年M月D日');
|
||||
const endDate = dayjs(medication.endDate).format(format);
|
||||
return `${startDate} - ${endDate}`;
|
||||
} else {
|
||||
// 没有结束日期,显示长期
|
||||
@@ -581,22 +579,23 @@ export default function MedicationDetailScreen() {
|
||||
|
||||
// 计算有效期显示
|
||||
const expiryDateLabel = useMemo(() => {
|
||||
if (!medication?.expiryDate) return '未设置';
|
||||
if (!medication?.expiryDate) return t('medications.detail.plan.expiryStatus.notSet');
|
||||
|
||||
const expiry = dayjs(medication.expiryDate);
|
||||
const today = dayjs();
|
||||
const daysUntilExpiry = expiry.diff(today, 'day');
|
||||
const format = t('medications.detail.plan.dateFormat', { defaultValue: 'YYYY年M月D日' });
|
||||
|
||||
if (daysUntilExpiry < 0) {
|
||||
return `${expiry.format('YYYY年M月D日')} (已过期)`;
|
||||
return `${expiry.format(format)} (${t('medications.detail.plan.expiryStatus.expired')})`;
|
||||
} else if (daysUntilExpiry === 0) {
|
||||
return `${expiry.format('YYYY年M月D日')} (今天到期)`;
|
||||
return `${expiry.format(format)} (${t('medications.detail.plan.expiryStatus.expiresToday')})`;
|
||||
} else if (daysUntilExpiry <= 30) {
|
||||
return `${expiry.format('YYYY年M月D日')} (${daysUntilExpiry}天后到期)`;
|
||||
return `${expiry.format(format)} (${t('medications.detail.plan.expiryStatus.expiresInDays', { days: daysUntilExpiry })})`;
|
||||
} else {
|
||||
return expiry.format('YYYY年M月D日');
|
||||
return expiry.format(format);
|
||||
}
|
||||
}, [medication?.expiryDate]);
|
||||
}, [medication?.expiryDate, t]);
|
||||
|
||||
const reminderTimes = medication?.medicationTimes?.length
|
||||
? medication.medicationTimes.join('、')
|
||||
@@ -617,8 +616,8 @@ export default function MedicationDetailScreen() {
|
||||
const aiActionLabel = aiAnalysisLoading
|
||||
? t('medications.detail.aiAnalysis.analyzingButton')
|
||||
: hasAiAnalysis
|
||||
? '重新分析'
|
||||
: '获取 AI 分析';
|
||||
? t('medications.detail.aiAnalysis.reanalyzeButton')
|
||||
: t('medications.detail.aiAnalysis.getAnalysisButton');
|
||||
|
||||
const handleOpenNoteModal = useCallback(() => {
|
||||
setNoteDraft(medication?.note ?? '');
|
||||
@@ -645,15 +644,15 @@ export default function MedicationDetailScreen() {
|
||||
const trimmed = nameDraft.trim();
|
||||
if (!trimmed) {
|
||||
Alert.alert(
|
||||
'提示',
|
||||
'药物名称不能为空'
|
||||
t('common.hint'),
|
||||
t('medications.detail.name.errors.empty')
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (Array.from(trimmed).length > 10) {
|
||||
Alert.alert(
|
||||
'提示',
|
||||
'药物名称不能超过10个字'
|
||||
t('common.hint'),
|
||||
t('medications.detail.name.errors.tooLong')
|
||||
);
|
||||
return;
|
||||
}
|
||||
@@ -675,8 +674,8 @@ export default function MedicationDetailScreen() {
|
||||
} catch (err) {
|
||||
console.error('更新药物名称失败', err);
|
||||
Alert.alert(
|
||||
'提示',
|
||||
'名称更新失败,请稍后再试'
|
||||
t('common.hint'),
|
||||
t('medications.detail.name.errors.updateFailed')
|
||||
);
|
||||
} finally {
|
||||
setNameSaving(false);
|
||||
@@ -908,16 +907,17 @@ export default function MedicationDetailScreen() {
|
||||
const handleStartDatePress = useCallback(() => {
|
||||
if (!medication) return;
|
||||
|
||||
const startDate = dayjs(medication.startDate).format('YYYY年M月D日');
|
||||
const format = t('medications.detail.plan.dateFormat', { defaultValue: 'YYYY年M月D日' });
|
||||
const startDate = dayjs(medication.startDate).format(format);
|
||||
let message;
|
||||
if (medication.endDate) {
|
||||
const endDate = dayjs(medication.endDate).format('YYYY年M月D日');
|
||||
message = `从 ${startDate} 至 ${endDate}`;
|
||||
const endDate = dayjs(medication.endDate).format(format);
|
||||
message = t('medications.detail.plan.periodRange', { startDate, endDate, defaultValue: `从 ${startDate} 至 ${endDate}` });
|
||||
} else {
|
||||
message = `从 ${startDate} 至长期`;
|
||||
message = t('medications.detail.plan.periodLongTerm', { startDate, defaultValue: `从 ${startDate} 至长期` });
|
||||
}
|
||||
|
||||
Alert.alert('服药周期', message);
|
||||
Alert.alert(t('medications.detail.plan.period'), message);
|
||||
}, [medication, t]);
|
||||
|
||||
const handleTimePress = useCallback(() => {
|
||||
@@ -990,7 +990,7 @@ export default function MedicationDetailScreen() {
|
||||
|
||||
} catch (err) {
|
||||
console.error('更新有效期失败', err);
|
||||
Alert.alert('更新失败', '有效期更新失败,请稍后重试');
|
||||
Alert.alert(t('medications.detail.updateErrors.expiryDate'), t('medications.detail.updateErrors.expiryDateMessage'));
|
||||
} finally {
|
||||
setUpdatePending(false);
|
||||
}
|
||||
@@ -1185,7 +1185,7 @@ export default function MedicationDetailScreen() {
|
||||
});
|
||||
} catch (err: any) {
|
||||
console.error('[MEDICATION_DETAIL] AI 草稿保存失败', err);
|
||||
Alert.alert('保存失败', err?.message || '请稍后再试');
|
||||
Alert.alert(t('medications.detail.aiDraft.saveError.title'), err?.message || t('medications.detail.aiDraft.saveError.message'));
|
||||
} finally {
|
||||
setAiDraftSaving(false);
|
||||
}
|
||||
@@ -1297,7 +1297,7 @@ export default function MedicationDetailScreen() {
|
||||
<View style={styles.photoUploadingIndicator}>
|
||||
<ActivityIndicator color={colors.primary} size="small" />
|
||||
<Text style={[styles.uploadingText, { color: '#FFF' }]}>
|
||||
上传中...
|
||||
{t('medications.detail.photo.uploading')}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
@@ -1415,12 +1415,12 @@ export default function MedicationDetailScreen() {
|
||||
</TouchableOpacity>
|
||||
</Section>
|
||||
|
||||
<Section title="AI 分析" color={colors.text}>
|
||||
<Section title={t('medications.detail.sections.aiAnalysis')} color={colors.text}>
|
||||
<View style={[styles.aiCardContainer, { backgroundColor: colors.surface }]}>
|
||||
<View style={styles.aiHeaderRow}>
|
||||
<View style={styles.aiHeaderLeft}>
|
||||
<Ionicons name="sparkles-outline" size={18} color={colors.primary} />
|
||||
<Text style={[styles.aiHeaderTitle, { color: colors.text }]}>分析结果</Text>
|
||||
<Text style={[styles.aiHeaderTitle, { color: colors.text }]}>{t('medications.detail.aiAnalysis.title')}</Text>
|
||||
</View>
|
||||
<View
|
||||
style={[
|
||||
@@ -1439,7 +1439,7 @@ export default function MedicationDetailScreen() {
|
||||
{ color: hasAiAnalysis ? '#16a34a' : aiAnalysisLocked ? '#ef4444' : colors.primary },
|
||||
]}
|
||||
>
|
||||
{hasAiAnalysis ? '已生成' : aiAnalysisLocked ? '会员专享' : '待生成'}
|
||||
{hasAiAnalysis ? t('medications.detail.aiAnalysis.status.generated') : aiAnalysisLocked ? t('medications.detail.aiAnalysis.status.memberExclusive') : t('medications.detail.aiAnalysis.status.pending')}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
@@ -1467,7 +1467,7 @@ export default function MedicationDetailScreen() {
|
||||
style={styles.aiScoreBadge}
|
||||
>
|
||||
<Ionicons name="thumbs-up-outline" size={14} color="#fff" />
|
||||
<Text style={styles.aiScoreBadgeText}>AI 推荐</Text>
|
||||
<Text style={styles.aiScoreBadgeText}>{t('medications.detail.aiAnalysis.recommendation')}</Text>
|
||||
</LinearGradient>
|
||||
)}
|
||||
</View>
|
||||
@@ -1476,7 +1476,7 @@ export default function MedicationDetailScreen() {
|
||||
{medication.name}
|
||||
</Text>
|
||||
<Text style={[styles.aiHeroSubtitle, { color: colors.textSecondary }]} numberOfLines={3}>
|
||||
{aiAnalysisResult?.mainUsage || '获取 AI 分析,快速了解适用人群、成分安全与使用建议。'}
|
||||
{aiAnalysisResult?.mainUsage || t('medications.detail.aiAnalysis.placeholder')}
|
||||
</Text>
|
||||
<View style={styles.aiChipRow}>
|
||||
<View style={styles.aiChip}>
|
||||
@@ -1527,7 +1527,7 @@ export default function MedicationDetailScreen() {
|
||||
<View style={styles.aiColumns}>
|
||||
<View style={[styles.aiBubbleCard, { backgroundColor: '#ECFEFF', borderColor: '#BAF2F4' }]}>
|
||||
<View style={styles.aiBubbleHeader}>
|
||||
<Text style={[styles.aiBubbleTitle, { color: '#0284c7' }]}>适合人群</Text>
|
||||
<Text style={[styles.aiBubbleTitle, { color: '#0284c7' }]}>{t('medications.detail.aiAnalysis.categories.suitableFor')}</Text>
|
||||
<Ionicons name="checkmark-circle" size={16} color="#0ea5e9" />
|
||||
</View>
|
||||
{aiAnalysisResult.suitableFor.map((item, idx) => (
|
||||
@@ -1540,7 +1540,7 @@ export default function MedicationDetailScreen() {
|
||||
|
||||
<View style={[styles.aiBubbleCard, { backgroundColor: '#FEF2F2', borderColor: '#FEE2E2' }]}>
|
||||
<View style={styles.aiBubbleHeader}>
|
||||
<Text style={[styles.aiBubbleTitle, { color: '#ef4444' }]}>不适合人群</Text>
|
||||
<Text style={[styles.aiBubbleTitle, { color: '#ef4444' }]}>{t('medications.detail.aiAnalysis.categories.unsuitableFor')}</Text>
|
||||
<Ionicons name="alert-circle" size={16} color="#ef4444" />
|
||||
</View>
|
||||
{aiAnalysisResult.unsuitableFor.map((item, idx) => (
|
||||
@@ -1552,9 +1552,9 @@ export default function MedicationDetailScreen() {
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{renderAdviceCard('可能的副作用', aiAnalysisResult.sideEffects, 'warning-outline', '#f59e0b', '#FFFBEB')}
|
||||
{renderAdviceCard('储存建议', aiAnalysisResult.storageAdvice, 'cube-outline', '#10b981', '#ECFDF3')}
|
||||
{renderAdviceCard('健康/使用建议', aiAnalysisResult.healthAdvice, 'sparkles-outline', '#6366f1', '#EEF2FF')}
|
||||
{renderAdviceCard(t('medications.detail.aiAnalysis.categories.sideEffects'), aiAnalysisResult.sideEffects, 'warning-outline', '#f59e0b', '#FFFBEB')}
|
||||
{renderAdviceCard(t('medications.detail.aiAnalysis.categories.storageAdvice'), aiAnalysisResult.storageAdvice, 'cube-outline', '#10b981', '#ECFDF3')}
|
||||
{renderAdviceCard(t('medications.detail.aiAnalysis.categories.healthAdvice'), aiAnalysisResult.healthAdvice, 'sparkles-outline', '#6366f1', '#EEF2FF')}
|
||||
</>
|
||||
) : null}
|
||||
|
||||
@@ -1580,8 +1580,8 @@ export default function MedicationDetailScreen() {
|
||||
<View style={styles.aiMembershipLeft}>
|
||||
<Ionicons name="diamond-outline" size={18} color="#f59e0b" />
|
||||
<View>
|
||||
<Text style={styles.aiMembershipTitle}>会员专享 AI 深度解读</Text>
|
||||
<Text style={styles.aiMembershipSub}>解锁完整药品分析与无限次使用</Text>
|
||||
<Text style={styles.aiMembershipTitle}>{t('medications.detail.aiAnalysis.membershipCard.title')}</Text>
|
||||
<Text style={styles.aiMembershipSub}>{t('medications.detail.aiAnalysis.membershipCard.subtitle')}</Text>
|
||||
</View>
|
||||
</View>
|
||||
<Ionicons name="chevron-forward" size={18} color="#f59e0b" />
|
||||
@@ -1622,22 +1622,67 @@ export default function MedicationDetailScreen() {
|
||||
{isAiDraft ? (
|
||||
<View style={styles.footerButtonContainer}>
|
||||
<TouchableOpacity
|
||||
style={styles.secondaryFooterBtn}
|
||||
activeOpacity={0.9}
|
||||
onPress={() => router.replace('/medications/ai-camera')}
|
||||
>
|
||||
<Text style={styles.secondaryFooterText}>重新拍摄</Text>
|
||||
{isLiquidGlassAvailable() ? (
|
||||
<GlassView
|
||||
style={[styles.secondaryFooterBtn, { borderWidth: 0, overflow: 'hidden', backgroundColor: 'transparent' }]}
|
||||
glassEffectStyle="regular"
|
||||
isInteractive={true}
|
||||
>
|
||||
<Text style={styles.secondaryFooterText}>{t('medications.detail.aiDraft.reshoot')}</Text>
|
||||
</GlassView>
|
||||
) : (
|
||||
<View style={styles.secondaryFooterBtn}>
|
||||
<Text style={styles.secondaryFooterText}>{t('medications.detail.aiDraft.reshoot')}</Text>
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
<TouchableOpacity
|
||||
style={[styles.primaryFooterBtn, { backgroundColor: colors.primary }]}
|
||||
style={{ flex: 1 }}
|
||||
activeOpacity={0.9}
|
||||
onPress={handleAiDraftSave}
|
||||
disabled={aiDraftSaving}
|
||||
>
|
||||
{aiDraftSaving ? (
|
||||
<ActivityIndicator color={colors.onPrimary} />
|
||||
{isLiquidGlassAvailable() ? (
|
||||
<GlassView
|
||||
style={[
|
||||
styles.primaryFooterBtn,
|
||||
{
|
||||
width: '100%',
|
||||
shadowOpacity: 0,
|
||||
backgroundColor: 'transparent',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
]}
|
||||
glassEffectStyle="regular"
|
||||
tintColor={colors.primary}
|
||||
isInteractive={true}
|
||||
>
|
||||
{aiDraftSaving ? (
|
||||
<ActivityIndicator color="#fff" />
|
||||
) : (
|
||||
<Text style={[styles.primaryFooterText, { color: '#fff' }]}>
|
||||
{t('medications.detail.aiDraft.saveAndCreate')}
|
||||
</Text>
|
||||
)}
|
||||
</GlassView>
|
||||
) : (
|
||||
<Text style={[styles.primaryFooterText, { color: colors.onPrimary }]}>保存并创建</Text>
|
||||
<View
|
||||
style={[
|
||||
styles.primaryFooterBtn,
|
||||
{ width: '100%', backgroundColor: colors.primary },
|
||||
]}
|
||||
>
|
||||
{aiDraftSaving ? (
|
||||
<ActivityIndicator color={colors.onPrimary} />
|
||||
) : (
|
||||
<Text style={[styles.primaryFooterText, { color: colors.onPrimary }]}>
|
||||
{t('medications.detail.aiDraft.saveAndCreate')}
|
||||
</Text>
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
@@ -1726,7 +1771,7 @@ export default function MedicationDetailScreen() {
|
||||
<View style={styles.modalHandle} />
|
||||
<View style={styles.modalHeader}>
|
||||
<Text style={[styles.modalTitle, { color: colors.text }]}>
|
||||
编辑药物名称
|
||||
{t('medications.detail.name.edit')}
|
||||
</Text>
|
||||
<TouchableOpacity onPress={handleCloseNameModal} hitSlop={12}>
|
||||
<Ionicons name="close" size={20} color={colors.textSecondary} />
|
||||
@@ -1744,7 +1789,7 @@ export default function MedicationDetailScreen() {
|
||||
<TextInput
|
||||
value={nameDraft}
|
||||
onChangeText={handleNameChange}
|
||||
placeholder="请输入药物名称"
|
||||
placeholder={t('medications.detail.name.placeholder')}
|
||||
placeholderTextColor={colors.textMuted}
|
||||
style={[styles.nameInput, { color: colors.text }]}
|
||||
autoFocus
|
||||
@@ -1777,7 +1822,7 @@ export default function MedicationDetailScreen() {
|
||||
<ActivityIndicator color={colors.onPrimary} size="small" />
|
||||
) : (
|
||||
<Text style={[styles.modalActionPrimaryText, { color: colors.onPrimary }]}>
|
||||
保存
|
||||
{t('common.save')}
|
||||
</Text>
|
||||
)}
|
||||
</TouchableOpacity>
|
||||
@@ -2599,7 +2644,7 @@ const styles = StyleSheet.create({
|
||||
elevation: 4,
|
||||
},
|
||||
aiScoreBadgeText: {
|
||||
fontSize: 12,
|
||||
fontSize: 10,
|
||||
fontWeight: '700',
|
||||
color: '#fff',
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { HeaderBar } from '@/components/ui/HeaderBar';
|
||||
import { Colors } from '@/constants/Colors';
|
||||
import { Colors, palette } from '@/constants/Colors';
|
||||
import { useI18n } from '@/hooks/useI18n';
|
||||
import { getMedicationRecognitionStatus } from '@/services/medications';
|
||||
import { MedicationRecognitionTask } from '@/types/medication';
|
||||
import { Ionicons } from '@expo/vector-icons';
|
||||
@@ -13,14 +14,15 @@ import { useSafeAreaInsets } from 'react-native-safe-area-context';
|
||||
|
||||
const { width: SCREEN_WIDTH } = Dimensions.get('window');
|
||||
|
||||
const STATUS_STEPS: { key: MedicationRecognitionTask['status']; label: string }[] = [
|
||||
{ key: 'analyzing_product', label: '正在进行产品分析...' },
|
||||
{ key: 'analyzing_suitability', label: '正在检测适宜人群...' },
|
||||
{ key: 'analyzing_ingredients', label: '正在评估成分信息...' },
|
||||
{ key: 'analyzing_effects', label: '正在生成安全建议...' },
|
||||
const STEP_KEYS: MedicationRecognitionTask['status'][] = [
|
||||
'analyzing_product',
|
||||
'analyzing_suitability',
|
||||
'analyzing_ingredients',
|
||||
'analyzing_effects',
|
||||
];
|
||||
|
||||
export default function MedicationAiProgressScreen() {
|
||||
const { t } = useI18n();
|
||||
const { taskId, cover } = useLocalSearchParams<{ taskId?: string; cover?: string }>();
|
||||
const insets = useSafeAreaInsets();
|
||||
const [task, setTask] = useState<MedicationRecognitionTask | null>(null);
|
||||
@@ -35,11 +37,16 @@ export default function MedicationAiProgressScreen() {
|
||||
const floatAnim = useRef(new Animated.Value(0)).current;
|
||||
const opacityAnim = useRef(new Animated.Value(0.3)).current;
|
||||
|
||||
const steps = useMemo(() => STEP_KEYS.map(key => ({
|
||||
key,
|
||||
label: t(`medications.aiProgress.steps.${key}`)
|
||||
})), [t]);
|
||||
|
||||
const currentStepIndex = useMemo(() => {
|
||||
if (!task) return 0;
|
||||
const idx = STATUS_STEPS.findIndex((step) => step.key === task.status);
|
||||
const idx = STEP_KEYS.indexOf(task.status as any);
|
||||
if (idx >= 0) return idx;
|
||||
if (task.status === 'completed') return STATUS_STEPS.length;
|
||||
if (task.status === 'completed') return STEP_KEYS.length;
|
||||
return 0;
|
||||
}, [task]);
|
||||
|
||||
@@ -77,12 +84,12 @@ export default function MedicationAiProgressScreen() {
|
||||
pollingTimerRef.current = null;
|
||||
}
|
||||
// 显示错误提示弹窗
|
||||
setErrorMessage(data.errorMessage || '识别失败,请重新拍摄');
|
||||
setErrorMessage(data.errorMessage || t('medications.aiProgress.errors.default'));
|
||||
setShowErrorModal(true);
|
||||
}
|
||||
} catch (err: any) {
|
||||
console.error('[MEDICATION_AI] status failed', err);
|
||||
setError(err?.message || '查询失败,请稍后再试');
|
||||
setError(err?.message || t('medications.aiProgress.errors.queryFailed'));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
@@ -148,12 +155,12 @@ export default function MedicationAiProgressScreen() {
|
||||
};
|
||||
}, []);
|
||||
|
||||
const progress = task?.progress ?? Math.min(100, (currentStepIndex / STATUS_STEPS.length) * 100 + 10);
|
||||
const progress = task?.progress ?? Math.min(100, (currentStepIndex / steps.length) * 100 + 10);
|
||||
|
||||
return (
|
||||
<SafeAreaView style={styles.container}>
|
||||
<LinearGradient colors={['#fdfdfd', '#f3f6fb']} style={StyleSheet.absoluteFill} />
|
||||
<HeaderBar title="识别中" onBack={() => router.back()} transparent />
|
||||
<LinearGradient colors={[palette.gray[25], palette.gray[50]]} style={StyleSheet.absoluteFill} />
|
||||
<HeaderBar title={t('medications.aiProgress.title')} onBack={() => router.back()} transparent />
|
||||
<View style={{ height: insets.top }} />
|
||||
|
||||
<View style={styles.heroCard}>
|
||||
@@ -172,7 +179,7 @@ export default function MedicationAiProgressScreen() {
|
||||
|
||||
{/* 渐变蒙版边框,增加视觉层次 */}
|
||||
<LinearGradient
|
||||
colors={['rgba(14, 165, 233, 0.3)', 'rgba(6, 182, 212, 0.2)', 'transparent']}
|
||||
colors={[Colors.light.primary + '4D', Colors.light.accentPurple + '33', 'transparent']}
|
||||
style={styles.gradientBorder}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 1 }}
|
||||
@@ -206,7 +213,7 @@ export default function MedicationAiProgressScreen() {
|
||||
</View>
|
||||
|
||||
<View style={styles.stepList}>
|
||||
{STATUS_STEPS.map((step, index) => {
|
||||
{steps.map((step, index) => {
|
||||
const active = index === currentStepIndex;
|
||||
const done = index < currentStepIndex;
|
||||
return (
|
||||
@@ -221,7 +228,7 @@ export default function MedicationAiProgressScreen() {
|
||||
{task?.status === 'completed' && (
|
||||
<View style={styles.stepRow}>
|
||||
<View style={[styles.bullet, styles.bulletDone]} />
|
||||
<Text style={[styles.stepLabel, styles.stepLabelDone]}>识别完成,正在载入详情...</Text>
|
||||
<Text style={[styles.stepLabel, styles.stepLabelDone]}>{t('medications.aiProgress.steps.completed')}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
@@ -251,7 +258,7 @@ export default function MedicationAiProgressScreen() {
|
||||
<View style={styles.errorModalContent}>
|
||||
|
||||
{/* 标题 */}
|
||||
<Text style={styles.errorModalTitle}>需要重新拍摄</Text>
|
||||
<Text style={styles.errorModalTitle}>{t('medications.aiProgress.modal.title')}</Text>
|
||||
|
||||
{/* 提示信息 */}
|
||||
<View style={styles.errorMessageBox}>
|
||||
@@ -268,29 +275,29 @@ export default function MedicationAiProgressScreen() {
|
||||
<GlassView
|
||||
style={styles.retryButton}
|
||||
glassEffectStyle="regular"
|
||||
tintColor="rgba(14, 165, 233, 0.9)"
|
||||
tintColor={Colors.light.primary}
|
||||
isInteractive={true}
|
||||
>
|
||||
<LinearGradient
|
||||
colors={['rgba(14, 165, 233, 0.95)', 'rgba(6, 182, 212, 0.95)']}
|
||||
colors={[Colors.light.primary, Colors.light.accentPurple]}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={styles.retryButtonGradient}
|
||||
>
|
||||
<Ionicons name="camera" size={20} color="#FFFFFF" style={{ marginRight: 8 }} />
|
||||
<Text style={styles.retryButtonText}>重新拍摄</Text>
|
||||
<Text style={styles.retryButtonText}>{t('medications.aiProgress.modal.retry')}</Text>
|
||||
</LinearGradient>
|
||||
</GlassView>
|
||||
) : (
|
||||
<View style={styles.retryButton}>
|
||||
<LinearGradient
|
||||
colors={['#0ea5e9', '#06b6d4']}
|
||||
colors={[Colors.light.primary, Colors.light.accentPurple]}
|
||||
start={{ x: 0, y: 0 }}
|
||||
end={{ x: 1, y: 0 }}
|
||||
style={styles.retryButtonGradient}
|
||||
>
|
||||
<Ionicons name="camera" size={20} color="#FFFFFF" style={{ marginRight: 8 }} />
|
||||
<Text style={styles.retryButtonText}>重新拍摄</Text>
|
||||
<Text style={styles.retryButtonText}>{t('medications.aiProgress.modal.retry')}</Text>
|
||||
</LinearGradient>
|
||||
</View>
|
||||
)}
|
||||
@@ -311,9 +318,9 @@ const styles = StyleSheet.create({
|
||||
marginHorizontal: 20,
|
||||
marginTop: 24,
|
||||
borderRadius: 24,
|
||||
backgroundColor: '#fff',
|
||||
backgroundColor: Colors.light.card,
|
||||
padding: 16,
|
||||
shadowColor: '#0f172a',
|
||||
shadowColor: Colors.light.text,
|
||||
shadowOpacity: 0.08,
|
||||
shadowRadius: 18,
|
||||
shadowOffset: { width: 0, height: 10 },
|
||||
@@ -322,7 +329,7 @@ const styles = StyleSheet.create({
|
||||
height: 230,
|
||||
borderRadius: 18,
|
||||
overflow: 'hidden',
|
||||
backgroundColor: '#e2e8f0',
|
||||
backgroundColor: palette.gray[50],
|
||||
},
|
||||
heroImage: {
|
||||
width: '100%',
|
||||
@@ -330,7 +337,7 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
heroPlaceholder: {
|
||||
flex: 1,
|
||||
backgroundColor: '#e2e8f0',
|
||||
backgroundColor: palette.gray[50],
|
||||
},
|
||||
// 深色蒙版层,让点阵更清晰可见
|
||||
overlayMask: {
|
||||
@@ -368,15 +375,15 @@ const styles = StyleSheet.create({
|
||||
width: 5,
|
||||
height: 5,
|
||||
borderRadius: 2.5,
|
||||
backgroundColor: '#FFFFFF',
|
||||
shadowColor: '#0ea5e9',
|
||||
backgroundColor: Colors.light.background,
|
||||
shadowColor: Colors.light.primary,
|
||||
shadowOpacity: 0.9,
|
||||
shadowRadius: 6,
|
||||
shadowOffset: { width: 0, height: 0 },
|
||||
},
|
||||
progressRow: {
|
||||
height: 8,
|
||||
backgroundColor: '#f1f5f9',
|
||||
backgroundColor: palette.gray[50],
|
||||
borderRadius: 10,
|
||||
marginTop: 14,
|
||||
overflow: 'hidden',
|
||||
@@ -384,13 +391,13 @@ const styles = StyleSheet.create({
|
||||
progressBar: {
|
||||
height: '100%',
|
||||
borderRadius: 10,
|
||||
backgroundColor: '#0ea5e9',
|
||||
backgroundColor: Colors.light.primary,
|
||||
},
|
||||
progressText: {
|
||||
marginTop: 8,
|
||||
fontSize: 14,
|
||||
fontWeight: '700',
|
||||
color: '#0f172a',
|
||||
color: Colors.light.text,
|
||||
textAlign: 'right',
|
||||
},
|
||||
stepList: {
|
||||
@@ -407,24 +414,24 @@ const styles = StyleSheet.create({
|
||||
width: 14,
|
||||
height: 14,
|
||||
borderRadius: 7,
|
||||
backgroundColor: '#e2e8f0',
|
||||
backgroundColor: palette.gray[50],
|
||||
},
|
||||
bulletActive: {
|
||||
backgroundColor: '#0ea5e9',
|
||||
backgroundColor: Colors.light.primary,
|
||||
},
|
||||
bulletDone: {
|
||||
backgroundColor: '#22c55e',
|
||||
backgroundColor: Colors.light.success,
|
||||
},
|
||||
stepLabel: {
|
||||
fontSize: 15,
|
||||
color: '#94a3b8',
|
||||
color: Colors.light.textMuted,
|
||||
},
|
||||
stepLabelActive: {
|
||||
color: '#0f172a',
|
||||
color: Colors.light.text,
|
||||
fontWeight: '700',
|
||||
},
|
||||
stepLabelDone: {
|
||||
color: '#16a34a',
|
||||
color: Colors.light.successDark,
|
||||
fontWeight: '700',
|
||||
},
|
||||
loadingBox: {
|
||||
@@ -433,7 +440,7 @@ const styles = StyleSheet.create({
|
||||
gap: 12,
|
||||
},
|
||||
errorText: {
|
||||
color: '#ef4444',
|
||||
color: Colors.light.danger,
|
||||
fontSize: 14,
|
||||
},
|
||||
// Modal 样式
|
||||
@@ -445,10 +452,10 @@ const styles = StyleSheet.create({
|
||||
},
|
||||
errorModalContainer: {
|
||||
width: SCREEN_WIDTH - 48,
|
||||
backgroundColor: '#FFFFFF',
|
||||
backgroundColor: Colors.light.card,
|
||||
borderRadius: 28,
|
||||
overflow: 'hidden',
|
||||
shadowColor: '#0ea5e9',
|
||||
shadowColor: Colors.light.primary,
|
||||
shadowOpacity: 0.15,
|
||||
shadowRadius: 24,
|
||||
shadowOffset: { width: 0, height: 8 },
|
||||
@@ -465,36 +472,36 @@ const styles = StyleSheet.create({
|
||||
width: 96,
|
||||
height: 96,
|
||||
borderRadius: 48,
|
||||
backgroundColor: 'rgba(14, 165, 233, 0.08)',
|
||||
backgroundColor: palette.purple[50],
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
},
|
||||
errorModalTitle: {
|
||||
fontSize: 22,
|
||||
fontWeight: '700',
|
||||
color: '#0f172a',
|
||||
color: Colors.light.text,
|
||||
marginBottom: 16,
|
||||
textAlign: 'center',
|
||||
},
|
||||
errorMessageBox: {
|
||||
backgroundColor: '#f0f9ff',
|
||||
backgroundColor: palette.purple[25],
|
||||
borderRadius: 16,
|
||||
padding: 20,
|
||||
marginBottom: 28,
|
||||
width: '100%',
|
||||
borderWidth: 1,
|
||||
borderColor: 'rgba(14, 165, 233, 0.2)',
|
||||
borderColor: palette.purple[200],
|
||||
},
|
||||
errorMessageText: {
|
||||
fontSize: 15,
|
||||
lineHeight: 24,
|
||||
color: '#475569',
|
||||
color: Colors.light.textSecondary,
|
||||
textAlign: 'center',
|
||||
},
|
||||
retryButton: {
|
||||
borderRadius: 16,
|
||||
overflow: 'hidden',
|
||||
shadowColor: '#0ea5e9',
|
||||
shadowColor: Colors.light.primary,
|
||||
shadowOpacity: 0.25,
|
||||
shadowRadius: 12,
|
||||
shadowOffset: { width: 0, height: 6 },
|
||||
@@ -509,6 +516,6 @@ const styles = StyleSheet.create({
|
||||
retryButtonText: {
|
||||
fontSize: 18,
|
||||
fontWeight: '700',
|
||||
color: '#FFFFFF',
|
||||
color: Colors.light.onPrimary,
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user