2025-11-03 17:11:28 +08:00
|
|
|
|
// 推荐功能脚本
|
2025-11-02 22:23:10 +08:00
|
|
|
|
|
|
|
|
|
|
let currentUserId = null;
|
|
|
|
|
|
|
2025-11-03 17:11:28 +08:00
|
|
|
|
// DOM元素
|
2025-11-02 22:23:10 +08:00
|
|
|
|
const loginSection = document.getElementById('loginSection');
|
|
|
|
|
|
const requestSection = document.getElementById('requestSection');
|
|
|
|
|
|
const recommendationsSection = document.getElementById('recommendationsSection');
|
|
|
|
|
|
const messageArea = document.getElementById('messageArea');
|
|
|
|
|
|
|
|
|
|
|
|
const loginBtn = document.getElementById('loginBtn');
|
|
|
|
|
|
const userIdInput = document.getElementById('userId');
|
|
|
|
|
|
const getRecommendationBtn = document.getElementById('getRecommendationBtn');
|
|
|
|
|
|
const mealTypeSelect = document.getElementById('mealType');
|
|
|
|
|
|
const recommendationsList = document.getElementById('recommendationsList');
|
|
|
|
|
|
|
2025-11-03 17:11:28 +08:00
|
|
|
|
// 显示消息
|
2025-11-02 22:23:10 +08:00
|
|
|
|
function showMessage(message, type = 'info') {
|
|
|
|
|
|
messageArea.textContent = message;
|
|
|
|
|
|
messageArea.className = `message-area ${type}`;
|
2025-11-03 17:11:28 +08:00
|
|
|
|
messageArea.style.display = 'block';
|
2025-11-02 22:23:10 +08:00
|
|
|
|
setTimeout(() => {
|
|
|
|
|
|
messageArea.className = 'message-area';
|
|
|
|
|
|
messageArea.style.display = 'none';
|
|
|
|
|
|
}, 3000);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-03 17:11:28 +08:00
|
|
|
|
// 用户登录
|
2025-11-02 22:23:10 +08:00
|
|
|
|
loginBtn.addEventListener('click', async () => {
|
|
|
|
|
|
const userId = userIdInput.value.trim();
|
|
|
|
|
|
|
|
|
|
|
|
if (!userId) {
|
2025-11-03 17:11:28 +08:00
|
|
|
|
showMessage('请输入用户ID', 'error');
|
2025-11-02 22:23:10 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
loginBtn.disabled = true;
|
2025-11-03 17:11:28 +08:00
|
|
|
|
loginBtn.textContent = '登录中...';
|
2025-11-02 22:23:10 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await fetch('/api/user/login', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': 'application/json; charset=utf-8'
|
|
|
|
|
|
},
|
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
|
user_id: userId
|
|
|
|
|
|
})
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (data.success) {
|
|
|
|
|
|
currentUserId = userId;
|
|
|
|
|
|
loginSection.style.display = 'none';
|
|
|
|
|
|
requestSection.style.display = 'block';
|
2025-11-03 17:11:28 +08:00
|
|
|
|
showMessage(`欢迎,${data.name || userId}!`, 'success');
|
2025-11-02 22:23:10 +08:00
|
|
|
|
} else {
|
2025-11-03 17:11:28 +08:00
|
|
|
|
showMessage(data.message || '登录失败', 'error');
|
2025-11-02 22:23:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
2025-11-03 17:11:28 +08:00
|
|
|
|
console.error('登录失败:', error);
|
|
|
|
|
|
showMessage('登录失败,请检查网络连接', 'error');
|
2025-11-02 22:23:10 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
loginBtn.disabled = false;
|
2025-11-03 17:11:28 +08:00
|
|
|
|
loginBtn.textContent = '登录';
|
2025-11-02 22:23:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-11-03 17:11:28 +08:00
|
|
|
|
// 获取推荐
|
2025-11-02 22:23:10 +08:00
|
|
|
|
getRecommendationBtn.addEventListener('click', async () => {
|
|
|
|
|
|
const mealType = mealTypeSelect.value;
|
|
|
|
|
|
|
|
|
|
|
|
getRecommendationBtn.disabled = true;
|
2025-11-03 17:11:28 +08:00
|
|
|
|
getRecommendationBtn.textContent = '获取中...';
|
2025-11-02 22:23:10 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await fetch('/api/recommendation/get', {
|
|
|
|
|
|
method: 'POST',
|
|
|
|
|
|
headers: {
|
|
|
|
|
|
'Content-Type': 'application/json; charset=utf-8'
|
|
|
|
|
|
},
|
|
|
|
|
|
body: JSON.stringify({
|
|
|
|
|
|
user_id: currentUserId,
|
|
|
|
|
|
meal_type: mealType,
|
|
|
|
|
|
preferences: {},
|
|
|
|
|
|
context: {}
|
|
|
|
|
|
})
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const data = await response.json();
|
|
|
|
|
|
|
|
|
|
|
|
if (data.success && data.recommendations && data.recommendations.length > 0) {
|
|
|
|
|
|
displayRecommendations(data.recommendations);
|
|
|
|
|
|
recommendationsSection.style.display = 'block';
|
2025-11-03 17:11:28 +08:00
|
|
|
|
showMessage('推荐获取成功!', 'success');
|
2025-11-02 22:23:10 +08:00
|
|
|
|
} else {
|
2025-11-03 17:11:28 +08:00
|
|
|
|
showMessage(data.message || '暂无推荐内容,请先完善个人数据', 'info');
|
2025-11-02 22:23:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
2025-11-03 17:11:28 +08:00
|
|
|
|
console.error('获取推荐失败:', error);
|
|
|
|
|
|
showMessage('获取推荐失败,请检查网络连接', 'error');
|
2025-11-02 22:23:10 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
getRecommendationBtn.disabled = false;
|
2025-11-03 17:11:28 +08:00
|
|
|
|
getRecommendationBtn.textContent = '获取推荐';
|
2025-11-02 22:23:10 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-11-03 17:11:28 +08:00
|
|
|
|
// 显示推荐结果
|
2025-11-02 22:23:10 +08:00
|
|
|
|
function displayRecommendations(recommendations) {
|
|
|
|
|
|
recommendationsList.innerHTML = '';
|
|
|
|
|
|
|
|
|
|
|
|
recommendations.forEach((rec, index) => {
|
|
|
|
|
|
const card = document.createElement('div');
|
|
|
|
|
|
card.className = 'recommendation-card';
|
|
|
|
|
|
|
|
|
|
|
|
let foods = [];
|
|
|
|
|
|
if (rec.foods && Array.isArray(rec.foods)) {
|
|
|
|
|
|
foods = rec.foods;
|
|
|
|
|
|
} else if (rec.food) {
|
|
|
|
|
|
foods = [rec.food];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-03 17:11:28 +08:00
|
|
|
|
const foodsHtml = foods.map(food =>
|
|
|
|
|
|
`<div class="food-item">${escapeHtml(food)}</div>`
|
|
|
|
|
|
).join('');
|
|
|
|
|
|
|
|
|
|
|
|
const confidenceHtml = rec.confidence
|
|
|
|
|
|
? `<div class="confidence">置信度: ${(rec.confidence * 100).toFixed(1)}%</div>`
|
|
|
|
|
|
: '';
|
|
|
|
|
|
|
|
|
|
|
|
const reasonHtml = rec.reason
|
|
|
|
|
|
? `<div style="margin-top: 10px; color: #666;">推荐理由: ${escapeHtml(rec.reason)}</div>`
|
|
|
|
|
|
: '';
|
|
|
|
|
|
|
2025-11-02 22:23:10 +08:00
|
|
|
|
card.innerHTML = `
|
2025-11-03 17:11:28 +08:00
|
|
|
|
<h3>推荐方案 ${index + 1}</h3>
|
2025-11-02 22:23:10 +08:00
|
|
|
|
<div class="food-list">
|
2025-11-03 17:11:28 +08:00
|
|
|
|
${foodsHtml}
|
2025-11-02 22:23:10 +08:00
|
|
|
|
</div>
|
2025-11-03 17:11:28 +08:00
|
|
|
|
${confidenceHtml}
|
|
|
|
|
|
${reasonHtml}
|
2025-11-02 22:23:10 +08:00
|
|
|
|
`;
|
|
|
|
|
|
|
|
|
|
|
|
recommendationsList.appendChild(card);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-03 17:11:28 +08:00
|
|
|
|
// HTML转义函数,防止XSS攻击
|
|
|
|
|
|
function escapeHtml(text) {
|
|
|
|
|
|
const div = document.createElement('div');
|
|
|
|
|
|
div.textContent = text;
|
|
|
|
|
|
return div.innerHTML;
|
|
|
|
|
|
}
|