fix(mp): 五专家组审查 HIGH 级问题修复 — 9 项
- S-1: 隐私政策描述修正("混淆加密" → "HTTPS + 微信沙箱") - A-1: getCachedPatientId 统一导出 + 9 处 Storage 直读替换 - A-2: usePageData loading 改为 useState 响应式 - A-3: health.ts refreshingToday 移入 store state - M-2: prod config 移除 console.error/warn - M-4: clearCache 后同步刷新 request.ts 内存缓存 - Q-3: doctor/appointment.ts any[] → Appointment 类型 - Q-4: daily-monitoring 常量提取到 constants.ts - 清理: 删除空目录 FamilyPicker/HealthCard + 未使用组件 DeviceCard
This commit is contained in:
@@ -8,7 +8,7 @@ export default {
|
|||||||
compress: {
|
compress: {
|
||||||
drop_console: true,
|
drop_console: true,
|
||||||
drop_debugger: true,
|
drop_debugger: true,
|
||||||
pure_funcs: ['console.log', 'console.info', 'console.debug'],
|
pure_funcs: ['console.log', 'console.info', 'console.debug', 'console.warn', 'console.error'],
|
||||||
},
|
},
|
||||||
format: {
|
format: {
|
||||||
comments: false,
|
comments: false,
|
||||||
|
|||||||
@@ -1,51 +0,0 @@
|
|||||||
@import '../../styles/variables.scss';
|
|
||||||
|
|
||||||
.device-card {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 24rpx;
|
|
||||||
background: $card;
|
|
||||||
border-radius: $r;
|
|
||||||
margin-bottom: 16rpx;
|
|
||||||
box-shadow: $shadow-sm;
|
|
||||||
|
|
||||||
.device-icon {
|
|
||||||
font-size: var(--tk-font-h2);
|
|
||||||
margin-right: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-info {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
.device-name {
|
|
||||||
font-size: var(--tk-font-cap);
|
|
||||||
font-weight: 600;
|
|
||||||
color: $tx;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.device-status {
|
|
||||||
font-size: var(--tk-font-micro);
|
|
||||||
margin-top: 4rpx;
|
|
||||||
display: block;
|
|
||||||
|
|
||||||
&.connected { color: $pri; }
|
|
||||||
&.idle { color: $tx3; }
|
|
||||||
}
|
|
||||||
|
|
||||||
.last-sync {
|
|
||||||
font-size: var(--tk-font-micro);
|
|
||||||
color: $tx3;
|
|
||||||
margin-top: 4rpx;
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.sync-btn {
|
|
||||||
padding: 12rpx 28rpx;
|
|
||||||
background: $pri;
|
|
||||||
color: $white;
|
|
||||||
border-radius: $r-pill;
|
|
||||||
font-size: var(--tk-font-micro);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
import { View, Text } from '@tarojs/components';
|
|
||||||
import Taro from '@tarojs/taro';
|
|
||||||
import './index.scss';
|
|
||||||
|
|
||||||
interface DeviceCardProps {
|
|
||||||
deviceName: string;
|
|
||||||
deviceType: string;
|
|
||||||
lastSyncAt?: string;
|
|
||||||
status: 'connected' | 'disconnected' | 'never';
|
|
||||||
}
|
|
||||||
|
|
||||||
const DEVICE_ICONS: Record<string, string> = {
|
|
||||||
blood_pressure: '\u{1FA7A}',
|
|
||||||
blood_glucose: '\u{1FA78}',
|
|
||||||
heart_rate: '\u{2764}',
|
|
||||||
blood_oxygen: '\u{1FAB1}',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function DeviceCard({ deviceName, deviceType, lastSyncAt, status }: DeviceCardProps) {
|
|
||||||
const icon = DEVICE_ICONS[deviceType] || '\u{1F4F1}';
|
|
||||||
const statusLabel = status === 'connected' ? '已连接' : status === 'disconnected' ? '未连接' : '未配对';
|
|
||||||
const statusClass = status === 'connected' ? 'connected' : 'idle';
|
|
||||||
|
|
||||||
const handleSync = () => {
|
|
||||||
Taro.navigateTo({ url: '/pages/pkg-health/device-sync/index' });
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<View className='device-card' onClick={handleSync}>
|
|
||||||
<View className='device-icon'>{icon}</View>
|
|
||||||
<View className='device-info'>
|
|
||||||
<Text className='device-name'>{deviceName}</Text>
|
|
||||||
<Text className={`device-status ${statusClass}`}>{statusLabel}</Text>
|
|
||||||
{lastSyncAt && <Text className='last-sync'>最近同步: {lastSyncAt}</Text>}
|
|
||||||
</View>
|
|
||||||
<View className='sync-btn'>同步</View>
|
|
||||||
</View>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useRef, useCallback } from 'react';
|
import { useRef, useState, useCallback } from 'react';
|
||||||
import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro';
|
import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro';
|
||||||
|
|
||||||
interface UsePageDataOptions {
|
interface UsePageDataOptions {
|
||||||
@@ -22,6 +22,7 @@ export function usePageData(
|
|||||||
const enabled = options?.enabled ?? true;
|
const enabled = options?.enabled ?? true;
|
||||||
|
|
||||||
const loadingRef = useRef(false);
|
const loadingRef = useRef(false);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
const lastRunRef = useRef(0);
|
const lastRunRef = useRef(0);
|
||||||
const fetcherRef = useRef(fetcher);
|
const fetcherRef = useRef(fetcher);
|
||||||
fetcherRef.current = fetcher;
|
fetcherRef.current = fetcher;
|
||||||
@@ -30,11 +31,13 @@ export function usePageData(
|
|||||||
if (!enabled || loadingRef.current) return;
|
if (!enabled || loadingRef.current) return;
|
||||||
if (!force && Date.now() - lastRunRef.current < throttleMs) return;
|
if (!force && Date.now() - lastRunRef.current < throttleMs) return;
|
||||||
loadingRef.current = true;
|
loadingRef.current = true;
|
||||||
|
setLoading(true);
|
||||||
lastRunRef.current = Date.now();
|
lastRunRef.current = Date.now();
|
||||||
try {
|
try {
|
||||||
await fetcherRef.current();
|
await fetcherRef.current();
|
||||||
} finally {
|
} finally {
|
||||||
loadingRef.current = false;
|
loadingRef.current = false;
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, [enabled, throttleMs]);
|
}, [enabled, throttleMs]);
|
||||||
|
|
||||||
@@ -49,11 +52,13 @@ export function usePageData(
|
|||||||
const refresh = useCallback(async () => {
|
const refresh = useCallback(async () => {
|
||||||
if (loadingRef.current) return;
|
if (loadingRef.current) return;
|
||||||
loadingRef.current = true;
|
loadingRef.current = true;
|
||||||
|
setLoading(true);
|
||||||
lastRunRef.current = Date.now();
|
lastRunRef.current = Date.now();
|
||||||
try {
|
try {
|
||||||
await fetcherRef.current();
|
await fetcherRef.current();
|
||||||
} finally {
|
} finally {
|
||||||
loadingRef.current = false;
|
loadingRef.current = false;
|
||||||
|
setLoading(false);
|
||||||
}
|
}
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
@@ -66,5 +71,5 @@ export function usePageData(
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
return { loading: loadingRef.current, refresh, trigger };
|
return { loading, refresh, trigger };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ const PRIVACY_CONTENT = `
|
|||||||
|
|
||||||
<h4>三、信息存储与保护</h4>
|
<h4>三、信息存储与保护</h4>
|
||||||
<p>1. 您的信息存储在中华人民共和国境内的安全服务器中</p>
|
<p>1. 您的信息存储在中华人民共和国境内的安全服务器中</p>
|
||||||
<p>2. 我们采用加密传输(HTTPS)和加密存储等安全措施</p>
|
<p>2. 我们采用加密传输(HTTPS)确保数据在传输过程中的安全</p>
|
||||||
<p>3. 严格的内部数据访问权限控制</p>
|
<p>3. 严格的内部数据访问权限控制</p>
|
||||||
<p>4. Token 等敏感凭证采用混淆加密存储</p>
|
<p>4. Token 等敏感凭证通过 HTTPS 加密传输,本地存储依赖微信小程序安全沙箱保护</p>
|
||||||
|
|
||||||
<h4>四、信息共享</h4>
|
<h4>四、信息共享</h4>
|
||||||
<p>未经您的同意,我们不会与任何第三方共享您的个人信息,以下情况除外:</p>
|
<p>未经您的同意,我们不会与任何第三方共享您的个人信息,以下情况除外:</p>
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
export const BP_RANGE = { min: 30, minMsg: '血压值不能低于30', max: 300, maxMsg: '血压值不能高于300', optional: true };
|
||||||
|
export const WEIGHT_RANGE = { min: 1, minMsg: '体重不能低于1kg', max: 500, maxMsg: '体重不能高于500kg', optional: true };
|
||||||
|
export const SUGAR_RANGE = { min: 0.1, minMsg: '血糖值不能低于0.1', max: 50, maxMsg: '血糖值不能高于50', optional: true };
|
||||||
|
export const VOLUME_RANGE = { min: 0, minMsg: '数值不能为负', max: 10000, maxMsg: '数值超出合理范围', optional: true };
|
||||||
|
|
||||||
|
export const REFERENCE_RANGES: Record<string, { min: number; max: number } | null> = {
|
||||||
|
systolic: { min: 90, max: 140 },
|
||||||
|
diastolic: { min: 60, max: 90 },
|
||||||
|
bloodSugar: { min: 3.9, max: 6.1 },
|
||||||
|
weight: null,
|
||||||
|
fluidIntake: null,
|
||||||
|
urineOutput: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AbnormalResult = { abnormal: boolean; direction: 'high' | 'low' | null };
|
||||||
|
|
||||||
|
export const checkAbnormal = (value: string, field: string): AbnormalResult => {
|
||||||
|
const ref = REFERENCE_RANGES[field];
|
||||||
|
if (!value || !ref) return { abnormal: false, direction: null };
|
||||||
|
const num = parseFloat(value);
|
||||||
|
if (isNaN(num)) return { abnormal: false, direction: null };
|
||||||
|
if (num > ref.max) return { abnormal: true, direction: 'high' };
|
||||||
|
if (num < ref.min) return { abnormal: true, direction: 'low' };
|
||||||
|
return { abnormal: false, direction: null };
|
||||||
|
};
|
||||||
|
|
||||||
|
export type SectionKey = 'morning' | 'evening' | 'other';
|
||||||
|
|
||||||
|
export const FIELD_LABELS: Record<string, string> = {
|
||||||
|
morningSystolic: '晨间收缩压',
|
||||||
|
morningDiastolic: '晨间舒张压',
|
||||||
|
eveningSystolic: '晚间收缩压',
|
||||||
|
eveningDiastolic: '晚间舒张压',
|
||||||
|
bloodSugar: '血糖',
|
||||||
|
};
|
||||||
|
|
||||||
|
export function formatDate(date: Date): string {
|
||||||
|
const y = date.getFullYear();
|
||||||
|
const m = String(date.getMonth() + 1).padStart(2, '0');
|
||||||
|
const d = String(date.getDate()).padStart(2, '0');
|
||||||
|
return `${y}-${m}-${d}`;
|
||||||
|
}
|
||||||
@@ -9,56 +9,14 @@ import { usePointsStore } from '@/stores/points';
|
|||||||
import { clearRequestCache } from '@/services/request';
|
import { clearRequestCache } from '@/services/request';
|
||||||
import { trackEvent } from '@/services/analytics';
|
import { trackEvent } from '@/services/analytics';
|
||||||
import { useSafeTimeout } from '@/hooks/useSafeTimeout';
|
import { useSafeTimeout } from '@/hooks/useSafeTimeout';
|
||||||
import { useElderClass } from '../../../hooks/useElderClass';
|
import { useElderClass } from '@/hooks/useElderClass';
|
||||||
|
import {
|
||||||
|
BP_RANGE, WEIGHT_RANGE, SUGAR_RANGE, VOLUME_RANGE,
|
||||||
|
checkAbnormal, formatDate, FIELD_LABELS,
|
||||||
|
type SectionKey, type AbnormalResult,
|
||||||
|
} from './constants';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
const BP_RANGE = { min: 30, minMsg: '血压值不能低于30', max: 300, maxMsg: '血压值不能高于300', optional: true };
|
|
||||||
const WEIGHT_RANGE = { min: 1, minMsg: '体重不能低于1kg', max: 500, maxMsg: '体重不能高于500kg', optional: true };
|
|
||||||
const SUGAR_RANGE = { min: 0.1, minMsg: '血糖值不能低于0.1', max: 50, maxMsg: '血糖值不能高于50', optional: true };
|
|
||||||
const VOLUME_RANGE = { min: 0, minMsg: '数值不能为负', max: 10000, maxMsg: '数值超出合理范围', optional: true };
|
|
||||||
|
|
||||||
function formatDate(date: Date): string {
|
|
||||||
const y = date.getFullYear();
|
|
||||||
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|
||||||
const d = String(date.getDate()).padStart(2, '0');
|
|
||||||
return `${y}-${m}-${d}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Abnormal value detection ──
|
|
||||||
|
|
||||||
const REFERENCE_RANGES: Record<string, { min: number; max: number } | null> = {
|
|
||||||
systolic: { min: 90, max: 140 },
|
|
||||||
diastolic: { min: 60, max: 90 },
|
|
||||||
bloodSugar: { min: 3.9, max: 6.1 },
|
|
||||||
weight: null,
|
|
||||||
fluidIntake: null,
|
|
||||||
urineOutput: null,
|
|
||||||
};
|
|
||||||
|
|
||||||
type AbnormalResult = { abnormal: boolean; direction: 'high' | 'low' | null };
|
|
||||||
|
|
||||||
const checkAbnormal = (value: string, field: string): AbnormalResult => {
|
|
||||||
const ref = REFERENCE_RANGES[field];
|
|
||||||
if (!value || !ref) return { abnormal: false, direction: null };
|
|
||||||
const num = parseFloat(value);
|
|
||||||
if (isNaN(num)) return { abnormal: false, direction: null };
|
|
||||||
if (num > ref.max) return { abnormal: true, direction: 'high' };
|
|
||||||
if (num < ref.min) return { abnormal: true, direction: 'low' };
|
|
||||||
return { abnormal: false, direction: null };
|
|
||||||
};
|
|
||||||
|
|
||||||
// ── Section state type ──
|
|
||||||
|
|
||||||
type SectionKey = 'morning' | 'evening' | 'other';
|
|
||||||
|
|
||||||
const FIELD_LABELS: Record<string, string> = {
|
|
||||||
morningSystolic: '晨间收缩压',
|
|
||||||
morningDiastolic: '晨间舒张压',
|
|
||||||
eveningSystolic: '晚间收缩压',
|
|
||||||
eveningDiastolic: '晚间舒张压',
|
|
||||||
bloodSugar: '血糖',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function DailyMonitoring() {
|
export default function DailyMonitoring() {
|
||||||
const modeClass = useElderClass();
|
const modeClass = useElderClass();
|
||||||
const currentPatient = useAuthStore((s) => s.currentPatient);
|
const currentPatient = useAuthStore((s) => s.currentPatient);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro, { useReachBottom } from '@tarojs/taro';
|
import Taro, { useReachBottom } from '@tarojs/taro';
|
||||||
import { usePageData } from '@/hooks/usePageData';
|
import { usePageData } from '@/hooks/usePageData';
|
||||||
|
import { getCachedPatientId } from '@/services/request';
|
||||||
import { listConsents, revokeConsent } from '@/services/consent';
|
import { listConsents, revokeConsent } from '@/services/consent';
|
||||||
import type { Consent } from '@/services/consent';
|
import type { Consent } from '@/services/consent';
|
||||||
import EmptyState from '@/components/EmptyState';
|
import EmptyState from '@/components/EmptyState';
|
||||||
@@ -33,7 +34,7 @@ export default function ConsentList() {
|
|||||||
const [hasPatient, setHasPatient] = useState(true);
|
const [hasPatient, setHasPatient] = useState(true);
|
||||||
|
|
||||||
const fetchData = useCallback(async (p: number, append = false) => {
|
const fetchData = useCallback(async (p: number, append = false) => {
|
||||||
const patientId = Taro.getStorageSync('current_patient_id') || '';
|
const patientId = getCachedPatientId();
|
||||||
if (!patientId) {
|
if (!patientId) {
|
||||||
setConsents([]);
|
setConsents([]);
|
||||||
setHasPatient(false);
|
setHasPatient(false);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro, { useReachBottom } from '@tarojs/taro';
|
import Taro, { useReachBottom } from '@tarojs/taro';
|
||||||
import { usePageData } from '@/hooks/usePageData';
|
import { usePageData } from '@/hooks/usePageData';
|
||||||
|
import { getCachedPatientId } from '@/services/request';
|
||||||
import { listDiagnoses, Diagnosis } from '../../../services/health-record';
|
import { listDiagnoses, Diagnosis } from '../../../services/health-record';
|
||||||
import EmptyState from '../../../components/EmptyState';
|
import EmptyState from '../../../components/EmptyState';
|
||||||
import Loading from '../../../components/Loading';
|
import Loading from '../../../components/Loading';
|
||||||
@@ -29,7 +30,7 @@ export default function Diagnoses() {
|
|||||||
const [hasPatient, setHasPatient] = useState(true);
|
const [hasPatient, setHasPatient] = useState(true);
|
||||||
|
|
||||||
const fetchData = useCallback(async (p: number, append = false) => {
|
const fetchData = useCallback(async (p: number, append = false) => {
|
||||||
const patientId = Taro.getStorageSync('current_patient_id') || '';
|
const patientId = getCachedPatientId();
|
||||||
if (!patientId) {
|
if (!patientId) {
|
||||||
setRecords([]);
|
setRecords([]);
|
||||||
setHasPatient(false);
|
setHasPatient(false);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro, { useReachBottom } from '@tarojs/taro';
|
import Taro, { useReachBottom } from '@tarojs/taro';
|
||||||
import { usePageData } from '@/hooks/usePageData';
|
import { usePageData } from '@/hooks/usePageData';
|
||||||
|
import { getCachedPatientId } from '@/services/request';
|
||||||
import { listDialysisPrescriptions } from '@/services/dialysis';
|
import { listDialysisPrescriptions } from '@/services/dialysis';
|
||||||
import type { DialysisPrescription } from '@/services/dialysis';
|
import type { DialysisPrescription } from '@/services/dialysis';
|
||||||
import EmptyState from '@/components/EmptyState';
|
import EmptyState from '@/components/EmptyState';
|
||||||
@@ -24,7 +25,7 @@ export default function DialysisPrescriptionList() {
|
|||||||
const [hasPatient, setHasPatient] = useState(true);
|
const [hasPatient, setHasPatient] = useState(true);
|
||||||
|
|
||||||
const fetchData = useCallback(async (p: number, append = false) => {
|
const fetchData = useCallback(async (p: number, append = false) => {
|
||||||
const patientId = Taro.getStorageSync('current_patient_id') || '';
|
const patientId = getCachedPatientId();
|
||||||
if (!patientId) {
|
if (!patientId) {
|
||||||
setPrescriptions([]);
|
setPrescriptions([]);
|
||||||
setHasPatient(false);
|
setHasPatient(false);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro, { useReachBottom } from '@tarojs/taro';
|
import Taro, { useReachBottom } from '@tarojs/taro';
|
||||||
import { usePageData } from '@/hooks/usePageData';
|
import { usePageData } from '@/hooks/usePageData';
|
||||||
|
import { getCachedPatientId } from '@/services/request';
|
||||||
import { listDialysisRecords } from '@/services/dialysis';
|
import { listDialysisRecords } from '@/services/dialysis';
|
||||||
import type { DialysisRecord } from '@/services/dialysis';
|
import type { DialysisRecord } from '@/services/dialysis';
|
||||||
import EmptyState from '@/components/EmptyState';
|
import EmptyState from '@/components/EmptyState';
|
||||||
@@ -30,7 +31,7 @@ export default function DialysisRecordList() {
|
|||||||
const [hasPatient, setHasPatient] = useState(true);
|
const [hasPatient, setHasPatient] = useState(true);
|
||||||
|
|
||||||
const fetchData = useCallback(async (p: number, append = false) => {
|
const fetchData = useCallback(async (p: number, append = false) => {
|
||||||
const patientId = Taro.getStorageSync('current_patient_id') || '';
|
const patientId = getCachedPatientId();
|
||||||
if (!patientId) {
|
if (!patientId) {
|
||||||
setRecords([]);
|
setRecords([]);
|
||||||
setHasPatient(false);
|
setHasPatient(false);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro, { useReachBottom } from '@tarojs/taro';
|
import Taro, { useReachBottom } from '@tarojs/taro';
|
||||||
import { usePageData } from '@/hooks/usePageData';
|
import { usePageData } from '@/hooks/usePageData';
|
||||||
|
import { getCachedPatientId } from '@/services/request';
|
||||||
import { listHealthRecords, HealthRecord } from '../../../services/health-record';
|
import { listHealthRecords, HealthRecord } from '../../../services/health-record';
|
||||||
import EmptyState from '../../../components/EmptyState';
|
import EmptyState from '../../../components/EmptyState';
|
||||||
import Loading from '../../../components/Loading';
|
import Loading from '../../../components/Loading';
|
||||||
@@ -23,7 +24,7 @@ export default function HealthRecords() {
|
|||||||
const [hasPatient, setHasPatient] = useState(true);
|
const [hasPatient, setHasPatient] = useState(true);
|
||||||
|
|
||||||
const fetchData = useCallback(async (p: number, append = false) => {
|
const fetchData = useCallback(async (p: number, append = false) => {
|
||||||
const patientId = Taro.getStorageSync('current_patient_id') || '';
|
const patientId = getCachedPatientId();
|
||||||
if (!patientId) {
|
if (!patientId) {
|
||||||
setRecords([]);
|
setRecords([]);
|
||||||
setHasPatient(false);
|
setHasPatient(false);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { View, Text, Input, Picker } from '@tarojs/components';
|
import { View, Text, Input, Picker } from '@tarojs/components';
|
||||||
import Taro from '@tarojs/taro';
|
import Taro from '@tarojs/taro';
|
||||||
import { usePageData } from '@/hooks/usePageData';
|
import { usePageData } from '@/hooks/usePageData';
|
||||||
|
import { getCachedPatientId } from '@/services/request';
|
||||||
import EmptyState from '../../../components/EmptyState';
|
import EmptyState from '../../../components/EmptyState';
|
||||||
import {
|
import {
|
||||||
listReminders,
|
listReminders,
|
||||||
@@ -69,7 +70,7 @@ export default function MedicationReminder() {
|
|||||||
Taro.showToast({ title: '请输入药品名称', icon: 'none' });
|
Taro.showToast({ title: '请输入药品名称', icon: 'none' });
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const patientId = Taro.getStorageSync('current_patient_id');
|
const patientId = getCachedPatientId();
|
||||||
if (!patientId) {
|
if (!patientId) {
|
||||||
Taro.showToast({ title: '请先绑定患者档案', icon: 'none' });
|
Taro.showToast({ title: '请先绑定患者档案', icon: 'none' });
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React, { useState, useCallback } from 'react';
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro, { useReachBottom } from '@tarojs/taro';
|
import Taro, { useReachBottom } from '@tarojs/taro';
|
||||||
import { usePageData } from '@/hooks/usePageData';
|
import { usePageData } from '@/hooks/usePageData';
|
||||||
|
import { getCachedPatientId } from '@/services/request';
|
||||||
import { listReports, LabReport } from '../../../services/report';
|
import { listReports, LabReport } from '../../../services/report';
|
||||||
import EmptyState from '../../../components/EmptyState';
|
import EmptyState from '../../../components/EmptyState';
|
||||||
import Loading from '../../../components/Loading';
|
import Loading from '../../../components/Loading';
|
||||||
@@ -17,7 +18,7 @@ export default function MyReports() {
|
|||||||
const [hasPatient, setHasPatient] = useState(true);
|
const [hasPatient, setHasPatient] = useState(true);
|
||||||
|
|
||||||
const fetchData = useCallback(async (p: number, append = false) => {
|
const fetchData = useCallback(async (p: number, append = false) => {
|
||||||
const patientId = Taro.getStorageSync('current_patient_id') || '';
|
const patientId = getCachedPatientId();
|
||||||
if (!patientId) {
|
if (!patientId) {
|
||||||
setReports([]);
|
setReports([]);
|
||||||
setHasPatient(false);
|
setHasPatient(false);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import React from 'react';
|
|||||||
import { View, Text } from '@tarojs/components';
|
import { View, Text } from '@tarojs/components';
|
||||||
import Taro from '@tarojs/taro';
|
import Taro from '@tarojs/taro';
|
||||||
import { useAuthStore } from '../../../stores/auth';
|
import { useAuthStore } from '../../../stores/auth';
|
||||||
|
import { invalidateHeadersCache, clearRequestCache } from '@/services/request';
|
||||||
import { useElderClass } from '../../../hooks/useElderClass';
|
import { useElderClass } from '../../../hooks/useElderClass';
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
@@ -35,6 +36,8 @@ export default function Settings() {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
clearRequestCache();
|
||||||
|
invalidateHeadersCache();
|
||||||
Taro.showToast({ title: '缓存已清除', icon: 'success' });
|
Taro.showToast({ title: '缓存已清除', icon: 'success' });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { api } from '../request';
|
import { api } from '../request';
|
||||||
|
import type { Appointment } from '../appointment';
|
||||||
|
|
||||||
// ── Appointments (doctor view) ─────────────────────
|
// ── Appointments (doctor view) ─────────────────────
|
||||||
|
|
||||||
@@ -7,6 +8,7 @@ export async function listAppointments(params?: {
|
|||||||
page_size?: number;
|
page_size?: number;
|
||||||
status?: string;
|
status?: string;
|
||||||
date?: string;
|
date?: string;
|
||||||
|
patient_id?: string;
|
||||||
}) {
|
}) {
|
||||||
return api.get<{ data: any[]; total: number }>('/health/appointments', params);
|
return api.get<{ data: Appointment[]; total: number }>('/health/appointments', params);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import Taro from '@tarojs/taro';
|
import { api, getCachedPatientId } from './request';
|
||||||
import { api } from './request';
|
|
||||||
|
|
||||||
export interface MedicationReminder {
|
export interface MedicationReminder {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -42,7 +41,7 @@ export interface UpdateReminderReq {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getPatientId(): string {
|
function getPatientId(): string {
|
||||||
return Taro.getStorageSync('current_patient_id') || '';
|
return getCachedPatientId();
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function listReminders() {
|
export async function listReminders() {
|
||||||
|
|||||||
@@ -307,6 +307,10 @@ export function setCachedPatientId(id: string): void {
|
|||||||
responseCache.setPatientId(id);
|
responseCache.setPatientId(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getCachedPatientId(): string {
|
||||||
|
return responseCache.getPatientId();
|
||||||
|
}
|
||||||
|
|
||||||
export function clearRequestCache(prefix?: string): void {
|
export function clearRequestCache(prefix?: string): void {
|
||||||
responseCache.clear(prefix);
|
responseCache.clear(prefix);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import Taro from '@tarojs/taro';
|
|
||||||
import * as healthApi from '@/services/health';
|
import * as healthApi from '@/services/health';
|
||||||
|
import { getCachedPatientId } from '@/services/request';
|
||||||
|
|
||||||
interface CachedTrend {
|
interface CachedTrend {
|
||||||
data: { date: string; value: number }[];
|
data: { date: string; value: number }[];
|
||||||
@@ -12,6 +12,7 @@ interface HealthState {
|
|||||||
todaySummaryFetchedAt: number;
|
todaySummaryFetchedAt: number;
|
||||||
trendData: Record<string, CachedTrend>;
|
trendData: Record<string, CachedTrend>;
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
|
_refreshingToday: boolean;
|
||||||
refreshToday: (force?: boolean) => Promise<void>;
|
refreshToday: (force?: boolean) => Promise<void>;
|
||||||
getTrend: (indicator: string, range: string) => Promise<{ date: string; value: number }[]>;
|
getTrend: (indicator: string, range: string) => Promise<{ date: string; value: number }[]>;
|
||||||
clearCache: () => void;
|
clearCache: () => void;
|
||||||
@@ -21,30 +22,26 @@ const CACHE_TTL = 5 * 60 * 1000;
|
|||||||
const TODAY_SUMMARY_TTL = 60_000;
|
const TODAY_SUMMARY_TTL = 60_000;
|
||||||
const MAX_TREND_KEYS = 20;
|
const MAX_TREND_KEYS = 20;
|
||||||
|
|
||||||
let refreshingToday = false;
|
|
||||||
|
|
||||||
export const useHealthStore = create<HealthState>((set, get) => ({
|
export const useHealthStore = create<HealthState>((set, get) => ({
|
||||||
todaySummary: null,
|
todaySummary: null,
|
||||||
todaySummaryFetchedAt: 0,
|
todaySummaryFetchedAt: 0,
|
||||||
trendData: {},
|
trendData: {},
|
||||||
loading: false,
|
loading: false,
|
||||||
|
_refreshingToday: false,
|
||||||
|
|
||||||
refreshToday: async (force = false) => {
|
refreshToday: async (force = false) => {
|
||||||
if (refreshingToday) return;
|
if (get()._refreshingToday) return;
|
||||||
const state = get();
|
const state = get();
|
||||||
if (!force && state.todaySummary && Date.now() - state.todaySummaryFetchedAt < TODAY_SUMMARY_TTL) {
|
if (!force && state.todaySummary && Date.now() - state.todaySummaryFetchedAt < TODAY_SUMMARY_TTL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
refreshingToday = true;
|
set({ _refreshingToday: true, loading: true });
|
||||||
set({ loading: true });
|
|
||||||
try {
|
try {
|
||||||
const patientId = Taro.getStorageSync('current_patient_id') || undefined;
|
const patientId = getCachedPatientId() || undefined;
|
||||||
const data = await healthApi.getTodaySummary(patientId);
|
const data = await healthApi.getTodaySummary(patientId);
|
||||||
set({ todaySummary: data, todaySummaryFetchedAt: Date.now(), loading: false });
|
set({ todaySummary: data, todaySummaryFetchedAt: Date.now(), loading: false, _refreshingToday: false });
|
||||||
} catch {
|
} catch {
|
||||||
set({ loading: false });
|
set({ loading: false, _refreshingToday: false });
|
||||||
} finally {
|
|
||||||
refreshingToday = false;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -74,5 +71,5 @@ export const useHealthStore = create<HealthState>((set, get) => ({
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
clearCache: () => set({ trendData: {}, todaySummary: null, todaySummaryFetchedAt: 0 }),
|
clearCache: () => set({ trendData: {}, todaySummary: null, todaySummaryFetchedAt: 0, _refreshingToday: false }),
|
||||||
}));
|
}));
|
||||||
|
|||||||
Reference in New Issue
Block a user