feat: 配方volume字段存储编辑器选择的容量
All checks were successful
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 5s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 5s
PR Preview / deploy-preview (pull_request) Successful in 12s
Test / e2e-test (push) Successful in 52s
All checks were successful
PR Preview / teardown-preview (pull_request) Has been skipped
Test / unit-test (push) Successful in 5s
Test / build-check (push) Successful in 4s
PR Preview / test (pull_request) Successful in 5s
PR Preview / deploy-preview (pull_request) Successful in 12s
Test / e2e-test (push) Successful in 52s
- 后端: recipes表新增volume列,API返回/保存volume - 前端: 保存时发送formVolume,编辑时优先用stored volume - 容量显示优先级: stored volume > 椰子油计算 > 产品ml求和 - 修复编辑器容量选择保存后不生效的bug Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -48,6 +48,15 @@ const priceInfo = computed(() => oilsStore.fmtCostWithRetail(props.recipe.ingred
|
||||
const isFav = computed(() => recipesStore.isFavorite(props.recipe))
|
||||
|
||||
const volumeLabel = computed(() => {
|
||||
// Priority 1: stored volume from editor selection
|
||||
const vol = props.recipe.volume
|
||||
if (vol) {
|
||||
if (vol === 'single') return '单次'
|
||||
if (vol === 'custom') return ''
|
||||
if (/^\d+$/.test(vol)) return `${vol}ml`
|
||||
return vol
|
||||
}
|
||||
// Priority 2: calculate from ingredients
|
||||
const ings = props.recipe.ingredients || []
|
||||
const coco = ings.find(i => i.oil === '椰子油')
|
||||
if (coco && coco.drops) {
|
||||
@@ -56,7 +65,7 @@ const volumeLabel = computed(() => {
|
||||
if (ml <= 2) return '单次'
|
||||
return `${Math.round(ml)}ml`
|
||||
}
|
||||
// Non-coconut: sum all portion products as ml
|
||||
// Priority 3: sum portion products as ml
|
||||
let totalMl = 0
|
||||
let hasProduct = false
|
||||
for (const ing of ings) {
|
||||
|
||||
@@ -171,7 +171,7 @@
|
||||
/>
|
||||
<div class="row-info" @click="editRecipe(r)">
|
||||
<span class="row-name">{{ r.name }}</span>
|
||||
<span v-if="getVolumeLabel(r.ingredients)" class="row-volume">{{ getVolumeLabel(r.ingredients) }}</span>
|
||||
<span v-if="getVolumeLabel(r.ingredients, r.volume)" class="row-volume">{{ getVolumeLabel(r.ingredients, r.volume) }}</span>
|
||||
<span class="row-tags">
|
||||
<span v-for="t in [...(r.tags || [])].sort((a,b)=>a.localeCompare(b,'zh'))" :key="t" class="mini-tag">{{ t }}</span>
|
||||
</span>
|
||||
@@ -777,17 +777,25 @@ function editRecipe(recipe) {
|
||||
const coco = ings.find(i => i.oil === '椰子油')
|
||||
if (coco) {
|
||||
formCocoRow.value = { ...coco, _search: '椰子油', _open: false }
|
||||
// Guess volume from total drops
|
||||
const eoDrops = ings.filter(i => i.oil && i.oil !== '椰子油').reduce((s, i) => s + (i.drops || 0), 0)
|
||||
const totalDrops = eoDrops + coco.drops
|
||||
const ml = totalDrops / DROPS_PER_ML
|
||||
if (ml <= 2) formVolume.value = 'single'
|
||||
else if (Math.abs(ml - 5) < 1.5) formVolume.value = '5'
|
||||
else if (Math.abs(ml - 10) < 2.5) formVolume.value = '10'
|
||||
else if (Math.abs(ml - 15) < 2.5) formVolume.value = '15'
|
||||
else if (Math.abs(ml - 20) < 3) formVolume.value = '20'
|
||||
else if (Math.abs(ml - 30) < 6) formVolume.value = '30'
|
||||
else { formVolume.value = 'custom'; formCustomVolume.value = Math.round(ml) }
|
||||
// Use stored volume if available, otherwise guess from drops
|
||||
if (recipe.volume) {
|
||||
formVolume.value = recipe.volume
|
||||
if (recipe.volume === 'custom') {
|
||||
const totalDrops = ings.reduce((s, i) => s + (i.drops || 0), 0)
|
||||
formCustomVolume.value = Math.round(totalDrops / DROPS_PER_ML)
|
||||
}
|
||||
} else {
|
||||
const eoDrops = ings.filter(i => i.oil && i.oil !== '椰子油').reduce((s, i) => s + (i.drops || 0), 0)
|
||||
const totalDrops = eoDrops + coco.drops
|
||||
const ml = totalDrops / DROPS_PER_ML
|
||||
if (ml <= 2) formVolume.value = 'single'
|
||||
else if (Math.abs(ml - 5) < 1.5) formVolume.value = '5'
|
||||
else if (Math.abs(ml - 10) < 2.5) formVolume.value = '10'
|
||||
else if (Math.abs(ml - 15) < 2.5) formVolume.value = '15'
|
||||
else if (Math.abs(ml - 20) < 3) formVolume.value = '20'
|
||||
else if (Math.abs(ml - 30) < 6) formVolume.value = '30'
|
||||
else { formVolume.value = 'custom'; formCustomVolume.value = Math.round(ml) }
|
||||
}
|
||||
// Guess dilution
|
||||
if (eoDrops > 0 && coco.drops > 0) {
|
||||
const ratio = Math.round(coco.drops / eoDrops)
|
||||
@@ -1091,6 +1099,7 @@ async function saveCurrentRecipe() {
|
||||
ingredients: mappedIngs,
|
||||
note: formNote.value,
|
||||
tags: formTags.value,
|
||||
volume: formVolume.value || '',
|
||||
}
|
||||
try {
|
||||
await recipeStore.saveRecipe(payload)
|
||||
@@ -1455,7 +1464,15 @@ function openRecipeDetail(recipe) {
|
||||
if (idx >= 0) previewRecipeIndex.value = idx
|
||||
}
|
||||
|
||||
function getVolumeLabel(ingredients) {
|
||||
function getVolumeLabel(ingredients, volume) {
|
||||
// Priority 1: stored volume
|
||||
if (volume) {
|
||||
if (volume === 'single') return '单次'
|
||||
if (volume === 'custom') return ''
|
||||
if (/^\d+$/.test(volume)) return `${volume}ml`
|
||||
return volume
|
||||
}
|
||||
// Priority 2: calculate from ingredients
|
||||
const ings = ingredients || []
|
||||
const coco = ings.find(i => i.oil === '椰子油')
|
||||
if (coco && coco.drops) {
|
||||
@@ -1464,7 +1481,7 @@ function getVolumeLabel(ingredients) {
|
||||
if (ml <= 2) return '单次'
|
||||
return `${Math.round(ml)}ml`
|
||||
}
|
||||
// Non-coconut: sum portion products, mixed units all convert to ml
|
||||
// Priority 3: sum portion products as ml
|
||||
let totalMl = 0
|
||||
let hasProduct = false
|
||||
for (const ing of ings) {
|
||||
|
||||
Reference in New Issue
Block a user