mirror of
https://github.com/caojiezi2003/Snavigation.git
synced 2024-11-23 19:39:46 +00:00
feat: 完善搜索建议
This commit is contained in:
parent
382e0e9768
commit
83fcfa7a01
@ -22,7 +22,12 @@
|
|||||||
- [x] 网站背景自定义
|
- [x] 网站背景自定义
|
||||||
- [x] 数据备份及恢复
|
- [x] 数据备份及恢复
|
||||||
- [x] 移动端适配
|
- [x] 移动端适配
|
||||||
* [ ] 还没想好呢
|
* [ ] 切换搜索引擎
|
||||||
|
* [ ] 设置
|
||||||
|
* [ ] 备份
|
||||||
|
* [ ] 一言
|
||||||
|
* [ ] 书签
|
||||||
|
* [ ] 备忘
|
||||||
|
|
||||||
### 部署
|
### 部署
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "snavigation",
|
"name": "snavigation",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.0",
|
"version": "2.0.0 beta 1",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
@ -10,7 +10,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.4.0",
|
"axios": "^1.4.0",
|
||||||
"axios-jsonp": "^1.0.4",
|
"fetch-jsonp": "^1.3.0",
|
||||||
"pinia": "^2.1.4",
|
"pinia": "^2.1.4",
|
||||||
"pinia-plugin-persistedstate": "^3.2.0",
|
"pinia-plugin-persistedstate": "^3.2.0",
|
||||||
"sass": "^1.64.1",
|
"sass": "^1.64.1",
|
||||||
|
@ -8,9 +8,9 @@ dependencies:
|
|||||||
axios:
|
axios:
|
||||||
specifier: ^1.4.0
|
specifier: ^1.4.0
|
||||||
version: 1.4.0
|
version: 1.4.0
|
||||||
axios-jsonp:
|
fetch-jsonp:
|
||||||
specifier: ^1.0.4
|
specifier: ^1.3.0
|
||||||
version: 1.0.4
|
version: 1.3.0
|
||||||
pinia:
|
pinia:
|
||||||
specifier: ^2.1.4
|
specifier: ^2.1.4
|
||||||
version: 2.1.4(vue@3.3.4)
|
version: 2.1.4(vue@3.3.4)
|
||||||
@ -403,10 +403,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/axios-jsonp@1.0.4:
|
|
||||||
resolution: {integrity: sha512-KI5Fc4ery6DR+oneXG09hPZfGuNUW8Lblhe750h53Z0Eh5MRsrHn49YitDU4RsMk0HV+12zcvL2Q51QkOLGdIQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/axios@1.4.0:
|
/axios@1.4.0:
|
||||||
resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==}
|
resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -497,6 +493,10 @@ packages:
|
|||||||
/estree-walker@2.0.2:
|
/estree-walker@2.0.2:
|
||||||
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==}
|
||||||
|
|
||||||
|
/fetch-jsonp@1.3.0:
|
||||||
|
resolution: {integrity: sha512-hxCYGvmANEmpkHpeWY8Kawfa5Z1t2csTpIClIDG/0S92eALWHRU1RnGaj86Tf5Cc0QF+afSa4SQ4pFB2rFM5QA==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/fill-range@7.0.1:
|
/fill-range@7.0.1:
|
||||||
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
resolution: {integrity: sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
<template>
|
<template>
|
||||||
|
<!-- 壁纸 -->
|
||||||
<Cover />
|
<Cover />
|
||||||
|
<!-- 主界面 -->
|
||||||
<Transition name="fade" mode="out-in">
|
<Transition name="fade" mode="out-in">
|
||||||
<main v-if="status.imgLoadStatus" id="main" @click="mainClick">
|
<main v-if="status.imgLoadStatus" id="main" @click="mainClick">
|
||||||
<WeatherTime />
|
<WeatherTime />
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import axios from "@/utils/request";
|
import axios from "@/utils/request";
|
||||||
import jsonpAdapter from "axios-jsonp";
|
import fetchJsonp from "fetch-jsonp";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 获取天气
|
* 获取天气
|
||||||
@ -19,12 +19,16 @@ export const getWeather = () => {
|
|||||||
*/
|
*/
|
||||||
export const getSearchSuggestions = async (keyWord) => {
|
export const getSearchSuggestions = async (keyWord) => {
|
||||||
try {
|
try {
|
||||||
const response = await axios({
|
const encodedKeyword = encodeURIComponent(keyWord);
|
||||||
url: `https://suggestion.baidu.com/su?wd=${keyWord}&cb=json`,
|
const response = await fetchJsonp(
|
||||||
adapter: jsonpAdapter,
|
`https://suggestion.baidu.com/su?wd=${encodedKeyword}&cb=json`,
|
||||||
callbackParamName: "cb",
|
{
|
||||||
});
|
// 回调参数
|
||||||
return response.s;
|
jsonpCallback: "cb",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
const data = await response.json();
|
||||||
|
return data.s;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("处理搜索建议发生错误:", error);
|
console.error("处理搜索建议发生错误:", error);
|
||||||
return null;
|
return null;
|
||||||
|
@ -15,21 +15,27 @@
|
|||||||
</div>
|
</div>
|
||||||
<input
|
<input
|
||||||
class="input"
|
class="input"
|
||||||
|
id="main-input"
|
||||||
ref="searchInputRef"
|
ref="searchInputRef"
|
||||||
type="text"
|
type="text"
|
||||||
label="search"
|
label="search"
|
||||||
title="请输入搜索内容"
|
title="请输入搜索内容"
|
||||||
|
autocomplete="false"
|
||||||
:placeholder="inputTip"
|
:placeholder="inputTip"
|
||||||
v-model="inputValue"
|
v-model="inputValue"
|
||||||
@focus="status.setSiteStatus('focus')"
|
@focus="status.setSiteStatus('focus')"
|
||||||
@keydown="pressKeyboard"
|
@keydown.stop="pressKeyboard"
|
||||||
/>
|
/>
|
||||||
<div class="go" title="搜索" @click="toSearch">
|
<div class="go" title="搜索" @click="toSearch(inputValue)">
|
||||||
<SvgIcon iconName="icon-search" className="search" />
|
<SvgIcon iconName="icon-search" className="search" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<!-- 搜索建议 -->
|
<!-- 搜索建议 -->
|
||||||
<SearchSuggestions ref="searchSuggestionsRef" :keyWord="inputValue" />
|
<SearchSuggestions
|
||||||
|
ref="searchSuggestionsRef"
|
||||||
|
:keyWord="inputValue"
|
||||||
|
@toSearch="toSearch"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -56,14 +62,48 @@ const searchSuggestionsRef = ref(null);
|
|||||||
// 关闭搜索框
|
// 关闭搜索框
|
||||||
const closeSearchInput = () => {
|
const closeSearchInput = () => {
|
||||||
status.setSiteStatus("normal");
|
status.setSiteStatus("normal");
|
||||||
|
searchInputRef.value?.blur();
|
||||||
inputValue.value = "";
|
inputValue.value = "";
|
||||||
};
|
};
|
||||||
|
|
||||||
// 前往搜索
|
// 前往搜索
|
||||||
const toSearch = () => {
|
const toSearch = (val, type = 1) => {
|
||||||
const keywords = inputValue.value?.trim();
|
const searchValue = val?.trim();
|
||||||
if (keywords) {
|
// 定义跳转方法
|
||||||
console.log("前往搜索:" + keywords);
|
const jumpLink = (url) => {
|
||||||
|
if (set.urlJumpType === "open") {
|
||||||
|
window.location.href = url;
|
||||||
|
} else if (set.urlJumpType === "href") {
|
||||||
|
window.open(url, "_blank");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// 是否为空
|
||||||
|
if (searchValue) {
|
||||||
|
console.log("前往搜索:" + searchValue, type);
|
||||||
|
// type
|
||||||
|
// 1 默认 / 2 快捷翻译 / 3 电子邮件 / 4 直接访问
|
||||||
|
// 切换搜索引擎还没写,先这样
|
||||||
|
switch (type) {
|
||||||
|
case 1:
|
||||||
|
jumpLink(`https://www.bing.com/search?q=${searchValue}`);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
jumpLink(`https://fanyi.baidu.com/#en/zh/${searchValue}`);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
jumpLink(`mailto:${searchValue}`);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
const urlRegex = /^(https?:\/\/)/i;
|
||||||
|
const url = urlRegex.test(searchValue)
|
||||||
|
? searchValue
|
||||||
|
: `//${searchValue}`;
|
||||||
|
jumpLink(url);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
closeSearchInput();
|
||||||
} else {
|
} else {
|
||||||
status.setSiteStatus("focus");
|
status.setSiteStatus("focus");
|
||||||
searchInputRef.value?.focus();
|
searchInputRef.value?.focus();
|
||||||
@ -95,7 +135,7 @@ const pressKeyboard = (event) => {
|
|||||||
// 13 回车
|
// 13 回车
|
||||||
if (keyCode === 13) toSearch();
|
if (keyCode === 13) toSearch();
|
||||||
// 子组件事件
|
// 子组件事件
|
||||||
searchSuggestionsRef.value?.keyboardEvents(keyCode);
|
searchSuggestionsRef.value?.keyboardEvents(keyCode, event);
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@ -1,54 +1,113 @@
|
|||||||
<template>
|
<template>
|
||||||
<Transition name="fadeUp">
|
<Transition name="fadeUp" mode="out-in">
|
||||||
<div
|
<div
|
||||||
v-show="
|
v-if="
|
||||||
set.showSuggestions &&
|
set.showSuggestions &&
|
||||||
status.siteStatus === 'focus' &&
|
status.siteStatus === 'focus' &&
|
||||||
searchKeyword !== null
|
searchKeyword !== null
|
||||||
"
|
"
|
||||||
class="search-suggestions"
|
class="search-suggestions"
|
||||||
|
:style="{
|
||||||
|
height: suggestionsHeights !== 0 ? `${suggestionsHeights}px` : 'auto',
|
||||||
|
}"
|
||||||
>
|
>
|
||||||
|
<!-- 搜索建议 -->
|
||||||
<Transition name="fade" mode="out-in">
|
<Transition name="fade" mode="out-in">
|
||||||
<div
|
<div
|
||||||
v-if="searchKeyword !== null && searchSuggestionsData[0]"
|
v-show="
|
||||||
|
searchKeyword !== null &&
|
||||||
|
searchKeywordType === 'text' &&
|
||||||
|
searchSuggestionsData[0]
|
||||||
|
"
|
||||||
class="all-result"
|
class="all-result"
|
||||||
|
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"
|
||||||
:key="item"
|
|
||||||
class="s-result"
|
class="s-result"
|
||||||
|
:key="item"
|
||||||
|
@click="toSearch(item, 1)"
|
||||||
>
|
>
|
||||||
<SvgIcon iconName="icon-search" className="search" />
|
<SvgIcon iconName="icon-search" className="search" />
|
||||||
<span class="text">{{ item }}</span>
|
<span class="text">{{ item }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="no-result" v-else-if="!hasSuggestions">
|
</Transition>
|
||||||
|
<!-- 无搜索建议 -->
|
||||||
|
<Transition name="fade" mode="out-in">
|
||||||
|
<div
|
||||||
|
v-show="searchKeywordType === 'text' && !hasSuggestions"
|
||||||
|
class="no-result"
|
||||||
|
>
|
||||||
<SvgIcon iconName="icon-found" className="not-found" />
|
<SvgIcon iconName="icon-found" className="not-found" />
|
||||||
<div class="all-text">
|
<div class="all-text">
|
||||||
<span class="text">暂无搜索结果</span>
|
<span class="text">暂无搜索建议</span>
|
||||||
<span class="tip">请尝试其他关键词</span>
|
<span class="tip">请尝试其他关键词</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Transition>
|
</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>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { ref, watch } from "vue";
|
import { nextTick, ref, watch } from "vue";
|
||||||
import { statusStore, setStore } from "@/stores";
|
import { statusStore, setStore } from "@/stores";
|
||||||
import { getSearchSuggestions } from "@/api";
|
import { getSearchSuggestions } from "@/api";
|
||||||
import debounce from "@/utils/debounce";
|
import debounce from "@/utils/debounce";
|
||||||
|
import identifyInput from "@/utils/identifyInput";
|
||||||
|
|
||||||
const set = setStore();
|
const set = setStore();
|
||||||
const status = statusStore();
|
const status = statusStore();
|
||||||
|
const emit = defineEmits(["toSearch"]);
|
||||||
|
|
||||||
// 搜索关键字
|
// 搜索关键字
|
||||||
const searchKeyword = ref(null);
|
const searchKeyword = ref(null);
|
||||||
|
// 搜索关键字类别
|
||||||
|
const searchKeywordType = ref("text");
|
||||||
// 搜索建议数据
|
// 搜索建议数据
|
||||||
const searchSuggestionsData = ref([]);
|
const searchSuggestionsData = ref([]);
|
||||||
// 是否有搜索结果
|
// 是否有搜索结果
|
||||||
const hasSuggestions = ref(true);
|
const hasSuggestions = ref(true);
|
||||||
|
// 搜索建议元素
|
||||||
|
const allResultsRef = ref(null);
|
||||||
|
const specialResultsRef = ref(null);
|
||||||
|
// 搜索建议高度
|
||||||
|
const suggestionsHeights = ref(0);
|
||||||
// 接收搜索框内容
|
// 接收搜索框内容
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
// 搜索关键字
|
// 搜索关键字
|
||||||
@ -60,27 +119,98 @@ const props = defineProps({
|
|||||||
|
|
||||||
// 搜索框联想
|
// 搜索框联想
|
||||||
const keywordsSearch = debounce((val) => {
|
const keywordsSearch = debounce((val) => {
|
||||||
if (val?.trim()) {
|
const searchValue = val?.trim();
|
||||||
|
searchKeyword.value = searchValue;
|
||||||
|
// 判断类型
|
||||||
|
searchKeywordType.value = identifyInput(searchValue);
|
||||||
|
// 若为文字
|
||||||
|
if (searchKeywordType.value === "text") {
|
||||||
|
if (searchValue) {
|
||||||
console.log(val + "的搜索建议");
|
console.log(val + "的搜索建议");
|
||||||
searchKeyword.value = val;
|
|
||||||
// 调用搜索建议
|
// 调用搜索建议
|
||||||
getSearchSuggestions(val).then((res) => {
|
searchSuggestionsData.value = [];
|
||||||
|
getSearchSuggestions(searchValue)
|
||||||
|
.then((res) => {
|
||||||
console.log(res);
|
console.log(res);
|
||||||
// 是否有结果
|
// 是否有结果
|
||||||
hasSuggestions.value = res[0] ? true : false;
|
hasSuggestions.value = res[0] ? true : false;
|
||||||
// 写入结果
|
// 写入结果
|
||||||
searchSuggestionsData.value = Array.from(res);
|
searchSuggestionsData.value = Array.from(res);
|
||||||
|
// 获取元素高度
|
||||||
|
nextTick(() => {
|
||||||
|
const height = allResultsRef.value?.offsetHeight;
|
||||||
|
suggestionsHeights.value = res[0] ? height : 130;
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("处理搜索建议发生错误:", error);
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
searchKeyword.value = null;
|
searchKeyword.value = null;
|
||||||
hasSuggestions.value = true;
|
hasSuggestions.value = true;
|
||||||
|
suggestionsHeights.value = 0;
|
||||||
}
|
}
|
||||||
}, 500);
|
}
|
||||||
|
// 其他类型
|
||||||
|
else {
|
||||||
|
hasSuggestions.value = true;
|
||||||
|
// 获取元素高度
|
||||||
|
nextTick(() => {
|
||||||
|
const height = specialResultsRef.value?.offsetHeight;
|
||||||
|
suggestionsHeights.value = height ?? 62;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, 300);
|
||||||
|
|
||||||
// 响应键盘事件
|
// 响应键盘事件
|
||||||
const keyboardEvents = (keyCode) => {
|
const keyboardEvents = (keyCode, event) => {
|
||||||
console.log("键盘按下:" + keyCode);
|
try {
|
||||||
|
// 获取元素
|
||||||
|
const mainInput = document.getElementById("main-input");
|
||||||
// 38 上 / 40 下
|
// 38 上 / 40 下
|
||||||
|
if (keyCode === 38 || keyCode === 40) {
|
||||||
|
// 阻止默认事件
|
||||||
|
event.preventDefault();
|
||||||
|
if (mainInput && allResultsRef.value && hasSuggestions.value) {
|
||||||
|
const suggestionItems =
|
||||||
|
allResultsRef.value.querySelectorAll(".s-result");
|
||||||
|
if (suggestionItems.length > 0) {
|
||||||
|
// 获取当前已聚焦的元素
|
||||||
|
const focusedItem = document.querySelector(".s-result.focus");
|
||||||
|
// 确定当前聚焦的元素在列表中的索引
|
||||||
|
const currentIndex = Array.from(suggestionItems).indexOf(focusedItem);
|
||||||
|
// 移除所有元素的选中状态
|
||||||
|
suggestionItems.forEach((item) =>
|
||||||
|
item.classList.toggle("focus", false)
|
||||||
|
);
|
||||||
|
// 计算下一个要聚焦的元素的索引
|
||||||
|
let nextIndex = keyCode === 38 ? currentIndex - 1 : currentIndex + 1;
|
||||||
|
// 确保索引不越界
|
||||||
|
nextIndex = Math.max(
|
||||||
|
0,
|
||||||
|
Math.min(nextIndex, suggestionItems.length - 1)
|
||||||
|
);
|
||||||
|
// 操作元素
|
||||||
|
if (nextIndex !== -1) {
|
||||||
|
suggestionItems[nextIndex].classList.toggle("focus", true);
|
||||||
|
mainInput.value =
|
||||||
|
suggestionItems[nextIndex].querySelector(".text").textContent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 13 回车
|
||||||
|
if (keyCode === 13) {
|
||||||
|
toSearch(mainInput.value, 1);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("键盘事件出现错误:" + error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 触发父组件搜索事件
|
||||||
|
const toSearch = (val, type = 1) => {
|
||||||
|
emit("toSearch", val, type);
|
||||||
};
|
};
|
||||||
|
|
||||||
// 监听搜索框变化
|
// 监听搜索框变化
|
||||||
@ -99,12 +229,17 @@ defineExpose({ keyboardEvents });
|
|||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
max-height: 338px;
|
||||||
|
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(10px);
|
||||||
border-radius: 8px;
|
border-radius: 16px;
|
||||||
.all-result {
|
transition: height 0.25s ease, opacity 0.3s ease, transform 0.3s ease;
|
||||||
.s-result {
|
.all-result,
|
||||||
|
.special-result {
|
||||||
|
.s-result,
|
||||||
|
.translation {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -113,7 +248,7 @@ defineExpose({ keyboardEvents });
|
|||||||
padding: 6px 12px;
|
padding: 6px 12px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
transition: background-color 0.3s, padding-left 0.3s;
|
transition: background-color 0.3s, padding-left 0.3s;
|
||||||
.search {
|
.i-icon {
|
||||||
opacity: 0.8;
|
opacity: 0.8;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
}
|
}
|
||||||
@ -123,15 +258,16 @@ defineExpose({ keyboardEvents });
|
|||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
&:hover {
|
&:hover,
|
||||||
|
&.focus {
|
||||||
background-color: var(--main-background-light-color);
|
background-color: var(--main-background-light-color);
|
||||||
padding-left: 18px;
|
padding-left: 18px;
|
||||||
}
|
}
|
||||||
&:first-child {
|
&:first-child {
|
||||||
border-radius: 8px 8px 0 0;
|
border-radius: 16px 16px 0 0;
|
||||||
}
|
}
|
||||||
&:last-child {
|
&:last-child {
|
||||||
border-radius: 0 0 8px 8px;
|
border-radius: 0 0 16px 16px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,9 @@ const useSetDataStore = defineStore("setData", {
|
|||||||
showSeconds: false,
|
showSeconds: false,
|
||||||
// 是否显示搜索建议
|
// 是否显示搜索建议
|
||||||
showSuggestions: true,
|
showSuggestions: true,
|
||||||
|
// 跳转方式
|
||||||
|
// open 当前页面 / href 新标签页
|
||||||
|
urlJumpType: "href",
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
// 开启数据持久化
|
// 开启数据持久化
|
||||||
|
@ -46,7 +46,7 @@ body {
|
|||||||
|
|
||||||
.fadeUp-enter-active,
|
.fadeUp-enter-active,
|
||||||
.fadeUp-leave-active {
|
.fadeUp-leave-active {
|
||||||
transition: opacity 0.3s ease, transform 0.3s ease-in-out;
|
transition: opacity 0.3s ease, transform 0.3s ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.fadeUp-enter-from,
|
.fadeUp-enter-from,
|
||||||
|
File diff suppressed because one or more lines are too long
31
src/utils/identifyInput.js
Normal file
31
src/utils/identifyInput.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
/**
|
||||||
|
* 判断输入的字符串是网址、邮件地址还是普通文本。
|
||||||
|
*
|
||||||
|
* @param {string} input - 输入的字符串
|
||||||
|
* @returns {(string | boolean)} - 返回 "url" 表示网址,"email" 表示邮件地址,true 表示普通文本
|
||||||
|
*/
|
||||||
|
const identifyInput = (input) => {
|
||||||
|
/**
|
||||||
|
* 网址正则
|
||||||
|
* @type {RegExp}
|
||||||
|
*/
|
||||||
|
const urlRegex =
|
||||||
|
/^(?:(?:(?:https?|ftp):\/\/)?(?:www\.)?)?([a-zA-Z0-9.-]+(?:\.[a-zA-Z]{2,})+)(?:\/[^\s]*)?(?:\?[^#\s]*)?(?:#[^\s]*)?$/;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邮箱正则
|
||||||
|
* @type {RegExp}
|
||||||
|
*/
|
||||||
|
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||||
|
|
||||||
|
// 判断是否为网址
|
||||||
|
if (urlRegex.test(input)) return "url";
|
||||||
|
|
||||||
|
// 判断是否为邮件地址
|
||||||
|
if (emailRegex.test(input)) return "email";
|
||||||
|
|
||||||
|
// 默认返回普通文本
|
||||||
|
return "text";
|
||||||
|
};
|
||||||
|
|
||||||
|
export default identifyInput;
|
Loading…
Reference in New Issue
Block a user