feat(miniprogram): AI 建议卡片 — 健康页顶部显示待审批建议摘要

- 新增 listPendingSuggestions API
- 健康页加载待审批 AI 建议(最多 3 条)
- 风险等级圆点 + 建议摘要文字
- 点击卡片可跳转
This commit is contained in:
iven
2026-05-01 09:22:18 +08:00
parent 598c06885f
commit 8b837c0591
3 changed files with 100 additions and 1 deletions

View File

@@ -300,3 +300,50 @@
color: $tx;
font-weight: 500;
}
/* ─── AI 建议卡片 ─── */
.ai-suggestion-card {
background: $card;
border-radius: $r;
padding: 16px;
margin-bottom: 16px;
box-shadow: $shadow-sm;
border-left: 4px solid $pri;
}
.ai-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 10px;
}
.ai-card-title {
font-size: 15px;
font-weight: 600;
color: $tx;
}
.ai-card-count {
font-size: 12px;
color: $acc;
}
.ai-suggestion-item {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 0;
}
.ai-risk-dot {
width: 8px;
height: 8px;
border-radius: 50%;
flex-shrink: 0;
}
.ai-suggestion-text {
font-size: 13px;
color: $tx2;
}

View File

@@ -1,9 +1,10 @@
import { useState } from 'react';
import { useState, useEffect } from 'react';
import { View, Text, Input } from '@tarojs/components';
import Taro, { useDidShow } from '@tarojs/taro';
import { useHealthStore } from '../../stores/health';
import { useAuthStore } from '../../stores/auth';
import { inputVitalSign, getTrend } from '../../services/health';
import { listPendingSuggestions, type AiSuggestionItem } from '../../services/ai-analysis';
import Loading from '../../components/Loading';
import './index.scss';
@@ -41,12 +42,23 @@ export default function Health() {
const [saving, setSaving] = useState(false);
const [trendData, setTrendData] = useState<TrendPoint[]>([]);
const [trendLoading, setTrendLoading] = useState(false);
const [aiSuggestions, setAiSuggestions] = useState<AiSuggestionItem[]>([]);
useDidShow(() => {
refreshToday();
loadTrend(activeTab);
loadAiSuggestions();
});
const loadAiSuggestions = async () => {
try {
const items = await listPendingSuggestions();
setAiSuggestions(items.slice(0, 3));
} catch {
// 静默
}
};
const loadTrend = async (type: VitalType) => {
setTrendLoading(true);
try {
@@ -162,6 +174,28 @@ export default function Health() {
<Text className='health-title'></Text>
</View>
{/* AI 建议卡片 */}
{aiSuggestions.length > 0 && (
<View className='ai-suggestion-card' onClick={() => Taro.navigateTo({ url: '/pages/pkg-profile/settings/index' })}>
<View className='ai-card-header'>
<Text className='ai-card-title'>AI </Text>
<Text className='ai-card-count'>{aiSuggestions.length} </Text>
</View>
{aiSuggestions.map((s) => {
const riskColor = s.risk_level === 'high' ? '#ef4444' : s.risk_level === 'medium' ? '#f59e0b' : '#22c55e';
const typeLabel = s.suggestion_type === 'followup' ? '随访' : s.suggestion_type === 'appointment' ? '预约' : '预警';
const params = s.params as Record<string, unknown> | null;
const reason = (params?.reason as string) || (params?.message as string) || typeLabel;
return (
<View key={s.id} className='ai-suggestion-item'>
<View className='ai-risk-dot' style={{ background: riskColor }} />
<Text className='ai-suggestion-text'>{reason.slice(0, 40)}</Text>
</View>
);
})}
</View>
)}
{/* 类型 Tab */}
<View className='vital-tabs'>
{VITAL_TABS.map((tab) => {

View File

@@ -22,3 +22,21 @@ export async function listAiAnalysis(page = 1, pageSize = 20) {
export async function getAiAnalysisDetail(id: string) {
return api.get<AiAnalysisItem>(`/ai/analysis/${id}`);
}
export interface AiSuggestionItem {
id: string;
analysis_id: string;
suggestion_type: string;
risk_level: string;
params: Record<string, unknown> | null;
status: string;
created_at: string;
}
export async function listPendingSuggestions() {
const resp = await api.get<{ data: AiSuggestionItem[]; total: number }>(
'/ai/suggestions',
{ status: 'pending' },
);
return resp.data || [];
}