diff --git a/msh_single_uniapp/pages/tool/post-detail.vue b/msh_single_uniapp/pages/tool/post-detail.vue index 91c01a9..0ea6e0c 100644 --- a/msh_single_uniapp/pages/tool/post-detail.vue +++ b/msh_single_uniapp/pages/tool/post-detail.vue @@ -302,9 +302,13 @@ export default { }, computed: { // 营养统计数组,用于卡片显示与列表渲染(单一数据源,避免未定义) + // 若全部为占位符 "-" 则视为无有效数据,不展示卡片,以便显示「AI 补充营养」等 nutritionStats() { const stats = this.postData && this.postData.nutritionStats - return Array.isArray(stats) ? stats : [] + const arr = Array.isArray(stats) ? stats : [] + if (arr.length === 0) return [] + const allDash = arr.every(s => s.value === '-' || s.value === '') + return allDash ? [] : arr } }, onLoad(options) { @@ -461,29 +465,59 @@ export default { }, /** - * 根据打卡详情接口返回的数据构建 nutritionStats(蛋白质、热量、钾、磷) + * 根据打卡详情接口返回的数据构建 nutritionStats(热量、蛋白质、钾、磷) + * 后端返回:actualEnergy/actualProtein(驼峰)或 actual_energy/actual_protein(下划线);可能嵌套在 nutrition/dietaryData/aiResult 等 */ buildNutritionStatsFromCheckinDetail(detail) { if (!detail || typeof detail !== 'object') return [] - const energy = detail.actualEnergy ?? detail.energy - const protein = detail.actualProtein ?? detail.protein - return [ - { label: '热量(kcal)', value: energy != null ? String(energy) : '-' }, + // 优先从嵌套营养对象解析(兼容 aiResult、nutrition、dietaryData 等含完整营养字段) + const nested = detail.nutrition || detail.dietaryData || detail.dietary_data || detail.mealData || detail.meal_data + if (nested && typeof nested === 'object' && !Array.isArray(nested)) { + const obj = typeof nested === 'string' ? (() => { try { return JSON.parse(nested) } catch (_) { return null } })() : nested + if (obj && typeof obj === 'object') { + const stats = this.buildNutritionStatsFromNutritionObject(obj) + if (stats.length > 0 && !stats.every(s => s.value === '-')) return stats + } + } + // aiResult 可能为 JSON 字符串,内含营养字段 + const aiResult = detail.aiResult || detail.ai_result + if (aiResult) { + try { + const parsed = typeof aiResult === 'string' ? JSON.parse(aiResult) : aiResult + if (parsed && typeof parsed === 'object') { + const stats = this.buildNutritionStatsFromNutritionObject(parsed) + if (stats.length > 0 && !stats.every(s => s.value === '-')) return stats + } + } catch (_) {} + } + // 顶层字段:后端 getDetail 返回 actualEnergy、actualProtein(驼峰) + const energy = detail.actualEnergy ?? detail.actual_energy ?? detail.energy ?? detail.energy_kcal + const protein = detail.actualProtein ?? detail.actual_protein ?? detail.protein ?? detail.protein_g ?? detail.proteinG + const pot = detail.potassiumMg ?? detail.potassium_mg ?? detail.potassium ?? detail.k + const pho = detail.phosphorusMg ?? detail.phosphorus_mg ?? detail.phosphorus ?? detail.p + const stats = [ + { label: '热量(kcal)', value: energy != null && energy !== '' ? String(energy) : '-' }, { label: '蛋白质', value: this.formatNutritionValue(protein, 'g') }, - { label: '钾', value: '-' }, - { label: '磷', value: '-' } + { label: '钾', value: pot != null && pot !== '' ? (typeof pot === 'number' ? pot + 'mg' : String(pot)) : '-' }, + { label: '磷', value: pho != null && pho !== '' ? (typeof pho === 'number' ? pho + 'mg' : String(pho)) : '-' } ] + return stats }, /** * 根据打卡记录 ID 拉取打卡详情,并填充 postData.nutritionStats(不阻塞主流程) + * 接口路径:GET tool/checkin/detail/{id},返回 CommonResult 即 { code, data: { actualEnergy, actualProtein, ... } } */ async fillNutritionStatsFromCheckin(checkInRecordId) { try { const res = await getCheckinDetail(checkInRecordId) - const detail = res.data || res + // 兼容:res 为 { code, data } 时取 res.data;部分封装直接返回 payload 则 res 即为 detail + const detail = (res && res.data !== undefined && res.data !== null) ? res.data : res + if (!detail || typeof detail !== 'object') return const stats = this.buildNutritionStatsFromCheckinDetail(detail) - if (stats.length > 0) { + // 仅当至少有一项有效值时才更新,避免展示全部为 "-" 的卡片 + const hasAnyValue = stats.length > 0 && stats.some(s => s.value !== '-' && s.value !== '') + if (hasAnyValue) { this.$set(this.postData, 'nutritionStats', stats) } } catch (e) {