fix: 修复6项测试问题并补全配套资源
- 修复油脂类食物推荐量系数 (5.7→2.5) [ToolCalculatorServiceImpl] - AI营养师接入真实Coze API,替换Mock回复 [ToolAiNutritionistServiceImpl] - 食物百科详情新增钙/铁/维C/嘌呤/重量基准字段返回 [ToolFoodServiceImpl] - V2Food模型新增purine、servingSize字段 [V2Food.java] - 食物百科详情页动态重量标注+新增4项营养展示+替换Figma URL [food-detail.vue] - 修复营养素列表dataset传参Bug(WeChat camelCase) [nutrition-knowledge.vue] - 营养素详情页接入后端API+兜底本地数据+替换Figma URL [nutrient-detail.vue] - 新增数据库迁移脚本及参考初始化数据 [docs/sql/] - 新增前端占位图标5个 [static/images/] - 新增开发任务完成报告 [docs/] Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
49
docs/sql/migration_2026-03-25_add_food_fields.sql
Normal file
49
docs/sql/migration_2026-03-25_add_food_fields.sql
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
-- ============================================================
|
||||||
|
-- 数据库迁移脚本
|
||||||
|
-- 版本: v1.0
|
||||||
|
-- 日期: 2026-03-25
|
||||||
|
-- 描述: 为 v2_food 表新增嘌呤含量和重量基准字段
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 1. 新增 purine 字段(嘌呤含量,单位 mg)
|
||||||
|
ALTER TABLE v2_food
|
||||||
|
ADD COLUMN purine DECIMAL(10, 2) DEFAULT NULL COMMENT '嘌呤含量(mg)';
|
||||||
|
|
||||||
|
-- 2. 新增 serving_size 字段(营养成分对应重量基准)
|
||||||
|
ALTER TABLE v2_food
|
||||||
|
ADD COLUMN serving_size VARCHAR(50) DEFAULT '每100g' COMMENT '营养成分对应的食物重量基准,如"每100g"、"每份(50g)"';
|
||||||
|
|
||||||
|
-- 验证
|
||||||
|
DESC v2_food;
|
||||||
|
|
||||||
|
-- ============================================================
|
||||||
|
-- 参考数据初始化示例(按需执行)
|
||||||
|
-- 数据来源: https://www.ishen365.com/article/cereal
|
||||||
|
-- ============================================================
|
||||||
|
|
||||||
|
-- 谷薯类
|
||||||
|
UPDATE v2_food SET calcium = 13, iron = 2.3, vitamin_c = 0, purine = 18.4, serving_size = '每100g' WHERE name = '大米' AND category_name LIKE '%谷%';
|
||||||
|
UPDATE v2_food SET calcium = 34, iron = 5.1, vitamin_c = 0, purine = 25.0, serving_size = '每100g' WHERE name = '小米' AND category_name LIKE '%谷%';
|
||||||
|
UPDATE v2_food SET calcium = 38, iron = 5.9, vitamin_c = 0, purine = 22.4, serving_size = '每100g' WHERE name = '玉米' AND category_name LIKE '%谷%';
|
||||||
|
UPDATE v2_food SET calcium = 31, iron = 3.5, vitamin_c = 0, purine = 17.1, serving_size = '每100g' WHERE name = '面粉' AND category_name LIKE '%谷%';
|
||||||
|
|
||||||
|
-- 蔬菜类
|
||||||
|
UPDATE v2_food SET calcium = 48, iron = 1.2, vitamin_c = 14, purine = 10.1, serving_size = '每100g' WHERE name = '菠菜' AND category_name LIKE '%蔬%';
|
||||||
|
UPDATE v2_food SET calcium = 36, iron = 0.5, vitamin_c = 4, purine = 5.5, serving_size = '每100g' WHERE name = '西红柿' AND category_name LIKE '%蔬%';
|
||||||
|
UPDATE v2_food SET calcium = 21, iron = 0.3, vitamin_c = 2, purine = 3.4, serving_size = '每100g' WHERE name = '土豆' AND category_name LIKE '%蔬%';
|
||||||
|
|
||||||
|
-- 水果类
|
||||||
|
UPDATE v2_food SET calcium = 4, iron = 0.6, vitamin_c = 8, purine = 0.9, serving_size = '每100g' WHERE name = '苹果' AND category_name LIKE '%水果%';
|
||||||
|
UPDATE v2_food SET calcium = 7, iron = 0.2, vitamin_c = 8, purine = 1.9, serving_size = '每100g' WHERE name = '香蕉' AND category_name LIKE '%水果%';
|
||||||
|
|
||||||
|
-- 肉蛋类
|
||||||
|
UPDATE v2_food SET calcium = 11, iron = 2.5, vitamin_c = 0, purine = 122.5, serving_size = '每100g' WHERE name = '猪肉' AND category_name LIKE '%肉%';
|
||||||
|
UPDATE v2_food SET calcium = 9, iron = 2.3, vitamin_c = 0, purine = 107.6, serving_size = '每100g' WHERE name = '牛肉' AND category_name LIKE '%肉%';
|
||||||
|
UPDATE v2_food SET calcium = 56, iron = 2.0, vitamin_c = 0, purine = 2.6, serving_size = '每100g' WHERE name = '鸡蛋' AND category_name LIKE '%蛋%';
|
||||||
|
|
||||||
|
-- 豆类
|
||||||
|
UPDATE v2_food SET calcium = 191, iron = 8.2, vitamin_c = 0, purine = 166.5, serving_size = '每100g' WHERE name = '黄豆' AND category_name LIKE '%豆%';
|
||||||
|
UPDATE v2_food SET calcium = 164, iron = 6.4, vitamin_c = 0, purine = 116.9, serving_size = '每100g' WHERE name = '豆腐' AND category_name LIKE '%豆%';
|
||||||
|
|
||||||
|
-- 将所有未设置 serving_size 的记录默认填充
|
||||||
|
UPDATE v2_food SET serving_size = '每100g' WHERE serving_size IS NULL OR serving_size = '';
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
> **版本:** v1.0
|
> **版本:** v1.0
|
||||||
> **日期:** 2026-03-25
|
> **日期:** 2026-03-25
|
||||||
> **依据:** 《测试问题分析报告_2026-03-22》
|
> **依据:** 《测试问题分析报告_2026-03-22》
|
||||||
> **项目:** 民生汇 - 慢性肾病营养管理小程序
|
> **项目:** 慢生活- 慢性肾病营养管理小程序
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -320,7 +320,7 @@ async sendToCoze(content, images = []) {
|
|||||||
**建议系统提示词:**
|
**建议系统提示词:**
|
||||||
|
|
||||||
```
|
```
|
||||||
你是民生汇小程序的 AI 营养师,专注于慢性肾脏病(CKD)患者的饮食营养指导。
|
你是慢生活小程序的 AI 营养师,专注于慢性肾脏病(CKD)患者的饮食营养指导。
|
||||||
|
|
||||||
回复规范:
|
回复规范:
|
||||||
1. 【一句话建议】用一句话直接回答用户问题
|
1. 【一句话建议】用一句话直接回答用户问题
|
||||||
|
|||||||
94
docs/开发任务完成报告_2026-03-25.md
Normal file
94
docs/开发任务完成报告_2026-03-25.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# 开发任务完成报告 2026-03-25
|
||||||
|
|
||||||
|
## 任务总览
|
||||||
|
|
||||||
|
| 任务 | 状态 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| 任务 0:数据库表结构变更 | ✅ 完成 | V2Food.java 新增 purine、servingSize 字段 |
|
||||||
|
| 任务 1:油脂系数 Bug 修复 | ✅ 完成 | 油脂类系数 5.7 → 2.5 |
|
||||||
|
| 任务 2:AI 营养师接入 Coze API | ✅ 完成 | 替换 Mock 回复为 Coze API 调用 |
|
||||||
|
| 任务 3:食物百科详情字段补全 | ✅ 完成 | 新增钙、铁、维生素C、嘌呤、重量基准 |
|
||||||
|
| 任务 4:食物百科详情页前端修复 | ✅ 完成 | 动态重量标注 + Figma URL 替换 + 营养成分扩展 |
|
||||||
|
| 任务 5:营养素列表传参 Bug 修复 | ✅ 完成 | dataset 传参改为直接传参 |
|
||||||
|
| 任务 6:营养素详情页接入后端 API | ✅ 完成 | 优先 API、本地 map 兜底 + Figma URL 替换 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 详细修改记录
|
||||||
|
|
||||||
|
### 任务 0:数据库表结构变更
|
||||||
|
|
||||||
|
**文件:** `msh_crmeb_22/crmeb-common/src/main/java/com/zbkj/common/model/tool/V2Food.java`
|
||||||
|
|
||||||
|
- 在 vitaminC 字段后(约第 70 行)新增:
|
||||||
|
- `purine` (BigDecimal):嘌呤含量(mg),对应数据库字段 `purine`
|
||||||
|
- `servingSize` (String):营养成分重量基准,对应数据库字段 `serving_size`
|
||||||
|
- 类已使用 `@Data` 注解(Lombok),无需手动添加 getter/setter
|
||||||
|
|
||||||
|
### 任务 1:油脂系数 Bug 修复
|
||||||
|
|
||||||
|
**文件:** `msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolCalculatorServiceImpl.java`
|
||||||
|
|
||||||
|
- 第 516 行:`round(5.7 * energyRatio)` → `round(2.5 * energyRatio)`
|
||||||
|
- 验证:文件中仅第 510 行谷薯类保留 `5.7` 系数,油脂类已改为 `2.5`
|
||||||
|
|
||||||
|
### 任务 2:AI 营养师接入 Coze API
|
||||||
|
|
||||||
|
**文件:** `msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolAiNutritionistServiceImpl.java`
|
||||||
|
|
||||||
|
- 新增注入:`ToolCozeService toolCozeService`
|
||||||
|
- 新增 import:`ToolCozeService`、`CozeChatRequest`、`CozeBaseResponse`
|
||||||
|
- `sendMessage()` 方法中:
|
||||||
|
- 移除 Mock 回复 `"这是一个模拟的AI回复。"`
|
||||||
|
- 替换为 `toolCozeService.chat(cozeRequest)` 调用
|
||||||
|
- Bot ID: `7591133240535449654`
|
||||||
|
- 添加 try-catch 异常处理,失败时设置 `aiResponseStatus("failed")`
|
||||||
|
|
||||||
|
### 任务 3:食物百科详情字段补全
|
||||||
|
|
||||||
|
**文件:** `msh_crmeb_22/crmeb-service/src/main/java/com/zbkj/service/service/impl/tool/ToolFoodServiceImpl.java`
|
||||||
|
|
||||||
|
- `getDetail()` 方法中(约第 121 行后)新增 5 个字段返回:
|
||||||
|
- `calcium`、`iron`、`vitaminC`、`purine`、`servingSize`
|
||||||
|
|
||||||
|
### 任务 4:食物百科详情页前端修复
|
||||||
|
|
||||||
|
**文件:** `msh_single_uniapp/pages/tool/food-detail.vue`
|
||||||
|
|
||||||
|
- **修复点 A:** 两处 `"每100g"` 硬编码替换为 `{{ foodData.servingSize || '每100g' }}`
|
||||||
|
- **修复点 B:** `parseNutritionTable` 中新增铁 (iron) 和维生素C (vitaminC) 营养项;`parseKeyNutrients` 和 `parseNutritionTable` 的 nutrients 来源改为兼容平铺字段 (`data.nutrients || data.nutritionData || data`)
|
||||||
|
- **修复点 C:** 3 处 Figma 临时 URL 替换为本地占位图路径(`/static/images/icon-share.png`、`/static/images/icon-search.png`、空字符串)
|
||||||
|
|
||||||
|
### 任务 5:营养素列表传参 Bug 修复
|
||||||
|
|
||||||
|
**文件:** `msh_single_uniapp/pages/tool/nutrition-knowledge.vue`
|
||||||
|
|
||||||
|
- 第 39 行:`@click="goToNutrientDetail" :data-nutrient-index="index"` → `@click="goToNutrientDetail(index)"`
|
||||||
|
- `goToNutrientDetail` 方法:参数从 `event` 改为直接接收 `index`,移除 `event.currentTarget.dataset.nutrientIndex` 取值逻辑
|
||||||
|
|
||||||
|
### 任务 6:营养素详情页接入后端 API
|
||||||
|
|
||||||
|
**文件:** `msh_single_uniapp/pages/tool/nutrient-detail.vue`
|
||||||
|
|
||||||
|
- **修复点 A:** `nutrientData` 默认值从硬编码的"钠"数据改为空对象 `{}`
|
||||||
|
- **修复点 B:** `loadNutrientData()` 方法改为 async,优先调用 `getNutrientDetail(name)` API,失败时使用本地 `nutrientMap` 兜底
|
||||||
|
- **修复点 C:** 3 处 Figma 临时图片 URL 替换为本地占位图路径
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 遇到的问题及处理方式
|
||||||
|
|
||||||
|
1. **ToolCozeService 接口设计**:`chat()` 方法接收 `CozeChatRequest` 对象而非简单的 `(botId, message, userId)` 参数。处理方式:构造完整的 `CozeChatRequest` 对象,设置 `botId`、`userId`、`additionalMessages` 等字段。
|
||||||
|
|
||||||
|
2. **food-detail.vue 营养数据解析**:后端返回的是平铺字段(如 `data.calcium`),而前端解析方法原本从 `data.nutrients` 子对象取值。处理方式:将 nutrients 来源改为 `data.nutrients || data.nutritionData || data`,兼容两种数据结构。
|
||||||
|
|
||||||
|
3. **Figma 临时图片 URL**:多个 Vue 文件中使用了 Figma API 的临时资源 URL。处理方式:统一替换为本地静态资源路径(`/static/images/xxx.png`),需后续补充实际图标文件。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 后续待办
|
||||||
|
|
||||||
|
- [ ] 数据库执行 ALTER TABLE 添加 `purine` 和 `serving_size` 字段
|
||||||
|
- [ ] 补充 `/static/images/` 下的图标文件(icon-share.png、icon-search.png、icon-why-important.png、icon-recommendation.png、icon-suggestions.png)
|
||||||
|
- [ ] 后端实现 `getNutrientDetail` 接口(若尚未实现)
|
||||||
|
- [ ] 用户手动执行 `git push origin main`
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
> **日期:** 2026-03-22
|
> **日期:** 2026-03-22
|
||||||
> **分析人:** Claude AI
|
> **分析人:** Claude AI
|
||||||
> **项目:** 民生汇 - 慢性肾病营养管理小程序
|
> **项目:** 慢生活 - 慢性肾病营养管理小程序
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -68,6 +68,16 @@ public class V2Food implements Serializable {
|
|||||||
@ApiModelProperty(value = "维生素C(mg)")
|
@ApiModelProperty(value = "维生素C(mg)")
|
||||||
private BigDecimal vitaminC;
|
private BigDecimal vitaminC;
|
||||||
|
|
||||||
|
/** 嘌呤含量(mg) */
|
||||||
|
@ApiModelProperty(value = "嘌呤含量(mg)")
|
||||||
|
@com.baomidou.mybatisplus.annotation.TableField("purine")
|
||||||
|
private BigDecimal purine;
|
||||||
|
|
||||||
|
/** 营养成分重量基准 */
|
||||||
|
@ApiModelProperty(value = "营养成分重量基准")
|
||||||
|
@com.baomidou.mybatisplus.annotation.TableField("serving_size")
|
||||||
|
private String servingSize;
|
||||||
|
|
||||||
@ApiModelProperty(value = "其他营养成分,JSON格式")
|
@ApiModelProperty(value = "其他营养成分,JSON格式")
|
||||||
private String nutrientsJson;
|
private String nutrientsJson;
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ import com.zbkj.common.token.FrontTokenComponent;
|
|||||||
import com.zbkj.service.dao.tool.V2AiConversationDao;
|
import com.zbkj.service.dao.tool.V2AiConversationDao;
|
||||||
import com.zbkj.service.dao.tool.V2AiMessageDao;
|
import com.zbkj.service.dao.tool.V2AiMessageDao;
|
||||||
import com.zbkj.service.service.tool.ToolAiNutritionistService;
|
import com.zbkj.service.service.tool.ToolAiNutritionistService;
|
||||||
|
import com.zbkj.service.service.tool.ToolCozeService;
|
||||||
|
import com.zbkj.common.request.coze.CozeChatRequest;
|
||||||
|
import com.zbkj.common.response.CozeBaseResponse;
|
||||||
import lombok.extern.slf4j.Slf4j;
|
import lombok.extern.slf4j.Slf4j;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
@@ -39,6 +42,9 @@ public class ToolAiNutritionistServiceImpl implements ToolAiNutritionistService
|
|||||||
@Autowired
|
@Autowired
|
||||||
private FrontTokenComponent frontTokenComponent;
|
private FrontTokenComponent frontTokenComponent;
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private ToolCozeService toolCozeService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 发送消息给AI营养师
|
* 发送消息给AI营养师
|
||||||
* @param data 消息数据
|
* @param data 消息数据
|
||||||
@@ -86,13 +92,43 @@ public class ToolAiNutritionistServiceImpl implements ToolAiNutritionistService
|
|||||||
|
|
||||||
v2AiMessageDao.insert(message);
|
v2AiMessageDao.insert(message);
|
||||||
|
|
||||||
// Mock AI response
|
// 调用 Coze API 获取 AI 回复
|
||||||
// In real system, this would call AI service
|
try {
|
||||||
// Here we just set a mock response for now
|
String botId = "7591133240535449654";
|
||||||
message.setAiResponse("这是一个模拟的AI回复。");
|
String userMessage = (String) data.get("content");
|
||||||
|
|
||||||
|
CozeChatRequest cozeRequest = new CozeChatRequest();
|
||||||
|
cozeRequest.setBotId(botId);
|
||||||
|
cozeRequest.setUserId(String.valueOf(userId));
|
||||||
|
cozeRequest.setStream(false);
|
||||||
|
|
||||||
|
// 构建消息
|
||||||
|
CozeChatRequest.ChatMessage chatMessage = new CozeChatRequest.ChatMessage();
|
||||||
|
chatMessage.setRole("user");
|
||||||
|
chatMessage.setContent(userMessage);
|
||||||
|
chatMessage.setContentType("text");
|
||||||
|
cozeRequest.setAdditionalMessages(java.util.Collections.singletonList(
|
||||||
|
new java.util.HashMap<String, Object>() {{
|
||||||
|
put("role", "user");
|
||||||
|
put("content", userMessage);
|
||||||
|
put("content_type", "text");
|
||||||
|
}}
|
||||||
|
));
|
||||||
|
|
||||||
|
CozeBaseResponse<Object> cozeResponse = toolCozeService.chat(cozeRequest);
|
||||||
|
if (cozeResponse != null && cozeResponse.getData() != null) {
|
||||||
|
message.setAiResponse(String.valueOf(cozeResponse.getData()));
|
||||||
message.setAiResponseStatus("success");
|
message.setAiResponseStatus("success");
|
||||||
|
} else {
|
||||||
|
message.setAiResponse("AI服务暂时无法响应,请稍后再试。");
|
||||||
|
message.setAiResponseStatus("failed");
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.error("Coze API调用失败, userId={}, conversationId={}", userId, conversationId, e);
|
||||||
|
message.setAiResponse("AI服务调用失败,请稍后再试。");
|
||||||
|
message.setAiResponseStatus("failed");
|
||||||
|
}
|
||||||
message.setAiResponseTime(new Date());
|
message.setAiResponseTime(new Date());
|
||||||
// message.setSender("ai"); // Keep sender as user, response is in aiResponse field
|
|
||||||
|
|
||||||
v2AiMessageDao.updateById(message);
|
v2AiMessageDao.updateById(message);
|
||||||
|
|
||||||
|
|||||||
@@ -513,7 +513,7 @@ public class ToolCalculatorServiceImpl extends ServiceImpl<V2CalculatorResultDao
|
|||||||
list.add(createFoodPortion(4, "瓜果蔬菜200g", "2"));
|
list.add(createFoodPortion(4, "瓜果蔬菜200g", "2"));
|
||||||
list.add(createFoodPortion(5, "奶类230g", "1"));
|
list.add(createFoodPortion(5, "奶类230g", "1"));
|
||||||
list.add(createFoodPortion(6, "肉蛋类50/60g", round(protein.doubleValue() / 8.0))); // 约 8g 蛋白/份
|
list.add(createFoodPortion(6, "肉蛋类50/60g", round(protein.doubleValue() / 8.0))); // 约 8g 蛋白/份
|
||||||
list.add(createFoodPortion(7, "油脂类10g", round(5.7 * energyRatio)));
|
list.add(createFoodPortion(7, "油脂类10g", round(2.5 * energyRatio)));
|
||||||
|
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,6 +119,11 @@ public class ToolFoodServiceImpl implements ToolFoodService {
|
|||||||
map.put("cautionLevel", food.getCautionLevel());
|
map.put("cautionLevel", food.getCautionLevel());
|
||||||
map.put("cautionDesc", food.getCautionDesc());
|
map.put("cautionDesc", food.getCautionDesc());
|
||||||
map.put("cookingTips", food.getCookingTips());
|
map.put("cookingTips", food.getCookingTips());
|
||||||
|
map.put("calcium", food.getCalcium());
|
||||||
|
map.put("iron", food.getIron());
|
||||||
|
map.put("vitaminC", food.getVitaminC());
|
||||||
|
map.put("purine", food.getPurine());
|
||||||
|
map.put("servingSize", food.getServingSize());
|
||||||
return map;
|
return map;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<view class="key-nutrients-section">
|
<view class="key-nutrients-section">
|
||||||
<view class="section-header">
|
<view class="section-header">
|
||||||
<text class="section-title">关键营养成分</text>
|
<text class="section-title">关键营养成分</text>
|
||||||
<view class="unit-badge">每100g</view>
|
<view class="unit-badge">{{ foodData.servingSize || '每100g' }}</view>
|
||||||
</view>
|
</view>
|
||||||
<view class="key-nutrients-grid">
|
<view class="key-nutrients-grid">
|
||||||
<view
|
<view
|
||||||
@@ -51,7 +51,7 @@
|
|||||||
<view class="nutrition-table-section">
|
<view class="nutrition-table-section">
|
||||||
<view class="section-header">
|
<view class="section-header">
|
||||||
<text class="section-title">营养成分表</text>
|
<text class="section-title">营养成分表</text>
|
||||||
<text class="unit-text">每100g</text>
|
<text class="unit-text">{{ foodData.servingSize || '每100g' }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="nutrition-table">
|
<view class="nutrition-table">
|
||||||
<view
|
<view
|
||||||
@@ -92,8 +92,8 @@ import { getFoodDetail } from '@/api/tool.js';
|
|||||||
export default {
|
export default {
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
iconShare: 'https://www.figma.com/api/mcp/asset/f9f0d7b9-89c0-48d4-9e04-7140229e42f0',
|
iconShare: '/static/images/icon-share.png',
|
||||||
iconSearch: 'https://www.figma.com/api/mcp/asset/aa6bb75b-0a9d-43cb-aaa4-6a71993fbd4d',
|
iconSearch: '/static/images/icon-search.png',
|
||||||
loading: false,
|
loading: false,
|
||||||
/** 加载失败时的具体错误信息,用于调试;有值时页面会展示「当前数据来自缓存」提示 */
|
/** 加载失败时的具体错误信息,用于调试;有值时页面会展示「当前数据来自缓存」提示 */
|
||||||
loadError: '',
|
loadError: '',
|
||||||
@@ -114,7 +114,7 @@ export default {
|
|||||||
category: '谷薯类',
|
category: '谷薯类',
|
||||||
categoryType: 'grain',
|
categoryType: 'grain',
|
||||||
safetyTag: '放心吃',
|
safetyTag: '放心吃',
|
||||||
image: 'https://www.figma.com/api/mcp/asset/bf4ff04c-1322-474a-bd03-5b8a49fe33ad',
|
image: '',
|
||||||
keyNutrients: [
|
keyNutrients: [
|
||||||
{ name: '磷', value: '13', unit: 'mg', status: '正常' },
|
{ name: '磷', value: '13', unit: 'mg', status: '正常' },
|
||||||
{ name: '钾', value: '7', unit: 'mg', status: '正常' },
|
{ name: '钾', value: '7', unit: 'mg', status: '正常' },
|
||||||
@@ -315,8 +315,8 @@ export default {
|
|||||||
if (data.keyNutrients && Array.isArray(data.keyNutrients)) {
|
if (data.keyNutrients && Array.isArray(data.keyNutrients)) {
|
||||||
return data.keyNutrients
|
return data.keyNutrients
|
||||||
}
|
}
|
||||||
// 从详细营养数据中提取关键营养素
|
// 从详细营养数据中提取关键营养素(兼容 data.nutrients 子对象和 data 平铺字段)
|
||||||
const nutrients = data.nutrients || data.nutritionData || {}
|
const nutrients = data.nutrients || data.nutritionData || data
|
||||||
const keyList = []
|
const keyList = []
|
||||||
if (nutrients.phosphorus !== undefined) keyList.push({ name: '磷', value: String(nutrients.phosphorus), unit: 'mg', status: this.getStatus(nutrients.phosphorus, 'phosphorus') })
|
if (nutrients.phosphorus !== undefined) keyList.push({ name: '磷', value: String(nutrients.phosphorus), unit: 'mg', status: this.getStatus(nutrients.phosphorus, 'phosphorus') })
|
||||||
if (nutrients.potassium !== undefined) keyList.push({ name: '钾', value: String(nutrients.potassium), unit: 'mg', status: this.getStatus(nutrients.potassium, 'potassium') })
|
if (nutrients.potassium !== undefined) keyList.push({ name: '钾', value: String(nutrients.potassium), unit: 'mg', status: this.getStatus(nutrients.potassium, 'potassium') })
|
||||||
@@ -328,7 +328,8 @@ export default {
|
|||||||
if (data.nutritionTable && Array.isArray(data.nutritionTable)) {
|
if (data.nutritionTable && Array.isArray(data.nutritionTable)) {
|
||||||
return data.nutritionTable
|
return data.nutritionTable
|
||||||
}
|
}
|
||||||
const nutrients = data.nutrients || data.nutritionData || {}
|
// 兼容 data.nutrients 子对象和 data 平铺字段
|
||||||
|
const nutrients = data.nutrients || data.nutritionData || data
|
||||||
const table = []
|
const table = []
|
||||||
const items = [
|
const items = [
|
||||||
{ key: 'potassium', name: '钾', unit: 'mg', thresholds: [200, 500] },
|
{ key: 'potassium', name: '钾', unit: 'mg', thresholds: [200, 500] },
|
||||||
@@ -336,12 +337,15 @@ export default {
|
|||||||
{ key: 'phosphorus', name: '磷', unit: 'mg', thresholds: [100, 300] },
|
{ key: 'phosphorus', name: '磷', unit: 'mg', thresholds: [100, 300] },
|
||||||
{ key: 'protein', name: '蛋白质', unit: 'g', thresholds: [5, 15] },
|
{ key: 'protein', name: '蛋白质', unit: 'g', thresholds: [5, 15] },
|
||||||
{ key: 'sodium', name: '钠', unit: 'mg', thresholds: [100, 500] },
|
{ key: 'sodium', name: '钠', unit: 'mg', thresholds: [100, 500] },
|
||||||
|
{ key: 'iron', name: '铁', unit: 'mg', thresholds: [5, 15] },
|
||||||
|
{ key: 'vitaminC', name: '维生素C', unit: 'mg', thresholds: [20, 60] },
|
||||||
{ key: 'purine', name: '嘌呤', unit: 'mg', thresholds: [50, 150] }
|
{ key: 'purine', name: '嘌呤', unit: 'mg', thresholds: [50, 150] }
|
||||||
]
|
]
|
||||||
items.forEach(item => {
|
items.forEach(item => {
|
||||||
const val = nutrients[item.key]
|
const val = nutrients[item.key]
|
||||||
if (val !== undefined) {
|
if (val !== undefined && val !== null) {
|
||||||
const level = val <= item.thresholds[0] ? 'low' : (val <= item.thresholds[1] ? 'medium' : 'high')
|
const numVal = Number(val)
|
||||||
|
const level = numVal <= item.thresholds[0] ? 'low' : (numVal <= item.thresholds[1] ? 'medium' : 'high')
|
||||||
const levelText = level === 'low' ? '低' : (level === 'medium' ? '中' : '高')
|
const levelText = level === 'low' ? '低' : (level === 'medium' ? '中' : '高')
|
||||||
table.push({ name: item.name, value: String(val), unit: item.unit, level, levelText })
|
table.push({ name: item.name, value: String(val), unit: item.unit, level, levelText })
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -108,40 +108,34 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
// 图标资源
|
// 图标资源
|
||||||
iconWhyImportant: 'https://www.figma.com/api/mcp/asset/51e00c9b-5719-4391-9dff-68b70d24aece',
|
iconWhyImportant: '/static/images/icon-why-important.png',
|
||||||
iconRecommendation: 'https://www.figma.com/api/mcp/asset/29e03ae9-a3ba-4416-8c6e-962bf3a8c8bb',
|
iconRecommendation: '/static/images/icon-recommendation.png',
|
||||||
iconSuggestions: 'https://www.figma.com/api/mcp/asset/9027fced-f029-4f65-bba1-a0b5883b8d2b',
|
iconSuggestions: '/static/images/icon-suggestions.png',
|
||||||
nutrientData: {
|
nutrientName: '',
|
||||||
name: '钠',
|
nutrientData: {}
|
||||||
english: 'Sodium (Na)',
|
|
||||||
icon: '🧂',
|
|
||||||
description: '调节体液平衡的电解质',
|
|
||||||
status: '适量控制',
|
|
||||||
statusDesc: '可适量补充,保持均衡',
|
|
||||||
importance: '钠参与调节体液平衡和血压,过多摄入会导致水肿和高血压,增加心血管负担。',
|
|
||||||
recommendation: 'CKD患者:2-3g食盐/天(相当于800-1200mg钠)',
|
|
||||||
foodSources: ['食盐', '酱油', '腌制食品', '加工肉类', '咸菜', '味精'],
|
|
||||||
riskWarning: '摄入过多会导致水肿、高血压、心力衰竭等问题。',
|
|
||||||
suggestions: [
|
|
||||||
'每日食盐控制在3-5g(约一啤酒瓶盖)',
|
|
||||||
'避免腌制、熏制食品',
|
|
||||||
'少用酱油、味精等调味品',
|
|
||||||
'可用葱姜蒜、柠檬汁调味',
|
|
||||||
'查看食品标签,选择低钠产品',
|
|
||||||
'透析患者控制饮水量'
|
|
||||||
],
|
|
||||||
disclaimer: '以上建议仅供参考,\n\r 具体方案请咨询您的医生或营养师'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onLoad(options) {
|
onLoad(options) {
|
||||||
// 根据传入的营养素名称加载对应数据
|
// 根据传入的营养素名称加载对应数据
|
||||||
if (options.name) {
|
if (options.name) {
|
||||||
this.loadNutrientData(options.name)
|
this.nutrientName = decodeURIComponent(options.name)
|
||||||
|
this.loadNutrientData(this.nutrientName)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
loadNutrientData(name) {
|
async loadNutrientData(name) {
|
||||||
|
// 优先调用后端接口获取营养素详情
|
||||||
|
try {
|
||||||
|
const { getNutrientDetail } = await import('@/api/tool.js')
|
||||||
|
const res = await getNutrientDetail(name)
|
||||||
|
if (res && res.data) {
|
||||||
|
this.nutrientData = res.data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('API获取营养素详情失败,使用本地数据', e)
|
||||||
|
}
|
||||||
|
// 兜底:使用本地 nutrientMap
|
||||||
const nutrientMap = {
|
const nutrientMap = {
|
||||||
'钠': {
|
'钠': {
|
||||||
name: '钠',
|
name: '钠',
|
||||||
@@ -271,9 +265,7 @@ export default {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (nutrientMap[name]) {
|
this.nutrientData = nutrientMap[name] || {}
|
||||||
this.nutrientData = nutrientMap[name]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
class="nutrient-card"
|
class="nutrient-card"
|
||||||
v-for="(item, index) in nutrientList"
|
v-for="(item, index) in nutrientList"
|
||||||
:key="index"
|
:key="index"
|
||||||
@click="goToNutrientDetail" :data-nutrient-index="index"
|
@click="goToNutrientDetail(index)"
|
||||||
>
|
>
|
||||||
<view class="nutrient-icon-wrapper">
|
<view class="nutrient-icon-wrapper">
|
||||||
<text class="nutrient-icon">{{ item.icon }}</text>
|
<text class="nutrient-icon">{{ item.icon }}</text>
|
||||||
@@ -290,8 +290,7 @@ export default {
|
|||||||
this.currentTab = tab;
|
this.currentTab = tab;
|
||||||
await this.loadKnowledgeList();
|
await this.loadKnowledgeList();
|
||||||
},
|
},
|
||||||
goToNutrientDetail(event) {
|
goToNutrientDetail(index) {
|
||||||
const index = event.currentTarget.dataset.nutrientIndex;
|
|
||||||
const item = this.nutrientList[index];
|
const item = this.nutrientList[index];
|
||||||
if (!item) return;
|
if (!item) return;
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
|
|||||||
BIN
msh_single_uniapp/static/images/icon-recommendation.png
Normal file
BIN
msh_single_uniapp/static/images/icon-recommendation.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 238 B |
BIN
msh_single_uniapp/static/images/icon-search.png
Normal file
BIN
msh_single_uniapp/static/images/icon-search.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 238 B |
BIN
msh_single_uniapp/static/images/icon-share.png
Normal file
BIN
msh_single_uniapp/static/images/icon-share.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 238 B |
BIN
msh_single_uniapp/static/images/icon-suggestions.png
Normal file
BIN
msh_single_uniapp/static/images/icon-suggestions.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 238 B |
BIN
msh_single_uniapp/static/images/icon-why-important.png
Normal file
BIN
msh_single_uniapp/static/images/icon-why-important.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 238 B |
Reference in New Issue
Block a user