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 13s
Test / e2e-test (push) Failing after 53s
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 13s
Test / e2e-test (push) Failing after 53s
- 批量操作改为按钮点击展开菜单(打标签/导出卡片/共享/删除) - 共享到公共库仅在全选我的配方且未选公共配方时显示 - 我的配方和公共配方库各有独立的✓全选按钮 - 两个区域都全选后,顶部全选按钮激活 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -29,25 +29,25 @@
|
||||
<button v-if="auth.canEdit" class="btn-outline btn-sm" @click="showAddOverlay = true">新增</button>
|
||||
<button
|
||||
class="btn-sm"
|
||||
:class="totalSelected > 0 ? 'btn-select-active' : 'btn-outline'"
|
||||
@click="toggleSelectAllDiary"
|
||||
:class="isAllSelected ? 'btn-select-active' : 'btn-outline'"
|
||||
@click="toggleSelectAll"
|
||||
>全选</button>
|
||||
<span v-if="totalSelected > 0" class="select-count">共{{ totalSelected }}个</span>
|
||||
<button class="tag-toggle-btn" @click="showTagFilter = !showTagFilter">
|
||||
标签 {{ showTagFilter ? '▾' : '▸' }}
|
||||
</button>
|
||||
<!-- Batch -->
|
||||
<template v-if="totalSelected > 0">
|
||||
<select v-model="batchAction" class="batch-select" @change="onBatchSelect">
|
||||
<option value="">批量操作...</option>
|
||||
<option value="tag">打标签</option>
|
||||
<option v-if="selectedDiaryIds.size > 0" value="share_public">分享到公共库</option>
|
||||
<option value="delete">删除</option>
|
||||
</select>
|
||||
<button class="btn-sm btn-outline" @click="clearSelection">取消</button>
|
||||
</template>
|
||||
<button v-if="totalSelected > 0" class="tag-toggle-btn" @click="showBatchMenu = !showBatchMenu">
|
||||
批量操作 {{ showBatchMenu ? '▾' : '▸' }}
|
||||
</button>
|
||||
<button v-if="totalSelected > 0" class="btn-sm btn-outline" @click="clearSelection">取消</button>
|
||||
<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"
|
||||
@@ -65,6 +65,7 @@
|
||||
<!-- 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>
|
||||
<span>📖 我的配方 ({{ myRecipes.length }})</span>
|
||||
<span v-if="!auth.isAdmin" class="contrib-tag">已贡献 {{ sharedCount.adopted }}/{{ sharedCount.total }} 条</span>
|
||||
<span class="toggle-icon">{{ showMyRecipes ? '▾' : '▸' }}</span>
|
||||
@@ -107,6 +108,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>
|
||||
<span>🌿 公共配方库 ({{ publicRecipes.length }})</span>
|
||||
<span class="toggle-icon">{{ showPublicRecipes ? '▾' : '▸' }}</span>
|
||||
</h3>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user