feat(miniprogram): AI 建议卡片 — 健康页顶部显示待审批建议摘要
- 新增 listPendingSuggestions API - 健康页加载待审批 AI 建议(最多 3 条) - 风险等级圆点 + 建议摘要文字 - 点击卡片可跳转
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) => {
|
||||
|
||||
@@ -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 || [];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user