From 30bb69f52c807cc14e80c959c7acb68788c351ee Mon Sep 17 00:00:00 2001 From: Hera Zhao Date: Mon, 13 Apr 2026 16:30:46 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=BA=A7=E5=93=81?= =?UTF-8?q?=E8=A1=A8=E5=8D=95(ml/g/=E9=A2=97)+=E9=85=8D=E6=96=B9=E5=8D=A1?= =?UTF-8?q?=E7=89=87=E6=98=BE=E7=A4=BA=E4=BA=A7=E5=93=81=E5=AE=B9=E9=87=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 精油价目新增分两个tab:新增精油(标准容量) / 新增其他产品(ml/g/颗) - saveOil支持unit参数 - 配方卡片:含产品的配方直接显示产品用量+单位(如30g) Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/src/components/RecipeCard.vue | 11 ++--- frontend/src/stores/oils.js | 8 ++-- frontend/src/views/OilReference.vue | 57 +++++++++++++++++++++++++- 3 files changed, 65 insertions(+), 11 deletions(-) diff --git a/frontend/src/components/RecipeCard.vue b/frontend/src/components/RecipeCard.vue index 2d633e7..7606d35 100644 --- a/frontend/src/components/RecipeCard.vue +++ b/frontend/src/components/RecipeCard.vue @@ -56,13 +56,10 @@ const volumeLabel = computed(() => { if (ml <= 2) return '单次' return `${Math.round(ml)}ml` } - // Non-coconut: check if has portion product, extract volume from note - const hasPortion = ings.some(i => oilsStore.isPortionUnit(i.oil)) - if (hasPortion) { - const note = props.recipe.note || '' - const m = note.match(/(\d+)\s*(ml|毫升|克|g)/i) - if (m) return `${m[1]}${m[2].toLowerCase()}` - return '调配' + // Non-coconut: find portion product and show its amount + unit + const portionIng = ings.find(i => oilsStore.isPortionUnit(i.oil)) + if (portionIng) { + return `${portionIng.drops}${oilsStore.unitLabel(portionIng.oil)}` } return '' }) diff --git a/frontend/src/stores/oils.js b/frontend/src/stores/oils.js index 234573f..256b5fe 100644 --- a/frontend/src/stores/oils.js +++ b/frontend/src/stores/oils.js @@ -77,14 +77,16 @@ export const useOilsStore = defineStore('oils', () => { oilsMeta.value = newMeta } - async function saveOil(name, bottlePrice, dropCount, retailPrice, enName = null) { - await api.post('/api/oils', { + async function saveOil(name, bottlePrice, dropCount, retailPrice, enName = null, unit = null) { + const payload = { name, bottle_price: bottlePrice, drop_count: dropCount, retail_price: retailPrice, en_name: enName, - }) + } + if (unit) payload.unit = unit + await api.post('/api/oils', payload) await loadOils() } diff --git a/frontend/src/views/OilReference.vue b/frontend/src/views/OilReference.vue index 4123a5f..ced2b89 100644 --- a/frontend/src/views/OilReference.vue +++ b/frontend/src/views/OilReference.vue @@ -107,7 +107,12 @@
-
+
+ + +
+ +
@@ -124,6 +129,20 @@
+ +
+ + + + + + + +
@@ -413,12 +432,15 @@ const activeCardName = ref(null) const activeCard = ref(null) // Add oil form +const addType = ref('oil') const newOilName = ref('') const newOilEnName = ref('') const newBottlePrice = ref(null) const newVolume = ref('5') const newCustomDrops = ref(null) const newRetailPrice = ref(null) +const newProductAmount = ref(null) +const newProductUnit = ref('ml') // Edit oil const editingOilName = ref(null) @@ -657,6 +679,29 @@ async function addOil() { } } +async function addProduct() { + if (!newOilName.value.trim() || !newProductAmount.value) return + try { + await oils.saveOil( + newOilName.value.trim(), + newBottlePrice.value || 0, + newProductAmount.value, + newRetailPrice.value || null, + newOilEnName.value.trim() || null, + newProductUnit.value + ) + ui.showToast(`已添加: ${newOilName.value}`) + newOilName.value = '' + newOilEnName.value = '' + newBottlePrice.value = null + newProductAmount.value = null + newProductUnit.value = 'ml' + newRetailPrice.value = null + } catch (e) { + ui.showToast('添加失败: ' + (e.message || '')) + } +} + function editOil(name) { editingOilName.value = name editOilDisplayName.value = name @@ -1065,6 +1110,16 @@ async function saveCardImage(name) { } /* ===== Add Oil Form ===== */ +.add-type-tabs { display: flex; gap: 0; margin-bottom: 10px; } +.add-type-tab { + flex: 1; padding: 6px 0; text-align: center; font-size: 13px; cursor: pointer; + border: 1.5px solid var(--border, #d4cfc7); background: #fff; color: var(--text-mid, #6b6375); + font-family: inherit; +} +.add-type-tab:first-child { border-radius: 8px 0 0 8px; } +.add-type-tab:last-child { border-radius: 0 8px 8px 0; border-left: none; } +.add-type-tab.active { background: var(--sage, #7a9e7e); color: #fff; border-color: var(--sage, #7a9e7e); } + .add-oil-form { margin-bottom: 16px; padding: 14px;