feat: 更新前端多个页面和后端服务

- 前端: 更新AI营养师、计算器、打卡、食物详情等页面
- 前端: 更新食物百科、知识详情、营养知识页面
- 前端: 更新社区首页
- 后端: 更新ToolKieAIServiceImpl服务
- API: 更新models-api.js和user.js
This commit is contained in:
2026-03-07 22:26:37 +08:00
parent 1632801880
commit f692c75f7b
11 changed files with 221 additions and 85 deletions

View File

@@ -14,13 +14,13 @@
<scroll-view class="content-scroll" scroll-y>
<!-- 食物大图 -->
<view class="food-image-section">
<image class="food-image" :src="foodData.image || defaultFoodData.image" mode="aspectFill"></image>
<image class="food-image" :src="displayImage" mode="aspectFill"></image>
<view class="image-overlay"></view>
<view class="food-info-overlay">
<view class="food-name-overlay">{{ foodData.name || '—' }}</view>
<view class="food-name-overlay">{{ displayName }}</view>
<view class="food-tags">
<view class="food-tag category">{{ foodData.category || '—' }}</view>
<view class="food-tag safe">{{ foodData.safetyTag || '—' }}</view>
<view class="food-tag category">{{ displayCategory }}</view>
<view class="food-tag safe">{{ displaySafetyTag }}</view>
</view>
</view>
</view>
@@ -34,7 +34,7 @@
<view class="key-nutrients-grid">
<view
class="nutrient-card"
v-for="(nutrient, index) in foodData.keyNutrients"
v-for="(nutrient, index) in displayKeyNutrients"
:key="index"
>
<text class="nutrient-name">{{ nutrient.name }}</text>
@@ -56,7 +56,7 @@
<view class="nutrition-table">
<view
class="nutrition-row"
v-for="(item, index) in foodData.nutritionTable"
v-for="(item, index) in displayNutritionTable"
:key="index"
>
<view class="nutrition-left">
@@ -132,21 +132,58 @@ export default {
}
}
},
computed: {
// 保证 .food-name-overlay / .nutrient-card / .nutrition-row 在 defaultFoodData 状态下也有非空数据可渲染
displayName() {
return (this.foodData && this.foodData.name) ? this.foodData.name : (this.defaultFoodData.name || '—')
},
displayCategory() {
return (this.foodData && this.foodData.category) ? this.foodData.category : (this.defaultFoodData.category || '—')
},
displaySafetyTag() {
return (this.foodData && this.foodData.safetyTag) ? this.foodData.safetyTag : (this.defaultFoodData.safetyTag || '—')
},
displayImage() {
return (this.foodData && this.foodData.image) ? this.foodData.image : (this.defaultFoodData.image || '')
},
displayKeyNutrients() {
const arr = this.foodData && this.foodData.keyNutrients
if (Array.isArray(arr) && arr.length > 0) return arr
const def = this.defaultFoodData.keyNutrients
return Array.isArray(def) && def.length > 0 ? def : [{ name: '—', value: '—', unit: '', status: '—' }]
},
displayNutritionTable() {
const arr = this.foodData && this.foodData.nutritionTable
if (Array.isArray(arr) && arr.length > 0) return arr
const def = this.defaultFoodData.nutritionTable
return Array.isArray(def) && def.length > 0 ? def : [{ name: '—', value: '—', unit: '', level: 'low', levelText: '—' }]
}
},
onLoad(options) {
this.pageParams = { id: options.id || '', name: options.name || '' }
options = options || {}
// 若列表误将 name 传成 id如 id=羊肉(熟)),用 id 作为展示用 name避免请求接口 400
const rawId = options.id
const rawName = options.name
const hasNonNumericId = rawId !== undefined && rawId !== '' && isNaN(Number(rawId))
const displayName = rawName || (hasNonNumericId ? rawId : '')
this.pageParams = {
id: (rawId !== undefined && rawId !== '') ? String(rawId) : '',
name: (displayName !== undefined && displayName !== '') ? String(displayName) : ''
}
// 打印入参便于排查:后端详情接口仅接受 Long 类型 id传 name 会导致 400
console.log('[food-detail] onLoad options:', options)
console.log('[food-detail] onLoad pageParams (id/name):', this.pageParams)
const rawId = options.id
const isNumericId = rawId !== undefined && rawId !== '' && !isNaN(Number(rawId))
if (isNumericId) {
this.loadFoodData(Number(rawId))
} else if (options.name) {
// 无有效 id 仅有 name 时,不请求接口(避免传 name 导致后端 NumberFormatException直接展示默认数据 + 当前名称
const numId = Number(rawId)
console.log('[food-detail] 使用数字 id 请求详情,不传 name 字段:', numId)
this.loadFoodData(numId)
} else if (this.pageParams.name) {
// 无有效 id、仅有 name 时,不请求接口(避免传 name 导致后端 NumberFormatException直接展示默认数据 + 当前名称
this.loadError = '暂无该食物详情数据,展示参考数据'
this.applyDefaultFoodData(false)
try {
this.foodData.name = decodeURIComponent(String(options.name))
this.foodData.name = decodeURIComponent(this.pageParams.name)
} catch (e) {}
} else {
this.applyDefaultFoodData()
@@ -192,8 +229,10 @@ export default {
console.log('[food-detail] getFoodDetail request param:', { id, type: typeof id })
try {
const res = await getFoodDetail(id)
const data = res.data || res
console.log('[food-detail] getFoodDetail 响应:', data ? { hasName: !!data.name, hasImage: !!data.image, keys: Object.keys(data) } : null)
// 打印响应结构便于确认request 成功时 resolve 的是 res.data即 { code: 200, data: {...} }
console.log('[food-detail] getFoodDetail 响应结构:', res ? { hasData: !!res.data, code: res.code, keys: Object.keys(res || {}) } : null)
const data = res.data != null ? res.data : res
console.log('[food-detail] getFoodDetail 解析后 data:', data ? { hasName: !!data.name, hasImage: !!data.image, keys: Object.keys(data) } : null)
if (data && data.name) {
// 解析API返回的食物数据
this.foodData = {
@@ -219,12 +258,14 @@ export default {
uni.showToast({ title: '数据加载失败', icon: 'none' })
}
} catch (error) {
const errMsg = (error && (error.message || error.msg || error.errMsg || error)) ? String(error.message || error.msg || error.errMsg || error) : '未知错误'
// a. 将 loadError 置为具体错误信息(用于调试):兼容 Error、{ message/msg }、字符串
const errMsg = (error && (error.message || error.msg || error.errMsg))
? String(error.message || error.msg || error.errMsg)
: (typeof error === 'string' ? error : (error ? String(error) : '未知错误'))
console.error('[food-detail] 加载食物数据失败:', error)
console.error('[food-detail] loadError(用于调试):', errMsg)
// a. 将 loadError 置为具体错误信息(用于调试)
this.loadError = errMsg
// b. 使用 defaultFoodData 填充页面,保证用户能看到基础界面;不清空 loadError 以便展示「当前数据来自缓存」提示
// b. 使用 defaultFoodData 填充页面,保证用户能看到基础界面而不是空白
this.applyDefaultFoodData(false)
// 若有入参 name用其覆盖展示名称避免显示默认「五谷香」
if (this.pageParams && this.pageParams.name) {
@@ -232,7 +273,7 @@ export default {
this.foodData.name = decodeURIComponent(String(this.pageParams.name))
} catch (e) {}
}
// c. 页面通过 v-if="loadError" 显示「当前数据来自缓存,可能不是最新」;再弹出轻提示
// c. 页面通过 v-if="loadError" 显示「当前数据来自缓存,可能不是最新」;再弹出轻提示
uni.showToast({
title: '数据加载失败',
icon: 'none'