feat(miniapp): 饲养阶段风险提示选择器与展示(#13)

This commit is contained in:
weijuesen
2026-08-12 14:46:07 +08:00
parent 6d9cfc7aa9
commit 6dc6c27c03
3 changed files with 151 additions and 1 deletions
+15
View File
@@ -29,8 +29,23 @@ export interface KnowledgeArticle {
sortOrder?: number;
}
export interface StageDiseaseHint {
id: string;
name: string;
reason: string;
}
export interface StageHint {
stage: string;
label: string;
summary: string;
diseases: StageDiseaseHint[];
}
export const listDiseases = () => get<Disease[]>('/knowledge/diseases');
export const getDisease = (id: string) => get<Disease>(`/knowledge/diseases/${id}`);
export const listAIGuides = () => get<KnowledgeArticle[]>('/knowledge/articles', { kind: 'ai_guide' });
export const listLAMPGuides = () => get<KnowledgeArticle[]>('/knowledge/articles', { kind: 'lamp_guide' });
export const getArticle = (id: string) => get<KnowledgeArticle>(`/knowledge/articles/${id}`);
export const getStageHints = (stage?: string) =>
get<StageHint[]>('/knowledge/stage-hints', stage ? { stage } : undefined);
@@ -58,6 +58,78 @@
margin: $spacing-lg 0 $spacing-md;
}
.stageChips {
display: flex;
flex-wrap: wrap;
gap: $spacing-sm;
margin-bottom: $spacing-md;
}
.stageChip {
padding: 8rpx $spacing-md;
border-radius: $radius-round;
background: $color-bg-card;
border: 1rpx solid rgba(0, 0, 0, 0.08);
}
.stageChipActive {
background: $color-primary;
border-color: $color-primary;
}
.stageChipText {
font-size: $font-size-xs;
color: $color-text-secondary;
}
.stageChipActive .stageChipText {
color: $color-text-white;
}
.stageHintCard {
background: $color-bg-card;
border-radius: $radius-md;
padding: $spacing-lg;
box-shadow: $shadow-card;
margin-bottom: $spacing-md;
}
.stageHintLabel {
display: block;
font-size: $font-size-md;
font-weight: $font-weight-semibold;
color: $color-primary;
margin-bottom: $spacing-xs;
}
.stageHintSummary {
display: block;
font-size: $font-size-xs;
color: $color-text-secondary;
line-height: $line-height-normal;
margin-bottom: $spacing-sm;
}
.stageDiseaseRow {
padding: $spacing-sm 0;
border-top: 1rpx solid rgba(0, 0, 0, 0.06);
}
.stageDiseaseName {
display: block;
font-size: $font-size-md;
font-weight: $font-weight-medium;
color: $color-text-primary;
}
.stageDiseaseReason {
display: block;
font-size: $font-size-xs;
color: $color-text-secondary;
line-height: $line-height-normal;
margin-top: 4rpx;
}
.diseaseList {
display: flex;
flex-direction: column;
+64 -1
View File
@@ -6,8 +6,10 @@ import {
listAIGuides,
listLAMPGuides,
listDiseases,
getStageHints,
type Disease,
type KnowledgeArticle,
type StageHint,
} from '@/api/knowledge';
const GUIDE_LABELS: Record<string, string> = {
@@ -15,6 +17,13 @@ const GUIDE_LABELS: Record<string, string> = {
lamp_guide: 'LAMP 教程',
};
const STAGE_OPTIONS = [
{ key: 'young', label: '小蚕期(1-3龄)' },
{ key: 'grown', label: '大蚕期(4-5龄)' },
{ key: 'late5', label: '5龄后期' },
{ key: 'pupa', label: '蛹期' },
];
const CATEGORY_LABELS: Record<string, string> = {
viral: '病毒性',
fungal: '真菌性',
@@ -27,7 +36,20 @@ const KnowledgePage: React.FC = () => {
const [diseases, setDiseases] = useState<Disease[]>([]);
const [guides, setGuides] = useState<KnowledgeArticle[]>([]);
const [lampGuides, setLampGuides] = useState<KnowledgeArticle[]>([]);
const [stage, setStage] = useState('young');
const [stageHints, setStageHints] = useState<StageHint[]>([]);
const [loading, setLoading] = useState(true);
const [stageLoading, setStageLoading] = useState(false);
const fetchStageHints = useCallback(async (key: string) => {
setStageLoading(true);
try {
const res = await getStageHints(key).catch(() => [] as StageHint[]);
setStageHints(res);
} finally {
setStageLoading(false);
}
}, []);
const fetchData = useCallback(async () => {
try {
@@ -54,8 +76,12 @@ const KnowledgePage: React.FC = () => {
fetchData();
}, [fetchData]);
useEffect(() => {
fetchStageHints(stage);
}, [stage, fetchStageHints]);
usePullDownRefresh(() => {
fetchData().then(() => Taro.stopPullDownRefresh());
Promise.all([fetchData(), fetchStageHints(stage)]).then(() => Taro.stopPullDownRefresh());
});
const openGuide = (a: KnowledgeArticle) => {
@@ -90,6 +116,43 @@ const KnowledgePage: React.FC = () => {
</View>
))}
<Text className={styles.sectionTitle}></Text>
<View className={styles.stageChips}>
{STAGE_OPTIONS.map((opt) => (
<View
key={opt.key}
className={`${styles.stageChip} ${stage === opt.key ? styles.stageChipActive : ''}`}
onClick={() => setStage(opt.key)}
>
<Text className={styles.stageChipText}>{opt.label}</Text>
</View>
))}
</View>
{stageLoading ? (
<Text className={styles.emptyText}>...</Text>
) : (
stageHints.map((h) => (
<View key={h.stage} className={styles.stageHintCard}>
<Text className={styles.stageHintLabel}>{h.label}</Text>
<Text className={styles.stageHintSummary}>{h.summary}</Text>
{h.diseases.map((d) => (
<View
key={d.name}
className={styles.stageDiseaseRow}
onClick={() => {
if (d.id) {
Taro.navigateTo({ url: `/pages/knowledge/detail/index?kind=disease&id=${d.id}` });
}
}}
>
<Text className={styles.stageDiseaseName}>{d.name}</Text>
<Text className={styles.stageDiseaseReason}>{d.reason}</Text>
</View>
))}
</View>
))
)}
<Text className={styles.sectionTitle}></Text>
<View className={styles.diseaseList}>
{diseases.map((d) => (