499 lines
17 KiB
HTML
499 lines
17 KiB
HTML
{% extends "base.html" %}
|
||
|
||
{% block title %}智能饭菜规划 - 提示词大师{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="container-fluid">
|
||
<!-- 移动端优化的页面标题 -->
|
||
<div class="page-header-mobile">
|
||
<h1 class="page-title-mobile">
|
||
<i class="fas fa-utensils"></i>
|
||
智能饭菜规划
|
||
</h1>
|
||
<p class="page-subtitle-mobile">AI驱动的个性化饭菜清单规划师</p>
|
||
</div>
|
||
|
||
<!-- 移动端优化的表单区域 -->
|
||
<div class="mobile-form-container">
|
||
<div class="card mobile-card">
|
||
<div class="card-header mobile-card-header">
|
||
<h5 class="card-title-mobile">
|
||
<i class="fas fa-cog"></i>
|
||
规划参数
|
||
</h5>
|
||
</div>
|
||
<div class="card-body mobile-card-body">
|
||
<form id="mealPlanningFormMobile">
|
||
<!-- 地区类型 -->
|
||
<div class="form-group-mobile mb-3">
|
||
<label for="regionType" class="form-label-mobile">地区类型</label>
|
||
<select class="form-select-mobile" id="regionType" name="region_type">
|
||
<option value="全国">全国</option>
|
||
<option value="北方">北方</option>
|
||
<option value="南方">南方</option>
|
||
<option value="川菜">川菜</option>
|
||
<option value="粤菜">粤菜</option>
|
||
<option value="鲁菜">鲁菜</option>
|
||
<option value="苏菜">苏菜</option>
|
||
<option value="浙菜">浙菜</option>
|
||
<option value="闽菜">闽菜</option>
|
||
<option value="湘菜">湘菜</option>
|
||
<option value="徽菜">徽菜</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- 就餐人数 -->
|
||
<div class="form-group-mobile mb-3">
|
||
<label for="dinerCount" class="form-label-mobile">就餐人数</label>
|
||
<select class="form-select-mobile" id="dinerCount" name="diner_count">
|
||
<option value="1">1人</option>
|
||
<option value="2" selected>2人</option>
|
||
<option value="3">3人</option>
|
||
<option value="4">4人</option>
|
||
<option value="5">5人</option>
|
||
<option value="6">6人</option>
|
||
<option value="8">8人</option>
|
||
<option value="10">10人</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- 用餐类型 -->
|
||
<div class="form-group-mobile mb-3">
|
||
<label for="mealType" class="form-label-mobile">用餐类型</label>
|
||
<select class="form-select-mobile" id="mealType" name="meal_type">
|
||
<option value="早餐">早餐</option>
|
||
<option value="午餐" selected>午餐</option>
|
||
<option value="晚餐">晚餐</option>
|
||
<option value="全天">全天</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- 用餐者家乡 -->
|
||
<div class="form-group-mobile mb-3">
|
||
<label for="hometown" class="form-label-mobile">用餐者家乡 <span class="text-danger">*</span></label>
|
||
<input type="text" class="form-control-mobile" id="hometown" name="hometown"
|
||
placeholder="如:四川成都" required>
|
||
</div>
|
||
|
||
<!-- 个人喜好 -->
|
||
<div class="form-group-mobile mb-3">
|
||
<label for="preferences" class="form-label-mobile">个人喜好</label>
|
||
<textarea class="form-control-mobile" id="preferences" name="preferences" rows="3"
|
||
placeholder="如:喜欢辣味、偏爱素食、喜欢海鲜等"></textarea>
|
||
</div>
|
||
|
||
<!-- 饮食禁忌 -->
|
||
<div class="form-group-mobile mb-3">
|
||
<label for="dietaryRestrictions" class="form-label-mobile">饮食禁忌</label>
|
||
<textarea class="form-control-mobile" id="dietaryRestrictions" name="dietary_restrictions" rows="3"
|
||
placeholder="如:不吃猪肉、对花生过敏、素食主义等"></textarea>
|
||
</div>
|
||
|
||
<!-- 预算范围 -->
|
||
<div class="form-group-mobile mb-3">
|
||
<label for="budget" class="form-label-mobile">预算范围(元)</label>
|
||
<select class="form-select-mobile" id="budget" name="budget">
|
||
<option value="50">50元以下</option>
|
||
<option value="100" selected>50-100元</option>
|
||
<option value="150">100-150元</option>
|
||
<option value="200">150-200元</option>
|
||
<option value="300">200-300元</option>
|
||
<option value="500">300-500元</option>
|
||
<option value="1000">500元以上</option>
|
||
</select>
|
||
</div>
|
||
|
||
<!-- 生成按钮 -->
|
||
<div class="d-grid">
|
||
<button type="submit" class="btn btn-primary-mobile btn-lg" id="generateBtnMobile">
|
||
<i class="fas fa-magic"></i>
|
||
生成饭菜规划
|
||
</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 移动端优化的结果展示区域 -->
|
||
<div class="mobile-result-container" id="resultContainerMobile" style="display: none;">
|
||
<div class="card mobile-card">
|
||
<div class="card-header mobile-card-header">
|
||
<h5 class="card-title-mobile">
|
||
<i class="fas fa-clipboard-list"></i>
|
||
饭菜规划结果
|
||
</h5>
|
||
<div class="card-actions-mobile">
|
||
<button class="btn btn-sm btn-outline-primary-mobile" id="copyBtnMobile">
|
||
<i class="fas fa-copy"></i>
|
||
复制
|
||
</button>
|
||
<button class="btn btn-sm btn-outline-success-mobile" id="saveBtnMobile">
|
||
<i class="fas fa-save"></i>
|
||
保存
|
||
</button>
|
||
</div>
|
||
</div>
|
||
<div class="card-body mobile-card-body">
|
||
<div class="meal-plan-content-mobile" id="mealPlanContentMobile">
|
||
<!-- 结果内容将在这里显示 -->
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 移动端优化的加载动画 -->
|
||
<div class="modal fade" id="loadingModalMobile" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false">
|
||
<div class="modal-dialog modal-dialog-centered">
|
||
<div class="modal-content mobile-modal-content">
|
||
<div class="modal-body text-center py-4">
|
||
<div class="spinner-border text-primary mb-3" role="status">
|
||
<span class="visually-hidden">生成中...</span>
|
||
</div>
|
||
<h5>正在生成饭菜规划...</h5>
|
||
<p class="text-muted">AI正在为您制定个性化的饭菜清单,请稍候</p>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<style>
|
||
/* 移动端专用样式 */
|
||
@media (max-width: 768px) {
|
||
.page-header-mobile {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
color: white;
|
||
padding: 1.5rem 1rem;
|
||
margin: -1rem -1rem 1.5rem -1rem;
|
||
border-radius: 0 0 20px 20px;
|
||
}
|
||
|
||
.page-title-mobile {
|
||
font-size: 1.8rem;
|
||
font-weight: 700;
|
||
margin-bottom: 0.5rem;
|
||
text-align: center;
|
||
}
|
||
|
||
.page-subtitle-mobile {
|
||
font-size: 1rem;
|
||
opacity: 0.9;
|
||
margin-bottom: 0;
|
||
text-align: center;
|
||
}
|
||
|
||
.mobile-form-container {
|
||
margin-bottom: 1.5rem;
|
||
}
|
||
|
||
.mobile-card {
|
||
border: none;
|
||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
|
||
border-radius: 15px;
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.mobile-card-header {
|
||
background: #f8f9fa;
|
||
border-bottom: 1px solid #dee2e6;
|
||
border-radius: 15px 15px 0 0 !important;
|
||
padding: 1rem;
|
||
}
|
||
|
||
.card-title-mobile {
|
||
margin-bottom: 0;
|
||
font-weight: 600;
|
||
color: #495057;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.mobile-card-body {
|
||
padding: 1rem;
|
||
}
|
||
|
||
.form-group-mobile {
|
||
margin-bottom: 1rem;
|
||
}
|
||
|
||
.form-label-mobile {
|
||
font-weight: 600;
|
||
color: #495057;
|
||
margin-bottom: 0.5rem;
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.form-control-mobile, .form-select-mobile {
|
||
border-radius: 10px;
|
||
border: 1px solid #ced4da;
|
||
padding: 0.75rem;
|
||
font-size: 1rem;
|
||
width: 100%;
|
||
}
|
||
|
||
.form-control-mobile:focus, .form-select-mobile:focus {
|
||
border-color: #667eea;
|
||
box-shadow: 0 0 0 0.2rem rgba(102, 126, 234, 0.25);
|
||
}
|
||
|
||
.btn-primary-mobile {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
border: none;
|
||
border-radius: 10px;
|
||
padding: 1rem 1.5rem;
|
||
font-weight: 600;
|
||
font-size: 1.1rem;
|
||
width: 100%;
|
||
}
|
||
|
||
.btn-primary-mobile:hover {
|
||
background: linear-gradient(135deg, #5a6fd8 0%, #6a4190 100%);
|
||
transform: translateY(-1px);
|
||
box-shadow: 0 4px 8px rgba(102, 126, 234, 0.3);
|
||
}
|
||
|
||
.mobile-result-container {
|
||
margin-bottom: 2rem;
|
||
}
|
||
|
||
.card-actions-mobile {
|
||
display: flex;
|
||
gap: 0.5rem;
|
||
margin-top: 0.5rem;
|
||
}
|
||
|
||
.btn-outline-primary-mobile, .btn-outline-success-mobile {
|
||
border-radius: 8px;
|
||
padding: 0.5rem 1rem;
|
||
font-size: 0.9rem;
|
||
flex: 1;
|
||
}
|
||
|
||
.meal-plan-content-mobile {
|
||
background: #f8f9fa;
|
||
border-radius: 10px;
|
||
padding: 1rem;
|
||
border-left: 4px solid #667eea;
|
||
font-size: 0.95rem;
|
||
line-height: 1.6;
|
||
}
|
||
|
||
.meal-plan-content-mobile h1,
|
||
.meal-plan-content-mobile h2,
|
||
.meal-plan-content-mobile h3 {
|
||
color: #495057;
|
||
margin-top: 1rem;
|
||
margin-bottom: 0.5rem;
|
||
font-size: 1.1rem;
|
||
}
|
||
|
||
.meal-plan-content-mobile h1:first-child,
|
||
.meal-plan-content-mobile h2:first-child,
|
||
.meal-plan-content-mobile h3:first-child {
|
||
margin-top: 0;
|
||
}
|
||
|
||
.meal-plan-content-mobile ul,
|
||
.meal-plan-content-mobile ol {
|
||
padding-left: 1.2rem;
|
||
}
|
||
|
||
.meal-plan-content-mobile li {
|
||
margin-bottom: 0.3rem;
|
||
}
|
||
|
||
.meal-plan-content-mobile strong {
|
||
color: #667eea;
|
||
}
|
||
|
||
.mobile-modal-content {
|
||
border-radius: 15px;
|
||
border: none;
|
||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
/* 触摸优化 */
|
||
.btn, .form-control, .form-select {
|
||
min-height: 44px; /* iOS推荐的最小触摸目标 */
|
||
}
|
||
|
||
/* 防止双击缩放 */
|
||
input, select, textarea, button {
|
||
touch-action: manipulation;
|
||
}
|
||
}
|
||
|
||
/* 超小屏幕优化 */
|
||
@media (max-width: 480px) {
|
||
.page-title-mobile {
|
||
font-size: 1.5rem;
|
||
}
|
||
|
||
.mobile-card-body {
|
||
padding: 0.75rem;
|
||
}
|
||
|
||
.form-control-mobile, .form-select-mobile {
|
||
padding: 0.6rem;
|
||
font-size: 0.95rem;
|
||
}
|
||
|
||
.btn-primary-mobile {
|
||
padding: 0.8rem 1rem;
|
||
font-size: 1rem;
|
||
}
|
||
}
|
||
</style>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const form = document.getElementById('mealPlanningFormMobile');
|
||
const generateBtn = document.getElementById('generateBtnMobile');
|
||
const resultContainer = document.getElementById('resultContainerMobile');
|
||
const copyBtn = document.getElementById('copyBtnMobile');
|
||
const saveBtn = document.getElementById('saveBtnMobile');
|
||
const loadingModal = new bootstrap.Modal(document.getElementById('loadingModalMobile'));
|
||
|
||
// 表单提交处理
|
||
form.addEventListener('submit', function(e) {
|
||
e.preventDefault();
|
||
|
||
// 验证必填字段
|
||
const hometown = document.getElementById('hometown').value.trim();
|
||
if (!hometown) {
|
||
showAlert('请输入用餐者家乡', 'danger');
|
||
return;
|
||
}
|
||
|
||
// 收集表单数据
|
||
const formData = new FormData(form);
|
||
const data = Object.fromEntries(formData.entries());
|
||
|
||
// 显示加载动画
|
||
loadingModal.show();
|
||
generateBtn.disabled = true;
|
||
|
||
// 发送请求
|
||
fetch('/api/meal-planning/generate', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(data)
|
||
})
|
||
.then(response => response.json())
|
||
.then(result => {
|
||
loadingModal.hide();
|
||
generateBtn.disabled = false;
|
||
|
||
if (result.success) {
|
||
displayResult(result.data.meal_plan);
|
||
resultContainer.style.display = 'block';
|
||
showAlert('饭菜规划生成成功!', 'success');
|
||
|
||
// 滚动到结果区域
|
||
resultContainer.scrollIntoView({ behavior: 'smooth' });
|
||
} else {
|
||
showAlert(result.message || '生成失败,请重试', 'danger');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
loadingModal.hide();
|
||
generateBtn.disabled = false;
|
||
console.error('Error:', error);
|
||
showAlert('网络错误,请检查网络连接后重试', 'danger');
|
||
});
|
||
});
|
||
|
||
// 显示结果
|
||
function displayResult(mealPlan) {
|
||
// 简单的Markdown转HTML函数
|
||
function simpleMarkdownToHtml(text) {
|
||
return text
|
||
.replace(/^### (.*$)/gim, '<h3>$1</h3>')
|
||
.replace(/^## (.*$)/gim, '<h2>$1</h2>')
|
||
.replace(/^# (.*$)/gim, '<h1>$1</h1>')
|
||
.replace(/\*\*(.*)\*\*/gim, '<strong>$1</strong>')
|
||
.replace(/\*(.*)\*/gim, '<em>$1</em>')
|
||
.replace(/^\* (.*$)/gim, '<li>$1</li>')
|
||
.replace(/^\d+\. (.*$)/gim, '<li>$1</li>')
|
||
.replace(/\n\n/gim, '</p><p>')
|
||
.replace(/\n/gim, '<br>')
|
||
.replace(/^(.*)$/gim, '<p>$1</p>');
|
||
}
|
||
|
||
document.getElementById('mealPlanContentMobile').innerHTML = simpleMarkdownToHtml(mealPlan);
|
||
}
|
||
|
||
// 复制功能
|
||
copyBtn.addEventListener('click', function() {
|
||
const content = document.getElementById('mealPlanContentMobile').textContent;
|
||
navigator.clipboard.writeText(content).then(() => {
|
||
showAlert('饭菜规划已复制到剪贴板', 'success');
|
||
}).catch(() => {
|
||
showAlert('复制失败,请手动复制', 'danger');
|
||
});
|
||
});
|
||
|
||
// 保存功能
|
||
saveBtn.addEventListener('click', function() {
|
||
const content = document.getElementById('mealPlanContentMobile').textContent;
|
||
|
||
// 获取当前表单参数
|
||
const formData = {
|
||
meal_plan_content: content,
|
||
region_type: document.getElementById('regionType').value,
|
||
diner_count: document.getElementById('dinerCount').value,
|
||
meal_type: document.getElementById('mealType').value,
|
||
hometown: document.getElementById('hometown').value,
|
||
preferences: document.getElementById('preferences').value,
|
||
dietary_restrictions: document.getElementById('dietaryRestrictions').value,
|
||
budget: document.getElementById('budget').value
|
||
};
|
||
|
||
fetch('/api/meal-planning/save', {
|
||
method: 'POST',
|
||
headers: {
|
||
'Content-Type': 'application/json',
|
||
},
|
||
body: JSON.stringify(formData)
|
||
})
|
||
.then(response => response.json())
|
||
.then(result => {
|
||
if (result.success) {
|
||
showAlert('饭菜规划保存成功!', 'success');
|
||
console.log('保存成功,规划ID:', result.data.meal_plan_id);
|
||
} else {
|
||
showAlert(result.message || '保存失败', 'danger');
|
||
}
|
||
})
|
||
.catch(error => {
|
||
console.error('Error:', error);
|
||
showAlert('保存失败,请重试', 'danger');
|
||
});
|
||
});
|
||
|
||
// 显示提示信息
|
||
function showAlert(message, type) {
|
||
const alertDiv = document.createElement('div');
|
||
alertDiv.className = `alert alert-${type} alert-dismissible fade show`;
|
||
alertDiv.innerHTML = `
|
||
${message}
|
||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||
`;
|
||
|
||
// 插入到页面顶部
|
||
const container = document.querySelector('.container-fluid');
|
||
container.insertBefore(alertDiv, container.firstChild);
|
||
|
||
// 自动消失
|
||
setTimeout(() => {
|
||
if (alertDiv.parentNode) {
|
||
alertDiv.remove();
|
||
}
|
||
}, 5000);
|
||
}
|
||
});
|
||
</script>
|
||
{% endblock %}
|