feat: 配方卡片加入上传个人二维码功能 #5
@@ -36,6 +36,17 @@
|
||||
>English</button>
|
||||
</div>
|
||||
|
||||
<!-- Volume selector -->
|
||||
<div class="volume-controls card-volume-controls">
|
||||
<button
|
||||
v-for="(drops, ml) in VOLUME_DROPS"
|
||||
:key="ml"
|
||||
class="volume-btn"
|
||||
:class="{ active: selectedCardVolume === ml }"
|
||||
@click="onCardVolumeChange(ml)"
|
||||
>{{ ml === '单次' ? '单次' : ml + 'ml' }}</button>
|
||||
</div>
|
||||
|
||||
<!-- QR / brand upload hint -->
|
||||
<div v-if="showBrandHint" class="brand-upload-hint">
|
||||
<span class="hint-icon">✨</span>
|
||||
@@ -382,6 +393,7 @@ const viewMode = ref('card')
|
||||
const cardRef = ref(null)
|
||||
const cardImageUrl = ref(null)
|
||||
const cardLang = ref('zh')
|
||||
const selectedCardVolume = ref('单次')
|
||||
const showTranslationEditor = ref(false)
|
||||
const customRecipeNameEn = ref('')
|
||||
const customOilNameEn = ref({})
|
||||
@@ -400,14 +412,30 @@ const canEditThisRecipe = computed(() => {
|
||||
|
||||
const isFav = computed(() => recipesStore.isFavorite(recipe.value))
|
||||
|
||||
// Card ingredients: exclude coconut oil
|
||||
const cardIngredients = computed(() =>
|
||||
recipe.value.ingredients.filter(ing => ing.oil !== '椰子油')
|
||||
// Scale ingredients proportionally to target volume; '单次' = no scaling
|
||||
function scaleIngredients(ingredients, volume) {
|
||||
const targetDrops = VOLUME_DROPS[volume]
|
||||
if (!targetDrops) return ingredients // 单次:不缩放
|
||||
const totalDrops = ingredients.reduce((sum, ing) => sum + (ing.drops || 0), 0)
|
||||
if (totalDrops === 0) return ingredients
|
||||
return ingredients.map(ing => ({
|
||||
...ing,
|
||||
drops: Math.round(ing.drops * targetDrops / totalDrops),
|
||||
}))
|
||||
}
|
||||
|
||||
// Card ingredients: scaled to selected volume, coconut oil excluded from display
|
||||
const scaledCardIngredients = computed(() =>
|
||||
scaleIngredients(recipe.value.ingredients, selectedCardVolume.value)
|
||||
)
|
||||
|
||||
// Coconut oil drops
|
||||
const cardIngredients = computed(() =>
|
||||
scaledCardIngredients.value.filter(ing => ing.oil !== '椰子油')
|
||||
)
|
||||
|
||||
// Coconut oil drops (from scaled set)
|
||||
const coconutDrops = computed(() => {
|
||||
const coco = recipe.value.ingredients.find(ing => ing.oil === '椰子油')
|
||||
const coco = scaledCardIngredients.value.find(ing => ing.oil === '椰子油')
|
||||
return coco ? coco.drops : 0
|
||||
})
|
||||
|
||||
@@ -433,7 +461,7 @@ const dilutionDesc = computed(() => {
|
||||
: `该配方适用于单次用量(共${totalDrops}滴),其中纯精油 ${totalEoDrops.value} 滴,椰子油 ${coconutDrops.value} 滴,稀释比例为 1:${ratio}`
|
||||
})
|
||||
|
||||
const priceInfo = computed(() => oilsStore.fmtCostWithRetail(recipe.value.ingredients))
|
||||
const priceInfo = computed(() => oilsStore.fmtCostWithRetail(scaledCardIngredients.value))
|
||||
|
||||
// Today string
|
||||
const todayStr = computed(() => {
|
||||
@@ -512,6 +540,12 @@ function switchLang(lang) {
|
||||
nextTick(() => generateCardImage())
|
||||
}
|
||||
|
||||
function onCardVolumeChange(ml) {
|
||||
selectedCardVolume.value = ml
|
||||
cardImageUrl.value = null
|
||||
nextTick(() => generateCardImage())
|
||||
}
|
||||
|
||||
async function saveImage() {
|
||||
if (!cardImageUrl.value) {
|
||||
await generateCardImage()
|
||||
@@ -1469,6 +1503,10 @@ async function saveRecipe() {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.card-volume-controls {
|
||||
margin: 8px 0 12px;
|
||||
}
|
||||
|
||||
.volume-btn {
|
||||
padding: 7px 16px;
|
||||
border: 1.5px solid var(--border, #e0d4c0);
|
||||
|
||||
@@ -5,6 +5,7 @@ import { api } from '../composables/useApi'
|
||||
export const DROPS_PER_ML = 18.6
|
||||
|
||||
export const VOLUME_DROPS = {
|
||||
'单次': null,
|
||||
'2.5': 46,
|
||||
'5': 93,
|
||||
'10': 186,
|
||||
|
||||
Reference in New Issue
Block a user