fix: 修复关注按钮相关问题

- 食谱详情页: 修复 applyDefaultData 中未定义变量 id 的问题
- 帖子详情页: 优化 toggleFollow 方法,提前校验 author.id,兼容多种后端字段
- 为帖子详情页已关注状态添加灰色样式
This commit is contained in:
msh-agent
2026-03-09 18:56:53 +08:00
parent b516089c4f
commit c1857ce852
14 changed files with 3590 additions and 102 deletions

View File

@@ -269,30 +269,35 @@ export default {
console.error('加载食物列表失败:', error);
}
},
// 兼容 result.data.list / result.list / result.data 为数组 等响应结构
// 兼容 result.data.list / result.list / result.data 为数组 等响应结构(后端 CommonPage 为 result.data.list
getRawFoodList(result) {
if (!result) return [];
// 若整个 result 就是列表数组(部分网关/封装可能直接返回数组)
if (Array.isArray(result)) return result;
const page = result.data !== undefined && result.data !== null ? result.data : result;
if (page && Array.isArray(page.list)) return page.list;
if (Array.isArray(page)) return page;
if (page && Array.isArray(page)) return page;
// 部分接口直接返回 { list: [], total: 0 }
if (result && Array.isArray(result.list)) return result.list;
return [];
},
getFoodImage(item) {
if (!item) return this.defaultPlaceholder;
const id = item.id != null ? item.id : item.foodId;
if (id != null && this.imageErrorIds[String(id)]) return this.defaultPlaceholder;
// 兼容后端 image / image_url / 前端 imageUrl、img、pic、coverImage、cover_image
const raw = item.imageUrl || item.image || item.image_url || item.img || item.pic || item.coverImage || item.cover_image || '';
const s = (raw && String(raw).trim()) || '';
// 兼容后端 imageToolFoodServiceImpl 返回 image/ image_url / 前端 imageUrl、img、pic、coverImage、cover_image
const raw = item.imageUrl != null ? item.imageUrl : (item.image != null ? item.image : (item.image_url || item.img || item.pic || item.coverImage || item.cover_image || ''));
const s = (raw != null && String(raw).trim()) ? String(raw).trim() : '';
if (!s || s === 'null' || s === 'undefined') return this.defaultPlaceholder;
const url = (s.startsWith('//') || s.startsWith('http')) ? s : (s.startsWith('/') ? (HTTP_REQUEST_URL || '') + s : s);
return (url && String(url).trim()) ? url : this.defaultPlaceholder;
},
getNutritionList(item) {
if (!item) return [];
// 优先使用已规范化的 nutrition兼容后端 nutrients / nutritions 数组
const arr = item.nutrition || item.nutrients || item.nutritions;
if (Array.isArray(arr) && arr.length > 0) return arr;
// 无数组时从扁平字段组装,确保列表始终有营养简介
// 后端列表接口ToolFoodServiceImpl仅返回扁平字段 energy/protein/potassium/phosphorus 等,无数组时从此组装
const list = [];
const push = (label, val, unit) => {
const value = (val != null && val !== '') ? String(val) + (unit || '') : '—';
@@ -322,27 +327,26 @@ export default {
};
const safety = item.safety != null ? { safety: item.safety, safetyClass: item.safetyClass || 'safe' } : (safetyMap[item.suitabilityLevel] || { safety: '—', safetyClass: 'safe' });
// 图片:兼容 image/image_url/imageUrl/img/pic/coverImage/cover_image相对路径补全空或无效则由 getFoodImage 用占位图
const rawImg = item.imageUrl || item.image || item.image_url || item.img || item.pic || item.coverImage || item.cover_image || '';
const rawStr = (rawImg && String(rawImg).trim()) || '';
// 图片:后端列表返回 image,兼容 image_url/imageUrl/img/pic/coverImage/cover_image相对路径补全空或无效则由 getFoodImage 用占位图
const rawImg = item.image || item.imageUrl || item.image_url || item.img || item.pic || item.coverImage || item.cover_image || '';
const rawStr = (rawImg != null && String(rawImg).trim()) ? String(rawImg).trim() : '';
const validRaw = rawStr && rawStr !== 'null' && rawStr !== 'undefined';
const imageUrl = validRaw && (rawStr.startsWith('//') || rawStr.startsWith('http')) ? rawStr : (validRaw && rawStr.startsWith('/') ? (HTTP_REQUEST_URL || '') + rawStr : (validRaw ? rawStr : ''));
const image = imageUrl || '';
// 营养简介:优先 item.nutrition其次 item.nutrients兼容后端否则由扁平字段 energy/protein/potassium 等组装
// 营养简介:优先 item.nutrition其次 item.nutrients / item.nutritions(兼容后端),否则由扁平字段 energy/protein/potassium 等组装
let nutrition = item.nutrition;
const mapNut = (n) => ({
label: n.label || n.name || n.labelName || n.nutrientName || '—',
value: n.value != null ? String(n.value) : (n.amount != null ? String(n.amount) + (n.unit || '') : '—'),
colorClass: n.colorClass || 'green'
});
if (Array.isArray(nutrition) && nutrition.length > 0) {
nutrition = nutrition.map(n => ({
label: n.label || n.name || n.labelName || '—',
value: n.value != null ? String(n.value) : '—',
colorClass: n.colorClass || 'green'
}));
nutrition = nutrition.map(mapNut);
} else if (Array.isArray(item.nutrients) && item.nutrients.length > 0) {
nutrition = item.nutrients.map(n => ({
label: n.label || n.name || n.labelName || '—',
value: n.value != null ? String(n.value) : '—',
colorClass: n.colorClass || 'green'
}));
nutrition = item.nutrients.map(mapNut);
} else if (Array.isArray(item.nutritions) && item.nutritions.length > 0) {
nutrition = item.nutritions.map(mapNut);
} else {
// 后端列表仅返回扁平字段,无 nutrition/nutrients 数组,此处组装并始终展示主要项(空值显示 —)
nutrition = [];
@@ -363,11 +367,12 @@ export default {
const numericId = (rawId !== undefined && rawId !== null && rawId !== '' && !isNaN(Number(rawId)))
? (typeof rawId === 'number' ? rawId : Number(rawId))
: undefined;
// 保证列表项必有 image/imageUrl空时由 getFoodImage 用 defaultPlaceholder和 nutrition 数组(.nutrition-item 数据来源)
return {
...item,
id: numericId,
image,
imageUrl: image || undefined,
image: image || '',
imageUrl: image || '',
category: item.category || '',
safety: safety.safety,
safetyClass: safety.safetyClass,