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);
}