fix: 修复帖子详情营养统计显示全为 "-" 的问题
问题原因: 1. fillNutritionStatsFromCheckin 即使全为 "-" 也会更新视图 2. buildNutritionStatsFromCheckinDetail 钾、磷被写死为 "-" 3. 字段解析不兼容(下划线命名、嵌套对象) 修复内容: - 仅当至少一项有效值时才更新 nutritionStats - 兼容多种字段命名(驼峰/下划线/别名) - 支持从嵌套对象解析(nutrition/dietaryData/mealData/aiResult) - 补全钾、磷字段解析 - 计算属性保护:全为 "-" 时视为空数组 由 Cursor CLI 检测并修复
This commit is contained in:
@@ -302,9 +302,13 @@ export default {
|
|||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
// 营养统计数组,用于卡片显示与列表渲染(单一数据源,避免未定义)
|
// 营养统计数组,用于卡片显示与列表渲染(单一数据源,避免未定义)
|
||||||
|
// 若全部为占位符 "-" 则视为无有效数据,不展示卡片,以便显示「AI 补充营养」等
|
||||||
nutritionStats() {
|
nutritionStats() {
|
||||||
const stats = this.postData && this.postData.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) {
|
onLoad(options) {
|
||||||
@@ -461,29 +465,59 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据打卡详情接口返回的数据构建 nutritionStats(蛋白质、热量、钾、磷)
|
* 根据打卡详情接口返回的数据构建 nutritionStats(热量、蛋白质、钾、磷)
|
||||||
|
* 后端返回:actualEnergy/actualProtein(驼峰)或 actual_energy/actual_protein(下划线);可能嵌套在 nutrition/dietaryData/aiResult 等
|
||||||
*/
|
*/
|
||||||
buildNutritionStatsFromCheckinDetail(detail) {
|
buildNutritionStatsFromCheckinDetail(detail) {
|
||||||
if (!detail || typeof detail !== 'object') return []
|
if (!detail || typeof detail !== 'object') return []
|
||||||
const energy = detail.actualEnergy ?? detail.energy
|
// 优先从嵌套营养对象解析(兼容 aiResult、nutrition、dietaryData 等含完整营养字段)
|
||||||
const protein = detail.actualProtein ?? detail.protein
|
const nested = detail.nutrition || detail.dietaryData || detail.dietary_data || detail.mealData || detail.meal_data
|
||||||
return [
|
if (nested && typeof nested === 'object' && !Array.isArray(nested)) {
|
||||||
{ label: '热量(kcal)', value: energy != null ? String(energy) : '-' },
|
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: this.formatNutritionValue(protein, 'g') },
|
||||||
{ label: '钾', value: '-' },
|
{ label: '钾', value: pot != null && pot !== '' ? (typeof pot === 'number' ? pot + 'mg' : String(pot)) : '-' },
|
||||||
{ label: '磷', value: '-' }
|
{ label: '磷', value: pho != null && pho !== '' ? (typeof pho === 'number' ? pho + 'mg' : String(pho)) : '-' }
|
||||||
]
|
]
|
||||||
|
return stats
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 根据打卡记录 ID 拉取打卡详情,并填充 postData.nutritionStats(不阻塞主流程)
|
* 根据打卡记录 ID 拉取打卡详情,并填充 postData.nutritionStats(不阻塞主流程)
|
||||||
|
* 接口路径:GET tool/checkin/detail/{id},返回 CommonResult 即 { code, data: { actualEnergy, actualProtein, ... } }
|
||||||
*/
|
*/
|
||||||
async fillNutritionStatsFromCheckin(checkInRecordId) {
|
async fillNutritionStatsFromCheckin(checkInRecordId) {
|
||||||
try {
|
try {
|
||||||
const res = await getCheckinDetail(checkInRecordId)
|
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)
|
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)
|
this.$set(this.postData, 'nutritionStats', stats)
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
Reference in New Issue
Block a user