diff --git a/backend/database.py b/backend/database.py index b62f382..3bc6b07 100644 --- a/backend/database.py +++ b/backend/database.py @@ -240,6 +240,8 @@ def init_db(): c.execute("ALTER TABLE recipes ADD COLUMN updated_by INTEGER") if "en_name" not in cols: c.execute("ALTER TABLE recipes ADD COLUMN en_name TEXT DEFAULT ''") + if "en_oils" not in cols: + c.execute("ALTER TABLE recipes ADD COLUMN en_oils TEXT DEFAULT '{}'") # Seed admin user if no users exist count = c.execute("SELECT COUNT(*) FROM users").fetchone()[0] diff --git a/backend/main.py b/backend/main.py index 0931c4c..cf04be1 100644 --- a/backend/main.py +++ b/backend/main.py @@ -96,6 +96,7 @@ class RecipeIn(BaseModel): class RecipeUpdate(BaseModel): name: Optional[str] = None en_name: Optional[str] = None + en_oils: Optional[str] = None note: Optional[str] = None ingredients: Optional[list[IngredientIn]] = None tags: Optional[list[str]] = None @@ -309,7 +310,7 @@ def symptom_search(body: dict, user=Depends(get_current_user)): conn = get_db() # Search in recipe names rows = conn.execute( - "SELECT id, name, note, owner_id, version, en_name FROM recipes ORDER BY id" + "SELECT id, name, note, owner_id, version, en_name, en_oils FROM recipes ORDER BY id" ).fetchall() exact = [] related = [] @@ -698,6 +699,7 @@ def _recipe_to_dict(conn, row): "id": rid, "name": row["name"], "en_name": row["en_name"] if "en_name" in row.keys() else "", + "en_oils": row["en_oils"] if "en_oils" in row.keys() else "{}", "note": row["note"], "owner_id": row["owner_id"], "owner_name": (owner["display_name"] or owner["username"]) if owner else None, @@ -712,19 +714,19 @@ def list_recipes(user=Depends(get_current_user)): conn = get_db() # Admin sees all; others see admin-owned (adopted) + their own if user["role"] == "admin": - rows = conn.execute("SELECT id, name, note, owner_id, version, en_name FROM recipes ORDER BY id").fetchall() + rows = conn.execute("SELECT id, name, note, owner_id, version, en_name, en_oils FROM recipes ORDER BY id").fetchall() else: admin = conn.execute("SELECT id FROM users WHERE role = 'admin' LIMIT 1").fetchone() admin_id = admin["id"] if admin else 1 user_id = user.get("id") if user_id: rows = conn.execute( - "SELECT id, name, note, owner_id, version, en_name FROM recipes WHERE owner_id = ? OR owner_id = ? ORDER BY id", + "SELECT id, name, note, owner_id, version, en_name, en_oils FROM recipes WHERE owner_id = ? OR owner_id = ? ORDER BY id", (admin_id, user_id) ).fetchall() else: rows = conn.execute( - "SELECT id, name, note, owner_id, version, en_name FROM recipes WHERE owner_id = ? ORDER BY id", + "SELECT id, name, note, owner_id, version, en_name, en_oils FROM recipes WHERE owner_id = ? ORDER BY id", (admin_id,) ).fetchall() result = [_recipe_to_dict(conn, r) for r in rows] @@ -735,7 +737,7 @@ def list_recipes(user=Depends(get_current_user)): @app.get("/api/recipes/{recipe_id}") def get_recipe(recipe_id: int): conn = get_db() - row = conn.execute("SELECT id, name, note, owner_id, version, en_name FROM recipes WHERE id = ?", (recipe_id,)).fetchone() + row = conn.execute("SELECT id, name, note, owner_id, version, en_name, en_oils FROM recipes WHERE id = ?", (recipe_id,)).fetchone() if not row: conn.close() raise HTTPException(404, "Recipe not found") @@ -808,6 +810,8 @@ def update_recipe(recipe_id: int, update: RecipeUpdate, user=Depends(get_current c.execute("UPDATE recipes SET note = ? WHERE id = ?", (update.note, recipe_id)) if update.en_name is not None: c.execute("UPDATE recipes SET en_name = ? WHERE id = ?", (update.en_name, recipe_id)) + if update.en_oils is not None: + c.execute("UPDATE recipes SET en_oils = ? WHERE id = ?", (update.en_oils, recipe_id)) if update.ingredients is not None: c.execute("DELETE FROM recipe_ingredients WHERE recipe_id = ?", (recipe_id,)) for ing in update.ingredients: @@ -837,7 +841,7 @@ def delete_recipe(recipe_id: int, user=Depends(get_current_user)): conn = get_db() row = _check_recipe_permission(conn, recipe_id, user) # Save full snapshot for undo - full = conn.execute("SELECT id, name, note, owner_id, version, en_name FROM recipes WHERE id = ?", (recipe_id,)).fetchone() + full = conn.execute("SELECT id, name, note, owner_id, version, en_name, en_oils FROM recipes WHERE id = ?", (recipe_id,)).fetchone() snapshot = _recipe_to_dict(conn, full) log_audit(conn, user["id"], "delete_recipe", "recipe", recipe_id, row["name"], json.dumps(snapshot, ensure_ascii=False)) @@ -1340,7 +1344,7 @@ def recipes_by_inventory(user=Depends(get_current_user)): if not inv: conn.close() return [] - rows = conn.execute("SELECT id, name, note, owner_id, version, en_name FROM recipes ORDER BY id").fetchall() + rows = conn.execute("SELECT id, name, note, owner_id, version, en_name, en_oils FROM recipes ORDER BY id").fetchall() result = [] for r in rows: recipe = _recipe_to_dict(conn, r) diff --git a/frontend/src/components/RecipeDetailOverlay.vue b/frontend/src/components/RecipeDetailOverlay.vue index 87987de..a40ae70 100644 --- a/frontend/src/components/RecipeDetailOverlay.vue +++ b/frontend/src/components/RecipeDetailOverlay.vue @@ -576,11 +576,12 @@ function copyText() { async function applyTranslation() { showTranslationEditor.value = false - // Persist en_name to backend - if (recipe.value._id && customRecipeNameEn.value) { + // Persist en_name and en_oils to backend + if (recipe.value._id) { try { await api.put(`/api/recipes/${recipe.value._id}`, { en_name: customRecipeNameEn.value, + en_oils: JSON.stringify(customOilNameEn.value), version: recipe.value._version, }) ui.showToast('翻译已保存') @@ -708,9 +709,10 @@ onMounted(() => { editIngredients.value = (r.ingredients || []).map(i => ({ oil: i.oil, drops: i.drops })) // Init translation defaults customRecipeNameEn.value = r.en_name || recipeNameEn(r.name) + const savedOilMap = r.en_oils ? (() => { try { return JSON.parse(r.en_oils) } catch { return {} } })() : {} const enMap = {} ;(r.ingredients || []).forEach(ing => { - enMap[ing.oil] = oilEn(ing.oil) || ing.oil + enMap[ing.oil] = savedOilMap[ing.oil] || oilEn(ing.oil) || ing.oil }) customOilNameEn.value = enMap diff --git a/frontend/src/stores/recipes.js b/frontend/src/stores/recipes.js index a4ce180..53571c4 100644 --- a/frontend/src/stores/recipes.js +++ b/frontend/src/stores/recipes.js @@ -17,6 +17,7 @@ export const useRecipesStore = defineStore('recipes', () => { _version: r._version ?? r.version ?? 1, name: r.name, en_name: r.en_name ?? '', + en_oils: r.en_oils ?? '{}', note: r.note ?? '', tags: r.tags ?? [], ingredients: (r.ingredients ?? []).map((ing) => ({