1 Commits

Author SHA1 Message Date
ca37d9aa1d UI: 批量操作展开菜单+区域独立全选(保持原布局)
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 4s
PR Preview / deploy-preview (pull_request) Successful in 15s
Test / e2e-test (push) Failing after 56s
- 批量操作改为按钮展开下一行菜单(打标签/导出卡片/共享/删除)
- 共享仅在只选了我的配方时显示
- 我的配方和公共配方库标题加✓小全选按钮
- 两个都全选后顶部全选按钮激活
- 保持原有工具栏布局不变

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 15:41:58 +00:00

View File

@@ -36,18 +36,15 @@
<button class="tag-toggle-btn" @click="showTagFilter = !showTagFilter">
标签 {{ showTagFilter ? '' : '' }}
</button>
<button v-if="totalSelected > 0" class="tag-toggle-btn" @click="showBatchMenu = !showBatchMenu">
<!-- Batch -->
<template v-if="totalSelected > 0">
<button class="tag-toggle-btn" @click="showBatchMenu = !showBatchMenu">
批量操作 {{ showBatchMenu ? '' : '' }}
</button>
<button v-if="totalSelected > 0" class="btn-sm btn-outline" @click="clearSelection">取消</button>
<button class="btn-sm btn-outline" @click="clearSelection">取消</button>
</template>
<button v-if="auth.isAdmin" class="export-btn" @click="exportExcel" title="导出Excel">📥</button>
</div>
<div v-if="showBatchMenu && totalSelected > 0" class="batch-menu">
<button class="batch-menu-btn" @click="executeBatchAction('tag')">🏷 批量打标签</button>
<button class="batch-menu-btn" @click="executeBatchAction('export')">📷 批量导出卡片</button>
<button v-if="selectedDiaryIds.size > 0 && selectedIds.size === 0" class="batch-menu-btn" @click="executeBatchAction('share_public')">📤 批量共享到公共库</button>
<button class="batch-menu-btn batch-delete" @click="executeBatchAction('delete')">🗑 批量删除</button>
</div>
<div v-if="showTagFilter" class="tag-list-bar">
<span
v-for="tag in recipeStore.allTags"
@@ -62,10 +59,17 @@
</div>
</div>
<div v-if="showBatchMenu && totalSelected > 0" class="batch-menu">
<button class="batch-menu-btn" @click="doBatch('tag')">🏷 批量打标签</button>
<button class="batch-menu-btn" @click="doBatch('export')">📷 批量导出卡片</button>
<button v-if="selectedDiaryIds.size > 0 && selectedIds.size === 0" class="batch-menu-btn" @click="doBatch('share_public')">📤 批量共享到公共库</button>
<button class="batch-menu-btn batch-delete" @click="doBatch('delete')">🗑 批量删除</button>
</div>
<!-- My Recipes Section (from diary) -->
<div class="recipe-section">
<h3 class="section-title clickable" @click="showMyRecipes = !showMyRecipes">
<button class="mini-select" :class="{ active: isMyAllSelected }" @click.stop="toggleMySelect"></button>
<button class="mini-select" :class="{ active: isMyAllSelected }" @click.stop="toggleMySelect" title="全选我的配方"></button>
<span>📖 我的配方 ({{ myRecipes.length }})</span>
<span v-if="!auth.isAdmin" class="contrib-tag">已贡献 {{ sharedCount.adopted }}/{{ sharedCount.total }} </span>
<span class="toggle-icon">{{ showMyRecipes ? '▾' : '▸' }}</span>
@@ -108,7 +112,7 @@
<!-- Public Recipes Section (editor+) -->
<div v-if="auth.canEdit" class="recipe-section">
<h3 class="section-title clickable" @click="showPublicRecipes = !showPublicRecipes">
<button class="mini-select" :class="{ active: isPubAllSelected }" @click.stop="togglePubSelect"></button>
<button class="mini-select" :class="{ active: isPubAllSelected }" @click.stop="togglePubSelect" title="全选公共配方"></button>
<span>🌿 公共配方库 ({{ publicRecipes.length }})</span>
<span class="toggle-icon">{{ showPublicRecipes ? '▾' : '▸' }}</span>
</h3>
@@ -546,6 +550,11 @@ function clearSelection() {
showBatchMenu.value = false
}
function doBatch(action) {
showBatchMenu.value = false
executeBatchAction(action)
}
function toggleSelectAll() {
if (isAllSelected.value) {
clearSelection()
@@ -970,7 +979,11 @@ 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 isAllSelected = computed(() => {
const myOk = myFilteredRecipes.value.length > 0 && isMyAllSelected.value
const pubOk = !auth.canEdit || (publicFilteredRecipes.value.length > 0 && isPubAllSelected.value)
return myOk && pubOk
})
const showMyRecipes = ref(false)
const showPublicRecipes = ref(false)
const showReviewHistory = ref(false)
@@ -1620,9 +1633,7 @@ 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 { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 8px; }
.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;
@@ -1631,10 +1642,10 @@ watch(() => recipeStore.recipes, () => {
.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;
width: 18px; height: 18px; border: 1.5px solid #d4cfc7; border-radius: 4px;
background: #fff; color: transparent; font-size: 11px; cursor: pointer;
display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0;
margin-right: 4px;
padding: 0; margin-right: 4px; line-height: 1;
}
.mini-select.active { background: #4a9d7e; border-color: #4a9d7e; color: #fff; }
.tag-list-bar {