feat(miniapp): 饲养阶段风险提示选择器与展示(#13)
This commit is contained in:
@@ -29,8 +29,23 @@ export interface KnowledgeArticle {
|
|||||||
sortOrder?: number;
|
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 listDiseases = () => get<Disease[]>('/knowledge/diseases');
|
||||||
export const getDisease = (id: string) => get<Disease>(`/knowledge/diseases/${id}`);
|
export const getDisease = (id: string) => get<Disease>(`/knowledge/diseases/${id}`);
|
||||||
export const listAIGuides = () => get<KnowledgeArticle[]>('/knowledge/articles', { kind: 'ai_guide' });
|
export const listAIGuides = () => get<KnowledgeArticle[]>('/knowledge/articles', { kind: 'ai_guide' });
|
||||||
export const listLAMPGuides = () => get<KnowledgeArticle[]>('/knowledge/articles', { kind: 'lamp_guide' });
|
export const listLAMPGuides = () => get<KnowledgeArticle[]>('/knowledge/articles', { kind: 'lamp_guide' });
|
||||||
export const getArticle = (id: string) => get<KnowledgeArticle>(`/knowledge/articles/${id}`);
|
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;
|
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 {
|
.diseaseList {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
@@ -6,8 +6,10 @@ import {
|
|||||||
listAIGuides,
|
listAIGuides,
|
||||||
listLAMPGuides,
|
listLAMPGuides,
|
||||||
listDiseases,
|
listDiseases,
|
||||||
|
getStageHints,
|
||||||
type Disease,
|
type Disease,
|
||||||
type KnowledgeArticle,
|
type KnowledgeArticle,
|
||||||
|
type StageHint,
|
||||||
} from '@/api/knowledge';
|
} from '@/api/knowledge';
|
||||||
|
|
||||||
const GUIDE_LABELS: Record<string, string> = {
|
const GUIDE_LABELS: Record<string, string> = {
|
||||||
@@ -15,6 +17,13 @@ const GUIDE_LABELS: Record<string, string> = {
|
|||||||
lamp_guide: 'LAMP 教程',
|
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> = {
|
const CATEGORY_LABELS: Record<string, string> = {
|
||||||
viral: '病毒性',
|
viral: '病毒性',
|
||||||
fungal: '真菌性',
|
fungal: '真菌性',
|
||||||
@@ -27,7 +36,20 @@ const KnowledgePage: React.FC = () => {
|
|||||||
const [diseases, setDiseases] = useState<Disease[]>([]);
|
const [diseases, setDiseases] = useState<Disease[]>([]);
|
||||||
const [guides, setGuides] = useState<KnowledgeArticle[]>([]);
|
const [guides, setGuides] = useState<KnowledgeArticle[]>([]);
|
||||||
const [lampGuides, setLampGuides] = useState<KnowledgeArticle[]>([]);
|
const [lampGuides, setLampGuides] = useState<KnowledgeArticle[]>([]);
|
||||||
|
const [stage, setStage] = useState('young');
|
||||||
|
const [stageHints, setStageHints] = useState<StageHint[]>([]);
|
||||||
const [loading, setLoading] = useState(true);
|
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 () => {
|
const fetchData = useCallback(async () => {
|
||||||
try {
|
try {
|
||||||
@@ -54,8 +76,12 @@ const KnowledgePage: React.FC = () => {
|
|||||||
fetchData();
|
fetchData();
|
||||||
}, [fetchData]);
|
}, [fetchData]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
fetchStageHints(stage);
|
||||||
|
}, [stage, fetchStageHints]);
|
||||||
|
|
||||||
usePullDownRefresh(() => {
|
usePullDownRefresh(() => {
|
||||||
fetchData().then(() => Taro.stopPullDownRefresh());
|
Promise.all([fetchData(), fetchStageHints(stage)]).then(() => Taro.stopPullDownRefresh());
|
||||||
});
|
});
|
||||||
|
|
||||||
const openGuide = (a: KnowledgeArticle) => {
|
const openGuide = (a: KnowledgeArticle) => {
|
||||||
@@ -90,6 +116,43 @@ const KnowledgePage: React.FC = () => {
|
|||||||
</View>
|
</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>
|
<Text className={styles.sectionTitle}>蚕病百科</Text>
|
||||||
<View className={styles.diseaseList}>
|
<View className={styles.diseaseList}>
|
||||||
{diseases.map((d) => (
|
{diseases.map((d) => (
|
||||||
|
|||||||
Reference in New Issue
Block a user