diff --git a/frontend/src/views/UserManagement.vue b/frontend/src/views/UserManagement.vue index 8b84b34..25cd3e3 100644 --- a/frontend/src/views/UserManagement.vue +++ b/frontend/src/views/UserManagement.vue @@ -113,35 +113,53 @@
-

📋 方案请求

+

📋 方案请求 ({{ pendingPlanRequests.length }})

-
-
- {{ p.user_name || p.username || '用户' }} - {{ p.health_desc }} +
+
+ {{ p.user_name || p.username || '用户' }} 的需求 +
-
- +
{{ p.health_desc }}
+
+
+
+ + +
+

📋 定制记录

+
+
+
+ {{ p.user_name || p.username || '用户' }} + {{ {active:'进行中',archived:'已归档',pending:'待定制'}[p.status] }} + +
+
{{ p.title }}
+
+ {{ r.recipe_name }} ×{{ r.times_per_month }}/月
-
-
+
+
-

{{ planEditing.user_name || '用户' }} 的方案

- +

为 {{ planEditing.user_name || '用户' }} 定制方案

+
健康需求:{{ planEditing.health_desc }}
+ - + +
在下方搜索框搜索配方添加
{{ pr.recipe_name }} @@ -150,18 +168,24 @@
+
- -
+ +
{{ r.name }} + {{ r.ingredients.map(i => i.oil).join('、') }}
+
+ 未找到匹配的配方 +
+
@@ -411,6 +435,9 @@ const planRecipeSearch = ref('') const pendingPlanRequests = computed(() => plansStore.plans.filter(p => p.status === 'pending') ) +const allTeacherPlans = computed(() => + plansStore.plans.filter(p => p.status !== 'pending') +) const planSearchResults = computed(() => { if (!planRecipeSearch.value.trim()) return [] @@ -451,6 +478,35 @@ function addRecipeToPlan(recipe) { planRecipeSearch.value = '' } +async function saveDraft() { + if (!planEditing.value) return + try { + let planId = planEditing.value.id + if (planId) { + await api(`/api/oil-plans/${planId}`, { + method: 'PUT', + body: JSON.stringify({ title: planTitle.value }), + }) + // Sync recipes + const existing = planEditing.value.recipes || [] + for (const r of existing) { + await api(`/api/oil-plans/${planId}/recipes/${r.id}`, { method: 'DELETE' }) + } + for (const r of planRecipes.value) { + await api(`/api/oil-plans/${planId}/recipes`, { + method: 'POST', + body: JSON.stringify({ recipe_name: r.recipe_name, ingredients: r.ingredients, times_per_month: r.times_per_month }), + }) + } + await plansStore.loadPlans() + ui.showToast('草稿已保存') + } + } catch { + ui.showToast('保存失败') + } + planEditing.value = null +} + async function savePlan() { try { let planId = planEditing.value.id @@ -505,6 +561,7 @@ onMounted(() => { loadTranslations() loadBusinessApps() plansStore.loadPlans() + recipeStore.loadRecipes() }) @@ -936,6 +993,19 @@ onMounted(() => { border: 1px solid #e5e4e7; border-radius: 8px; max-height: 200px; overflow-y: auto; z-index: 10; box-shadow: 0 4px 12px rgba(0,0,0,0.1); } -.plan-search-item { padding: 8px 12px; cursor: pointer; font-size: 13px; } +.plan-search-item { padding: 8px 12px; cursor: pointer; font-size: 13px; border-bottom: 1px solid #f0f0f0; } .plan-search-item:hover { background: #f8faf8; } +.plan-search-ings { display: block; font-size: 11px; color: #b0aab5; margin-top: 2px; } +.plan-no-results { font-size: 12px; color: #b0aab5; padding: 8px 0; } +.plan-empty-hint { font-size: 12px; color: #b0aab5; padding: 8px 0; } +.plan-request-card { background: #fff; border: 1.5px solid #e5e4e7; border-radius: 10px; padding: 12px; margin-bottom: 8px; } +.plan-request-header { display: flex; align-items: center; gap: 8px; margin-bottom: 6px; } +.plan-request-user { font-weight: 600; font-size: 14px; color: #3e3a44; flex: 1; } +.plan-request-desc { font-size: 13px; color: #6b6375; line-height: 1.5; } +.plan-status-tag { font-size: 11px; padding: 2px 8px; border-radius: 8px; } +.status-active { background: #e8f5e9; color: #2e7d5a; } +.status-archived { background: #f0eeeb; color: #6b6375; } +.status-pending { background: #fff3e0; color: #e65100; } +.plan-recipe-chips { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 6px; } +.plan-chip { font-size: 11px; padding: 2px 8px; border-radius: 6px; background: #e8f5e9; color: #2e7d5a; }