diff --git a/frontend/src/views/RecipeManager.vue b/frontend/src/views/RecipeManager.vue index 4654118..06edcf7 100644 --- a/frontend/src/views/RecipeManager.vue +++ b/frontend/src/views/RecipeManager.vue @@ -29,25 +29,25 @@ 共{{ totalSelected }}个 - - + + +
+ + + + +

+ 📖 我的配方 ({{ myRecipes.length }}) 已贡献 {{ sharedCount.adopted }}/{{ sharedCount.total }} 条 {{ showMyRecipes ? '▾' : '▸' }} @@ -107,6 +108,7 @@

+ 🌿 公共配方库 ({{ publicRecipes.length }}) {{ showPublicRecipes ? '▾' : '▸' }}

@@ -541,29 +543,35 @@ function toggleDiarySelect(id) { function clearSelection() { selectedIds.clear() selectedDiaryIds.clear() - batchAction.value = '' + showBatchMenu.value = false } -function onBatchSelect() { - if (batchAction.value) { - executeBatchAction(batchAction.value) - batchAction.value = '' +function toggleSelectAll() { + if (isAllSelected.value) { + clearSelection() + } else { + myFilteredRecipes.value.forEach(d => selectedDiaryIds.add(d.id)) + if (auth.canEdit) publicFilteredRecipes.value.forEach(r => selectedIds.add(r._id)) + showMyRecipes.value = true + if (auth.canEdit) showPublicRecipes.value = true } } -function toggleSelectAllDiary() { - if (selectedDiaryIds.size > 0 || selectedIds.size > 0) { - // Any selected → deselect all +function toggleMySelect() { + if (isMyAllSelected.value) { selectedDiaryIds.clear() + } else { + myFilteredRecipes.value.forEach(d => selectedDiaryIds.add(d.id)) + showMyRecipes.value = true + } +} + +function togglePubSelect() { + if (isPubAllSelected.value) { selectedIds.clear() } else { - // Select all - myFilteredRecipes.value.forEach(d => selectedDiaryIds.add(d.id)) - if (auth.canEdit) { - publicFilteredRecipes.value.forEach(r => selectedIds.add(r._id)) - } - showMyRecipes.value = true - if (auth.canEdit) showPublicRecipes.value = true + publicFilteredRecipes.value.forEach(r => selectedIds.add(r._id)) + showPublicRecipes.value = true } } @@ -958,8 +966,11 @@ async function saveAllParsed() { const sharedCount = ref({ adopted: 0, total: 0 }) const previewRecipeIndex = ref(null) -const batchAction = ref('') +const showBatchMenu = ref(false) const totalSelected = computed(() => selectedIds.size + selectedDiaryIds.size) +const isMyAllSelected = computed(() => myFilteredRecipes.value.length > 0 && selectedDiaryIds.size === myFilteredRecipes.value.length) +const isPubAllSelected = computed(() => publicFilteredRecipes.value.length > 0 && selectedIds.size === publicFilteredRecipes.value.length) +const isAllSelected = computed(() => isMyAllSelected.value && (!auth.canEdit || isPubAllSelected.value)) const showMyRecipes = ref(false) const showPublicRecipes = ref(false) const showReviewHistory = ref(false) @@ -1609,6 +1620,23 @@ watch(() => recipeStore.recipes, () => { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 8px; } .select-count { font-size: 12px; color: #4a9d7e; font-weight: 500; white-space: nowrap; } +.batch-menu { + display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; padding: 8px 0; +} +.batch-menu-btn { + padding: 5px 12px; border: 1.5px solid #e5e4e7; border-radius: 8px; background: #fff; + font-size: 12px; cursor: pointer; font-family: inherit; color: #6b6375; +} +.batch-menu-btn:hover { border-color: #7ec6a4; color: #4a9d7e; } +.batch-delete { color: #c0392b; border-color: #e8b4b0; } +.batch-delete:hover { background: #fdf0ee; border-color: #c0392b; } +.mini-select { + width: 20px; height: 20px; border: 1.5px solid #d4cfc7; border-radius: 4px; + background: #fff; color: transparent; font-size: 12px; cursor: pointer; + display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; + margin-right: 4px; +} +.mini-select.active { background: #4a9d7e; border-color: #4a9d7e; color: #fff; } .tag-list-bar { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; padding: 8px 0; }