mirror of
https://github.com/caojiezi2003/Snavigation.git
synced 2024-11-10 06:39:45 +00:00
fix: 搜索建议动画不连贯
This commit is contained in:
parent
83fcfa7a01
commit
6084f75913
@ -11,7 +11,7 @@
|
|||||||
> 由于 `CDN` 缓存原因,查看最新效果可能需要 `Ctrl` + `F5` 强制刷新浏览器缓存
|
> 由于 `CDN` 缓存原因,查看最新效果可能需要 `Ctrl` + `F5` 强制刷新浏览器缓存
|
||||||
|
|
||||||
- [Snavigation](https://nav.imsyy.top)
|
- [Snavigation](https://nav.imsyy.top)
|
||||||
- [Snavigation Dev](https://snavigation.vercel.app)
|
- [Snavigation Dev](https://snavigation-git-dev-imsyy.vercel.app/)
|
||||||
|
|
||||||
### 功能
|
### 功能
|
||||||
|
|
||||||
|
@ -31,8 +31,8 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 搜索建议 -->
|
<!-- 搜索建议 -->
|
||||||
<SearchSuggestions
|
<Suggestions
|
||||||
ref="searchSuggestionsRef"
|
ref="suggestionsRef"
|
||||||
:keyWord="inputValue"
|
:keyWord="inputValue"
|
||||||
@toSearch="toSearch"
|
@toSearch="toSearch"
|
||||||
/>
|
/>
|
||||||
@ -42,7 +42,7 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, onMounted } from "vue";
|
import { ref, onMounted } from "vue";
|
||||||
import { statusStore, setStore } from "@/stores";
|
import { statusStore, setStore } from "@/stores";
|
||||||
import SearchSuggestions from "@/components/SearchSuggestions.vue";
|
import Suggestions from "@/components/Suggestions.vue";
|
||||||
|
|
||||||
const set = setStore();
|
const set = setStore();
|
||||||
const status = statusStore();
|
const status = statusStore();
|
||||||
@ -57,7 +57,7 @@ const inputClickable = ref(true);
|
|||||||
const inputValue = ref("");
|
const inputValue = ref("");
|
||||||
|
|
||||||
// 搜索建议子组件
|
// 搜索建议子组件
|
||||||
const searchSuggestionsRef = ref(null);
|
const suggestionsRef = ref(null);
|
||||||
|
|
||||||
// 关闭搜索框
|
// 关闭搜索框
|
||||||
const closeSearchInput = () => {
|
const closeSearchInput = () => {
|
||||||
@ -135,7 +135,7 @@ const pressKeyboard = (event) => {
|
|||||||
// 13 回车
|
// 13 回车
|
||||||
if (keyCode === 13) toSearch();
|
if (keyCode === 13) toSearch();
|
||||||
// 子组件事件
|
// 子组件事件
|
||||||
searchSuggestionsRef.value?.keyboardEvents(keyCode, event);
|
suggestionsRef.value?.keyboardEvents(keyCode, event);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@ -1,20 +1,65 @@
|
|||||||
<template>
|
<template>
|
||||||
<Transition name="fadeUp" mode="out-in">
|
<Transition name="fadeDown" mode="out-in">
|
||||||
<div
|
<div
|
||||||
v-if="
|
v-if="
|
||||||
set.showSuggestions &&
|
set.showSuggestions &&
|
||||||
status.siteStatus === 'focus' &&
|
status.siteStatus === 'focus' &&
|
||||||
searchKeyword !== null
|
searchKeyword !== null
|
||||||
"
|
"
|
||||||
class="search-suggestions"
|
class="suggestions"
|
||||||
:style="{
|
:style="{ height: `${suggestionsHeights}px` }"
|
||||||
height: suggestionsHeights !== 0 ? `${suggestionsHeights}px` : 'auto',
|
>
|
||||||
}"
|
<!-- 快捷操作 -->
|
||||||
|
<Transition
|
||||||
|
name="fade"
|
||||||
|
mode="out-in"
|
||||||
|
@after-enter="changeSuggestionsHeights"
|
||||||
|
@after-leave="changeSuggestionsHeights"
|
||||||
>
|
>
|
||||||
<!-- 搜索建议 -->
|
|
||||||
<Transition name="fade" mode="out-in">
|
|
||||||
<div
|
<div
|
||||||
v-show="
|
v-if="searchKeyword !== null"
|
||||||
|
class="special-result"
|
||||||
|
ref="specialallResultsRef"
|
||||||
|
>
|
||||||
|
<!-- 快捷翻译 -->
|
||||||
|
<div
|
||||||
|
v-if="searchKeywordType === 'text'"
|
||||||
|
class="s-result"
|
||||||
|
@click="toSearch(keyWord, 2)"
|
||||||
|
>
|
||||||
|
<SvgIcon iconName="icon-translation" />
|
||||||
|
<span class="text">快捷翻译:{{ keyWord }}</span>
|
||||||
|
</div>
|
||||||
|
<!-- 直接访问 -->
|
||||||
|
<div
|
||||||
|
v-if="searchKeywordType !== 'text'"
|
||||||
|
class="s-result"
|
||||||
|
@click="
|
||||||
|
toSearch(searchKeyword, searchKeywordType === 'email' ? 3 : 4)
|
||||||
|
"
|
||||||
|
>
|
||||||
|
<SvgIcon
|
||||||
|
:iconName="`icon-${
|
||||||
|
searchKeywordType === 'email' ? 'email' : 'link'
|
||||||
|
}`"
|
||||||
|
/>
|
||||||
|
<span class="text">
|
||||||
|
{{
|
||||||
|
searchKeywordType === "email" ? "发送邮件至" : "直接访问"
|
||||||
|
}}:{{ searchKeyword }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Transition>
|
||||||
|
<!-- 搜索建议 -->
|
||||||
|
<Transition
|
||||||
|
name="fade"
|
||||||
|
mode="out-in"
|
||||||
|
@after-enter="changeSuggestionsHeights"
|
||||||
|
@after-leave="changeSuggestionsHeights"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
v-if="
|
||||||
searchKeyword !== null &&
|
searchKeyword !== null &&
|
||||||
searchKeywordType === 'text' &&
|
searchKeywordType === 'text' &&
|
||||||
searchSuggestionsData[0]
|
searchSuggestionsData[0]
|
||||||
@ -22,12 +67,6 @@
|
|||||||
class="all-result"
|
class="all-result"
|
||||||
ref="allResultsRef"
|
ref="allResultsRef"
|
||||||
>
|
>
|
||||||
<!-- 快捷翻译 -->
|
|
||||||
<div class="translation" @click="toSearch(searchKeyword, 2)">
|
|
||||||
<SvgIcon iconName="icon-translation" />
|
|
||||||
<span class="text">快捷翻译:{{ searchKeyword }}</span>
|
|
||||||
</div>
|
|
||||||
<!-- 建议 -->
|
|
||||||
<div
|
<div
|
||||||
v-for="item in searchSuggestionsData"
|
v-for="item in searchSuggestionsData"
|
||||||
class="s-result"
|
class="s-result"
|
||||||
@ -39,47 +78,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
<!-- 无搜索建议 -->
|
|
||||||
<Transition name="fade" mode="out-in">
|
|
||||||
<div
|
|
||||||
v-show="searchKeywordType === 'text' && !hasSuggestions"
|
|
||||||
class="no-result"
|
|
||||||
>
|
|
||||||
<SvgIcon iconName="icon-found" className="not-found" />
|
|
||||||
<div class="all-text">
|
|
||||||
<span class="text">暂无搜索建议</span>
|
|
||||||
<span class="tip">请尝试其他关键词</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
<!-- 特殊类型 -->
|
|
||||||
<Transition name="fade" mode="out-in">
|
|
||||||
<div
|
|
||||||
v-show="searchKeywordType !== 'text'"
|
|
||||||
class="special-result"
|
|
||||||
ref="specialResultsRef"
|
|
||||||
>
|
|
||||||
<!-- 直接访问 -->
|
|
||||||
<div
|
|
||||||
class="s-result"
|
|
||||||
@click="
|
|
||||||
toSearch(searchKeyword, searchKeywordType === 'email' ? 3 : 4)
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<SvgIcon iconName="icon-link" />
|
|
||||||
<span class="text">
|
|
||||||
{{
|
|
||||||
searchKeywordType === "email" ? "发送邮件至" : "直接访问"
|
|
||||||
}}:{{ searchKeyword }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<!-- 直接搜索 -->
|
|
||||||
<div class="s-result" @click="toSearch(searchKeyword, 1)">
|
|
||||||
<SvgIcon iconName="icon-search" />
|
|
||||||
<span class="text">搜索:{{ searchKeyword }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</Transition>
|
||||||
</template>
|
</template>
|
||||||
@ -101,11 +99,9 @@ const searchKeyword = ref(null);
|
|||||||
const searchKeywordType = ref("text");
|
const searchKeywordType = ref("text");
|
||||||
// 搜索建议数据
|
// 搜索建议数据
|
||||||
const searchSuggestionsData = ref([]);
|
const searchSuggestionsData = ref([]);
|
||||||
// 是否有搜索结果
|
|
||||||
const hasSuggestions = ref(true);
|
|
||||||
// 搜索建议元素
|
// 搜索建议元素
|
||||||
|
const specialallResultsRef = ref(null);
|
||||||
const allResultsRef = ref(null);
|
const allResultsRef = ref(null);
|
||||||
const specialResultsRef = ref(null);
|
|
||||||
// 搜索建议高度
|
// 搜索建议高度
|
||||||
const suggestionsHeights = ref(0);
|
const suggestionsHeights = ref(0);
|
||||||
// 接收搜索框内容
|
// 接收搜索框内容
|
||||||
@ -120,45 +116,32 @@ const props = defineProps({
|
|||||||
// 搜索框联想
|
// 搜索框联想
|
||||||
const keywordsSearch = debounce((val) => {
|
const keywordsSearch = debounce((val) => {
|
||||||
const searchValue = val?.trim();
|
const searchValue = val?.trim();
|
||||||
|
// 是否为空
|
||||||
|
if (!searchValue || searchValue === "") {
|
||||||
|
searchKeyword.value = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// 赋值关键字
|
||||||
searchKeyword.value = searchValue;
|
searchKeyword.value = searchValue;
|
||||||
// 判断类型
|
|
||||||
searchKeywordType.value = identifyInput(searchValue);
|
|
||||||
// 若为文字
|
// 若为文字
|
||||||
if (searchKeywordType.value === "text") {
|
if (searchKeyword.value && searchKeywordType.value === "text") {
|
||||||
if (searchValue) {
|
|
||||||
console.log(val + "的搜索建议");
|
console.log(val + "的搜索建议");
|
||||||
// 调用搜索建议
|
// 调用搜索建议
|
||||||
searchSuggestionsData.value = [];
|
|
||||||
getSearchSuggestions(searchValue)
|
getSearchSuggestions(searchValue)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
console.log(res);
|
console.log(res);
|
||||||
// 是否有结果
|
|
||||||
hasSuggestions.value = res[0] ? true : false;
|
|
||||||
// 写入结果
|
// 写入结果
|
||||||
searchSuggestionsData.value = Array.from(res);
|
searchSuggestionsData.value = Array.from(res);
|
||||||
// 获取元素高度
|
// 计算高度
|
||||||
nextTick(() => {
|
nextTick(() => {
|
||||||
const height = allResultsRef.value?.offsetHeight;
|
changeSuggestionsHeights();
|
||||||
suggestionsHeights.value = res[0] ? height : 130;
|
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.catch((error) => {
|
.catch((error) => {
|
||||||
|
// 清空结果
|
||||||
|
searchSuggestionsData.value = [];
|
||||||
console.error("处理搜索建议发生错误:", error);
|
console.error("处理搜索建议发生错误:", error);
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
searchKeyword.value = null;
|
|
||||||
hasSuggestions.value = true;
|
|
||||||
suggestionsHeights.value = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 其他类型
|
|
||||||
else {
|
|
||||||
hasSuggestions.value = true;
|
|
||||||
// 获取元素高度
|
|
||||||
nextTick(() => {
|
|
||||||
const height = specialResultsRef.value?.offsetHeight;
|
|
||||||
suggestionsHeights.value = height ?? 62;
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}, 300);
|
}, 300);
|
||||||
|
|
||||||
@ -171,7 +154,7 @@ const keyboardEvents = (keyCode, event) => {
|
|||||||
if (keyCode === 38 || keyCode === 40) {
|
if (keyCode === 38 || keyCode === 40) {
|
||||||
// 阻止默认事件
|
// 阻止默认事件
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
if (mainInput && allResultsRef.value && hasSuggestions.value) {
|
if (mainInput && allResultsRef.value && searchSuggestionsData.value[0]) {
|
||||||
const suggestionItems =
|
const suggestionItems =
|
||||||
allResultsRef.value.querySelectorAll(".s-result");
|
allResultsRef.value.querySelectorAll(".s-result");
|
||||||
if (suggestionItems.length > 0) {
|
if (suggestionItems.length > 0) {
|
||||||
@ -208,6 +191,18 @@ const keyboardEvents = (keyCode, event) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 计算元素高度并改变
|
||||||
|
const changeSuggestionsHeights = () => {
|
||||||
|
try {
|
||||||
|
const allResultsHeight = allResultsRef.value?.offsetHeight;
|
||||||
|
const specialallResultsHeight = specialallResultsRef.value?.offsetHeight;
|
||||||
|
suggestionsHeights.value =
|
||||||
|
(specialallResultsHeight || 0) + (allResultsHeight || 0);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("计算高度时出现错误:" + error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// 触发父组件搜索事件
|
// 触发父组件搜索事件
|
||||||
const toSearch = (val, type = 1) => {
|
const toSearch = (val, type = 1) => {
|
||||||
emit("toSearch", val, type);
|
emit("toSearch", val, type);
|
||||||
@ -216,7 +211,14 @@ const toSearch = (val, type = 1) => {
|
|||||||
// 监听搜索框变化
|
// 监听搜索框变化
|
||||||
watch(
|
watch(
|
||||||
() => props.keyWord,
|
() => props.keyWord,
|
||||||
(val) => keywordsSearch(val)
|
(val) => {
|
||||||
|
// 清空结果
|
||||||
|
searchSuggestionsData.value = [];
|
||||||
|
// 判断类型
|
||||||
|
searchKeywordType.value = identifyInput(val);
|
||||||
|
// 调用搜索结果
|
||||||
|
keywordsSearch(val);
|
||||||
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
// 暴露方法
|
// 暴露方法
|
||||||
@ -224,7 +226,7 @@ defineExpose({ keyboardEvents });
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.search-suggestions {
|
.suggestions {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
@ -233,13 +235,12 @@ defineExpose({ keyboardEvents });
|
|||||||
overflow-y: hidden;
|
overflow-y: hidden;
|
||||||
color: var(--main-text-color);
|
color: var(--main-text-color);
|
||||||
background-color: var(--main-background-light-color);
|
background-color: var(--main-background-light-color);
|
||||||
backdrop-filter: blur(10px);
|
backdrop-filter: blur(30px) saturate(1.25);
|
||||||
border-radius: 16px;
|
border-radius: 16px;
|
||||||
transition: height 0.25s ease, opacity 0.3s ease, transform 0.3s ease;
|
transition: height 0.2s ease, opacity 0.3s ease, transform 0.3s ease;
|
||||||
.all-result,
|
.all-result,
|
||||||
.special-result {
|
.special-result {
|
||||||
.s-result,
|
.s-result {
|
||||||
.translation {
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -263,37 +264,6 @@ defineExpose({ keyboardEvents });
|
|||||||
background-color: var(--main-background-light-color);
|
background-color: var(--main-background-light-color);
|
||||||
padding-left: 18px;
|
padding-left: 18px;
|
||||||
}
|
}
|
||||||
&:first-child {
|
|
||||||
border-radius: 16px 16px 0 0;
|
|
||||||
}
|
|
||||||
&:last-child {
|
|
||||||
border-radius: 0 0 16px 16px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.no-result {
|
|
||||||
width: 100%;
|
|
||||||
height: 130px;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
.not-found {
|
|
||||||
font-size: 32px;
|
|
||||||
margin-bottom: 8px;
|
|
||||||
}
|
|
||||||
.all-text {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
.text {
|
|
||||||
font-size: 18px;
|
|
||||||
display: inline-block;
|
|
||||||
margin-bottom: 6px;
|
|
||||||
}
|
|
||||||
.tip {
|
|
||||||
font-size: 14px;
|
|
||||||
opacity: 0.8;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -44,15 +44,15 @@ body {
|
|||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fadeUp-enter-active,
|
.fadeDown-enter-active,
|
||||||
.fadeUp-leave-active {
|
.fadeDown-leave-active {
|
||||||
transition: opacity 0.3s ease, transform 0.3s ease;
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fadeUp-enter-from,
|
.fadeDown-enter-from,
|
||||||
.fadeUp-leave-to {
|
.fadeDown-leave-to {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
transform: translateY(10px);
|
transform: translateY(-10px);
|
||||||
}
|
}
|
||||||
|
|
||||||
.show-enter-active,
|
.show-enter-active,
|
||||||
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user