From a3bf13c58dfb99d6979d287f9e40c22f12aff3cc Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 14:48:11 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=8A=A4=E8=82=A4=E5=93=81=E7=94=A8ml?= =?UTF-8?q?=E5=8D=95=E4=BD=8D=EF=BC=8C=E7=B2=BE=E6=B2=B9=E7=94=A8=E6=BB=B4?= =?UTF-8?q?=EF=BC=8C=E9=80=9A=E8=BF=87unit=E5=AD=97=E6=AE=B5=E5=8C=BA?= =?UTF-8?q?=E5=88=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 后端: oils表新增unit列(drop/ml),API返回unit字段 - 前端: 根据unit='ml'显示ml单位,精油显示滴/drop - 护肤品drop_count改为实际ml容量,成本按用量比例计算 如玫瑰护手霜100ml ¥135,配方用30ml → 成本¥40.5 Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/database.py | 2 ++ backend/main.py | 9 +++++---- frontend/src/stores/oils.js | 13 +++++++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/backend/database.py b/backend/database.py index 137ec68..52eef67 100644 --- a/backend/database.py +++ b/backend/database.py @@ -227,6 +227,8 @@ def init_db(): c.execute("ALTER TABLE oils ADD COLUMN is_active INTEGER DEFAULT 1") if "en_name" not in oil_cols: c.execute("ALTER TABLE oils ADD COLUMN en_name TEXT DEFAULT ''") + if "unit" not in oil_cols: + c.execute("ALTER TABLE oils ADD COLUMN unit TEXT DEFAULT 'drop'") # Migration: add new columns to category_modules if missing cat_cols = [row[1] for row in c.execute("PRAGMA table_info(category_modules)").fetchall()] diff --git a/backend/main.py b/backend/main.py index 55a5729..213ba2b 100644 --- a/backend/main.py +++ b/backend/main.py @@ -87,6 +87,7 @@ class OilIn(BaseModel): retail_price: Optional[float] = None en_name: Optional[str] = None is_active: Optional[int] = None + unit: Optional[str] = None class IngredientIn(BaseModel): @@ -715,7 +716,7 @@ def impersonate(body: dict, user=Depends(require_role("admin"))): @app.get("/api/oils") def list_oils(): conn = get_db() - rows = conn.execute("SELECT name, bottle_price, drop_count, retail_price, is_active, en_name FROM oils ORDER BY name").fetchall() + rows = conn.execute("SELECT name, bottle_price, drop_count, retail_price, is_active, en_name, unit FROM oils ORDER BY name").fetchall() conn.close() return [dict(r) for r in rows] @@ -724,11 +725,11 @@ def list_oils(): def upsert_oil(oil: OilIn, user=Depends(require_role("admin", "senior_editor"))): conn = get_db() conn.execute( - "INSERT INTO oils (name, bottle_price, drop_count, retail_price, en_name, is_active) VALUES (?, ?, ?, ?, ?, ?) " + "INSERT INTO oils (name, bottle_price, drop_count, retail_price, en_name, is_active, unit) VALUES (?, ?, ?, ?, ?, ?, ?) " "ON CONFLICT(name) DO UPDATE SET bottle_price=excluded.bottle_price, drop_count=excluded.drop_count, " "retail_price=excluded.retail_price, en_name=COALESCE(excluded.en_name, oils.en_name), " - "is_active=COALESCE(excluded.is_active, oils.is_active)", - (oil.name, oil.bottle_price, oil.drop_count, oil.retail_price, title_case(oil.en_name) if oil.en_name else oil.en_name, oil.is_active), + "is_active=COALESCE(excluded.is_active, oils.is_active), unit=COALESCE(excluded.unit, oils.unit)", + (oil.name, oil.bottle_price, oil.drop_count, oil.retail_price, title_case(oil.en_name) if oil.en_name else oil.en_name, oil.is_active, oil.unit), ) log_audit(conn, user["id"], "upsert_oil", "oil", oil.name, oil.name, json.dumps({"bottle_price": oil.bottle_price, "drop_count": oil.drop_count})) diff --git a/frontend/src/stores/oils.js b/frontend/src/stores/oils.js index a7cd4fa..071942f 100644 --- a/frontend/src/stores/oils.js +++ b/frontend/src/stores/oils.js @@ -70,6 +70,7 @@ export const useOilsStore = defineStore('oils', () => { retailPrice: oil.retail_price ?? null, isActive: oil.is_active !== 0, enName: oil.en_name ?? null, + unit: oil.unit || 'drop', } } oils.value = newOils @@ -93,18 +94,22 @@ export const useOilsStore = defineStore('oils', () => { delete oilsMeta.value[name] } - function isPortionUnit(name) { + function isMlUnit(name) { const meta = oilsMeta.value[name] - return meta && meta.dropCount === 1 + return meta && meta.unit === 'ml' + } + + function isPortionUnit(name) { + return isMlUnit(name) } function unitLabel(name, lang = 'zh') { - if (isPortionUnit(name)) return lang === 'en' ? 'portion' : '份' + if (isMlUnit(name)) return 'ml' return lang === 'en' ? 'drop' : '滴' } function unitLabelPlural(name, count, lang = 'zh') { - if (isPortionUnit(name)) return lang === 'en' ? (count === 1 ? 'portion' : 'portions') : '份' + if (isMlUnit(name)) return 'ml' return lang === 'en' ? (count === 1 ? 'drop' : 'drops') : '滴' }