From ddf5c196e4d430e558317c3ddd28b768e80c84c8 Mon Sep 17 00:00:00 2001 From: iven Date: Wed, 27 May 2026 19:37:25 +0800 Subject: [PATCH] =?UTF-8?q?fix(mp):=20=E5=81=A5=E5=BA=B7=E9=A1=B5=E6=BB=9A?= =?UTF-8?q?=E5=8A=A8=E5=8D=A1=E6=AD=BB=20+=20=E6=96=87=E7=AB=A0=E6=A0=B7?= =?UTF-8?q?=E5=BC=8F=E4=B8=A2=E5=A4=B1=20=E2=80=94=20ScrollView=20height:0?= =?UTF-8?q?=20=E4=BF=AE=E5=A4=8D=20+=20RichArticle=2018=20=E6=9D=A1=20tag-?= =?UTF-8?q?style=20=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 健康页:移除冗余"健康"标题栏,ScrollView scrollY 添加 height:0 修复 flex 高度分配 - 健康页:useReachBottom(页面级)替换为 ScrollView onScrollToLower,修复模拟器卡死 - RichArticle:新增 18 条 tag-style 规则(h1-h4/p/ul/ol/li/table/th/td 等),确保文章内容在小程序中正确渲染样式 --- .../src/components/RichArticle/index.tsx | 24 +- apps/miniprogram/src/pages/health/index.scss | 574 +++--------------- apps/miniprogram/src/pages/health/index.tsx | 352 ++++------- 3 files changed, 248 insertions(+), 702 deletions(-) diff --git a/apps/miniprogram/src/components/RichArticle/index.tsx b/apps/miniprogram/src/components/RichArticle/index.tsx index 95a2542..fd42dfe 100644 --- a/apps/miniprogram/src/components/RichArticle/index.tsx +++ b/apps/miniprogram/src/components/RichArticle/index.tsx @@ -7,6 +7,28 @@ interface RichArticleProps { className?: string; } +const TAG_STYLE = JSON.stringify({ + h1: 'font-size:20px;font-weight:700;color:#2D2A26;margin:16px 0 8px', + h2: 'font-size:18px;font-weight:700;color:#2D2A26;margin:16px 0 8px', + h3: 'font-size:16px;font-weight:700;color:#2D2A26;margin:16px 0 8px', + h4: 'font-size:15px;font-weight:600;color:#2D2A26;margin:12px 0 6px', + p: 'font-size:16px;color:#2D2A26;line-height:1.85;margin-bottom:12px', + ul: 'padding-left:20px;margin:8px 0;font-size:16px;line-height:1.9;color:#2D2A26', + ol: 'padding-left:20px;margin:8px 0;font-size:16px;line-height:1.9;color:#2D2A26', + li: 'margin-bottom:4px', + blockquote: 'border-left:3px solid #C4623A;padding:6px 12px;color:#5A554F;margin:12px 0', + strong: 'font-weight:700;color:#2D2A26', + em: 'font-style:italic', + code: 'background:#F5F0EB;padding:2px 6px;border-radius:4px;font-size:14px;color:#C4623A', + pre: 'background:#F5F0EB;padding:12px;border-radius:8px;margin:14px 0;overflow-x:auto', + table: 'width:100%;border-collapse:collapse;margin:8px 0;font-size:14px', + th: 'border:1px solid #E8E2DC;padding:6px 8px;background:#FAF8F5;font-weight:600;text-align:left', + td: 'border:1px solid #E8E2DC;padding:6px 8px', + hr: 'border:none;border-top:1px dashed #D1D5DB;margin:14px 0', + img: 'max-width:100%;border-radius:8px;margin:8px 0;display:block', + a: 'color:#C4623A;text-decoration:none', +}); + function prepareHtml(raw: string): string { return sanitizeHtml(raw); } @@ -23,7 +45,7 @@ function RichArticle({ html, className }: RichArticleProps) { lazy-load selectable container-style="font-size:16px;color:#5A554F;line-height:1.8;word-break:break-word" - tag-style='{"img":"max-width:100%;border-radius:8px;margin:12px auto;display:block","a":"color:#C4623A;text-decoration:none"}' + tag-style={TAG_STYLE} /> ); diff --git a/apps/miniprogram/src/pages/health/index.scss b/apps/miniprogram/src/pages/health/index.scss index abefae1..3e61bcf 100644 --- a/apps/miniprogram/src/pages/health/index.scss +++ b/apps/miniprogram/src/pages/health/index.scss @@ -2,515 +2,131 @@ @import '../../styles/mixins.scss'; .health-page { - padding-bottom: calc(var(--tk-tabbar-space) + env(safe-area-inset-bottom)); -} - -/* ─── 页头 ─── */ -.health-header { - margin-bottom: var(--tk-gap-sm); - display: flex; - align-items: baseline; - justify-content: space-between; -} - -.health-title { - @include serif-number; - font-size: var(--tk-font-h1); - font-weight: 700; - color: $tx; - letter-spacing: -0.02em; -} - -.health-date { - font-size: var(--tk-font-cap); - color: $tx3; -} - -/* ─── 今日体征 hero 卡片 ─── */ -.vitals-grid { - margin-bottom: var(--tk-section-gap); - background: linear-gradient(135deg, $card 60%, $pri-l); - border-radius: var(--tk-card-radius); - box-shadow: $shadow-md; - padding: var(--tk-card-padding); - - /* 覆盖 ContentCard 默认 padding/margin */ - &.content-card { - padding: var(--tk-card-padding); - margin-bottom: var(--tk-section-gap); - } -} - -.vitals-header { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: var(--tk-gap-md); -} - -.vitals-title { - font-size: var(--tk-font-body-sm); - font-weight: 600; - color: $tx2; - letter-spacing: 0.04em; -} - -.vitals-badge { - font-size: var(--tk-font-micro); - color: $acc; - background: $acc-l; - padding: 3px 10px; - border-radius: $r-pill; - font-weight: 500; -} - -.vitals-row { - display: grid; - grid-template-columns: 1fr 1fr; - gap: var(--tk-gap-sm); -} - -.vital-cell { - text-align: center; - padding: var(--tk-gap-md) var(--tk-gap-sm); - border-radius: $r-sm; - background: $bg; - - &:active { - opacity: var(--tk-touch-feedback-opacity); - } -} - -.vital-value { - @include serif-number; - font-size: var(--tk-font-num); - font-weight: 700; - color: $tx; - font-variant-numeric: tabular-nums; - display: block; - line-height: 1.1; -} - -.vital-unit { - font-size: var(--tk-font-micro); - color: $tx3; - display: block; - margin-top: 2px; -} - -.vital-label { - font-size: var(--tk-font-cap); - color: $tx2; - font-weight: 500; - display: block; - margin-top: 6px; -} - -.vital-cell.vital-warn { - background: $wrn-l; - - .vital-value { - color: $wrn; - } -} - -.vital-cell.vital-ok { - .vital-value { - color: $acc; - } -} - -/* ─── 快捷入口 — 横排 4 格图标 ─── */ -.quick-entries { - display: grid; - grid-template-columns: repeat(4, 1fr); - gap: var(--tk-gap-xs); - margin-bottom: var(--tk-section-gap); -} - -.quick-entry { display: flex; flex-direction: column; + height: 100vh; +} + +/* ─── 分类标签 ─── */ +.health-categories { + white-space: nowrap; + padding: var(--tk-gap-xs) var(--tk-page-padding); + margin-bottom: var(--tk-gap-xs); + flex-shrink: 0; +} + +.health-cat-tab { + display: inline-flex; align-items: center; - gap: var(--tk-gap-xs); - min-height: var(--tk-touch-min); justify-content: center; - padding: var(--tk-gap-sm) 0; - - &:active { - opacity: var(--tk-touch-feedback-opacity); - } -} - -.quick-icon { - width: 44px; - height: 44px; - border-radius: $r-sm; - @include flex-center; -} - -.quick-icon-text { - font-size: 18px; - font-weight: 600; -} - -.quick-icon--input { - background: $pri-l; - - .quick-icon-text { - color: $pri; - } -} - -.quick-icon--trend { - background: $doc-pri-l; - - .quick-icon-text { - color: $doc-pri; - } -} - -.quick-icon--report { - background: $acc-l; - - .quick-icon-text { - color: $acc; - } -} - -.quick-icon--med { - background: $wrn-l; - - .quick-icon-text { - color: $wrn; - } -} - -.quick-label { - font-size: var(--tk-font-cap); - color: $tx2; - font-weight: 500; -} - -/* ─── 告警横幅 ─── */ -.alert-hint { - display: flex; - align-items: center; - gap: var(--tk-gap-sm); - margin-bottom: var(--tk-section-gap); - background: $dan-l; - border-radius: $r-sm; - - /* 覆盖 ContentCard 默认样式 */ - &.content-card { - background: $dan-l; - box-shadow: none; - border: none; - } - - &:active { - opacity: var(--tk-touch-feedback-opacity); - } -} - -.alert-dot { - width: 8px; - height: 8px; - border-radius: 50%; - background: $dan; - flex-shrink: 0; -} - -.alert-text { - flex: 1; + padding: 8px 18px; + margin-right: 8px; font-size: var(--tk-font-body-sm); - font-weight: 500; - color: $dan; -} - -.alert-arrow { - font-size: var(--tk-font-body); - color: $dan; - flex-shrink: 0; - opacity: 0.6; -} - -/* ─── 趋势图 ─── */ -.trend-section { - margin-bottom: var(--tk-gap-sm); -} - -.section-title { - @include section-title; -} - -.trend-empty { - text-align: center; -} - -.trend-empty-text { - font-size: var(--tk-font-cap); + font-weight: 400; color: $tx2; + background: $surface-alt; + border-radius: 20px; + transition: all 0.2s; + + &--active { + background: var(--tk-pri); + color: $white; + font-weight: 600; + box-shadow: 0 2px 8px rgba(196, 98, 58, 0.2); + } } -.trend-chart { - padding: var(--tk-gap-md); -} - -.trend-bars { - display: flex; - align-items: flex-end; - height: 120px; - background: $bg; - border-radius: $r-sm; - padding: var(--tk-gap-sm) var(--tk-gap-xs); - gap: 0; - position: relative; -} - -.trend-threshold-line { - position: absolute; - left: 8px; - right: 8px; - border-top: 1.5px dashed $wrn; - opacity: 0.5; - pointer-events: none; -} - -.trend-threshold-label { - position: absolute; - right: 0; - top: -16px; - font-size: var(--tk-font-micro); - color: $wrn; - opacity: 0.7; -} - -.trend-bar-col { +/* ─── 可滚动内容区 ─── */ +.health-scroll { flex: 1; + overflow: hidden; + /* 微信小程序 ScrollView scrollY 需要显式高度 */ + height: 0; /* flex:1 + height:0 让 flex 布局正确分配剩余高度 */ +} + +/* ─── 文章列表 ─── */ +.health-article-list { display: flex; flex-direction: column; - align-items: center; - height: 100%; - justify-content: flex-end; -} - -.trend-bar { - width: 24px; - border-radius: $r-xs $r-xs 0 0; - min-height: 6px; - - &.trend-bar-normal { - background: var(--tk-pri); - opacity: 0.75; - } - - &.trend-bar-warn { - background: $wrn; - opacity: 0.85; - } -} - -.trend-bar-label { - font-size: var(--tk-font-micro); - color: var(--tk-text-secondary); - margin-top: var(--tk-gap-2xs); -} - -/* ─── BLE 设备卡片 ─── */ -.device-section { - margin-bottom: var(--tk-gap-sm); -} - -.device-card { - display: flex; - align-items: center; gap: var(--tk-gap-sm); + padding: 0 var(--tk-page-padding) var(--tk-gap-lg); - &:active { - opacity: var(--tk-touch-feedback-opacity); + .content-card { + display: flex; + gap: 14px; } } -.device-icon { - width: 48px; - height: 48px; - border-radius: $r-sm; - background: var(--tk-pri-l); - @include flex-center; - flex-shrink: 0; -} - -.device-icon-text { - font-size: var(--tk-font-body); -} - -.device-info { +.health-article-body { flex: 1; + display: flex; min-width: 0; } -.device-name { - font-size: var(--tk-font-cap); - font-weight: 500; - color: $tx; - display: block; -} - -.device-desc { - font-size: var(--tk-font-cap); - color: $acc; - display: block; -} - -.device-arrow { - font-size: var(--tk-font-cap); - color: var(--tk-text-secondary); - flex-shrink: 0; -} - -/* ─── 健康资讯入口 ─── */ -.article-entry { - &:active { - opacity: var(--tk-touch-feedback-opacity); - } -} - -.article-entry-text { - font-size: var(--tk-font-body-sm); - color: $tx; - font-weight: 500; -} - -/* ─── AI 建议卡片 ─── */ -.ai-suggestion-card { - background: linear-gradient(135deg, #F0F7F0 0%, $acc-l 100%); - border-radius: $r; - padding: var(--tk-card-padding); - margin-bottom: var(--tk-section-gap); - box-shadow: none; - position: relative; - overflow: hidden; - - &::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 3px; - background: linear-gradient(90deg, $acc, $acc 60%, transparent); - border-radius: 3px 3px 0 0; - } -} - -.ai-card-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--tk-gap-md); -} - -.ai-card-title { - font-size: var(--tk-font-body-sm); - font-weight: 600; - color: $acc; -} - -.ai-card-count { - font-size: var(--tk-font-micro); - color: $acc; - opacity: 0.7; -} - -.ai-suggestion-item { - padding: var(--tk-gap-sm) 0; - border-bottom: 1px solid rgba($acc, 0.12); - - &:last-child { - border-bottom: none; - } -} - -.ai-suggestion-main { - display: flex; - align-items: flex-start; - gap: var(--tk-gap-xs); - - &:active { - opacity: var(--tk-touch-feedback-opacity); - } -} - -.ai-risk-dot { - width: 8px; - height: 8px; - border-radius: 50%; - flex-shrink: 0; - margin-top: 5px; - - &.ai-risk-high { - background: $dan; - } - - &.ai-risk-medium { - background: $wrn; - } - - &.ai-risk-low { - background: $acc; - } -} - -.ai-suggestion-text { - font-size: var(--tk-font-cap); - color: $tx2; - line-height: 1.6; +.health-article-content { flex: 1; -} - -/* ─── AI 建议反馈按钮 ─── */ -.ai-feedback-row { display: flex; - gap: var(--tk-gap-xs); - margin-top: var(--tk-gap-xs); - padding-left: 20px; + flex-direction: column; + justify-content: space-between; + min-width: 0; } -.ai-feedback-btn { - height: 36px; - min-height: 36px; - border-radius: $r-xs; - @include flex-center; - padding: 0 var(--tk-gap-sm); - - &:active { - opacity: var(--tk-touch-feedback-opacity); - } - - &.ai-feedback-adopt { - background: rgba($acc, 0.15); - } - - &.ai-feedback-ignore { - background: $surface-alt; - } - - &.ai-feedback-consult { - background: var(--tk-pri-l); - } +.health-article-title { + font-family: Georgia, 'Times New Roman', serif; + font-size: var(--tk-font-body); + font-weight: 700; + color: $tx; + line-height: 1.35; + margin-bottom: 4px; + overflow: hidden; + text-overflow: ellipsis; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; } -.ai-feedback-btn-text { - font-size: var(--tk-font-micro); - font-weight: 500; +.health-article-summary { + font-size: var(--tk-font-cap); color: $tx2; + line-height: 1.4; + display: block; + margin-bottom: var(--tk-gap-2xs); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } -.ai-feedback-adopt .ai-feedback-btn-text { - color: $acc; +.health-article-meta { + display: flex; + gap: var(--tk-gap-sm); + font-size: var(--tk-font-micro); + color: $tx3; + align-items: center; } -.ai-feedback-consult .ai-feedback-btn-text { +.health-article-tag { + font-size: var(--tk-font-micro); color: var(--tk-pri); + background: var(--tk-pri-l); + padding: 2px 8px; + border-radius: $r-xs; +} + +.health-article-date { + font-size: var(--tk-font-micro); + color: $tx3; +} + +// 长者模式 +.elder-mode .health-page { + .health-cat-tab { + padding: 10px 20px; + font-size: 15px; + } + .health-article-title { + font-size: 18px; + } + .health-article-summary { + font-size: 15px; + } } diff --git a/apps/miniprogram/src/pages/health/index.tsx b/apps/miniprogram/src/pages/health/index.tsx index 4045a4c..62e8c68 100644 --- a/apps/miniprogram/src/pages/health/index.tsx +++ b/apps/miniprogram/src/pages/health/index.tsx @@ -1,244 +1,152 @@ -import { View, Text } from '@tarojs/components'; +import { useState, useCallback } from 'react'; +import { View, Text, ScrollView } from '@tarojs/components'; import Taro from '@tarojs/taro'; import { safeNavigateTo } from '@/utils/navigate'; +import { usePageData } from '@/hooks/usePageData'; import { useAuthStore } from '../../stores/auth'; -import { useElderClass } from '../../hooks/useElderClass'; -import GuestGuard from '../../components/GuestGuard'; -import Loading from '../../components/Loading'; +import { + listArticles, + listCategories, + listPublicArticles, + listPublicCategories, + type Article, + type ArticleCategory, +} from '../../services/article'; import PageShell from '@/components/ui/PageShell'; import ContentCard from '@/components/ui/ContentCard'; -import SegmentTabs from '../../components/SegmentTabs'; -import { useHealthOverview, VITAL_TABS, type VitalType } from './useHealthOverview'; -import { submitSuggestionFeedback } from '../../services/ai-analysis'; +import EmptyState from '../../components/EmptyState'; +import ErrorState from '../../components/ErrorState'; +import Loading from '../../components/Loading'; +import { useElderClass } from '../../hooks/useElderClass'; import './index.scss'; -const QUICK_ENTRIES = [ - { label: '录入体征', icon: '✏', color: 'input', path: '/pages/pkg-health/input/index' }, - { label: '健康趋势', icon: '📈', color: 'trend', path: '/pages/pkg-health/trend/index' }, - { label: '我的报告', icon: '📋', color: 'report', path: '/pages/pkg-profile/reports/index' }, - { label: '健康档案', icon: '健', color: 'med', path: '/pages/pkg-profile/health-records/index' }, -] as const; - -function statusClass(status?: string): string { - if (!status) return ''; - if (status === 'high' || status === 'abnormal') return 'vital-warn'; - if (status === 'low') return 'vital-warn'; - return 'vital-ok'; -} - -function formatDate(): string { - const d = new Date(); - const month = d.getMonth() + 1; - const day = d.getDate(); - const weekDays = ['日', '一', '二', '三', '四', '五', '六']; - return `${month}月${day}日 周${weekDays[d.getDay()]}`; -} - export default function Health() { - const user = useAuthStore((s) => s.user); const modeClass = useElderClass(); - const { - todaySummary, loading, error, activeTab, trendData, trendLoading, - aiSuggestions, thresholds, alertCount, handleTabChange, fetchData, - } = useHealthOverview(); + const isLoggedIn = !!useAuthStore((s) => s.user); + const [articles, setArticles] = useState([]); + const [page, setPage] = useState(1); + const [total, setTotal] = useState(0); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(false); + const [categories, setCategories] = useState([]); + const [activeCategory, setActiveCategory] = useState(null); - if (!user) { - return ; - } - - if (error) { - return ( - - - 健康总览 - - - - ); - } - - const maxTrendValue = trendData.reduce((max, d) => Math.max(max, d.value), 1); - const dayLabels = ['日', '一', '二', '三', '四', '五', '六']; - - const summary = todaySummary || {}; - const vitals = [ - { label: '血压', value: summary.blood_pressure ? `${summary.blood_pressure.systolic}/${summary.blood_pressure.diastolic}` : '—', unit: 'mmHg', status: summary.blood_pressure?.status }, - { label: '心率', value: summary.heart_rate ? `${summary.heart_rate.value}` : '—', unit: 'bpm', status: summary.heart_rate?.status }, - { label: '血糖', value: summary.blood_sugar ? `${summary.blood_sugar.value}` : '—', unit: 'mmol/L', status: summary.blood_sugar?.status }, - { label: '体重', value: summary.weight ? `${summary.weight.value}` : '—', unit: 'kg', status: summary.weight?.status }, - ]; - const recordedCount = vitals.filter((v) => v.value !== '—').length; - - const getThresholdValue = (type: VitalType): number | null => { - if (!thresholds.length) return null; - const th = thresholds; - if (type === 'blood_pressure') { - const v = th.find((t) => t.indicator === 'systolic_bp' && t.level === 'high'); - return v?.threshold_value ?? 140; + const fetchData = useCallback(async (p: number, append = false, categoryId?: string | null) => { + setLoading(true); + setError(false); + try { + const cid = categoryId !== undefined ? categoryId : activeCategory; + const res = isLoggedIn + ? await listArticles({ page: p, category_id: cid || undefined }) + : await listPublicArticles({ page: p, category_id: cid || undefined }); + const list = res.data || []; + setArticles(append ? (prev) => [...prev, ...list] : list); + setTotal(res.total); + setPage(p); + } catch (err) { + console.warn('[health] 加载文章列表失败:', err); + setError(true); + Taro.showToast({ title: '加载失败', icon: 'none' }); + } finally { + setLoading(false); } - if (type === 'heart_rate') { - const v = th.find((t) => t.indicator === 'heart_rate' && t.level === 'high'); - return v?.threshold_value ?? 100; + }, [activeCategory, isLoggedIn]); + + usePageData( + useCallback(async () => { + try { + const cats = isLoggedIn + ? await listCategories() + : await listPublicCategories(); + setCategories(cats || []); + } catch (err) { + console.warn('[health] 加载分类失败:', err); + setCategories([]); + } + await fetchData(1); + }, [fetchData, isLoggedIn]), + { throttleMs: 10000, enablePullDown: true }, + ); + + const loadMore = useCallback(() => { + if (!loading && articles.length < total) { + fetchData(page + 1, true); } - if (type === 'blood_sugar') { - const v = th.find((t) => t.indicator === 'blood_sugar_fasting' && t.level === 'high'); - return v?.threshold_value ?? 6.1; - } - return null; + }, [loading, articles.length, total, page, fetchData]); + + const handleCategoryChange = (categoryId: string | null) => { + setActiveCategory(categoryId); + fetchData(1, false, categoryId); + }; + + const formatDate = (dateStr?: string) => { + if (!dateStr) return ''; + const d = new Date(dateStr); + const month = d.getMonth() + 1; + const day = d.getDate(); + return `${month}月${day}日`; }; return ( - - - 健康总览 - {formatDate()} - + + {/* 分类标签 */} + {categories.length > 0 && ( + + handleCategoryChange(null)} + > + 推荐 + + {categories.map((cat) => ( + handleCategoryChange(cat.id)} + > + {cat.name} + + ))} + + )} - {/* 今日体征 hero 卡片 */} - - - 今日体征 - {recordedCount > 0 && ( - 已记录 {recordedCount} 项 - )} - - {loading ? : ( - - {vitals.map((v) => ( - - {v.value} - {v.unit} - {v.label} - + {/* 文章列表 */} + + {error ? ( + fetchData(1, false, null)} /> + ) : articles.length === 0 && !loading ? ( + + ) : ( + + {articles.map((a) => ( + safeNavigateTo(`/pages/article/detail/index?id=${a.id}`)} + > + + + {a.title} + {a.summary && ( + {a.summary} + )} + + {(a.category_name || a.category) && ( + {a.category_name || a.category} + )} + {a.published_at && ( + {formatDate(a.published_at)} + )} + + + + ))} )} - - - {/* 快捷入口 — 横排 4 格图标 */} - - {QUICK_ENTRIES.map((e) => ( - safeNavigateTo(e.path)} - > - - {e.icon} - - {e.label} - - ))} - - - {/* 告警横幅 */} - {alertCount > 0 && ( - safeNavigateTo('/pages/pkg-health/alerts/index')} - > - - {alertCount} 条待处理告警 - - - )} - - {/* AI 建议 */} - {aiSuggestions.length > 0 && ( - - - AI 健康建议 - {aiSuggestions.length} 条 - - {aiSuggestions.map((s) => { - const riskCls = s.risk_level === 'high' ? 'ai-risk-high' : s.risk_level === 'medium' ? 'ai-risk-medium' : 'ai-risk-low'; - const params = s.params as Record | null; - const reason = (params?.reason as string) || (params?.message as string) || '健康建议'; - return ( - - { - if (s.suggestion_type === 'appointment') safeNavigateTo('/pages/appointment/create/index'); - else if (s.suggestion_type === 'followup') safeNavigateTo('/pages/pkg-profile/followups/index'); - }}> - - {reason.slice(0, 50)} - - - { - try { await submitSuggestionFeedback(s.id, 'adopt'); Taro.showToast({ title: '已采纳', icon: 'success' }); fetchData(); } - catch { Taro.showToast({ title: '操作失败', icon: 'none' }); } - }}> - 采纳 - - { - try { await submitSuggestionFeedback(s.id, 'ignore'); Taro.showToast({ title: '已忽略', icon: 'success' }); fetchData(); } - catch { Taro.showToast({ title: '操作失败', icon: 'none' }); } - }}> - 忽略 - - { - try { await submitSuggestionFeedback(s.id, 'consult'); safeNavigateTo('/pages/consultation/index'); } - catch { Taro.showToast({ title: '操作失败', icon: 'none' }); } - }}> - 咨询医生 - - - - ); - })} - - )} - - {/* 7天趋势 */} - - 近 7 天趋势 - - {trendLoading ? : trendData.length === 0 ? ( - - 暂无趋势数据 - - ) : ( - - - {(() => { - const tv = getThresholdValue(activeTab); - if (tv) { - const pct = Math.min(95, (tv / maxTrendValue) * 100); - return ( - - {tv} - - ); - } - return null; - })()} - {trendData.map((point, i) => { - const heightPct = Math.max(8, (point.value / maxTrendValue) * 100); - const tv = getThresholdValue(activeTab); - const isAbnormal = tv ? point.value >= tv : false; - const dayOfWeek = new Date(point.date).getDay(); - return ( - - - {dayLabels[dayOfWeek]} - - ); - })} - - - )} - - - {/* 健康资讯入口 */} - safeNavigateTo('/pages/article/index')}> - 最新健康资讯 › - + {loading && } + ); }