feat: 新功能改进 #20

Merged
hera merged 57 commits from feat/next-improvements into main 2026-04-10 20:30:37 +00:00
3 changed files with 32 additions and 9 deletions
Showing only changes of commit 8866e865f7 - Show all commits

View File

@@ -69,5 +69,5 @@ export function matchesPinyinInitials(name, query) {
const initials = getPinyinInitials(name)
if (!initials) return false
const q = query.toLowerCase()
return initials.includes(q)
return initials.startsWith(q)
}

View File

@@ -260,7 +260,7 @@
</div>
<!-- Edit Oil Overlay -->
<div v-if="editingOilName" class="modal-overlay" @click.self="editingOilName = null" @keydown.enter="saveEditOil">
<div v-if="editingOilName" class="modal-overlay" @click.self="editingOilName = null" @keydown.enter="$event.isComposing || saveEditOil()">
<div class="modal-panel">
<div class="modal-header">
<h3>{{ editingOilName }}</h3>

View File

@@ -65,7 +65,7 @@
<span v-if="!auth.isAdmin" class="contrib-tag">已贡献 {{ sharedCount.adopted }}/{{ sharedCount.total }} </span>
<span class="toggle-icon">{{ showMyRecipes ? '▾' : '▸' }}</span>
</h3>
<template v-if="showMyRecipes">
<template v-if="showMyRecipes || manageSearch">
<div class="recipe-list">
<div
v-for="d in myFilteredRecipes"
@@ -106,7 +106,7 @@
<span>🌿 公共配方库 ({{ publicRecipes.length }})</span>
<span class="toggle-icon">{{ showPublicRecipes ? '▾' : '▸' }}</span>
</h3>
<div v-if="showPublicRecipes" class="recipe-list">
<div v-if="showPublicRecipes || manageSearch" class="recipe-list">
<div
v-for="r in publicFilteredRecipes"
:key="r._id"
@@ -382,10 +382,10 @@ const newIngOil = ref('')
const newIngSearch = ref('')
const newIngDrops = ref(1)
const newIngDropdownOpen = ref(false)
const formVolume = ref('single')
const formVolume = ref('30')
const formCustomVolume = ref(100)
const formCustomUnit = ref('drops')
const formDilution = ref(3)
const formDilution = ref(6)
const formTotalCost = computed(() => {
const cost = formIngredients.value
@@ -529,12 +529,32 @@ async function executeBatchAction(action) {
clearSelection()
}
function calcDilutionFromIngs(ingredients) {
const ings = ingredients || []
const coco = ings.find(i => i.oil === '椰子油')
const eoDrops = ings.filter(i => i.oil && i.oil !== '椰子油').reduce((s, i) => s + (i.drops || 0), 0)
const cocoDrops = coco ? (coco.drops || 0) : 0
const totalDrops = eoDrops + cocoDrops
if (eoDrops > 0 && cocoDrops > 0) {
formDilution.value = Math.round(cocoDrops / eoDrops)
}
// Guess volume
const DROPS_PER_ML = 18.6
const ml = totalDrops / DROPS_PER_ML
if (ml <= 1.5) formVolume.value = 'single'
else if (Math.abs(ml - 5) < 1.5) formVolume.value = '5'
else if (Math.abs(ml - 10) < 3) formVolume.value = '10'
else if (Math.abs(ml - 30) < 8) formVolume.value = '30'
else { formVolume.value = 'custom'; formCustomVolume.value = Math.round(totalDrops); formCustomUnit.value = 'drops' }
}
function editRecipe(recipe) {
editingRecipe.value = recipe
formName.value = recipe.name
formIngredients.value = recipe.ingredients.map(i => ({ ...i, _search: i.oil, _open: false }))
formNote.value = recipe.note || ''
formTags.value = [...(recipe.tags || [])]
calcDilutionFromIngs(recipe.ingredients)
showAddOverlay.value = true
}
@@ -555,6 +575,8 @@ function resetForm() {
newIngOil.value = ''
newIngSearch.value = ''
newIngDrops.value = 1
formVolume.value = '30'
formDilution.value = 6
}
function handleSmartPaste() {
@@ -665,7 +687,7 @@ function applyVolumeDilution() {
const currentEoTotal = eoIngs.reduce((s, i) => s + (i.drops || 0), 0)
if (currentEoTotal <= 0) return
const scale = targetEoDrops / currentEoTotal
eoIngs.forEach(i => { i.drops = Math.max(0.5, Math.round(i.drops * scale * 2) / 2) })
eoIngs.forEach(i => { i.drops = Math.max(1, Math.round(i.drops * scale)) })
const actualEo = eoIngs.reduce((s, i) => s + i.drops, 0)
setFormCoconut(targetTotalDrops - actualEo)
ui.showToast('已应用容量设置')
@@ -845,6 +867,7 @@ function editDiaryRecipe(diary) {
formIngredients.value = (diary.ingredients || []).map(i => ({ ...i, _search: i.oil, _open: false }))
formNote.value = diary.note || ''
formTags.value = [...(diary.tags || [])]
calcDilutionFromIngs(diary.ingredients)
showAddOverlay.value = true
}
@@ -1391,8 +1414,8 @@ watch(() => recipeStore.recipes, () => {
.action-btn-primary { background: linear-gradient(135deg, #7ec6a4, #4a9d7e); color: #fff; border-color: transparent; }
.action-btn-primary:hover { opacity: 0.9; }
.action-btn-sm { padding: 5px 12px; font-size: 12px; }
.volume-controls { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
.volume-btn { padding: 6px 14px; border: 1.5px solid #d4cfc7; border-radius: 8px; background: #fff; font-size: 13px; cursor: pointer; font-family: inherit; color: #6b6375; }
.volume-controls { display: flex; gap: 6px; margin-bottom: 8px; }
.volume-btn { flex: 1; padding: 6px 0; border: 1.5px solid #d4cfc7; border-radius: 8px; background: #fff; font-size: 13px; cursor: pointer; font-family: inherit; color: #6b6375; text-align: center; }
.volume-btn.active { background: #e8f5e9; border-color: #7ec6a4; color: #2e7d5a; font-weight: 600; }
.volume-btn:hover { border-color: #7ec6a4; }
.custom-volume-row { display: flex; gap: 6px; align-items: center; margin-bottom: 6px; }