From cbf72946883f49560148bc96ff89d4e495da334d Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 22:22:22 +0000 Subject: [PATCH 01/37] =?UTF-8?q?feat:=20=E5=A5=97=E8=A3=85=E6=96=B9?= =?UTF-8?q?=E6=A1=88=E5=AF=B9=E6=AF=94=E4=B8=8E=E5=AF=BC=E5=87=BA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增套装方案对比页面(KitExport.vue),支持4个套装(芳香调理/家庭医生/居家呵护/全精油)的配方匹配和成本对比 - 按各精油原瓶价占比分摊套装总价,计算每滴套装成本 - 支持设置配方售价,自动计算利润率 - Excel导出完整版(含成分明细)和简版,含横向对比sheet - 抽取套装配置到共享config/kits.js,Inventory页复用 - 修复库存页模糊匹配bug(牛至错误匹配到牛至呵护) - 修正全精油套装列表(补芫荽叶/加州胡椒/罗马洋甘菊/道格拉斯冷杉/西班牙鼠尾草,修正广藿香/斯里兰卡肉桂皮名称) - 所有套装加入椰子油 Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/composables/useKitCost.js | 136 ++++++++ frontend/src/config/kits.js | 50 +++ frontend/src/router/index.js | 6 + frontend/src/views/Inventory.vue | 24 +- frontend/src/views/KitExport.vue | 446 +++++++++++++++++++++++++ frontend/src/views/Projects.vue | 1 + 6 files changed, 645 insertions(+), 18 deletions(-) create mode 100644 frontend/src/composables/useKitCost.js create mode 100644 frontend/src/config/kits.js create mode 100644 frontend/src/views/KitExport.vue diff --git a/frontend/src/composables/useKitCost.js b/frontend/src/composables/useKitCost.js new file mode 100644 index 0000000..cab8061 --- /dev/null +++ b/frontend/src/composables/useKitCost.js @@ -0,0 +1,136 @@ +import { computed } from 'vue' +import { useOilsStore } from '../stores/oils' +import { useRecipesStore } from '../stores/recipes' +import { KITS } from '../config/kits' + +/** + * 套装成本分摊与配方匹配 + * + * 分摊逻辑:按各精油原瓶价占比分摊套装总价 + * 某精油套装内成本 = (该油原瓶价 / 套装内所有油原瓶价之和) × 套装总价 + * 套装内每滴成本 = 套装内成本 / 该油滴数 + */ +export function useKitCost() { + const oils = useOilsStore() + const recipeStore = useRecipesStore() + + // Resolve kit oil name to system oil name (handles 牛至→西班牙牛至 etc.) + function resolveOilName(kitOilName) { + if (oils.oilsMeta[kitOilName]) return kitOilName + // Try finding system oil that ends with kit name + return oils.oilNames.find(n => n.endsWith(kitOilName) && n !== kitOilName) || kitOilName + } + + // Calculate per-drop costs for a kit + function calcKitPerDrop(kit) { + const resolved = kit.oils.map(name => resolveOilName(name)) + // Sum of bottle prices for all oils in kit + let totalBottlePrice = 0 + const oilBottlePrices = {} + for (const name of resolved) { + const meta = oils.oilsMeta[name] + const bp = meta ? meta.bottlePrice : 0 + oilBottlePrices[name] = bp + totalBottlePrice += bp + } + if (totalBottlePrice === 0) return {} + + // Proportional allocation + const perDrop = {} + for (const name of resolved) { + const meta = oils.oilsMeta[name] + const bp = oilBottlePrices[name] + const kitCostForOil = (bp / totalBottlePrice) * kit.price + const drops = meta ? meta.dropCount : 1 + perDrop[name] = drops > 0 ? kitCostForOil / drops : 0 + } + return perDrop + } + + // Check if a recipe can be made with a kit + function canMakeRecipe(kit, recipe) { + const resolvedSet = new Set(kit.oils.map(name => resolveOilName(name))) + return recipe.ingredients.every(ing => resolvedSet.has(ing.oil)) + } + + // Calculate recipe cost using kit pricing + function calcRecipeCostWithKit(kitPerDrop, recipe) { + return recipe.ingredients.reduce((sum, ing) => { + const ppd = kitPerDrop[ing.oil] || 0 + return sum + ppd * ing.drops + }, 0) + } + + // Get all matching recipes for a kit + function getKitRecipes(kit) { + const perDrop = calcKitPerDrop(kit) + return recipeStore.recipes + .filter(r => canMakeRecipe(kit, r)) + .map(r => ({ + ...r, + kitCost: calcRecipeCostWithKit(perDrop, r), + originalCost: oils.calcCost(r.ingredients), + })) + .sort((a, b) => a.name.localeCompare(b.name, 'zh')) + } + + // Build full analysis for all kits + const kitAnalysis = computed(() => { + return KITS.map(kit => { + const perDrop = calcKitPerDrop(kit) + const recipes = getKitRecipes(kit) + return { + ...kit, + perDrop, + recipes, + recipeCount: recipes.length, + } + }) + }) + + // Cross-kit comparison: all recipes that at least one kit can make + const crossComparison = computed(() => { + const analysis = kitAnalysis.value + const allRecipeIds = new Set() + for (const ka of analysis) { + for (const r of ka.recipes) allRecipeIds.add(r._id) + } + + const rows = [] + for (const id of allRecipeIds) { + // Find recipe info from any kit that has it + let recipe = null + const costs = {} + for (const ka of analysis) { + const found = ka.recipes.find(r => r._id === id) + if (found) { + if (!recipe) recipe = found + costs[ka.id] = found.kitCost + } else { + costs[ka.id] = null // kit can't make this recipe + } + } + rows.push({ + id, + name: recipe.name, + tags: recipe.tags, + ingredients: recipe.ingredients, + originalCost: recipe.originalCost, + costs, // { aroma: 12.3, family: null, home3988: 10.8, full: 9.5 } + }) + } + rows.sort((a, b) => a.name.localeCompare(b.name, 'zh')) + return rows + }) + + return { + KITS, + resolveOilName, + calcKitPerDrop, + canMakeRecipe, + calcRecipeCostWithKit, + getKitRecipes, + kitAnalysis, + crossComparison, + } +} diff --git a/frontend/src/config/kits.js b/frontend/src/config/kits.js new file mode 100644 index 0000000..cc55392 --- /dev/null +++ b/frontend/src/config/kits.js @@ -0,0 +1,50 @@ +// doTERRA 套装配置 +// 价格和内容更新频率低,手动维护即可 +export const KITS = [ + { + id: 'aroma', + name: '芳香调理套装', + price: 2950, + oils: [ + '茶树', '野橘', '椒样薄荷', '薰衣草', + '芳香调理', '安定情绪', '保卫', '舒缓', + '椰子油', + ], + }, + { + id: 'family', + name: '家庭医生套装', + price: 2250, + oils: [ + '乳香', '茶树', '薰衣草', '柠檬', '椒样薄荷', '西班牙牛至', + '乐活', '舒缓', '保卫', '顺畅呼吸', + '椰子油', + ], + }, + { + id: 'home3988', + name: '居家呵护套装', + price: 3988, + oils: [ + '乳香', '野橘', '柠檬', '薰衣草', '椒样薄荷', '冬青', '茶树', '生姜', '柠檬草', '西班牙牛至', + '西洋蓍草石榴籽', '乐活', '保卫', '新瑞活力', '舒缓', '安定情绪', '安宁神气', '顺畅呼吸', '柑橘清新', '芳香调理', '元气焕能', + '椰子油', + ], + }, + { + id: 'full', + name: '全精油套装', + price: 17700, + oils: [ + '侧柏', '乳香', '雪松', '芫荽', '芫荽叶', '丝柏', '圆柚', '红橘', '冬青', '没药', '扁柏', '檀香', '姜黄', '玫瑰', + '绿薄荷', '薰衣草', '永久花', '香蜂草', '迷迭香', '麦卢卡', '天竺葵', '蓝艾菊', '小茴香', + '古巴香脂', '依兰依兰', '丁香花蕾', '柠檬尤加利', '广藿香', + '罗勒', '莱姆', '生姜', '柠檬', '茶树', '野橘', '香茅', '枫香', + '芹菜籽', '岩兰草', '苦橙叶', '柠檬草', '山鸡椒', '黑云杉', + '马郁兰', '佛手柑', '黑胡椒', '小豆蔻', '尤加利', '百里香', + '椒样薄荷', '杜松浆果', '加州胡椒', '罗马洋甘菊', '道格拉斯冷杉', '西班牙鼠尾草', + '快乐鼠尾草', '西伯利亚冷杉', + '西班牙牛至', '斯里兰卡肉桂皮', '椰子油', + ], + }, +] diff --git a/frontend/src/router/index.js b/frontend/src/router/index.js index 68a69ff..01e2279 100644 --- a/frontend/src/router/index.js +++ b/frontend/src/router/index.js @@ -29,6 +29,12 @@ const routes = [ component: () => import('../views/Projects.vue'), meta: { requiresAuth: true }, }, + { + path: '/kit-export', + name: 'KitExport', + component: () => import('../views/KitExport.vue'), + meta: { requiresAuth: true }, + }, { path: '/mydiary', name: 'MyDiary', diff --git a/frontend/src/views/Inventory.vue b/frontend/src/views/Inventory.vue index 2d71d29..7eaa0f8 100644 --- a/frontend/src/views/Inventory.vue +++ b/frontend/src/views/Inventory.vue @@ -101,6 +101,7 @@ import { useOilsStore } from '../stores/oils' import { useRecipesStore } from '../stores/recipes' import { useUiStore } from '../stores/ui' import { api } from '../composables/useApi' +import { KITS as KIT_LIST } from '../config/kits' const auth = useAuthStore() const oils = useOilsStore() @@ -120,30 +121,17 @@ const searchResults = computed(() => { return oils.oilNames.filter(n => n.toLowerCase().includes(q)).slice(0, 15) }) -// Kit definitions -const KITS = { - family: ['乳香', '茶树', '薰衣草', '柠檬', '椒样薄荷', '保卫', '牛至', '乐活', '顺畅呼吸', '舒缓'], - home3988: ['乳香', '野橘', '柠檬', '薰衣草', '椒样薄荷', '冬青', '茶树', '生姜', '柠檬草', '西班牙牛至', - '西洋蓍草石榴籽', '乐活', '保卫', '新瑞活力', '舒缓', '安定情绪', '安宁神气', '顺畅呼吸', '柑橘清新', '芳香调理', '元气焕能'], - aroma: ['薰衣草', '舒缓', '安定情绪', '芳香调理', '野橘', '椒样薄荷', '保卫', '茶树'], - full: ['侧柏', '乳香', '雪松', '芫荽', '丝柏', '圆柚', '红橘', '冬青', '没药', '扁柏', '檀香', '姜黄', '玫瑰', - '绿薄荷', '薰衣草', '永久花', '香蜂草', '迷迭香', '麦卢卡', '天竺葵', '蓝艾菊', '小茴香', - '古巴香脂', '依兰依兰', '丁香花蕾', '柠檬尤加利', '藿香', '西班牙牛至尾草', - '罗勒', '莱姆', '生姜', '柠檬', '茶树', '野橘', '香茅', '枫香', - '芹菜籽', '岩兰草', '苦橙叶', '柠檬草', '山鸡椒', '黑云杉', - '马郁兰', '佛手柑', '黑胡椒', '小豆蔻', '尤加利', '百里香', - '椒样薄荷', '杜松浆果', '加州白鼠尾草', - '快乐鼠尾草', '西伯利亚冷杉', - '西班牙牛至', '斯里兰卡肉桂'] -} +// Kit definitions from shared config +const KITS = Object.fromEntries(KIT_LIST.map(k => [k.id, k.oils])) function addKit(kitName) { const kit = KITS[kitName] if (!kit) return let added = 0 for (const name of kit) { - // Match existing oil names (fuzzy) - const match = oils.oilNames.find(n => n === name) || oils.oilNames.find(n => n.includes(name) || name.includes(n)) + // Match existing oil names: exact first, then oil name ending with kit name (西班牙牛至 matches 牛至, but 牛至呵护 does not) + const match = oils.oilNames.find(n => n === name) + || oils.oilNames.find(n => n.endsWith(name) && n !== name) if (match && !ownedOils.value.includes(match)) { ownedOils.value.push(match) added++ diff --git a/frontend/src/views/KitExport.vue b/frontend/src/views/KitExport.vue new file mode 100644 index 0000000..aaa7702 --- /dev/null +++ b/frontend/src/views/KitExport.vue @@ -0,0 +1,446 @@ + + + + + diff --git a/frontend/src/views/Projects.vue b/frontend/src/views/Projects.vue index fbef44e..07c9871 100644 --- a/frontend/src/views/Projects.vue +++ b/frontend/src/views/Projects.vue @@ -10,6 +10,7 @@

📊 服务项目成本利润分析

+
-- 2.49.1 From c2369e9bee218a69c43b7f527cb273447a36d3c1 Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 22:46:33 +0000 Subject: [PATCH 02/37] =?UTF-8?q?fix:=20=E6=92=A4=E9=94=80=E5=95=86?= =?UTF-8?q?=E4=B8=9A=E8=AE=A4=E8=AF=81=E6=8E=A5=E5=8F=A3500=E9=94=99?= =?UTF-8?q?=E8=AF=AF=EF=BC=8CSELECT=E7=BC=BA=E5=B0=91display=5Fname?= =?UTF-8?q?=E5=92=8Cusername=E5=AD=97=E6=AE=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/main.py b/backend/main.py index b521877..f70e706 100644 --- a/backend/main.py +++ b/backend/main.py @@ -682,7 +682,7 @@ def revoke_business(user_id: int, body: dict = None, user=Depends(require_role(" conn = get_db() conn.execute("UPDATE users SET business_verified = 0 WHERE id = ?", (user_id,)) reason = (body or {}).get("reason", "").strip() - target = conn.execute("SELECT role FROM users WHERE id = ?", (user_id,)).fetchone() + target = conn.execute("SELECT role, display_name, username FROM users WHERE id = ?", (user_id,)).fetchone() if target: msg = "你的商业用户资格已被取消。" if reason: -- 2.49.1 From 1c1f91012d155e9154342c18f3f7dd673af22f23 Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 22:50:16 +0000 Subject: [PATCH 03/37] =?UTF-8?q?fix:=20=E4=BF=AE=E6=AD=A3=E7=B2=BE?= =?UTF-8?q?=E6=B2=B9=E5=90=8D=E7=A7=B0=20=E8=A5=BF=E6=B4=8B=E8=93=8D?= =?UTF-8?q?=E8=8D=89=E2=86=92=E8=A5=BF=E6=B4=8B=E8=93=8D=E8=8D=89=E7=9F=B3?= =?UTF-8?q?=E6=A6=B4=E7=B1=BD,=20=E5=85=83=E6=B0=94=E2=86=92=E5=85=83?= =?UTF-8?q?=E6=B0=94=E7=84=95=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit defaults.json和数据库迁移同步更新,修复居家呵护套装少匹配1-2个油的问题。 Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/database.py | 8 +++++ backend/defaults.json | 82 +++++++++++++++++++++---------------------- 2 files changed, 49 insertions(+), 41 deletions(-) diff --git a/backend/database.py b/backend/database.py index 241c163..6164c08 100644 --- a/backend/database.py +++ b/backend/database.py @@ -251,6 +251,14 @@ def init_db(): if "volume" not in cols: c.execute("ALTER TABLE recipes ADD COLUMN volume TEXT DEFAULT ''") + # Migration: rename oils 西洋蓍草→西洋蓍草石榴籽, 元气→元气焕能 + _oil_renames = [("西洋蓍草", "西洋蓍草石榴籽"), ("元气", "元气焕能")] + for old_name, new_name in _oil_renames: + exists = c.execute("SELECT 1 FROM oils WHERE name = ?", (old_name,)).fetchone() + if exists: + c.execute("UPDATE oils SET name = ? WHERE name = ?", (new_name, old_name)) + c.execute("UPDATE recipe_ingredients SET oil_name = ? WHERE oil_name = ?", (new_name, old_name)) + # Seed admin user if no users exist count = c.execute("SELECT COUNT(*) FROM users").fetchone()[0] if count == 0: diff --git a/backend/defaults.json b/backend/defaults.json index a8227f6..b24d087 100644 --- a/backend/defaults.json +++ b/backend/defaults.json @@ -328,11 +328,11 @@ "bottlePrice": 110, "dropCount": 280 }, - "西洋蓍草": { + "西洋蓍草石榴籽": { "bottlePrice": 450, "dropCount": 280 }, - "元气": { + "元气焕能": { "bottlePrice": 230, "dropCount": 280 }, @@ -514,7 +514,7 @@ "drops": 1 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 1 }, { @@ -797,7 +797,7 @@ "drops": 1 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 1 }, { @@ -867,7 +867,7 @@ "note": "", "ingredients": [ { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 2 }, { @@ -929,7 +929,7 @@ "drops": 1 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 2 }, { @@ -1169,7 +1169,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 20 }, { @@ -1196,7 +1196,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 20 }, { @@ -1615,7 +1615,7 @@ "drops": 20 }, { - "oil": "元气", + "oil": "元气焕能", "drops": 20 }, { @@ -1910,7 +1910,7 @@ "drops": 10 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 10 } ] @@ -2072,7 +2072,7 @@ "drops": 5 }, { - "oil": "元气", + "oil": "元气焕能", "drops": 5 }, { @@ -2216,7 +2216,7 @@ "drops": 5 }, { - "oil": "元气", + "oil": "元气焕能", "drops": 5 } ] @@ -2285,7 +2285,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 5 }, { @@ -2328,7 +2328,7 @@ "drops": 5 }, { - "oil": "元气", + "oil": "元气焕能", "drops": 8 }, { @@ -2491,7 +2491,7 @@ "drops": 10 }, { - "oil": "元气", + "oil": "元气焕能", "drops": 10 } ] @@ -2643,7 +2643,7 @@ "drops": 2 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 2 } ] @@ -2666,7 +2666,7 @@ "drops": 5 }, { - "oil": "元气", + "oil": "元气焕能", "drops": 5 }, { @@ -2709,7 +2709,7 @@ "drops": 3 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 10 } ] @@ -2778,7 +2778,7 @@ "drops": 5 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 10 }, { @@ -2816,7 +2816,7 @@ "tags": [], "ingredients": [ { - "oil": "元气", + "oil": "元气焕能", "drops": 15 }, { @@ -2901,7 +2901,7 @@ "tags": [], "ingredients": [ { - "oil": "元气", + "oil": "元气焕能", "drops": 5 }, { @@ -3041,7 +3041,7 @@ "drops": 5 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 10 } ] @@ -3111,7 +3111,7 @@ "drops": 10 }, { - "oil": "元气", + "oil": "元气焕能", "drops": 5 }, { @@ -3276,7 +3276,7 @@ "drops": 8 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 10 }, { @@ -3858,7 +3858,7 @@ "drops": 30 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 30 }, { @@ -4327,7 +4327,7 @@ "drops": 5 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 20 } ] @@ -4393,7 +4393,7 @@ "drops": 3 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 3 }, { @@ -4564,7 +4564,7 @@ "drops": 2 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 10 }, { @@ -4696,7 +4696,7 @@ "drops": 5 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 10 } ] @@ -4723,7 +4723,7 @@ "drops": 5 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 5 }, { @@ -4758,7 +4758,7 @@ "drops": 5 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 5 } ] @@ -4866,7 +4866,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 10 }, { @@ -4893,7 +4893,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 20 }, { @@ -5056,7 +5056,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 10 }, { @@ -6583,7 +6583,7 @@ "drops": 4 }, { - "oil": "元气", + "oil": "元气焕能", "drops": 4 }, { @@ -6662,7 +6662,7 @@ "drops": 4 }, { - "oil": "元气", + "oil": "元气焕能", "drops": 4 }, { @@ -6729,7 +6729,7 @@ "drops": 4 }, { - "oil": "元气", + "oil": "元气焕能", "drops": 4 }, { @@ -7051,7 +7051,7 @@ "drops": 10 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 20 }, { @@ -7112,7 +7112,7 @@ "drops": 3 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 3 } ] @@ -7588,7 +7588,7 @@ "drops": 10 }, { - "oil": "西洋蓍草", + "oil": "西洋蓍草石榴籽", "drops": 10 }, { @@ -7819,7 +7819,7 @@ "drops": 2 }, { - "oil": "元气", + "oil": "元气焕能", "drops": 3 }, { -- 2.49.1 From 9562cbfe25398efb6dff85bc43208184142b8e4b Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 23:05:57 +0000 Subject: [PATCH 04/37] =?UTF-8?q?fix:=20=E6=B2=B9=E5=90=8D=E8=BF=81?= =?UTF-8?q?=E7=A7=BB=E5=A4=84=E7=90=86=E6=96=B0=E6=97=A7=E5=90=8D=E5=AD=97?= =?UTF-8?q?=E5=90=8C=E6=97=B6=E5=AD=98=E5=9C=A8=E7=9A=84=E6=83=85=E5=86=B5?= =?UTF-8?q?=EF=BC=8C=E9=81=BF=E5=85=8DUNIQUE=E5=86=B2=E7=AA=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/database.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/backend/database.py b/backend/database.py index 6164c08..ed862b8 100644 --- a/backend/database.py +++ b/backend/database.py @@ -254,8 +254,13 @@ def init_db(): # Migration: rename oils 西洋蓍草→西洋蓍草石榴籽, 元气→元气焕能 _oil_renames = [("西洋蓍草", "西洋蓍草石榴籽"), ("元气", "元气焕能")] for old_name, new_name in _oil_renames: - exists = c.execute("SELECT 1 FROM oils WHERE name = ?", (old_name,)).fetchone() - if exists: + old_exists = c.execute("SELECT 1 FROM oils WHERE name = ?", (old_name,)).fetchone() + new_exists = c.execute("SELECT 1 FROM oils WHERE name = ?", (new_name,)).fetchone() + if old_exists and new_exists: + # Both exist: delete old, update recipe references to new + c.execute("DELETE FROM oils WHERE name = ?", (old_name,)) + c.execute("UPDATE recipe_ingredients SET oil_name = ? WHERE oil_name = ?", (new_name, old_name)) + elif old_exists: c.execute("UPDATE oils SET name = ? WHERE name = ?", (new_name, old_name)) c.execute("UPDATE recipe_ingredients SET oil_name = ? WHERE oil_name = ?", (new_name, old_name)) -- 2.49.1 From 8600f16ceaa47ffacaaca43ae60bcdf216860266 Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 23:07:07 +0000 Subject: [PATCH 05/37] =?UTF-8?q?fix:=20=E8=A5=BF=E6=B4=8B=E8=93=8D?= =?UTF-8?q?=E8=8D=89=E6=98=AF=E6=AD=A3=E7=A1=AE=E5=90=8D=E5=AD=97=EF=BC=8C?= =?UTF-8?q?=E6=92=A4=E5=9B=9E=E8=AF=AF=E6=94=B9=EF=BC=9B=E5=A5=97=E8=A3=85?= =?UTF-8?q?=E5=88=97=E8=A1=A8=E5=90=8C=E6=AD=A5=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- backend/database.py | 2 +- backend/defaults.json | 54 ++++++++++++++++++------------------- frontend/src/config/kits.js | 2 +- 3 files changed, 29 insertions(+), 29 deletions(-) diff --git a/backend/database.py b/backend/database.py index ed862b8..bfa9119 100644 --- a/backend/database.py +++ b/backend/database.py @@ -252,7 +252,7 @@ def init_db(): c.execute("ALTER TABLE recipes ADD COLUMN volume TEXT DEFAULT ''") # Migration: rename oils 西洋蓍草→西洋蓍草石榴籽, 元气→元气焕能 - _oil_renames = [("西洋蓍草", "西洋蓍草石榴籽"), ("元气", "元气焕能")] + _oil_renames = [("元气", "元气焕能")] for old_name, new_name in _oil_renames: old_exists = c.execute("SELECT 1 FROM oils WHERE name = ?", (old_name,)).fetchone() new_exists = c.execute("SELECT 1 FROM oils WHERE name = ?", (new_name,)).fetchone() diff --git a/backend/defaults.json b/backend/defaults.json index b24d087..1f96d80 100644 --- a/backend/defaults.json +++ b/backend/defaults.json @@ -328,7 +328,7 @@ "bottlePrice": 110, "dropCount": 280 }, - "西洋蓍草石榴籽": { + "西洋蓍草": { "bottlePrice": 450, "dropCount": 280 }, @@ -514,7 +514,7 @@ "drops": 1 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 1 }, { @@ -797,7 +797,7 @@ "drops": 1 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 1 }, { @@ -867,7 +867,7 @@ "note": "", "ingredients": [ { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 2 }, { @@ -929,7 +929,7 @@ "drops": 1 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 2 }, { @@ -1169,7 +1169,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 20 }, { @@ -1196,7 +1196,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 20 }, { @@ -1910,7 +1910,7 @@ "drops": 10 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 10 } ] @@ -2285,7 +2285,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 5 }, { @@ -2643,7 +2643,7 @@ "drops": 2 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 2 } ] @@ -2709,7 +2709,7 @@ "drops": 3 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 10 } ] @@ -2778,7 +2778,7 @@ "drops": 5 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 10 }, { @@ -3041,7 +3041,7 @@ "drops": 5 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 10 } ] @@ -3276,7 +3276,7 @@ "drops": 8 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 10 }, { @@ -3858,7 +3858,7 @@ "drops": 30 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 30 }, { @@ -4327,7 +4327,7 @@ "drops": 5 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 20 } ] @@ -4393,7 +4393,7 @@ "drops": 3 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 3 }, { @@ -4564,7 +4564,7 @@ "drops": 2 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 10 }, { @@ -4696,7 +4696,7 @@ "drops": 5 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 10 } ] @@ -4723,7 +4723,7 @@ "drops": 5 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 5 }, { @@ -4758,7 +4758,7 @@ "drops": 5 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 5 } ] @@ -4866,7 +4866,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 10 }, { @@ -4893,7 +4893,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 20 }, { @@ -5056,7 +5056,7 @@ "tags": [], "ingredients": [ { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 10 }, { @@ -7051,7 +7051,7 @@ "drops": 10 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 20 }, { @@ -7112,7 +7112,7 @@ "drops": 3 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 3 } ] @@ -7588,7 +7588,7 @@ "drops": 10 }, { - "oil": "西洋蓍草石榴籽", + "oil": "西洋蓍草", "drops": 10 }, { diff --git a/frontend/src/config/kits.js b/frontend/src/config/kits.js index cc55392..73f37fb 100644 --- a/frontend/src/config/kits.js +++ b/frontend/src/config/kits.js @@ -27,7 +27,7 @@ export const KITS = [ price: 3988, oils: [ '乳香', '野橘', '柠檬', '薰衣草', '椒样薄荷', '冬青', '茶树', '生姜', '柠檬草', '西班牙牛至', - '西洋蓍草石榴籽', '乐活', '保卫', '新瑞活力', '舒缓', '安定情绪', '安宁神气', '顺畅呼吸', '柑橘清新', '芳香调理', '元气焕能', + '西洋蓍草', '乐活', '保卫', '新瑞活力', '舒缓', '安定情绪', '安宁神气', '顺畅呼吸', '柑橘清新', '芳香调理', '元气焕能', '椰子油', ], }, -- 2.49.1 From 981765e4bba0176440ae13beb97351d8a4dab7fc Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 23:12:37 +0000 Subject: [PATCH 06/37] =?UTF-8?q?fix:=20=E5=A5=97=E8=A3=85=E5=AF=B9?= =?UTF-8?q?=E6=AF=94=E5=8E=BB=E6=8E=89=E6=A0=87=E7=AD=BE=E5=88=97=EF=BC=8C?= =?UTF-8?q?=E6=A8=AA=E5=90=91=E5=AF=B9=E6=AF=94=E6=8C=89=E5=8F=AF=E5=81=9A?= =?UTF-8?q?=E5=A5=97=E8=A3=85=E6=95=B0=E4=BB=8E=E5=B0=91=E5=88=B0=E5=A4=9A?= =?UTF-8?q?=E6=8E=92=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/composables/useKitCost.js | 8 +++++++- frontend/src/views/KitExport.vue | 2 -- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/frontend/src/composables/useKitCost.js b/frontend/src/composables/useKitCost.js index cab8061..cd63ef0 100644 --- a/frontend/src/composables/useKitCost.js +++ b/frontend/src/composables/useKitCost.js @@ -119,7 +119,13 @@ export function useKitCost() { costs, // { aroma: 12.3, family: null, home3988: 10.8, full: 9.5 } }) } - rows.sort((a, b) => a.name.localeCompare(b.name, 'zh')) + // Sort by number of kits that can make the recipe (fewest first), then by name + rows.sort((a, b) => { + const aCount = Object.values(a.costs).filter(v => v != null).length + const bCount = Object.values(b.costs).filter(v => v != null).length + if (aCount !== bCount) return aCount - bCount + return a.name.localeCompare(b.name, 'zh') + }) return rows }) diff --git a/frontend/src/views/KitExport.vue b/frontend/src/views/KitExport.vue index aaa7702..1dc89dc 100644 --- a/frontend/src/views/KitExport.vue +++ b/frontend/src/views/KitExport.vue @@ -39,7 +39,6 @@ 配方名 - 标签 套装成本 原价成本 售价 @@ -49,7 +48,6 @@ {{ r.name }} - {{ (r.tags || []).join('/') }} {{ fmtPrice(r.kitCost) }} {{ fmtPrice(r.originalCost) }} -- 2.49.1 From 750b247b5b9964e7d033ca600dee0d4fe786fe8d Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 23:15:56 +0000 Subject: [PATCH 07/37] =?UTF-8?q?fix:=20=E5=A5=97=E8=A3=85=E6=96=B9?= =?UTF-8?q?=E6=A1=88=E5=AF=B9=E6=AF=94=E4=BB=85=E5=95=86=E4=B8=9A=E8=AE=A4?= =?UTF-8?q?=E8=AF=81=E7=94=A8=E6=88=B7=E5=8F=AF=E7=94=A8=EF=BC=8C=E7=BE=8E?= =?UTF-8?q?=E5=8C=96=E5=85=A5=E5=8F=A3=E6=8C=89=E9=92=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/views/KitExport.vue | 8 ++++++++ frontend/src/views/Projects.vue | 27 ++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/frontend/src/views/KitExport.vue b/frontend/src/views/KitExport.vue index 1dc89dc..3536af5 100644 --- a/frontend/src/views/KitExport.vue +++ b/frontend/src/views/KitExport.vue @@ -109,11 +109,15 @@