diff --git a/apps/miniprogram/src/pages/consultation/create/index.scss b/apps/miniprogram/src/pages/consultation/create/index.scss index 5a3f48a..7d0a754 100644 --- a/apps/miniprogram/src/pages/consultation/create/index.scss +++ b/apps/miniprogram/src/pages/consultation/create/index.scss @@ -34,6 +34,17 @@ color: $tx3; } + &__textarea { + width: 100%; + min-height: 120px; + padding: var(--tk-gap-md); + font-size: var(--tk-font-body); + background: $card; + border: 1px solid $bd; + border-radius: $r-sm; + box-sizing: border-box; + } + &__hint { margin: var(--tk-gap-xl) 0; padding: var(--tk-gap-md); diff --git a/apps/miniprogram/src/pages/consultation/create/index.tsx b/apps/miniprogram/src/pages/consultation/create/index.tsx index 8957373..e552b32 100644 --- a/apps/miniprogram/src/pages/consultation/create/index.tsx +++ b/apps/miniprogram/src/pages/consultation/create/index.tsx @@ -27,6 +27,7 @@ export default function ConsultationCreate() { const [typeIdx, setTypeIdx] = useState(0); const [submitting, setSubmitting] = useState(false); const [doctorsLoaded, setDoctorsLoaded] = useState(false); + const [description, setDescription] = useState(''); const modeClass = useElderClass(); const loadDoctors = async () => { @@ -48,12 +49,24 @@ export default function ConsultationCreate() { return; } if (submitting) return; + + if (!description.trim()) { + const { confirm } = await Taro.showModal({ + title: '提示', + content: '建议描述症状以便医生更快响应,是否继续?', + confirmText: '继续提交', + cancelText: '去填写', + }); + if (!confirm) return; + } + setSubmitting(true); try { const session = await createSession({ patient_id: currentPatient.id, doctor_id: selectedDoctorIdx >= 0 ? doctorList[selectedDoctorIdx]?.id : undefined, consultation_type: CONSULTATION_TYPES[typeIdx], + description: description.trim() || undefined, }); Taro.showToast({ title: '创建成功', icon: 'success' }); setTimeout(() => { @@ -109,6 +122,18 @@ export default function ConsultationCreate() { + + 症状描述(建议填写) + setDescription(e.detail.value)} + maxlength={500} + /> + + 咨询创建后,医生将尽快回复您的消息。 diff --git a/apps/miniprogram/src/pages/health/index.tsx b/apps/miniprogram/src/pages/health/index.tsx index 8fb17c9..1e3760b 100644 --- a/apps/miniprogram/src/pages/health/index.tsx +++ b/apps/miniprogram/src/pages/health/index.tsx @@ -5,6 +5,7 @@ import { safeNavigateTo } from '@/utils/navigate'; import { useAuthStore } from '../../stores/auth'; import { useElderClass } from '../../hooks/useElderClass'; import { findThreshold, inputVitalSign, type HealthThreshold } from '../../services/health'; +import { validateNum } from '../../utils/validate'; import Loading from '../../components/Loading'; import ErrorState from '../../components/ErrorState'; import GuestGuard from '../../components/GuestGuard'; @@ -113,6 +114,10 @@ export default function Health() { const sys = parseFloat(systolic); const dia = parseFloat(diastolic); if (!sys || !dia) { Taro.showToast({ title: '请填写完整', icon: 'none' }); return; } + const sysErr = validateNum(sys, '收缩压', { min: 60, max: 250 }); + if (sysErr) { Taro.showToast({ title: sysErr, icon: 'none' }); return; } + const diaErr = validateNum(dia, '舒张压', { min: 40, max: 150 }); + if (diaErr) { Taro.showToast({ title: diaErr, icon: 'none' }); return; } await inputVitalSign(patientId, { indicator_type: 'blood_pressure', value: sys, @@ -125,6 +130,8 @@ export default function Health() { case 'heart_rate': { const val = parseFloat(heartRateVal); if (!val) { Taro.showToast({ title: '请填写心率', icon: 'none' }); return; } + const err = validateNum(val, '心率', { min: 30, max: 220 }); + if (err) { Taro.showToast({ title: err, icon: 'none' }); return; } await inputVitalSign(patientId, { indicator_type: 'heart_rate', value: val }); setHeartRateVal(''); break; @@ -132,6 +139,8 @@ export default function Health() { case 'blood_sugar': { const val = parseFloat(sugarVal); if (!val) { Taro.showToast({ title: '请填写血糖值', icon: 'none' }); return; } + const err = validateNum(val, '血糖', { min: 1.0, max: 33.3 }); + if (err) { Taro.showToast({ title: err, icon: 'none' }); return; } const bsType = sugarPeriod === 'fasting' ? 'blood_sugar_fasting' : 'blood_sugar_postprandial'; await inputVitalSign(patientId, { indicator_type: bsType, value: val }); setSugarVal(''); @@ -140,6 +149,8 @@ export default function Health() { case 'weight': { const val = parseFloat(weightVal); if (!val) { Taro.showToast({ title: '请填写体重', icon: 'none' }); return; } + const err = validateNum(val, '体重', { min: 20, max: 300 }); + if (err) { Taro.showToast({ title: err, icon: 'none' }); return; } await inputVitalSign(patientId, { indicator_type: 'weight', value: val }); setWeightVal(''); break; diff --git a/apps/miniprogram/src/pages/index/index.tsx b/apps/miniprogram/src/pages/index/index.tsx index 1cbe4f3..f506b53 100644 --- a/apps/miniprogram/src/pages/index/index.tsx +++ b/apps/miniprogram/src/pages/index/index.tsx @@ -316,7 +316,7 @@ export default function Index() { // 医护人员访问患者首页时,自动跳转到医生端 // 不渲染 HomeDashboard,避免触发患者首页的 API 请求(并发叠加问题) - const shouldRedirect = user && isMedicalStaff(); + const shouldRedirect = !!(user && isMedicalStaff()); useDidShow(() => { if (shouldRedirect) { @@ -329,11 +329,10 @@ export default function Index() { } }); - if (!user) { - return ; - } - if (shouldRedirect) { - return ; - } + // 未登录 → 访客首页 + if (!user) return ; + // 医护人员 → 等待跳转(返回 null 避免无用渲染) + if (shouldRedirect) return null; + // 患者用户 → 正常首页 return ; } diff --git a/apps/miniprogram/src/pages/mall/index.tsx b/apps/miniprogram/src/pages/mall/index.tsx index 48864cb..f378a3d 100644 --- a/apps/miniprogram/src/pages/mall/index.tsx +++ b/apps/miniprogram/src/pages/mall/index.tsx @@ -184,12 +184,7 @@ export default function Mall() { 签到打卡 - - - - - 积分任务 - + {/* TODO: 积分任务功能待实现后恢复 */} safeNavigateTo('/pages/pkg-mall/orders/index')}> diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/index.scss b/apps/miniprogram/src/pages/pkg-doctor-core/index.scss index 03e198f..7c2a113 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/index.scss +++ b/apps/miniprogram/src/pages/pkg-doctor-core/index.scss @@ -83,4 +83,30 @@ justify-content: space-between; margin-bottom: 16px; } + + // ── 错误状态 ── + &__error { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 120px 32px; + gap: 24px; + } + + &__error-text { + font-size: var(--tk-body-lg); + color: var(--tk-text-secondary); + } + + &__error-retry { + padding: 16px 48px; + background: var(--tk-primary); + border-radius: 12px; + } + + &__error-retry-text { + font-size: var(--tk-body); + color: #fff; + } } diff --git a/apps/miniprogram/src/pages/pkg-doctor-core/index.tsx b/apps/miniprogram/src/pages/pkg-doctor-core/index.tsx index a99fcb4..b75fa37 100644 --- a/apps/miniprogram/src/pages/pkg-doctor-core/index.tsx +++ b/apps/miniprogram/src/pages/pkg-doctor-core/index.tsx @@ -45,6 +45,7 @@ export default function DoctorHome() { const modeClass = useDoctorClass(); const [dashboard, setDashboard] = useState(null); const [loading, setLoading] = useState(true); + const [loadError, setLoadError] = useState(false); const hasRole = (allowed: string[] | undefined) => { if (!allowed) return true; @@ -57,8 +58,10 @@ export default function DoctorHome() { try { const data = await getDashboard(); setDashboard(data); + setLoadError(false); } catch (err) { console.warn('[doctor] 加载工作台数据失败:', err); + setLoadError(true); } finally { setLoading(false); } @@ -78,6 +81,19 @@ export default function DoctorHome() { if (loading) return ; + if (loadError && !dashboard) { + return ( + + + 加载失败 + { setLoading(true); loadDashboard(); }}> + 点击重试 + + + + ); + } + return ( diff --git a/apps/miniprogram/src/services/consultation.ts b/apps/miniprogram/src/services/consultation.ts index c21a3c3..1bf2fd6 100644 --- a/apps/miniprogram/src/services/consultation.ts +++ b/apps/miniprogram/src/services/consultation.ts @@ -79,6 +79,7 @@ export async function createSession(params: { patient_id: string; doctor_id?: string; consultation_type?: string; + description?: string; }) { return api.post('/health/consultation-sessions', params); }