// 老人端 · 膳食推荐(ArkTS 严格类型,无 any) // 接口:GET /elderly-portal/recommendations/ import { router } from '@kit.ArkUI'; import { promptAction } from '@kit.ArkUI'; import { HdsNavigation, HdsNavigationTitleMode, ScrollEffectType } from '@kit.UIDesignKit'; import { hdsMaterial } from '@hms.hds.hdsMaterial'; import { BusinessError } from '@kit.BasicServicesKit'; import { normalizeError } from '../../common/ApiConstants'; import { api, RequestError } from '../../utils/Request'; import { FontScaleLevel, FONT_SCALE_MAP } from '../../utils/FontScale'; interface Recommendation { id: number; service_date: string; meal_period: string; dish_name: string; price: string; reason: string; } interface RecommendationsResponse { elderly_id: number | null; recommendations: Array; } function periodIcon(period: string): string { if (period === '早餐') { return '🌅'; } if (period === '午餐') { return '🍚'; } return '🌙'; } function periodColor(period: string): string { if (period === '早餐') { return '#FF9500'; } if (period === '午餐') { return '#007AFF'; } return '#34C759'; } function formatDateLabel(dateStr: string): string { if (!dateStr || dateStr.length === 0) { return ''; } const parts: Array = dateStr.split('-'); if (parts.length !== 3) { return dateStr; } const month = parseInt(parts[1], 10); const day = parseInt(parts[2], 10); return `${month}月${day}日`; } function isToday(dateStr: string): boolean { if (!dateStr || dateStr.length === 0) { return false; } const now = new Date(); const mm = String(now.getMonth() + 1).padStart(2, '0'); const dd = String(now.getDate()).padStart(2, '0'); return dateStr === `${now.getFullYear()}-${mm}-${dd}`; } @Entry @Component struct ElderlyRecommendations { @StorageProp('fontScale') fontScaleLevel: FontScaleLevel = 'system'; @State recommendations: Array = []; @State loading: boolean = true; private scroller: Scroller = new Scroller(); private scaleFor(): number { return FONT_SCALE_MAP[this.fontScaleLevel] ?? 1.0; } aboutToAppear(): void { this.load(); } private async load(): Promise { this.loading = true; try { const res = await api.get('/elderly-portal/recommendations/'); this.recommendations = res.recommendations; } catch (err) { const message = err instanceof RequestError ? err.message : normalizeError(err as BusinessError | Error); promptAction.showToast({ message: message }); } finally { this.loading = false; } } @Builder RecommendCard(item: Recommendation) { Column({ space: 8 }) { Row({ space: 10 }) { Text(periodIcon(item.meal_period)) .fontSize(Math.round(24 * this.scaleFor())) Text(`${item.meal_period} · ${isToday(item.service_date) ? '今日' : formatDateLabel(item.service_date)}`) .fontSize(Math.round(17 * this.scaleFor())) .fontWeight(FontWeight.Bold) .fontColor($r('app.color.text_primary')) Blank() Text(`¥${item.price}`) .fontSize(Math.round(16 * this.scaleFor())) .fontWeight(FontWeight.Bold) .fontColor('#FF9500') } .width('100%') Text(item.dish_name) .fontSize(Math.round(20 * this.scaleFor())) .fontWeight(FontWeight.Medium) .fontColor(periodColor(item.meal_period)) .width('100%') if (item.reason && item.reason.length > 0) { Text(item.reason) .fontSize(Math.round(15 * this.scaleFor())) .fontColor($r('app.color.text_secondary')) .lineHeight(22) .width('100%') } } .width('100%') .padding(16) .backgroundColor($r('app.color.background_card')) .borderRadius(18) .alignItems(HorizontalAlign.Start) } build() { HdsNavigation() { Column() { Scroll(this.scroller) { Column({ space: 12 }) { if (this.loading) { Text('加载中...') .fontSize(Math.round(16 * this.scaleFor())) .fontColor($r('app.color.inactive_tab')) .margin({ top: 40 }) } else if (this.recommendations.length === 0) { Column({ space: 8 }) { Text('暂无膳食推荐') .fontSize(Math.round(20 * this.scaleFor())) .fontWeight(FontWeight.Bold) .fontColor($r('app.color.text_primary')) Text('护理团队会根据您的健康状况推送个性化餐食建议') .fontSize(Math.round(15 * this.scaleFor())) .fontColor($r('app.color.text_secondary')) .textAlign(TextAlign.Center) } .width('100%') .padding(24) .backgroundColor($r('app.color.background_card')) .borderRadius(16) .alignItems(HorizontalAlign.Center) } else { ForEach(this.recommendations, (item: Recommendation) => { Column() { this.RecommendCard(item) } .width('100%') }, (item: Recommendation) => String(item.id)) } } .width('100%') .padding({ left: 16, right: 16, top: 92, bottom: 30 }) } .width('100%') .layoutWeight(1) .scrollBar(BarState.Auto) .edgeEffect(EdgeEffect.Spring) } .width('100%') .height('100%') } .mode(NavigationMode.Stack) .titleBar({ content: { title: { mainTitle: '膳食推荐', }, }, style: { scrollEffectOpts: { enableScrollEffect: false, scrollEffectType: ScrollEffectType.GRADIENT_BLUR, }, systemMaterialEffect: { materialType: hdsMaterial.MaterialType.ADAPTIVE, materialLevel: hdsMaterial.MaterialLevel.EXQUISITE, }, }, avoidLayoutSafeArea: false, enableComponentSafeArea: false, }) .bindToScrollable([this.scroller]) .hideBackButton(false) .titleMode(HdsNavigationTitleMode.MINI) .ignoreLayoutSafeArea([LayoutSafeAreaType.SYSTEM], [LayoutSafeAreaEdge.TOP, LayoutSafeAreaEdge.BOTTOM]) .expandSafeArea([SafeAreaType.SYSTEM], [SafeAreaEdge.TOP, SafeAreaEdge.BOTTOM]) .backgroundColor($r('app.color.background')) } }