diff --git a/apps/miniprogram/src/hooks/usePageData.ts b/apps/miniprogram/src/hooks/usePageData.ts index 34812f4..85538d4 100644 --- a/apps/miniprogram/src/hooks/usePageData.ts +++ b/apps/miniprogram/src/hooks/usePageData.ts @@ -1,5 +1,5 @@ -import { useRef, useState, useCallback } from 'react'; -import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro'; +import { useRef, useState, useCallback, useEffect } from 'react'; +import Taro, { useDidShow, useDidHide, usePullDownRefresh } from '@tarojs/taro'; interface UsePageDataOptions { throttleMs?: number; @@ -14,7 +14,7 @@ interface UsePageDataResult { } export function usePageData( - fetcher: () => Promise, + fetcher: (signal?: AbortSignal) => Promise, options?: UsePageDataOptions, ): UsePageDataResult { const throttleMs = options?.throttleMs ?? 5000; @@ -26,6 +26,14 @@ export function usePageData( const lastRunRef = useRef(0); const fetcherRef = useRef(fetcher); fetcherRef.current = fetcher; + const abortRef = useRef(null); + + const abort = useCallback(() => { + if (abortRef.current) { + abortRef.current.abort(); + abortRef.current = null; + } + }, []); const run = useCallback(async (force = false) => { if (!enabled || loadingRef.current) return; @@ -33,11 +41,15 @@ export function usePageData( loadingRef.current = true; setLoading(true); lastRunRef.current = Date.now(); + abort(); + const ac = new AbortController(); + abortRef.current = ac; try { - await fetcherRef.current(); + await fetcherRef.current(ac.signal); } finally { loadingRef.current = false; setLoading(false); + if (abortRef.current === ac) abortRef.current = null; } }, [enabled, throttleMs]); @@ -45,6 +57,16 @@ export function usePageData( run(); }); + useDidHide(() => { + abort(); + }); + + useEffect(() => { + return () => { + abort(); + }; + }, []); + const trigger = useCallback(() => { run(true); }, [run]); @@ -54,11 +76,15 @@ export function usePageData( loadingRef.current = true; setLoading(true); lastRunRef.current = Date.now(); + abort(); + const ac = new AbortController(); + abortRef.current = ac; try { - await fetcherRef.current(); + await fetcherRef.current(ac.signal); } finally { loadingRef.current = false; setLoading(false); + if (abortRef.current === ac) abortRef.current = null; } }, []); diff --git a/apps/miniprogram/src/pages/index/index.tsx b/apps/miniprogram/src/pages/index/index.tsx index 676a424..540e6a7 100644 --- a/apps/miniprogram/src/pages/index/index.tsx +++ b/apps/miniprogram/src/pages/index/index.tsx @@ -314,14 +314,25 @@ export default function Index() { const modeClass = mode === 'elder' ? 'elder-mode' : ''; // 医护人员访问患者首页时,自动跳转到医生端 + // 不渲染 HomeDashboard,避免触发患者首页的 API 请求(并发叠加问题) + const shouldRedirect = user && isMedicalStaff(); + useDidShow(() => { - if (user && isMedicalStaff()) { - Taro.redirectTo({ url: '/pages/pkg-doctor-core/index' }); + if (shouldRedirect) { + Taro.reLaunch({ + url: '/pages/pkg-doctor-core/index', + fail: () => { + console.warn('跳转医生端失败,停留患者首页'); + }, + }); } }); if (!user) { return ; } + if (shouldRedirect) { + return ; + } return ; } diff --git a/apps/miniprogram/src/pages/index/useHomeData.ts b/apps/miniprogram/src/pages/index/useHomeData.ts index f944065..e9c2db8 100644 --- a/apps/miniprogram/src/pages/index/useHomeData.ts +++ b/apps/miniprogram/src/pages/index/useHomeData.ts @@ -1,4 +1,4 @@ -import { useState, useMemo, useRef } from 'react'; +import { useState, useMemo, useRef, useEffect } from 'react'; import { useHealthStore } from '@/stores/health'; import { useAuthStore } from '@/stores/auth'; import { usePageData } from '@/hooks/usePageData'; @@ -53,6 +53,17 @@ export function useHomeData() { enabled: !!user, }); + // currentPatient 从 null 变为有值时重新触发加载 + // 解决 loadPatients 异步完成前 useDidShow 已触发 fetchData 并因 patientId 为空提前返回的问题 + const prevPatientRef = useRef(null); + useEffect(() => { + const pid = currentPatient?.id ?? null; + if (pid && pid !== prevPatientRef.current) { + prevPatientRef.current = pid; + trigger(); + } + }, [currentPatient?.id, trigger]); + const loadUnread = async () => { try { const res = await notificationService.getUnreadCount() as { data?: { count?: number } | number }; diff --git a/apps/miniprogram/src/services/auth.ts b/apps/miniprogram/src/services/auth.ts index 069f09a..2801dd2 100644 --- a/apps/miniprogram/src/services/auth.ts +++ b/apps/miniprogram/src/services/auth.ts @@ -48,6 +48,12 @@ export async function wechatBindPhone(openid: string, encryptedData: string, iv: }); } -export async function getPatients() { - return api.get('/health/patients'); +interface PaginatedData { + data: T[]; + total: number; +} + +export async function getPatients() { + const res = await api.get>('/health/patients'); + return Array.isArray(res?.data) ? res.data : (Array.isArray(res) ? res : []); } diff --git a/apps/miniprogram/src/services/health.ts b/apps/miniprogram/src/services/health.ts index a720df7..8e41177 100644 --- a/apps/miniprogram/src/services/health.ts +++ b/apps/miniprogram/src/services/health.ts @@ -17,8 +17,9 @@ export interface TodaySummary { } export async function getTodaySummary(patientId?: string) { + const pid = patientId || Taro.getStorageSync('current_patient_id') || ''; const params: Record = {}; - if (patientId) params.patient_id = patientId; + if (pid) params.patient_id = pid; return api.get('/health/vital-signs/today', params); } diff --git a/apps/miniprogram/src/services/request.ts b/apps/miniprogram/src/services/request.ts index 83ab01b..711dc7e 100644 --- a/apps/miniprogram/src/services/request.ts +++ b/apps/miniprogram/src/services/request.ts @@ -56,7 +56,7 @@ class ConcurrencyLimiter { } } -const limiter = new ConcurrencyLimiter(12); +const limiter = new ConcurrencyLimiter(8); // --- Response cache + deduplication --- diff --git a/apps/miniprogram/src/stores/auth.ts b/apps/miniprogram/src/stores/auth.ts index b8df890..60e4c93 100644 --- a/apps/miniprogram/src/stores/auth.ts +++ b/apps/miniprogram/src/stores/auth.ts @@ -114,6 +114,9 @@ export const useAuthStore = create((set, get) => ({ setCachedPatientId(currentPatient.id); } + // 状态有变化时清理请求缓存,避免返回过期数据 + clearRequestCache(); + // 跳过未变更的 set() const cur = get(); const userChanged = cur.user?.id !== user?.id; @@ -145,6 +148,7 @@ export const useAuthStore = create((set, get) => ({ secureSet('tenant_id', user.tenant_id || ''); set({ user, roles, loading: false }); clearLoggingOut(); + get().loadPatients(); return true; } secureSet('wechat_openid', resp.openid); @@ -176,6 +180,8 @@ export const useAuthStore = create((set, get) => ({ secureSet('tenant_id', resp.user?.tenant_id || tenantId); set({ user: resp.user, roles, loading: false }); clearLoggingOut(); + // 登录成功后自动加载患者档案(如果有的话) + get().loadPatients(); return true; } catch { set({ loading: false }); @@ -209,6 +215,7 @@ export const useAuthStore = create((set, get) => ({ secureRemove('wechat_openid'); set({ user: tokenData.user, roles, loading: false }); clearLoggingOut(); + get().loadPatients(); return true; } catch (err: any) { secureRemove('wechat_openid'); diff --git a/apps/miniprogram/src/stores/health.ts b/apps/miniprogram/src/stores/health.ts index 817f1bd..7cffe3c 100644 --- a/apps/miniprogram/src/stores/health.ts +++ b/apps/miniprogram/src/stores/health.ts @@ -1,6 +1,7 @@ import { create } from 'zustand'; import * as healthApi from '@/services/health'; import { getCachedPatientId } from '@/services/request'; +import { useAuthStore } from './auth'; interface CachedTrend { data: { date: string; value: number }[]; @@ -37,7 +38,9 @@ export const useHealthStore = create((set, get) => ({ } set({ _refreshingToday: true, loading: true }); try { - const patientId = getCachedPatientId() || undefined; + const patientId = getCachedPatientId() + || useAuthStore.getState().currentPatient?.id + || undefined; const data = await healthApi.getTodaySummary(patientId); set({ todaySummary: data, todaySummaryFetchedAt: Date.now(), loading: false, _refreshingToday: false }); } catch {