From 42ccaa126511c68836e8cdb3a7297a6eb52e6c2a Mon Sep 17 00:00:00 2001 From: 15945162479 <15945162479@qq.com> Date: Sat, 13 Dec 2025 14:46:19 +0800 Subject: [PATCH] Add File --- src/family/pages/CarePlan.tsx | 779 ++++++++++++++++++++++++++++++++++ 1 file changed, 779 insertions(+) create mode 100644 src/family/pages/CarePlan.tsx diff --git a/src/family/pages/CarePlan.tsx b/src/family/pages/CarePlan.tsx new file mode 100644 index 0000000..8053294 --- /dev/null +++ b/src/family/pages/CarePlan.tsx @@ -0,0 +1,779 @@ +import React, { useState, useEffect } from 'react'; +import { + Plus, + Edit2, + Trash2, + X, + Clock, + AlertCircle, + Pill, + Heart, + Activity, + Utensils, + Droplets, + Moon, +} from 'lucide-react'; +import * as scheduleService from '../services/scheduleService'; +import { Toast, ToastType } from '../components/Toast'; +import { ConfirmDialog } from '../components/ConfirmDialog'; + +type CareType = 'medication' | 'exercise' | 'meal' | 'hydration' | 'sleep' | 'checkup' | 'other'; + +interface CareTask { + id: string; + type: CareType; + name: string; + description?: string; + times: string[]; + active: boolean; + autoRemind: boolean; // 数字人自动提醒 + status?: 'pending' | 'completed' | 'skipped' | 'missed'; + repeatType?: 'once' | 'daily' | 'weekly' | 'monthly'; + // 用药专用字段 + dosage?: string; + route?: string; + withFood?: boolean; + gracePeriod?: number; +} + +/** + * 家属端护理计划界面 + * 管理老人的用药、饮食、运动等护理任务 + */ +export const CarePlan: React.FC = () => { + const [activeTab, setActiveTab] = useState<'all' | CareType>('all'); + const [showForm, setShowForm] = useState(false); + const [editingTask, setEditingTask] = useState(null); + const [formType, setFormType] = useState('medication'); + const [schedules, setSchedules] = useState([]); + const [loading, setLoading] = useState(true); + const [apiConnected, setApiConnected] = useState(false); + const [formTitle, setFormTitle] = useState(''); + const [formDescription, setFormDescription] = useState(''); + const [formTime, setFormTime] = useState('08:00'); + const [formRepeatDaily, setFormRepeatDaily] = useState(true); + const [formAutoRemind, setFormAutoRemind] = useState(true); + const [toast, setToast] = useState<{ message: string; type: ToastType } | null>(null); + const [confirmDialog, setConfirmDialog] = useState<{ + message: string; + onConfirm: () => void; + } | null>(null); + const familyId = 'family_001'; // 实际使用时从用户上下文获取 + + // 加载日程数据 + useEffect(() => { + loadSchedules(); + }, []); + + const loadSchedules = async () => { + try { + setLoading(true); + console.log('开始加载日程,family_id:', familyId); + const data = await scheduleService.getFamilySchedules(familyId); + console.log('加载到的日程数据:', data); + setSchedules(data); + setApiConnected(true); // 标记 API 已连接 + console.log('API 已连接,日程数量:', data.length); + } catch (error) { + console.error('加载日程失败:', error); + setApiConnected(false); + // 只在第一次加载失败时提示 + if (!apiConnected) { + setToast({ + message: '加载日程失败,请检查服务器是否已启动', + type: 'warning', + }); + } + } finally { + setLoading(false); + } + }; + + // 将 Schedule 转换为 CareTask 格式(用于兼容现有 UI) + const convertScheduleToTask = (schedule: scheduleService.Schedule): CareTask => { + // 安全地提取时间部分 + let time = '00:00'; + try { + const dateTime = scheduleService.formatDateTime(new Date(schedule.schedule_time)); + const parts = dateTime.split(' '); + if (parts.length >= 2) { + time = parts[1].slice(0, 5); + } + } catch (error) { + console.error('时间格式转换失败:', schedule.schedule_time, error); + } + + const autoRemind = schedule.auto_remind === 1; + console.log(`转换日程 "${schedule.title}": auto_remind=${schedule.auto_remind} → autoRemind=${autoRemind}`); + + return { + id: schedule.id?.toString() || '', + type: (schedule.schedule_type || 'other') as CareType, + name: schedule.title, + description: schedule.description, + times: [time], + active: schedule.is_active === 1, + autoRemind: autoRemind, + status: schedule.status || 'pending', + repeatType: schedule.repeat_type || 'once', + }; + }; + + // 模拟数据已移除 - 始终使用真实API数据 + + const careTypeConfig = { + medication: { label: '用药', icon: Pill, color: 'blue' }, + exercise: { label: '运动', icon: Activity, color: 'green' }, + meal: { label: '饮食', icon: Utensils, color: 'orange' }, + hydration: { label: '饮水', icon: Droplets, color: 'cyan' }, + sleep: { label: '睡眠', icon: Moon, color: 'purple' }, + checkup: { label: '检查', icon: Heart, color: 'red' }, + other: { label: '其他', icon: Clock, color: 'gray' }, + }; + + // 根据护理类型获取提示词 + const getPlaceholders = (type: CareType) => { + const placeholders: Record = { + medication: { + title: '例如:氯沙坦、阿司匹林', + description: '例如:饭后服用,每日一次,50mg' + }, + exercise: { + title: '例如:晨间散步、太极拳', + description: '例如:小区公园散步30分钟,注意安全' + }, + meal: { + title: '例如:早餐、午餐、晚餐', + description: '例如:低盐低糖,营养均衡,多吃蔬菜' + }, + hydration: { + title: '例如:饮水提醒', + description: '例如:每次200ml左右,温开水为宜' + }, + sleep: { + title: '例如:午休、就寝', + description: '例如:30-60分钟,保证充足睡眠' + }, + checkup: { + title: '例如:血压测量、血糖检测', + description: '例如:记录数据并上传,如有异常及时联系医生' + }, + other: { + title: '例如:阅读时光、听音乐', + description: '例如:详细描述此活动的具体内容和注意事项' + } + }; + return placeholders[type]; + }; + + const getColorClasses = (color: string) => { + const colors = { + blue: 'bg-blue-50 text-blue-700 border-blue-200', + green: 'bg-green-50 text-green-700 border-green-200', + orange: 'bg-orange-50 text-orange-700 border-orange-200', + cyan: 'bg-cyan-50 text-cyan-700 border-cyan-200', + purple: 'bg-purple-50 text-purple-700 border-purple-200', + red: 'bg-red-50 text-red-700 border-red-200', + gray: 'bg-gray-50 text-gray-700 border-gray-200', + }; + return colors[color as keyof typeof colors] || colors.gray; + }; + + // 始终使用 API 数据 + let careTasks: CareTask[] = []; + try { + careTasks = schedules.map(convertScheduleToTask); + } catch (error) { + console.error('转换日程数据失败:', error); + careTasks = []; + } + + const filteredTasks = + activeTab === 'all' + ? careTasks + : careTasks.filter((task) => task.type === activeTab); + + const handleEdit = (task: CareTask) => { + setEditingTask(task); + setFormType(task.type); + setFormTitle(task.name); + setFormDescription(task.description || ''); + // 设置时间:从 times 数组中取第一个时间 + setFormTime(task.times && task.times.length > 0 ? task.times[0] : '08:00'); + // 设置重复:daily 表示每日重复,once 表示不重复 + setFormRepeatDaily(task.repeatType === 'daily'); + // 设置自动提醒:明确使用布尔值,undefined 时默认为 true + const autoRemindValue = task.autoRemind === undefined ? true : task.autoRemind; + setFormAutoRemind(autoRemindValue); + console.log('编辑任务,task.autoRemind:', task.autoRemind, '最终设置为:', autoRemindValue); + setShowForm(true); + }; + + const handleDelete = async (taskId: string) => { + setConfirmDialog({ + message: '确定要删除这个护理计划吗?删除后无法恢复。', + onConfirm: async () => { + try { + console.log('正在删除日程 ID:', taskId); + const success = await scheduleService.deleteSchedule(Number(taskId)); + console.log('删除结果:', success); + + // 重新加载列表 + await loadSchedules(); + console.log('重新加载后的日程数量:', schedules.length); + + setToast({ message: '删除成功', type: 'success' }); + } catch (error) { + console.error('删除失败:', error); + setToast({ message: '删除失败,请重试', type: 'error' }); + } + }, + }); + }; + + const handleAddNew = (type: CareType) => { + setEditingTask(null); + setFormType(type); + setFormTitle(''); + setFormDescription(''); + setFormTime('08:00'); + setFormRepeatDaily(true); + setFormAutoRemind(true); + setShowForm(true); + }; + + // 显示加载状态 + if (loading && schedules.length === 0 && !apiConnected) { + return ( +
+
+
+

正在加载护理计划...

+
+
+ ); + } + + return ( +
+ {/* 顶部导航 */} +
+
+
+

护理计划

+ +
+ + {/* 标签栏 */} +
+ + {(Object.entries(careTypeConfig) as [CareType, any][]).map( + ([type, config]) => { + const count = careTasks.filter((t) => t.type === type).length; + const Icon = config.icon; + return ( + + ); + } + )} +
+
+
+ + {/* 主要内容区 - 手机优化列表 */} +
+
+ {filteredTasks.map((task) => { + const config = careTypeConfig[task.type]; + const Icon = config.icon; + + return ( +
+ {/* 头部 */} +
+
+
+ +
+
+
+

+ {task.name} +

+ {/* 日程状态标签 */} + {task.status === 'pending' && ( + + ⏳ 待执行 + + )} + {task.status === 'completed' && ( + + ✓ 已完成 + + )} + {task.status === 'skipped' && ( + + ○ 已忽略 + + )} + {task.status === 'missed' && ( + + ⚠ 已错过 + + )} + {/* 如果没有状态,默认显示待执行 */} + {!task.status && ( + + ⏳ 待执行 + + )} + {task.autoRemind && ( + + 🤖 自动提醒 + + )} + {task.repeatType === 'daily' && ( + + 🔄 每日重复 + + )} +
+ + {/* 用药专用信息 */} + {task.type === 'medication' && ( +
+ {task.dosage} · {task.route} + {task.withFood && ' · 随餐'} +
+ )} + + {/* 其他描述 */} + {task.description && ( +

+ {task.description} +

+ )} + + {/* 时间标签 */} +
+ {task.times.map((time, index) => ( + + + {time} + + ))} +
+ + {/* 用药宽限期 */} + {task.type === 'medication' && task.gracePeriod && ( +
+ 宽限期:{task.gracePeriod} 分钟 +
+ )} +
+
+ + {/* 操作按钮 */} +
+ + +
+
+
+ ); + })} +
+ + {/* 空状态 */} + {filteredTasks.length === 0 && ( +
+

暂无护理计划

+ +
+ )} + + {/* 提示信息 */} +
+
+ +
+

护理计划提示

+
    +
  • 护理计划会在设定时间提醒老人执行
  • +
  • 用药计划超时未确认将自动发送通知给家属
  • +
  • 如需调整护理方案请咨询专业医护人员
  • +
+
+
+
+
+ + {/* 编辑/添加表单弹窗 */} + {showForm && ( +
+
+ {/* 头部 */} +
+

+ {editingTask ? '编辑' : '添加'} + {careTypeConfig[formType]?.label || ''}计划 +

+ +
+ + {/* 表单内容 - 可滚动 */} +
+ {/* 类型选择 */} + {!editingTask && ( +
+ +
+ {(Object.entries(careTypeConfig) as [CareType, any][]).map( + ([type, config]) => { + const Icon = config.icon; + return ( + + ); + } + )} +
+
+ )} + + {/* 名称 */} +
+ + setFormTitle(e.target.value)} + className="w-full px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-primary-500 focus:border-transparent" + /> +
+ + {/* 用药专用字段 */} + {formType === 'medication' && ( + <> +
+ + +
+ +
+ + +
+ +
+ +
+ +
+ + +
+ + )} + + {/* 非用药的描述字段 */} + {formType !== 'medication' && ( +
+ +