fix: 搜索过滤收藏、拼音首字母匹配、清除图片、滑动切换、通知已读

1. 搜索时收藏配方也按关键词过滤,不匹配的隐藏
2. 编辑配方添加精油时支持拼音首字母匹配(如xyc→薰衣草)
3. 品牌设置页清除图片立即保存到后端,不需点保存按钮
4. 左右滑动切换tab,轮播区域内滑动切换图片不触发tab切换
5. 通知列表每条未读通知加"已读"按钮,调用POST /api/notifications/{id}/read

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 17:54:18 +00:00
committed by fam
parent b764ff7ea3
commit 54003bc466
6 changed files with 182 additions and 9 deletions

View File

@@ -39,7 +39,7 @@
</div>
<!-- Main content -->
<div class="main">
<div class="main" @touchstart="onSwipeStart" @touchend="onSwipeEnd">
<router-view />
</div>
@@ -54,7 +54,7 @@
</template>
<script setup>
import { ref, onMounted, watch } from 'vue'
import { ref, computed, onMounted, watch } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useAuthStore } from './stores/auth'
import { useOilsStore } from './stores/oils'
@@ -106,6 +106,48 @@ function toggleUserMenu() {
showUserMenu.value = !showUserMenu.value
}
// Swipe to switch tabs
const swipeStartX = ref(0)
const swipeStartY = ref(0)
// Tab order for swipe navigation (only user-accessible tabs)
const tabOrder = computed(() => {
const tabs = ['search', 'oils']
if (auth.isLoggedIn) {
tabs.splice(1, 0, 'manage', 'inventory')
}
if (auth.isBusiness) tabs.push('projects')
return tabs
})
function onSwipeStart(e) {
const touch = e.touches[0]
swipeStartX.value = touch.clientX
swipeStartY.value = touch.clientY
}
function onSwipeEnd(e) {
const touch = e.changedTouches[0]
const dx = touch.clientX - swipeStartX.value
const dy = touch.clientY - swipeStartY.value
// Only trigger if horizontal swipe is dominant and > 50px
if (Math.abs(dx) < 50 || Math.abs(dy) > Math.abs(dx)) return
// Check if the swipe originated inside a carousel (data-no-tab-swipe)
if (e.target.closest && e.target.closest('[data-no-tab-swipe]')) return
const tabs = tabOrder.value
const currentIdx = tabs.indexOf(ui.currentSection)
if (currentIdx < 0) return
if (dx < -50 && currentIdx < tabs.length - 1) {
// Swipe left -> next tab
goSection(tabs[currentIdx + 1])
} else if (dx > 50 && currentIdx > 0) {
// Swipe right -> previous tab
goSection(tabs[currentIdx - 1])
}
}
onMounted(async () => {
await auth.initToken()
await Promise.all([