feat: 集成 KieAI 服务,移除 models-integration 子项目
- 添加 Gemini 2.5 Flash 对话接口(流式+非流式) - 添加 NanoBanana 图像生成/编辑接口 - 添加 Sora2 视频生成接口(文生视频、图生视频、去水印) - 移除 models-integration 子项目(功能已迁移至主后端) - 新增测试文档和 Playwright E2E 配置 - 更新前端页面和 API 接口 - 更新后端配置和日志处理
This commit is contained in:
@@ -110,8 +110,8 @@
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 营养统计卡片 -->
|
||||
<view class="nutrition-stats-card" v-if="postData.nutritionStats && postData.nutritionStats.length > 0">
|
||||
<!-- 营养统计卡片:仅根据是否有数据展示 -->
|
||||
<view class="nutrition-stats-card" v-if="nutritionStatsLength > 0">
|
||||
<view class="stats-header">
|
||||
<view class="stats-title">
|
||||
<text class="title-icon">📊</text>
|
||||
@@ -233,6 +233,7 @@
|
||||
<script>
|
||||
import {
|
||||
getCommunityDetail,
|
||||
getCheckinDetail,
|
||||
toggleLike as apiToggleLike,
|
||||
toggleCollect,
|
||||
toggleFollow as apiToggleFollow,
|
||||
@@ -284,6 +285,13 @@ export default {
|
||||
commentText: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
// 营养统计条数,用于卡片显示条件(避免 postData.nutritionStats 未定义时报错)
|
||||
nutritionStatsLength() {
|
||||
const stats = this.postData && this.postData.nutritionStats
|
||||
return Array.isArray(stats) ? stats.length : 0
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
if (options.id) {
|
||||
this.postId = options.id
|
||||
@@ -301,6 +309,11 @@ export default {
|
||||
|
||||
// 格式化帖子数据
|
||||
this.formatPostData(data)
|
||||
|
||||
// 若详情接口未返回营养数据且有关联打卡记录,则根据打卡详情补充营养统计(等待完成后再结束加载)
|
||||
if (this.postData.nutritionStats.length === 0 && (data.checkInRecordId != null)) {
|
||||
await this.fillNutritionStatsFromCheckin(data.checkInRecordId)
|
||||
}
|
||||
|
||||
// 同步状态
|
||||
this.isLiked = data.isLiked || false
|
||||
@@ -324,6 +337,95 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 从详情接口返回的数据中构建 nutritionStats 数组
|
||||
* 支持:nutritionStats/nutrition_stats 数组、nutritionDataJson/nutrition_data_json、dietaryData/mealData 等
|
||||
*/
|
||||
buildNutritionStatsFromDetailData(data) {
|
||||
if (!data) return []
|
||||
|
||||
// 1) 后端直接返回的 stat 数组(兼容不同命名)
|
||||
const rawStats = data.nutritionStats || data.nutrition_stats
|
||||
if (Array.isArray(rawStats) && rawStats.length > 0) {
|
||||
return rawStats.map(s => ({
|
||||
label: s.label || s.name || '',
|
||||
value: s.value != null ? String(s.value) : '-'
|
||||
})).filter(s => s.label)
|
||||
}
|
||||
|
||||
// 2) nutritionDataJson / nutrition_data_json
|
||||
const jsonRaw = data.nutritionDataJson || data.nutrition_data_json
|
||||
if (jsonRaw) {
|
||||
try {
|
||||
const nutritionData = typeof jsonRaw === 'string' ? JSON.parse(jsonRaw) : jsonRaw
|
||||
if (nutritionData && typeof nutritionData === 'object') {
|
||||
return [
|
||||
{ label: '热量(kcal)', value: nutritionData.calories != null ? String(nutritionData.calories) : '-' },
|
||||
{ label: '蛋白质', value: nutritionData.protein != null ? nutritionData.protein + 'g' : '-' },
|
||||
{ label: '钾', value: nutritionData.potassium != null ? nutritionData.potassium + 'mg' : '-' },
|
||||
{ label: '磷', value: nutritionData.phosphorus != null ? nutritionData.phosphorus + 'mg' : '-' }
|
||||
]
|
||||
}
|
||||
} catch (e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
// 3) dietaryData / mealData / dietary_data / meal_data(饮食打卡对象)
|
||||
const dietary = data.dietaryData || data.dietary_data || data.mealData || data.meal_data
|
||||
if (dietary) {
|
||||
const obj = typeof dietary === 'string' ? (() => { try { return JSON.parse(dietary) } catch (_) { return null } })() : dietary
|
||||
if (obj && typeof obj === 'object') {
|
||||
const cal = obj.calories ?? obj.energy ?? obj.calorie
|
||||
const pro = obj.protein ?? obj.proteins
|
||||
const pot = obj.potassium ?? obj.k
|
||||
const pho = obj.phosphorus ?? obj.p
|
||||
return [
|
||||
{ label: '热量(kcal)', value: cal != null ? String(cal) : '-' },
|
||||
{ label: '蛋白质', value: pro != null ? (typeof pro === 'number' ? pro + 'g' : String(pro)) : '-' },
|
||||
{ label: '钾', value: pot != null ? (typeof pot === 'number' ? pot + 'mg' : String(pot)) : '-' },
|
||||
{ label: '磷', value: pho != null ? (typeof pho === 'number' ? pho + 'mg' : String(pho)) : '-' }
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
return []
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据打卡详情接口返回的数据构建 nutritionStats(蛋白质、热量、钾、磷)
|
||||
*/
|
||||
buildNutritionStatsFromCheckinDetail(detail) {
|
||||
if (!detail || typeof detail !== 'object') return []
|
||||
const items = []
|
||||
// 热量
|
||||
const energy = detail.actualEnergy ?? detail.energy
|
||||
items.push({ label: '热量(kcal)', value: energy != null ? String(energy) : '-' })
|
||||
// 蛋白质
|
||||
const protein = detail.actualProtein ?? detail.protein
|
||||
items.push({ label: '蛋白质', value: protein != null ? (typeof protein === 'number' ? protein + 'g' : String(protein)) : '-' })
|
||||
// 钾、磷:打卡详情可能没有,用 -
|
||||
items.push({ label: '钾', value: '-' })
|
||||
items.push({ label: '磷', value: '-' })
|
||||
return items
|
||||
},
|
||||
|
||||
/**
|
||||
* 根据打卡记录 ID 拉取打卡详情,并填充 postData.nutritionStats(不阻塞主流程)
|
||||
*/
|
||||
async fillNutritionStatsFromCheckin(checkInRecordId) {
|
||||
try {
|
||||
const res = await getCheckinDetail(checkInRecordId)
|
||||
const detail = res.data || res
|
||||
const stats = this.buildNutritionStatsFromCheckinDetail(detail)
|
||||
if (stats.length > 0) {
|
||||
this.postData.nutritionStats = stats
|
||||
}
|
||||
} catch (e) {
|
||||
console.warn('拉取打卡详情补充营养数据失败:', e)
|
||||
}
|
||||
},
|
||||
|
||||
// 格式化帖子数据
|
||||
formatPostData(data) {
|
||||
// 解析图片
|
||||
@@ -368,23 +470,8 @@ export default {
|
||||
}
|
||||
}
|
||||
|
||||
// 解析营养数据
|
||||
let nutritionStats = []
|
||||
if (data.nutritionDataJson) {
|
||||
try {
|
||||
const nutritionData = typeof data.nutritionDataJson === 'string'
|
||||
? JSON.parse(data.nutritionDataJson)
|
||||
: data.nutritionDataJson
|
||||
nutritionStats = [
|
||||
{ value: nutritionData.calories || '-', label: '热量(kcal)' },
|
||||
{ value: nutritionData.protein ? nutritionData.protein + 'g' : '-', label: '蛋白质' },
|
||||
{ value: nutritionData.potassium ? nutritionData.potassium + 'mg' : '-', label: '钾' },
|
||||
{ value: nutritionData.phosphorus ? nutritionData.phosphorus + 'mg' : '-', label: '磷' }
|
||||
]
|
||||
} catch (e) {
|
||||
nutritionStats = []
|
||||
}
|
||||
}
|
||||
// 解析营养数据:支持多种后端字段名,以及从打卡数据计算
|
||||
let nutritionStats = this.buildNutritionStatsFromDetailData(data)
|
||||
|
||||
// 提取纯文本内容
|
||||
let description = ''
|
||||
|
||||
Reference in New Issue
Block a user