fix(mp): P1+P2 稳定性加固 — 导航安全+生产日志+分包预加载+logout清理

P1:
- 全局 23 个页面 Taro.navigateTo → safeNavigateTo,防止页栈超10层
- 生产构建保留 console.warn/error,便于线上问题排查
- 添加 preloadRule 分包预加载(首页预加载健康/医生/文章分包)

P2:
- logout 时清理 ai_chat_history + BLE DataBuffer 缓存
- restore() 移除冗余的双重 Storage 读取(secureGet 已包含 getStorageSync)
- 首页文章图片添加 lazyLoad
This commit is contained in:
iven
2026-05-17 17:13:35 +08:00
parent 1576709342
commit 59dd5ef38e
26 changed files with 122 additions and 80 deletions

View File

@@ -8,7 +8,7 @@ export default {
compress: {
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log', 'console.info', 'console.debug', 'console.warn', 'console.error'],
pure_funcs: ['console.log', 'console.info', 'console.debug'],
},
format: {
comments: false,

View File

@@ -76,6 +76,20 @@ export default defineAppConfig({
{ pagePath: 'pages/profile/index', text: '我的', iconPath: 'assets/tabbar/profile.png', selectedIconPath: 'assets/tabbar/profile-active.png' },
],
},
preloadRule: {
'pages/index/index': {
network: 'all',
packages: ['pages/pkg-health', 'pages/pkg-doctor-core', 'pages/article'],
},
'pages/health/index': {
network: 'all',
packages: ['pages/pkg-health'],
},
'pages/consultation/index': {
network: 'all',
packages: ['pages/pkg-consultation'],
},
},
window: {
backgroundTextStyle: 'dark',
navigationBarBackgroundColor: '#FFFFFF',

View File

@@ -1,6 +1,7 @@
import React, { useState, useCallback } from 'react';
import { View, Text, ScrollView } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { listAiAnalysis, type AiAnalysisItem } from '@/services/ai-analysis';
import Loading from '@/components/Loading';
@@ -49,7 +50,7 @@ export default function AiReportList() {
usePageData(async () => { await loadList(1); }, { throttleMs: 5000, enablePullDown: true });
const goDetail = (id: string) => {
Taro.navigateTo({ url: `/pages/ai-report/detail/index?id=${id}` });
safeNavigateTo(`/pages/ai-report/detail/index?id=${id}`);
};
const loadMore = () => {

View File

@@ -1,6 +1,7 @@
import React, { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useReachBottom } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { listAppointments } from '../../services/appointment';
import type { Appointment } from '../../services/appointment';
@@ -65,11 +66,11 @@ export default function AppointmentList() {
});
const goCreate = () => {
Taro.navigateTo({ url: '/pages/appointment/create/index' });
safeNavigateTo('/pages/appointment/create/index');
};
const goDetail = (id: string) => {
Taro.navigateTo({ url: `/pages/appointment/detail/index?id=${id}` });
safeNavigateTo(`/pages/appointment/detail/index?id=${id}`);
};
const getStatusTag = (status: string) => {

View File

@@ -1,8 +1,9 @@
import React, { useState, useCallback, useEffect } from 'react';
import { View, Text, Image, ScrollView } from '@tarojs/components';
import Taro, { useReachBottom } from '@tarojs/taro';
import { useState, useCallback } from 'react';
import { View, Text, ScrollView } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { listArticles, listCategories, Article, ArticleCategory } from '../../services/article';
import { listArticles, listCategories } from '../../services/article';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import LoadingCard from '@/components/ui/LoadingCard';
@@ -12,9 +13,27 @@ import Loading from '@/components/Loading';
import { useElderClass } from '../../hooks/useElderClass';
import './index.scss';
interface ArticleItem {
id: string;
title: string;
summary?: string;
cover_image?: string;
category?: string;
category_id?: string;
category_name?: string;
published_at?: string;
status?: string;
}
interface ArticleCategory {
id: string;
name: string;
parent_id?: string | null;
}
export default function ArticleList() {
const modeClass = useElderClass();
const [articles, setArticles] = useState<Article[]>([]);
const [articles, setArticles] = useState<ArticleItem[]>([]);
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(false);
@@ -22,15 +41,6 @@ export default function ArticleList() {
const [categories, setCategories] = useState<ArticleCategory[]>([]);
const [activeCategory, setActiveCategory] = useState<string | null>(null);
const fetchCategories = useCallback(async () => {
try {
const data = await listCategories();
setCategories(data || []);
} catch {
setCategories([]);
}
}, []);
const fetchData = useCallback(async (p: number, append = false, categoryId?: string | null) => {
setLoading(true);
setError(false);
@@ -52,37 +62,34 @@ export default function ArticleList() {
}
}, [activeCategory]);
useEffect(() => {
fetchCategories();
}, [fetchCategories]);
usePageData(
useCallback(() => fetchData(1, false, null), [fetchData]),
useCallback(async () => {
try {
const cats = await listCategories();
setCategories(cats || []);
} catch {
setCategories([]);
}
await fetchData(1);
}, [fetchData]),
{ throttleMs: 10000, enablePullDown: true },
);
useReachBottom(() => {
if (!loading && articles.length < total) {
fetchData(page + 1, true);
}
});
const handleCategoryChange = (categoryId: string | null) => {
setActiveCategory(categoryId);
fetchData(1, false, categoryId);
};
const goToDetail = (id: string) => {
Taro.navigateTo({ url: `/pages/article/detail/index?id=${id}` });
safeNavigateTo(`/pages/article/detail/index?id=${id}`);
};
if (!loading && articles.length === 0 && !error && !categories.length) {
if (loading && articles.length === 0 && !error) {
return <LoadingCard count={3} />;
}
return (
<PageShell safeBottom padding="none" className={modeClass}>
{/* 分类筛选 */}
{categories.length > 0 && (
<ScrollView scrollX className='article-categories'>
<View
@@ -132,11 +139,6 @@ export default function ArticleList() {
)}
</View>
</View>
{a.cover_image && (
<View className='article-card-cover'>
<Image className='cover-img' src={a.cover_image} mode='aspectFill' lazyLoad />
</View>
)}
</ContentCard>
))}
</View>

View File

@@ -1,6 +1,7 @@
import { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useReachBottom } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { useAuthStore } from '@/stores/auth';
import { listConsultations, ConsultationSession } from '@/services/consultation';
@@ -99,7 +100,7 @@ export default function Consultation() {
});
const handleTapSession = (session: ConsultationSession) => {
Taro.navigateTo({ url: `/pages/pkg-consultation/detail/index?id=${session.id}` });
safeNavigateTo(`/pages/pkg-consultation/detail/index?id=${session.id}`);
};
if (!user) {
@@ -130,7 +131,7 @@ export default function Consultation() {
{/* 发起咨询按钮 */}
<View
className='consultation-create-btn'
onClick={() => Taro.navigateTo({ url: '/pages/consultation/create/index' })}
onClick={() => safeNavigateTo('/pages/consultation/create/index')}
>
<Text className='consultation-create-btn-text'></Text>
</View>

View File

@@ -1,6 +1,7 @@
import { useState } from 'react';
import { View, Text, Input } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { useAuthStore } from '../../stores/auth';
import { useElderClass } from '../../hooks/useElderClass';
import { findThreshold, inputVitalSign, type HealthThreshold } from '../../services/health';
@@ -173,9 +174,9 @@ export default function Health() {
<View className='ai-suggestion-card' onClick={() => {
const first = aiSuggestions[0];
if (first?.suggestion_type === 'appointment') {
Taro.navigateTo({ url: `/pages/appointment/create/index` });
safeNavigateTo(`/pages/appointment/create/index`);
} else if (first?.suggestion_type === 'followup') {
Taro.navigateTo({ url: '/pages/pkg-profile/followups/index' });
safeNavigateTo('/pages/pkg-profile/followups/index');
} else {
Taro.switchTab({ url: '/pages/health/index' });
}
@@ -326,7 +327,7 @@ export default function Health() {
</View>
<ContentCard
onPress={() => Taro.navigateTo({ url: '/pages/article/index' })}
onPress={() => safeNavigateTo('/pages/article/index')}
>
<Text className='article-entry-text'> </Text>
</ContentCard>

View File

@@ -1,6 +1,7 @@
import { View, Text, Swiper, SwiperItem, Image } from '@tarojs/components';
import { useState } from 'react';
import Taro, { useDidShow, useDidHide } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { useAuthStore } from '../../stores/auth';
import { useUIStore } from '../../stores/ui';
import { navigateToLogin } from '../../utils/navigate';
@@ -120,12 +121,12 @@ function GuestHome({ modeClass }: { modeClass: string }) {
{articles.map((article) => (
<ContentCard
key={article.id}
onPress={() => Taro.navigateTo({ url: `/pages/article/detail/index?id=${article.id}` })}
onPress={() => safeNavigateTo(`/pages/article/detail/index?id=${article.id}`)}
activeFeedback="opacity"
padding="none"
>
{article.cover_image && (
<Image className='guest-article-cover' src={article.cover_image} mode='aspectFill' />
<Image className='guest-article-cover' src={article.cover_image} mode='aspectFill' lazyLoad />
)}
<View className='guest-article-body'>
<Text className='guest-article-title'>{article.title}</Text>
@@ -229,7 +230,7 @@ function HomeDashboard({ modeClass }: { modeClass: string }) {
return (
<ContentCard
key={item.label}
onPress={() => Taro.navigateTo({ url: `/pages/pkg-health/trend/index?indicator=${item.indicator}` })}
onPress={() => safeNavigateTo(`/pages/pkg-health/trend/index?indicator=${item.indicator}`)}
activeFeedback="opacity"
>
<Text className='vital-label'>{item.label}</Text>
@@ -259,8 +260,8 @@ function HomeDashboard({ modeClass }: { modeClass: string }) {
key={r.id}
className={`reminder-item ${i > 0 ? 'reminder-item-border' : ''}`}
onClick={() => {
if (r.type === 'appointment') Taro.navigateTo({ url: '/pages/appointment/index' });
else if (r.type === 'followup') Taro.navigateTo({ url: `/pages/pkg-profile/followups/detail/index?id=${r.id}` });
if (r.type === 'appointment') safeNavigateTo('/pages/appointment/index');
else if (r.type === 'followup') safeNavigateTo(`/pages/pkg-profile/followups/detail/index?id=${r.id}`);
}}
>
<Text className='reminder-tag'>{r.tag}</Text>
@@ -275,7 +276,7 @@ function HomeDashboard({ modeClass }: { modeClass: string }) {
<View className='action-btn action-primary' onClick={() => Taro.switchTab({ url: '/pages/health/index' })}>
<Text className='action-btn-text'></Text>
</View>
<View className='action-btn action-outline' onClick={() => Taro.navigateTo({ url: '/pages/appointment/create/index' })}>
<View className='action-btn action-outline' onClick={() => safeNavigateTo('/pages/appointment/create/index')}>
<Text className='action-btn-text'></Text>
</View>
</View>

View File

@@ -1,6 +1,7 @@
import { useState } from 'react';
import { View, Text, Input, Button } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { useAuthStore } from '../../stores/auth';
import './index.scss';
@@ -191,9 +192,9 @@ export default function Login() {
</View>
<Text className="agreement-text">
<Text className="agreement-link" onClick={() => Taro.navigateTo({ url: '/pages/legal/user-agreement' })}></Text>
<Text className="agreement-link" onClick={() => safeNavigateTo('/pages/legal/user-agreement')}></Text>
<Text className="agreement-link" onClick={() => Taro.navigateTo({ url: '/pages/legal/privacy-policy' })}></Text>
<Text className="agreement-link" onClick={() => safeNavigateTo('/pages/legal/privacy-policy')}></Text>
</Text>
</View>

View File

@@ -1,6 +1,7 @@
import React, { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useReachBottom } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { listProducts } from '../../services/points';
import type { PointsProduct } from '../../services/points';
@@ -132,7 +133,7 @@ export default function Mall() {
Taro.showToast({ title: '已兑完', icon: 'none' });
return;
}
Taro.navigateTo({ url: `/pages/pkg-mall/exchange/index?product_id=${item.id}` });
safeNavigateTo(`/pages/pkg-mall/exchange/index?product_id=${item.id}`);
};
const balance = account?.balance ?? 0;
@@ -145,7 +146,7 @@ export default function Mall() {
text='请先完善个人档案'
hint='建档后即可使用积分商城、签到等功能'
actionText='去建档'
onAction={() => Taro.navigateTo({ url: '/pages/pkg-profile/family-add/index' })}
onAction={() => safeNavigateTo('/pages/pkg-profile/family-add/index')}
/>
</PageShell>
);

View File

@@ -1,6 +1,7 @@
import { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import {
getDialysisRecord, reviewDialysisRecord,
@@ -167,9 +168,7 @@ export default function DialysisDetail() {
</View>
)}
{record.status === 'draft' && (
<View className='action-btn action-btn--secondary' onClick={() => Taro.navigateTo({
url: `/pages/pkg-doctor-clinical/dialysis/create/index?id=${id}&version=${record.version}`,
})}>
<View className='action-btn action-btn--secondary' onClick={() => safeNavigateTo(`/pages/pkg-doctor-clinical/dialysis/create/index?id=${id}&version=${record.version}`)}>
<Text className='action-btn__text'></Text>
</View>
)}

View File

@@ -1,6 +1,7 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { listLabReports, type LabReportItem } from '@/services/doctor/labReport';
import { listPatients } from '@/services/doctor/patient';
@@ -99,9 +100,7 @@ export default function ReportList() {
{reports.map((r) => (
<ContentCard
key={r.id}
onPress={() => Taro.navigateTo({
url: `/pages/pkg-doctor-clinical/report/detail/index?patientId=${currentPatientId}&id=${r.id}`,
})}
onPress={() => safeNavigateTo(`/pages/pkg-doctor-clinical/report/detail/index?patientId=${currentPatientId}&id=${r.id}`)}
>
<View className="report-card__header">
<Text className="report-card__type">{r.report_type}</Text>

View File

@@ -1,6 +1,7 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { listFollowUpTasks, type FollowUpTask } from '@/services/doctor/followup';
import PageShell from '@/components/ui/PageShell';
@@ -106,7 +107,7 @@ export default function FollowUpList() {
{tasks.map((task) => (
<ContentCard
key={task.id}
onPress={() => Taro.navigateTo({ url: `/pages/pkg-doctor-core/followup/detail/index?id=${task.id}` })}
onPress={() => safeNavigateTo(`/pages/pkg-doctor-core/followup/detail/index?id=${task.id}`)}
>
<View className="task-card__header">
<Text className="task-card__type">{getTypeLabel(task.follow_up_type)}</Text>

View File

@@ -1,6 +1,7 @@
import { useState, useMemo, useCallback } from 'react';
import { View, Text, Input } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { useAuthStore } from '@/stores/auth';
import { useDoctorClass } from '@/hooks/useDoctorClass';
import { usePageData } from '@/hooks/usePageData';
@@ -93,7 +94,7 @@ export default function DoctorHome() {
usePageData(loadDashboard, { throttleMs: 10000 });
const handleCardClick = (card: CardConfig) => {
Taro.navigateTo({ url: card.route });
safeNavigateTo(card.route);
};
const handleLogout = () => {
@@ -123,7 +124,7 @@ export default function DoctorHome() {
<View className='doctor-home__alert'>
<Text className='doctor-home__alert-icon'>!</Text>
<Text className='doctor-home__alert-text'>{alertCount} </Text>
<Text className='doctor-home__alert-link' onClick={() => Taro.navigateTo({ url: '/pages/pkg-doctor-clinical/alerts/index' })}> </Text>
<Text className='doctor-home__alert-link' onClick={() => safeNavigateTo('/pages/pkg-doctor-clinical/alerts/index')}> </Text>
</View>
)}
@@ -131,7 +132,7 @@ export default function DoctorHome() {
<Input
className='doctor-home__search-input'
placeholder='搜索患者姓名...'
onFocus={() => Taro.navigateTo({ url: '/pages/pkg-doctor-core/patients/index' })}
onFocus={() => safeNavigateTo('/pages/pkg-doctor-core/patients/index')}
/>
</View>
@@ -176,7 +177,7 @@ export default function DoctorHome() {
<View
key={action.route}
className='quick-action'
onClick={() => Taro.navigateTo({ url: action.route })}
onClick={() => safeNavigateTo(action.route)}
>
<View className='quick-action__icon-wrap'>
<Text className='quick-action__initial'>{action.initial}</Text>

View File

@@ -1,6 +1,7 @@
import { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { getPatient, getHealthSummary, type PatientDetail, type HealthSummary } from '@/services/doctor/patient';
import Loading from '@/components/Loading';
@@ -127,7 +128,7 @@ export default function PatientDetail() {
<Text className='section-title'></Text>
<View
className='lab-item'
onClick={() => Taro.navigateTo({ url: `/pages/pkg-doctor-clinical/report/detail/index?patientId=${patientId}&id=${summary.latest_lab_report!.id}` })}
onClick={() => safeNavigateTo(`/pages/pkg-doctor-clinical/report/detail/index?patientId=${patientId}&id=${summary.latest_lab_report!.id}`)}
>
<View className='lab-item__header'>
<Text className='lab-item__type'>{summary.latest_lab_report.report_type}</Text>
@@ -144,10 +145,10 @@ export default function PatientDetail() {
<ContentCard>
<Text className='section-title'></Text>
<View className='action-buttons'>
<View className='action-btn' onClick={() => Taro.navigateTo({ url: `/pages/pkg-doctor-clinical/report/index?patientId=${patientId}` })}>
<View className='action-btn' onClick={() => safeNavigateTo(`/pages/pkg-doctor-clinical/report/index?patientId=${patientId}`)}>
<Text></Text>
</View>
<View className='action-btn' onClick={() => Taro.navigateTo({ url: `/pages/pkg-doctor-core/followup/index?patientId=${patientId}` })}>
<View className='action-btn' onClick={() => safeNavigateTo(`/pages/pkg-doctor-core/followup/index?patientId=${patientId}`)}>
<Text>访</Text>
</View>
</View>

View File

@@ -1,6 +1,7 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useReachBottom } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { listPatients, listPatientTags, type PatientItem, type PatientTag } from '@/services/doctor/patient';
import PageShell from '@/components/ui/PageShell';
@@ -118,7 +119,7 @@ export default function PatientList() {
{patients.map((p) => (
<ContentCard
key={p.id}
onPress={() => Taro.navigateTo({ url: `/pages/pkg-doctor-core/patients/detail/index?id=${p.id}` })}
onPress={() => safeNavigateTo(`/pages/pkg-doctor-core/patients/detail/index?id=${p.id}`)}
>
<View className="patient-card__header">
<Text className="patient-card__name">{p.name}</Text>

View File

@@ -1,6 +1,7 @@
import React, { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { listPatientAlerts, type Alert } from '@/services/alert';
import { useAuthStore } from '@/stores/auth';
@@ -82,7 +83,7 @@ export default function PatientAlerts() {
if (!currentPatient) {
return (
<PageShell padding="none" className={modeClass}>
<ErrorState text='请先完善个人档案' onRetry={() => Taro.navigateTo({ url: '/pages/pkg-profile/family-add/index' })} />
<ErrorState text='请先完善个人档案' onRetry={() => safeNavigateTo('/pages/pkg-profile/family-add/index')} />
</PageShell>
);
}

View File

@@ -1,6 +1,7 @@
import { useState, useCallback } from 'react';
import { View, Text, Input, Picker } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { num, validateStr } from '@/utils/validate';
import { inputVitalSign, getHealthThresholds, findThreshold, DEFAULT_THRESHOLDS, type HealthThreshold } from '../../../services/health';
@@ -193,7 +194,7 @@ export default function HealthInput() {
</View>
{/* 从设备同步入口 */}
<View className='input-sync-entry' onClick={() => Taro.navigateTo({ url: '/pages/pkg-health/device-sync/index?returnTo=input' })}>
<View className='input-sync-entry' onClick={() => safeNavigateTo('/pages/pkg-health/device-sync/index?returnTo=input')}>
<Text className='input-sync-entry-text'></Text>
<Text className='input-sync-entry-hint'></Text>
</View>

View File

@@ -1,6 +1,7 @@
import React, { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useReachBottom } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { getCachedPatientId } from '@/services/request';
import { listDialysisPrescriptions } from '@/services/dialysis';
@@ -68,7 +69,7 @@ export default function DialysisPrescriptionList() {
<View
className='prescription-card'
key={p.id}
onClick={() => Taro.navigateTo({ url: `/pages/pkg-profile/dialysis-prescriptions/detail/index?id=${p.id}` })}
onClick={() => safeNavigateTo(`/pages/pkg-profile/dialysis-prescriptions/detail/index?id=${p.id}`)}
>
<View className='prescription-card-top'>
<Text className='prescription-model'>{p.dialyzer_model || '未指定型号'}</Text>

View File

@@ -1,6 +1,7 @@
import React, { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useReachBottom } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { getCachedPatientId } from '@/services/request';
import { listDialysisRecords } from '@/services/dialysis';
@@ -76,7 +77,7 @@ export default function DialysisRecordList() {
<View
className='record-card'
key={r.id}
onClick={() => Taro.navigateTo({ url: `/pages/pkg-profile/dialysis-records/detail/index?id=${r.id}` })}
onClick={() => safeNavigateTo(`/pages/pkg-profile/dialysis-records/detail/index?id=${r.id}`)}
>
<View className='record-card-top'>
<Text className={`type-tag ${ti.cls}`}>{ti.label}</Text>

View File

@@ -1,6 +1,7 @@
import React, { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { listPatients, Patient } from '../../../services/patient';
import { useAuthStore } from '../../../stores/auth';
@@ -42,12 +43,12 @@ export default function FamilyList() {
};
const goToAdd = () => {
Taro.navigateTo({ url: '/pages/pkg-profile/family-add/index' });
safeNavigateTo('/pages/pkg-profile/family-add/index');
};
const goToEdit = (patient: Patient) => {
Taro.setStorageSync('edit_patient', patient);
Taro.navigateTo({ url: `/pages/pkg-profile/family-add/index?id=${patient.id}` });
safeNavigateTo(`/pages/pkg-profile/family-add/index?id=${patient.id}`);
};
const genderText = (g?: string) => {

View File

@@ -1,6 +1,7 @@
import React, { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { listTasks, FollowUpTask } from '../../../services/followup';
import EmptyState from '../../../components/EmptyState';
@@ -45,7 +46,7 @@ export default function MyFollowUps() {
};
const goToDetail = (id: string) => {
Taro.navigateTo({ url: `/pages/pkg-profile/followups/detail/index?id=${id}` });
safeNavigateTo(`/pages/pkg-profile/followups/detail/index?id=${id}`);
};
const getStatusClass = (status: string) => {

View File

@@ -1,6 +1,7 @@
import React, { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useReachBottom } from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { usePageData } from '@/hooks/usePageData';
import { getCachedPatientId } from '@/services/request';
import { listReports, LabReport } from '../../../services/report';
@@ -49,7 +50,7 @@ export default function MyReports() {
});
const goToDetail = (id: string) => {
Taro.navigateTo({ url: `/pages/pkg-profile/reports/detail/index?id=${id}` });
safeNavigateTo(`/pages/pkg-profile/reports/detail/index?id=${id}`);
};
const formatStatus = (report: LabReport) => {

View File

@@ -1,6 +1,7 @@
import React from 'react';
import { View, Text } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { useAuthStore } from '../../../stores/auth';
import { invalidateHeadersCache, clearRequestCache } from '@/services/request';
import { useElderClass } from '../../../hooks/useElderClass';
@@ -51,7 +52,7 @@ export default function Settings() {
};
const handlePrivacy = () => {
Taro.navigateTo({ url: '/pages/legal/privacy-policy' });
safeNavigateTo('/pages/legal/privacy-policy');
};
const handleLogout = () => {

View File

@@ -1,5 +1,6 @@
import { View, Text } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { safeNavigateTo } from '@/utils/navigate';
import { useState, useCallback } from 'react';
import { useAuthStore } from '../../stores/auth';
import { usePointsStore } from '../../stores/points';
@@ -113,7 +114,7 @@ export default function Profile() {
if (item.isSwitchTab) {
Taro.switchTab({ url: item.path });
} else {
Taro.navigateTo({ url: item.path });
safeNavigateTo(item.path);
}
};
@@ -188,7 +189,7 @@ export default function Profile() {
<ContentCard
padding="none"
margin="none"
onPress={() => Taro.navigateTo({ url: '/pages/pkg-profile/notifications/index' })}
onPress={() => safeNavigateTo('/pages/pkg-profile/notifications/index')}
>
<View className='menu-item'>
<View className='menu-icon menu-icon--pri-l'>

View File

@@ -81,12 +81,12 @@ export const useAuthStore = create<AuthState>((set, get) => ({
restore: () => {
// 利用内存缓存避免重复 Storage IPC + JSON.parse
try {
const userData = secureGet('user_data') || Taro.getStorageSync('user_data') || '';
const userData = secureGet('user_data');
if (userData !== cachedUserJson) {
cachedUserJson = userData;
cachedUserObj = userData ? JSON.parse(userData) : null;
}
const rolesData = secureGet('user_roles') || Taro.getStorageSync('user_roles') || '';
const rolesData = secureGet('user_roles');
if (rolesData !== cachedRolesJson) {
cachedRolesJson = rolesData;
cachedRolesObj = rolesData ? JSON.parse(rolesData) : [];
@@ -252,6 +252,14 @@ export const useAuthStore = create<AuthState>((set, get) => ({
Taro.removeStorageSync('current_patient_id');
Taro.removeStorageSync('analytics_queue');
Taro.removeStorageSync('edit_patient');
Taro.removeStorageSync('ai_chat_history');
// 清理 BLE DataBuffer 缓存key 格式ble_buffer_{patientId}_{bucket}
const storageInfo = Taro.getStorageInfoSync();
storageInfo.keys.forEach((key) => {
if (key.startsWith('ble_buffer_') || key.startsWith('last_ble_sync')) {
Taro.removeStorageSync(key);
}
});
resetAllStores();
set({ user: null, roles: [], currentPatient: null, patients: [] });
Taro.reLaunch({ url: '/pages/index/index' });