From d4bc468ed7193ed884aee9518b6df33bee4b0973 Mon Sep 17 00:00:00 2001 From: sendevia Date: Sun, 30 Nov 2025 20:44:42 +0800 Subject: [PATCH] feat: refactor search state from composable to Pinia store --- .vitepress/theme/components/AppBar.vue | 62 ++++++++------- .vitepress/theme/components/NavBar.vue | 13 +--- .../theme/composables/useSearchState.ts | 35 --------- .vitepress/theme/stores/searchState.ts | 78 +++++++++++++++++++ 4 files changed, 117 insertions(+), 71 deletions(-) delete mode 100644 .vitepress/theme/composables/useSearchState.ts create mode 100644 .vitepress/theme/stores/searchState.ts diff --git a/.vitepress/theme/components/AppBar.vue b/.vitepress/theme/components/AppBar.vue index ed82baa..b4d2a24 100644 --- a/.vitepress/theme/components/AppBar.vue +++ b/.vitepress/theme/components/AppBar.vue @@ -3,13 +3,13 @@ import { ref, computed, onMounted, onUnmounted, watch, nextTick } from "vue"; import { useGlobalData } from "../composables/useGlobalData"; import { useGlobalScroll } from "../composables/useGlobalScroll"; import { useAllPosts, type Post } from "../composables/useAllPosts"; -import { useSearchState } from "../composables/useSearchState"; +import { useSearchStateStore } from "../stores/searchState"; import { useScreenWidthStore } from "../stores/screenWidth"; import { handleTabNavigation } from "../utils/tabNavigation"; const { frontmatter } = useGlobalData(); const { isScrolled } = useGlobalScroll({ threshold: 100 }); -const { isSearchActive, isSearchTyping, deactivateSearch, setSearchFocus, setSearchTyping } = useSearchState(); +const searchStateStore = useSearchStateStore(); const screenWidthStore = useScreenWidthStore(); const isHome = computed(() => frontmatter.value.home === true); const articlesRef = useAllPosts(true); @@ -36,31 +36,31 @@ const filteredPosts = computed(() => { // 处理输入框焦点 (仅激活,不处理关闭) const handleFocus = () => { - if (!isSearchActive.value) { - isSearchActive.value = true; + if (!searchStateStore.isSearchActive) { + searchStateStore.activate(); } - setSearchFocus(true); + searchStateStore.setFocus(true); }; // 处理输入框失焦 (只改变焦点状态,不关闭搜索,解决双击问题) const handleBlur = () => { - setSearchFocus(false); + searchStateStore.setFocus(false); }; // 处理输入 const handleInput = () => { const hasContent = query.value.trim().length > 0; - setSearchTyping(hasContent); - if (hasContent && !isSearchActive.value) { - isSearchActive.value = true; + searchStateStore.setTyping(hasContent); + if (hasContent && !searchStateStore.isSearchActive) { + searchStateStore.activate(); } }; // 清除搜索状态 const clearSearchState = () => { query.value = ""; - setSearchTyping(false); - deactivateSearch(); + searchStateStore.setTyping(false); + searchStateStore.deactivate(); if (searchInput.value) { searchInput.value.blur(); } @@ -75,7 +75,7 @@ const handleResultClick = () => { // 处理外部点击 const handleDocumentClick = (event: Event) => { - if (!isSearchActive.value) return; + if (!searchStateStore.isSearchActive) return; const target = event.target as HTMLElement; const isClickInsideInput = searchInput.value && searchInput.value.contains(target); @@ -87,23 +87,26 @@ const handleDocumentClick = (event: Event) => { }; // 监听状态自动聚焦 -watch(isSearchActive, async (isActive) => { - if (isActive && searchInput.value) { - await nextTick(); - setTimeout(() => { - searchInput.value?.focus(); - }, 100); - } else if (!isActive) { - if (query.value !== "") { - query.value = ""; - setSearchTyping(false); +watch( + () => searchStateStore.isSearchActive, + async (isActive) => { + if (isActive && searchInput.value) { + await nextTick(); + setTimeout(() => { + searchInput.value?.focus(); + }, 100); + } else if (!isActive) { + if (query.value !== "") { + query.value = ""; + searchStateStore.setTyping(false); + } } } -}); +); // 键盘事件 const handleKeydown = (event: KeyboardEvent) => { - if (!isSearchActive.value) return; + if (!searchStateStore.isSearchActive) return; const container = appbar.value; const items = container?.querySelectorAll(".searchInput, .item") || null; @@ -113,9 +116,14 @@ const handleKeydown = (event: KeyboardEvent) => { clearSearchState(); } - if (event.key === "Tab" && query.value.trim() !== "") { + if (event.key === "Tab") { event.preventDefault(); handleTabNavigation(container, items, event.shiftKey); + + // 当搜索框没有内容时,Tab 键取消搜索激活状态 + if (query.value.trim() === "") { + searchStateStore.deactivate(); + } } }; @@ -137,8 +145,8 @@ onUnmounted(() => { :class="{ scroll: isScrolled, homeLayout: isHome, - searching: isSearchActive, - typing: isSearchTyping, + searching: searchStateStore.isSearchActive, + typing: searchStateStore.isSearchTyping, }" :tabindex="isTabFocusable ? 0 : -1" > diff --git a/.vitepress/theme/components/NavBar.vue b/.vitepress/theme/components/NavBar.vue index 08e866e..5b1fae3 100644 --- a/.vitepress/theme/components/NavBar.vue +++ b/.vitepress/theme/components/NavBar.vue @@ -2,11 +2,11 @@ import { computed } from "vue"; import { useGlobalData } from "../composables/useGlobalData"; import { useScreenWidthStore } from "../stores/screenWidth"; -import { useSearchState } from "../composables/useSearchState"; +import { useSearchStateStore } from "../stores/searchState"; const { page, theme } = useGlobalData(); const screenWidthStore = useScreenWidthStore(); -const { isSearchActive, activateSearch, deactivateSearch } = useSearchState(); +const searchStateStore = useSearchStateStore(); // 计算导航段落 const navSegment = computed(() => { @@ -30,19 +30,14 @@ function isActive(link: string): boolean { // 处理fab点击事件 - 切换搜索状态 function toggleSearch(event: MouseEvent) { event.stopPropagation(); - - if (isSearchActive.value) { - deactivateSearch(); - } else { - activateSearch(); - } + searchStateStore.toggle(); }