import { useState } from 'react'; import { View, Text, Input, Picker } from '@tarojs/components'; import Taro from '@tarojs/taro'; import { z } from 'zod'; import { inputVitalSign } from '../../../services/health'; import { useAuthStore } from '../../../stores/auth'; import { useHealthStore } from '@/stores/health'; import { trackEvent } from '@/services/analytics'; import './index.scss'; const INDICATORS = [ { value: 'blood_pressure', label: '血压 (mmHg)' }, { value: 'heart_rate', label: '心率 (bpm)' }, { value: 'blood_sugar_fasting', label: '空腹血糖 (mmol/L)' }, { value: 'blood_sugar_postprandial', label: '餐后血糖 (mmol/L)' }, { value: 'weight', label: '体重 (kg)' }, { value: 'temperature', label: '体温 (℃)' }, ]; const vitalSignSchema = z.object({ indicator_type: z.enum(['blood_pressure', 'heart_rate', 'blood_sugar_fasting', 'blood_sugar_postprandial', 'weight', 'temperature']), value: z.number().positive({ message: '请输入有效数值' }), extra: z.object({ systolic: z.number().min(60, '收缩压过低').max(250, '收缩压过高,请及时就医').optional(), diastolic: z.number().min(40, '舒张压过低').max(150, '舒张压过高,请及时就医').optional(), }).optional(), note: z.string().max(200, '备注不能超过200字').optional(), }); const WARN_THRESHOLDS: Record = { blood_pressure: { max: 180, warning: '收缩压偏高,建议及时就医' }, heart_rate: { max: 120, min: 50, warning: '心率异常,请注意休息' }, blood_sugar_fasting: { max: 11.0, warning: '血糖偏高,建议就医检查' }, }; export default function HealthInput() { const [indicatorIdx, setIndicatorIdx] = useState(0); const [value, setValue] = useState(''); const [systolic, setSystolic] = useState(''); const [diastolic, setDiastolic] = useState(''); const [note, setNote] = useState(''); const [submitting, setSubmitting] = useState(false); const { currentPatient } = useAuthStore(); const { clearCache } = useHealthStore(); const handleSubmit = async () => { if (!currentPatient) { Taro.showToast({ title: '请先选择就诊人', icon: 'none' }); return; } const currentIndicator = INDICATORS[indicatorIdx].value; const input = currentIndicator === 'blood_pressure' ? { indicator_type: 'blood_pressure' as const, value: parseFloat(systolic), extra: { systolic: parseFloat(systolic), diastolic: parseFloat(diastolic) } } : { indicator_type: currentIndicator as any, value: parseFloat(value) }; const result = vitalSignSchema.safeParse(input); if (!result.success) { Taro.showToast({ title: result.error.errors[0].message, icon: 'none' }); return; } const threshold = WARN_THRESHOLDS[currentIndicator]; if (threshold) { const val = input.value; if ((threshold.max && val > threshold.max) || (threshold.min && val < threshold.min)) { await Taro.showModal({ title: '健康提示', content: threshold.warning, showCancel: false }); } } setSubmitting(true); try { await inputVitalSign(currentPatient.id, { ...input, note: note || undefined, }); clearCache(); Taro.showToast({ title: '录入成功', icon: 'success' }); trackEvent('health_data_input', { type: indicatorType }); setTimeout(() => Taro.navigateBack(), 1000); } catch (e: unknown) { const msg = e instanceof Error ? e.message : '录入失败'; Taro.showToast({ title: msg, icon: 'none' }); } finally { setSubmitting(false); } }; return ( 指标类型 i.label)} value={indicatorIdx} onChange={(e) => setIndicatorIdx(Number(e.detail.value))} > {INDICATORS[indicatorIdx].label} {INDICATORS[indicatorIdx].value === 'blood_pressure' ? ( <> 收缩压 setSystolic(e.detail.value)} /> 舒张压 setDiastolic(e.detail.value)} /> ) : ( 数值 setValue(e.detail.value)} /> )} 备注(可选) setNote(e.detail.value)} /> {submitting ? '提交中...' : '提交'} ); }