From e8a2915962c84801e947af1f3bbd55ced40bcbc6 Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 10:43:18 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B9=E6=A1=88=E5=AE=9A=E5=88=B6?= =?UTF-8?q?=E7=95=8C=E9=9D=A2=E9=87=8D=E5=86=99=20=E2=80=94=20=E8=AF=B7?= =?UTF-8?q?=E6=B1=82=E5=B1=95=E7=A4=BA+=E9=85=8D=E6=96=B9=E6=90=9C?= =?UTF-8?q?=E7=B4=A2+=E8=8D=89=E7=A8=BF=E4=BF=9D=E5=AD=98+=E5=AE=9A?= =?UTF-8?q?=E5=88=B6=E8=AE=B0=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 方案请求卡片显示用户名和健康需求 - 编辑器: 搜索配方添加(显示成分)、设频率、保存草稿/发送 - 定制记录列表: 显示所有方案状态(进行中/已归档/待定制) - 修复: loadRecipes 加入 onMounted 使配方搜索可用 Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/views/UserManagement.vue | 102 ++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 16 deletions(-) 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; }