- 以 D:\SmartSenior 生产工程为源,纳入 AppScope、entry、cjagent、hvigor 及根配置 - 移除 build-profile.json5 的 signingConfigs(p12/p7b/cer 路径与加密口令)及 products.signingConfig 引用 - 不纳入本地与开发产物:local.properties、.idea、.hvigor、.codegenie、demo、backup_a2a_native、SESSION_LOG.md、build_err.txt、build/、oh_modules - .gitignore 追加 .codegenie 与密钥文件通配(*.p12/*.p7b/*.cer/*.jks/*.keystore/*.pem)
212 lines
6.4 KiB
Plaintext
212 lines
6.4 KiB
Plaintext
// 老人端 · 膳食推荐(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<Recommendation>;
|
|
}
|
|
|
|
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<string> = 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<Recommendation> = [];
|
|
@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<void> {
|
|
this.loading = true;
|
|
try {
|
|
const res = await api.get<RecommendationsResponse>('/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'))
|
|
}
|
|
}
|