Fix card overlay: scrollable buttons, doTERRA casing, English text
Some checks failed
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 27s
Test / e2e-test (push) Failing after 1m4s

- Move action buttons (favorite, save-to-diary) inside card view so
  they scroll with content instead of sticking at top
- Remove text-transform:uppercase so doTERRA renders correctly
- Fix English dilution text to match main branch (bottle vs single-use)
- Add close button to editor view header

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-07 21:50:07 +00:00
parent 4655040153
commit 86be739667

View File

@@ -1,30 +1,28 @@
<template> <template>
<div class="detail-overlay" @click.self="$emit('close')"> <div class="detail-overlay" @click.self="$emit('close')">
<div class="detail-panel"> <div class="detail-panel">
<!-- Top bar: close button only (no "卡片预览" text) --> <!-- ==================== CARD VIEW ==================== -->
<div class="detail-top-bar"> <div v-if="viewMode === 'card'" class="detail-card-view">
<div class="card-top-actions" v-if="viewMode === 'card' && authStore.isLoggedIn"> <!-- Top bar with close + edit -->
<button class="action-btn action-btn-fav" @click="handleToggleFavorite"> <div class="card-header">
{{ isFav ? ' 已收藏' : ' 收藏' }} <div class="card-top-actions" v-if="authStore.isLoggedIn">
</button> <button class="action-btn action-btn-fav action-btn-sm" @click="handleToggleFavorite">
<button v-if="!recipe._diary_id" class="action-btn action-btn-diary" @click="saveToDiary"> {{ isFav ? ' 已收藏' : ' 收藏' }}
📔 存为我的 </button>
</button> <button v-if="!recipe._diary_id" class="action-btn action-btn-diary action-btn-sm" @click="saveToDiary">
</div> 📔 存为我的
<div class="top-bar-spacer" v-else></div> </button>
<div class="top-bar-right"> </div>
<div style="flex:1"></div>
<button <button
v-if="canEditThisRecipe && viewMode === 'card'" v-if="canEditThisRecipe"
class="action-btn action-btn-sm" class="action-btn action-btn-sm"
@click="viewMode = 'editor'" @click="viewMode = 'editor'"
>编辑</button> >编辑</button>
<button class="detail-close-btn" @click="$emit('close')"></button> <button class="detail-close-btn" @click="$emit('close')"></button>
</div> </div>
</div>
<!-- ==================== CARD VIEW ==================== --> <!-- Language toggle -->
<div v-if="viewMode === 'card'" class="detail-card-view">
<!-- Language toggle (at top, matching main branch) -->
<div class="card-lang-toggle"> <div class="card-lang-toggle">
<button <button
class="lang-btn" class="lang-btn"
@@ -92,7 +90,9 @@
<div v-if="dilutionDesc" class="card-dilution">{{ dilutionDesc }}</div> <div v-if="dilutionDesc" class="card-dilution">{{ dilutionDesc }}</div>
<!-- Note --> <!-- Note -->
<div v-if="recipe.note" class="card-note">{{ recipe.note }}</div> <div v-if="recipe.note" class="card-note">
{{ cardLang === 'en' ? '📝 ' + recipe.note : '📝 ' + recipe.note }}
</div>
<!-- Total cost bar --> <!-- Total cost bar -->
<div class="card-total"> <div class="card-total">
@@ -150,10 +150,13 @@
<div v-if="viewMode === 'editor'" class="detail-editor-view"> <div v-if="viewMode === 'editor'" class="detail-editor-view">
<!-- Header --> <!-- Header -->
<div class="editor-header"> <div class="editor-header">
<input v-model="editName" type="text" class="editor-name-input" placeholder="配方名称" /> <div style="flex:1;min-width:0">
<input v-model="editName" type="text" class="editor-name-input" placeholder="配方名称" />
</div>
<div class="editor-header-actions"> <div class="editor-header-actions">
<button class="action-btn action-btn-primary" @click="saveRecipe">保存</button> <button class="action-btn action-btn-primary action-btn-sm" @click="saveRecipe">💾 保存</button>
<button class="action-btn" @click="previewFromEditor">预览</button> <button class="action-btn action-btn-sm" @click="previewFromEditor">👁 预览</button>
<button class="detail-close-btn" @click="$emit('close')"></button>
</div> </div>
</div> </div>
@@ -404,12 +407,17 @@ const totalEoDrops = computed(() =>
const dilutionDesc = computed(() => { const dilutionDesc = computed(() => {
if (coconutDrops.value <= 0 || totalEoDrops.value <= 0) return '' if (coconutDrops.value <= 0 || totalEoDrops.value <= 0) return ''
const totalDrops = coconutDrops.value + totalEoDrops.value const totalDrops = coconutDrops.value + totalEoDrops.value
const mlTotal = totalDrops / DROPS_PER_ML const vol = Math.round(totalDrops / DROPS_PER_ML)
const ratio = Math.round(coconutDrops.value / totalEoDrops.value) const ratio = Math.round(coconutDrops.value / totalEoDrops.value)
if (cardLang.value === 'en') { const isEn = cardLang.value === 'en'
return `This formula is for a ${mlTotal.toFixed(0)}ml bottle. Pure EO: ${totalEoDrops.value} drops, filled with coconut oil, dilution ratio 1:${ratio}.` if (vol >= 5) {
return isEn
? `For ${vol}ml bottle: ${totalEoDrops.value} drops essential oils, fill with coconut oil. Dilution 1:${ratio}`
: `该配方适用于 ${vol}ml 瓶,其中纯精油 ${totalEoDrops.value} 滴,其余用椰子油填满,稀释比例为 1:${ratio}`
} }
return `该配方适用于 ${mlTotal.toFixed(0)}ml 瓶,其中纯精油 ${totalEoDrops.value} 滴,其余用椰子油填满,稀释比例为 1:${ratio}` return isEn
? `Single use (${totalDrops} drops): ${totalEoDrops.value} drops essential oils + ${coconutDrops.value} drops coconut oil. Dilution 1:${ratio}`
: `该配方适用于单次用量(共${totalDrops}滴),其中纯精油 ${totalEoDrops.value} 滴,椰子油 ${coconutDrops.value} 滴,稀释比例为 1:${ratio}`
}) })
const priceInfo = computed(() => oilsStore.fmtCostWithRetail(recipe.value.ingredients)) const priceInfo = computed(() => oilsStore.fmtCostWithRetail(recipe.value.ingredients))
@@ -813,30 +821,12 @@ async function saveRecipe() {
position: relative; position: relative;
} }
/* Top bar */ /* Card header */
.detail-top-bar { .card-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
position: sticky;
top: 0;
background: #fff;
z-index: 10;
border-radius: 18px 18px 0 0;
border-bottom: 1px solid var(--border, #e0d4c0);
gap: 8px;
}
.top-bar-spacer {
flex: 1;
}
.top-bar-right {
display: flex; display: flex;
align-items: center; align-items: center;
gap: 6px; gap: 6px;
margin-left: auto; margin-bottom: 12px;
} }
.detail-close-btn { .detail-close-btn {
@@ -854,6 +844,7 @@ async function saveRecipe() {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
flex-shrink: 0;
} }
.detail-close-btn:hover { .detail-close-btn:hover {
@@ -970,7 +961,6 @@ async function saveRecipe() {
font-size: 11px; font-size: 11px;
letter-spacing: 3px; letter-spacing: 3px;
color: var(--sage, #7a9e7e); color: var(--sage, #7a9e7e);
text-transform: uppercase;
margin-bottom: 8px; margin-bottom: 8px;
font-family: 'Noto Sans SC', sans-serif; font-family: 'Noto Sans SC', sans-serif;
} }