import { View, Text, Swiper, SwiperItem, Image } from '@tarojs/components'; import { useState } from 'react'; import Taro, { useDidShow, useDidHide } from '@tarojs/taro'; import { safeNavigateTo } from '@/utils/navigate'; import { useAuthStore } from '../../stores/auth'; import { useUIStore } from '../../stores/ui'; import { navigateToLogin } from '../../utils/navigate'; import { usePageData } from '@/hooks/usePageData'; import { useThrottledDidShow } from '@/hooks/useThrottledDidShow'; import { api } from '@/services/request'; import type { Article } from '@/services/article'; import ProgressRing from '@/components/ui/ProgressRing'; import Loading from '../../components/Loading'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; import { useHomeData, type ReminderItem } from './useHomeData'; import './index.scss'; interface PublicBanner { id: string; title?: string; subtitle?: string; desc?: string; image_url?: string; link_type?: string; link_target?: string; } const FALLBACK_SLIDES = [ { id: 'slide-1', title: '专业血透中心', desc: '三甲级医护团队全程守护', image_url: '' }, { id: 'slide-2', title: '智慧健康管理', desc: 'AI 驱动个性化健康方案', image_url: '' }, { id: 'slide-3', title: '温馨就医环境', desc: '舒适安全的治疗体验', image_url: '' }, ]; // ─── 访客首页 ─── function GuestHome({ modeClass }: { modeClass: string }) { const [banners, setBanners] = useState([]); const [articles, setArticles] = useState([]); const [swiperAutoplay, setSwiperAutoplay] = useState(false); useDidShow(() => { setSwiperAutoplay(true); }); useDidHide(() => { setSwiperAutoplay(false); }); const loadPublicData = async () => { let tenantId = Taro.getStorageSync('tenant_id'); if (!tenantId) { tenantId = process.env.TARO_APP_DEFAULT_TENANT_ID || ''; } if (!tenantId) { setBanners(FALLBACK_SLIDES); return; } try { const [bannerData, articleData] = await Promise.allSettled([ api.get('/public/banners', { tenant_id: tenantId }, 300_000), api.get<{ data: Article[]; total: number }>('/public/articles', { tenant_id: tenantId, page_size: 4, }, 300_000), ]); if (bannerData.status === 'fulfilled' && bannerData.value?.length > 0) { const apiBase = process.env.TARO_APP_API_URL || 'http://localhost:3000/api/v1'; const resolved = bannerData.value.map((b) => { if (!b.image_url) return b; const fullUrl = b.image_url.startsWith('http') ? b.image_url : `${apiBase}${b.image_url}`; return { ...b, image_url: fullUrl }; }); setBanners(resolved); } else { setBanners(FALLBACK_SLIDES); } if (articleData.status === 'fulfilled' && articleData.value?.data?.length > 0) { setArticles(articleData.value.data); } } catch (err) { console.warn('[home] 加载首页数据失败:', err); setBanners(FALLBACK_SLIDES); Taro.showToast({ title: '内容加载失败', icon: 'none' }); } }; usePageData(loadPublicData, { throttleMs: 10_000, enablePullDown: true, enabled: true }); const slides = banners.length > 0 ? banners : FALLBACK_SLIDES; const ARTICLE_ICONS = ['♥', '◇', '✦']; const ARTICLE_COLORS = ['pri', 'acc', 'wrn'] as const; const formatDate = (dateStr?: string) => { if (!dateStr) return ''; const d = new Date(dateStr); return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`; }; return ( {slides.map((slide, idx) => ( {(slide.image_url) ? ( ) : ( )} {slide.title} {slide.subtitle || slide.desc} ))} {/* 健康资讯 */} 健康资讯 safeNavigateTo('/pages/article/index')} > 查看全部 › {articles.length > 0 ? ( {articles.map((article, i) => ( safeNavigateTo(`/pages/article/detail/index?id=${article.id}`)} > {ARTICLE_ICONS[i % 3]} {article.title} {formatDate(article.published_at)} ))} ) : ( 健康数据管理 记录并追踪您的体征数据 积分商城 签到赚积分,好礼兑不停 专业科普文章 权威健康知识推送 )} {/* 底部注册引导 */} 加入我们 注册后即可使用签到、积分商城等全部功能 注册 / 登录 ); } // ─── 登录后首页 ─── function SOSButton() { const user = useAuthStore((s) => s.user); const isMedicalStaff = useAuthStore((s) => s.isMedicalStaff); const currentPatient = useAuthStore((s) => s.currentPatient); if (!user || !currentPatient || isMedicalStaff()) return null; const handleSOS = async () => { const { confirm } = await Taro.showModal({ title: '紧急求助', content: '是否拨打急救电话?', confirmText: '拨打', cancelText: '取消', }); if (confirm) { Taro.makePhoneCall({ phoneNumber: '120', fail: () => Taro.showToast({ title: '拨号失败', icon: 'none' }), }); } }; return ( SOS ); } function HomeDashboard({ modeClass }: { modeClass: string }) { const { healthItems, indicatorCapsules, completedCount, progressPercent, loading, todaySummary, reminders, remindersLoading, unreadCount, greeting, displayName, } = useHomeData(); const getStatusTag = (status?: string) => { if (status === 'high' || status === 'low') return { label: status === 'high' ? '偏高' : '偏低', cls: 'tag-warn' }; if (status === 'normal') return { label: '正常', cls: 'tag-ok' }; return null; }; return ( {greeting},{displayName} {new Date().toLocaleDateString('zh-CN', { month: 'long', day: 'numeric', weekday: 'short' })} Taro.switchTab({ url: '/pages/messages/index' })}> {unreadCount > 0 && } Taro.switchTab({ url: '/pages/health/index' })}> {completedCount === 4 ? '今日体征已全部记录' : completedCount === 0 ? '今日尚未记录体征' : `今日已记录 ${completedCount}/4 项`} {indicatorCapsules.map((cap) => ( {cap.done ? '✓ ' : ''}{cap.label} ))} 今日体征 {loading && !todaySummary ? ( ) : ( {healthItems.map((item) => { const tag = getStatusTag(item.status); return ( safeNavigateTo(`/pages/pkg-health/trend/index?indicator=${item.indicator}`)} activeFeedback="opacity" > {item.label} {item.value} {item.unit} {tag && {tag.label}} {!item.status && 未记录} ); })} )} {!remindersLoading && reminders.length > 0 && ( 智能提醒 {reminders.length} 条待处理 {reminders.map((r, i) => ( 0 ? 'reminder-item-border' : ''}`} onClick={() => { if (r.type === 'appointment') safeNavigateTo('/pages/appointment/index'); else if (r.type === 'followup') safeNavigateTo(`/pages/pkg-profile/followups/detail/index?id=${r.id}`); }} > {r.tag} {r.text} ))} )} Taro.switchTab({ url: '/pages/health/index' })}> 记录体征 safeNavigateTo('/pages/appointment/create/index')}> 预约挂号 ); } // ─── 首页入口 ─── export default function Index() { const user = useAuthStore((s) => s.user); const isMedicalStaff = useAuthStore((s) => s.isMedicalStaff); const mode = useUIStore((s) => s.mode); const modeClass = mode === 'elder' ? 'elder-mode' : ''; // 医护人员访问患者首页时,自动跳转到医生端 // 不渲染 HomeDashboard,避免触发患者首页的 API 请求(并发叠加问题) const shouldRedirect = !!(user && isMedicalStaff()); useDidShow(() => { if (shouldRedirect) { Taro.reLaunch({ url: '/pages/pkg-doctor-core/index', fail: () => { console.warn('跳转医生端失败,停留患者首页'); }, }); } }); // 未登录 → 访客首页 if (!user) return ; // 医护人员 → 等待跳转(返回 null 避免无用渲染) if (shouldRedirect) return null; // 患者用户 → 正常首页 return ; }