feat: Header重排、共享配方到公共库、待审核配方、权限优化 #17

Merged
fam merged 39 commits from fix/ui-polish-round2 into main 2026-04-09 18:37:12 +00:00
2 changed files with 44 additions and 29 deletions
Showing only changes of commit 73a041d9c8 - Show all commits

View File

@@ -103,7 +103,7 @@
<button
v-if="cardLang === 'en' && authStore.canManage"
class="action-btn"
@click="showTranslationEditor = true"
@click="openTranslationEditor"
> 修改翻译</button>
<button
v-if="showBrandHint"
@@ -595,6 +595,18 @@ function copyText() {
})
}
function openTranslationEditor() {
// Pre-populate with existing English names from DB/translation table
const map = {}
for (const ing of cardIngredients.value) {
const existing = oilsStore.oilsMeta[ing.oil]?.enName || oilEn(ing.oil)
if (existing) map[ing.oil] = existing
}
customOilNameEn.value = map
customRecipeNameEn.value = recipe.value.en_name || recipeNameEn(recipe.value.name)
showTranslationEditor.value = true
}
async function applyTranslation() {
showTranslationEditor.value = false
let saved = 0

View File

@@ -2,15 +2,19 @@
<div class="oil-reference">
<!-- Knowledge Cards at Top -->
<div style="display:flex;gap:10px;margin-bottom:16px;flex-wrap:wrap">
<div @click="showDilution = true" style="flex:1;min-width:140px;background:linear-gradient(135deg,#e8f5e9,#c8e6c9);border-radius:14px;padding:16px;cursor:pointer;transition:transform 0.2s" @mouseover="$event.target.style.transform='translateY(-2px)'" @mouseout="$event.target.style.transform=''">
<div style="font-size:24px;margin-bottom:6px">💧</div>
<div style="font-size:14px;font-weight:600;color:#2e7d32">稀释比例</div>
<div style="font-size:11px;color:#558b2f;margin-top:4px">不同年龄段的稀释指南</div>
<div @click="showDilution = true" style="flex:1;min-width:140px;background:linear-gradient(135deg,#e8f5e9,#c8e6c9);border-radius:12px;padding:12px 16px;cursor:pointer;transition:transform 0.2s;display:flex;align-items:center;gap:10px" @mouseover="$event.currentTarget.style.transform='translateY(-2px)'" @mouseout="$event.currentTarget.style.transform=''">
<span style="font-size:22px">💧</span>
<div>
<div style="font-size:14px;font-weight:600;color:#2e7d32">稀释比例</div>
<div style="font-size:11px;color:#558b2f;margin-top:2px">不同年龄段的稀释指南</div>
</div>
</div>
<div @click="showContra = true" style="flex:1;min-width:140px;background:linear-gradient(135deg,#fff8e1,#ffecb3);border-radius:14px;padding:16px;cursor:pointer;transition:transform 0.2s" @mouseover="$event.target.style.transform='translateY(-2px)'" @mouseout="$event.target.style.transform=''">
<div style="font-size:24px;margin-bottom:6px"></div>
<div style="font-size:14px;font-weight:600;color:#f57f17">使用禁忌</div>
<div style="font-size:11px;color:#ff8f00;margin-top:4px">安全使用精油的注意事项</div>
<div @click="showContra = true" style="flex:1;min-width:140px;background:linear-gradient(135deg,#fff8e1,#ffecb3);border-radius:12px;padding:12px 16px;cursor:pointer;transition:transform 0.2s;display:flex;align-items:center;gap:10px" @mouseover="$event.currentTarget.style.transform='translateY(-2px)'" @mouseout="$event.currentTarget.style.transform=''">
<span style="font-size:22px"></span>
<div>
<div style="font-size:14px;font-weight:600;color:#f57f17">使用禁忌</div>
<div style="font-size:11px;color:#ff8f00;margin-top:2px">安全使用精油的注意事项</div>
</div>
</div>
</div>
@@ -673,27 +677,20 @@ async function toggleOilActive() {
const meta = getMeta(name)
const newActive = meta?.isActive === false ? 1 : 0
try {
const res = await api('/api/oils', {
method: 'POST',
body: JSON.stringify({
name,
bottle_price: meta?.bottlePrice || 0,
drop_count: meta?.dropCount || 1,
retail_price: meta?.retailPrice,
is_active: newActive,
}),
await api.post('/api/oils', {
name,
bottle_price: meta?.bottlePrice || 0,
drop_count: meta?.dropCount || 1,
retail_price: meta?.retailPrice || null,
en_name: meta?.enName || null,
is_active: newActive,
})
if (!res.ok) {
const err = await res.json().catch(() => ({}))
ui.showToast('操作失败: ' + (err.detail || res.status))
return
}
await oils.loadOils()
cardVersion.value++
ui.showToast(newActive ? '已重新上架' : '已下架')
editingOilName.value = null
} catch (e) {
ui.showToast('操作失败: ' + (e.message || ''))
ui.showToast('操作失败: ' + (e.message || e))
}
}
@@ -1206,33 +1203,39 @@ async function saveCardImage(name) {
background: #fff5f5 !important;
}
.oil-name-line {
font-size: clamp(10px, 2.5vw, 14px);
font-size: 14px;
font-weight: 500;
color: var(--text-dark);
white-space: nowrap;
}
.oil-en-line {
font-size: clamp(8px, 1.8vw, 10px);
font-size: 10px;
color: var(--text-light);
white-space: nowrap;
}
.oil-price-line {
font-size: clamp(10px, 2.2vw, 13px);
font-size: 13px;
color: var(--sage-dark);
font-weight: 600;
white-space: nowrap;
}
.oil-price-unit {
font-size: clamp(8px, 1.6vw, 10px);
font-size: 10px;
font-weight: 400;
color: var(--text-light);
}
.oil-retail-line {
font-size: clamp(8px, 1.6vw, 11px);
font-size: 11px;
color: var(--text-light);
text-decoration: line-through;
white-space: nowrap;
}
@media (max-width: 480px) {
.oil-name-line { font-size: 12px; }
.oil-en-line { font-size: 9px; }
.oil-price-line { font-size: 11px; }
.oil-retail-line { font-size: 9px; }
}
.oil-chip-actions {
position: absolute;