mirror of
https://github.com/caojiezi2003/Snavigation.git
synced 2024-11-10 06:39:45 +00:00
feat: 设置及盒子页面构建
This commit is contained in:
parent
1b016cb037
commit
c184efb8ce
@ -1,5 +1,6 @@
|
||||
<!doctype html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="favicon.png" />
|
||||
@ -19,8 +20,11 @@
|
||||
encodeURIComponent(window.location.href);
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="/lib/iconfont.js" async></script>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "snavigation",
|
||||
"private": true,
|
||||
"version": "2.0.0 beta 2",
|
||||
"version": "2.0.0 beta 3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
@ -21,6 +21,7 @@
|
||||
"@vitejs/plugin-vue": "^4.2.3",
|
||||
"naive-ui": "^2.34.4",
|
||||
"terser": "^5.19.2",
|
||||
"vite": "^4.4.5"
|
||||
"vite": "^4.4.5",
|
||||
"vite-plugin-compression": "^0.5.1"
|
||||
}
|
||||
}
|
||||
|
@ -40,6 +40,9 @@ devDependencies:
|
||||
vite:
|
||||
specifier: ^4.4.5
|
||||
version: 4.4.6(sass@1.64.1)(terser@5.19.2)
|
||||
vite-plugin-compression:
|
||||
specifier: ^0.5.1
|
||||
version: 0.5.1(vite@4.4.6)
|
||||
|
||||
packages:
|
||||
|
||||
@ -1691,7 +1694,6 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
color-convert: 2.0.1
|
||||
dev: false
|
||||
|
||||
/anymatch@3.1.3:
|
||||
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
|
||||
@ -1859,7 +1861,6 @@ packages:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
supports-color: 7.2.0
|
||||
dev: false
|
||||
|
||||
/chokidar@3.5.3:
|
||||
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
|
||||
@ -1886,7 +1887,6 @@ packages:
|
||||
engines: {node: '>=7.0.0'}
|
||||
dependencies:
|
||||
color-name: 1.1.4
|
||||
dev: false
|
||||
|
||||
/color-name@1.1.3:
|
||||
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
|
||||
@ -1894,7 +1894,6 @@ packages:
|
||||
|
||||
/color-name@1.1.4:
|
||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||
dev: false
|
||||
|
||||
/combined-stream@1.0.8:
|
||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||
@ -1969,7 +1968,6 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
ms: 2.1.2
|
||||
dev: false
|
||||
|
||||
/deepmerge@4.3.1:
|
||||
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
|
||||
@ -2185,6 +2183,15 @@ packages:
|
||||
mime-types: 2.1.35
|
||||
dev: false
|
||||
|
||||
/fs-extra@10.1.0:
|
||||
resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
|
||||
engines: {node: '>=12'}
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
jsonfile: 6.1.0
|
||||
universalify: 2.0.0
|
||||
dev: true
|
||||
|
||||
/fs-extra@9.1.0:
|
||||
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
|
||||
engines: {node: '>=10'}
|
||||
@ -2287,7 +2294,6 @@ packages:
|
||||
|
||||
/graceful-fs@4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
dev: false
|
||||
|
||||
/has-bigints@1.0.2:
|
||||
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
|
||||
@ -2301,7 +2307,6 @@ packages:
|
||||
/has-flag@4.0.0:
|
||||
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
|
||||
engines: {node: '>=8'}
|
||||
dev: false
|
||||
|
||||
/has-property-descriptors@1.0.0:
|
||||
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
|
||||
@ -2556,7 +2561,6 @@ packages:
|
||||
universalify: 2.0.0
|
||||
optionalDependencies:
|
||||
graceful-fs: 4.2.11
|
||||
dev: false
|
||||
|
||||
/jsonpointer@5.0.1:
|
||||
resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
|
||||
@ -2645,7 +2649,6 @@ packages:
|
||||
|
||||
/ms@2.1.2:
|
||||
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
|
||||
dev: false
|
||||
|
||||
/naive-ui@2.34.4(vue@3.3.4):
|
||||
resolution: {integrity: sha512-aPG8PDfhSzIzn/jSC9y3Jb3Pe2wHJ7F0cFV1EWlbImSrZECeUmoc+fIcOSWbizoztkKfaUAeKwYdMl09MKkj1g==}
|
||||
@ -3040,7 +3043,6 @@ packages:
|
||||
engines: {node: '>=8'}
|
||||
dependencies:
|
||||
has-flag: 4.0.0
|
||||
dev: false
|
||||
|
||||
/supports-preserve-symlinks-flag@1.0.0:
|
||||
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
|
||||
@ -3177,7 +3179,6 @@ packages:
|
||||
/universalify@2.0.0:
|
||||
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
dev: false
|
||||
|
||||
/upath@1.2.0:
|
||||
resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==}
|
||||
@ -3210,6 +3211,19 @@ packages:
|
||||
vue: 3.3.4
|
||||
dev: true
|
||||
|
||||
/vite-plugin-compression@0.5.1(vite@4.4.6):
|
||||
resolution: {integrity: sha512-5QJKBDc+gNYVqL/skgFAP81Yuzo9R+EAf19d+EtsMF/i8kFUpNi3J/H01QD3Oo8zBQn+NzoCIFkpPLynoOzaJg==}
|
||||
peerDependencies:
|
||||
vite: '>=2.0.0'
|
||||
dependencies:
|
||||
chalk: 4.1.2
|
||||
debug: 4.3.4
|
||||
fs-extra: 10.1.0
|
||||
vite: 4.4.6(sass@1.64.1)(terser@5.19.2)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/vite-plugin-pwa@0.16.4(vite@4.4.6)(workbox-build@7.0.0)(workbox-window@7.0.0):
|
||||
resolution: {integrity: sha512-lmwHFIs9zI2H9bXJld/zVTbCqCQHZ9WrpyDMqosICDV0FVnCJwniX1NMDB79HGTIZzOQkY4gSZaVTJTw6maz/Q==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
BIN
public/favicon.ico
Normal file
BIN
public/favicon.ico
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.2 KiB |
1
public/lib/iconfont.js
Normal file
1
public/lib/iconfont.js
Normal file
File diff suppressed because one or more lines are too long
106
src/App.vue
106
src/App.vue
@ -4,10 +4,43 @@
|
||||
<Cover @loadComplete="loadComplete" />
|
||||
<!-- 主界面 -->
|
||||
<Transition name="fade" mode="out-in">
|
||||
<main v-if="status.imgLoadStatus" id="main" @click="mainClick">
|
||||
<main
|
||||
v-if="status.imgLoadStatus"
|
||||
tabindex="0"
|
||||
id="main"
|
||||
:class="`main-${status.siteStatus}`"
|
||||
@click="status.setSiteStatus('normal')"
|
||||
@contextmenu="mainContextmenu"
|
||||
@keydown="mainPressKeyboard"
|
||||
>
|
||||
<WeatherTime />
|
||||
<SearchInp />
|
||||
<SearchInp @contextmenu.stop />
|
||||
<AllFunc @contextmenu.stop />
|
||||
<Footer />
|
||||
<!-- 状态切换 -->
|
||||
<Transition name="fade">
|
||||
<div
|
||||
v-show="
|
||||
status.siteStatus !== 'focus' && status.siteStatus !== 'normal'
|
||||
"
|
||||
class="change-status"
|
||||
:title="status.siteStatus !== 'set' ? '设置' : '首页'"
|
||||
@click.stop="
|
||||
status.setSiteStatus(
|
||||
status.siteStatus !== 'set' ? 'set' : 'normal'
|
||||
)
|
||||
"
|
||||
>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<SvgIcon
|
||||
:iconName="`icon-${
|
||||
status.siteStatus !== 'set' ? 'setting' : 'home'
|
||||
}`"
|
||||
:key="status.siteStatus !== 'set' ? 'setting' : 'home'"
|
||||
/>
|
||||
</Transition>
|
||||
</div>
|
||||
</Transition>
|
||||
<!-- Notification -->
|
||||
<Notification />
|
||||
</main>
|
||||
@ -26,7 +59,8 @@ import { getGreeting } from "@/utils/timeTools";
|
||||
import Provider from "@/components/Provider.vue";
|
||||
import Cover from "@/components/Cover.vue";
|
||||
import WeatherTime from "@/components/WeatherTime.vue";
|
||||
import SearchInp from "@/components/SearchInp.vue";
|
||||
import SearchInp from "@/components/SearchInput/SearchInp.vue";
|
||||
import AllFunc from "@/components/AllFunc/AllFunc.vue";
|
||||
import Footer from "@/components/Footer.vue";
|
||||
|
||||
const status = statusStore();
|
||||
@ -34,9 +68,10 @@ const status = statusStore();
|
||||
// 获取配置
|
||||
const welcomeText = import.meta.env.VITE_WELCOME_TEXT ?? "欢迎访问本站";
|
||||
|
||||
// 全局点击
|
||||
const mainClick = () => {
|
||||
status.setSiteStatus("normal");
|
||||
// 鼠标右键
|
||||
const mainContextmenu = (event) => {
|
||||
event.preventDefault();
|
||||
status.setSiteStatus("box");
|
||||
};
|
||||
|
||||
// 加载完成事件
|
||||
@ -48,6 +83,18 @@ const loadComplete = () => {
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// 全局键盘事件
|
||||
const mainPressKeyboard = (event) => {
|
||||
const keyCode = event.keyCode;
|
||||
// 回车
|
||||
if (keyCode === 13) {
|
||||
// focus 元素
|
||||
const mainInput = document.getElementById("main-input");
|
||||
status.setSiteStatus("focus");
|
||||
mainInput?.focus();
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@ -62,6 +109,53 @@ const loadComplete = () => {
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
&.main-normal,
|
||||
&.main-focus {
|
||||
.main-box {
|
||||
opacity: 0;
|
||||
margin-top: 0;
|
||||
transform: scale(0.35);
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
&.main-box,
|
||||
&.main-set {
|
||||
.main-box {
|
||||
opacity: 1;
|
||||
margin-top: 200px;
|
||||
transform: scale(1);
|
||||
visibility: visible;
|
||||
}
|
||||
.search-input {
|
||||
:deep(.all) {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
.change-status {
|
||||
cursor: pointer;
|
||||
position: fixed;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 28px;
|
||||
padding: 8px;
|
||||
border-radius: 8px;
|
||||
color: var(--main-text-color);
|
||||
z-index: 1;
|
||||
transition: opacity 0.3s, background-color 0.3s, transform 0.3s;
|
||||
&:hover {
|
||||
backdrop-filter: blur(20px);
|
||||
background-color: var(--main-background-light-color);
|
||||
}
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
}
|
||||
#loading {
|
||||
color: var(--main-text-color);
|
||||
|
12
src/components/AllFunc/AllBox.vue
Normal file
12
src/components/AllFunc/AllBox.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<!-- 全局设置 -->
|
||||
<template>
|
||||
<n-tabs class="func" size="large" justify-content="space-evenly" animated>
|
||||
<n-tab-pane name="link" tab="捷径"> 即将完善 </n-tab-pane>
|
||||
<n-tab-pane name="note" tab="便签"> 即将完善 </n-tab-pane>
|
||||
<n-tab-pane name="more" tab="还能有啥"> 还能有啥呢 😢 </n-tab-pane>
|
||||
</n-tabs>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { NTabs, NTabPane } from "naive-ui";
|
||||
</script>
|
67
src/components/AllFunc/AllFunc.vue
Normal file
67
src/components/AllFunc/AllFunc.vue
Normal file
@ -0,0 +1,67 @@
|
||||
<template>
|
||||
<div class="main-box" @click.stop>
|
||||
<Transition name="fade" mode="out-in">
|
||||
<AllBox v-if="status.siteStatus === 'box'" />
|
||||
<AllSet v-else-if="status.siteStatus === 'set'" />
|
||||
</Transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { statusStore } from "@/stores";
|
||||
import AllBox from "@/components/AllFunc/AllBox.vue";
|
||||
import AllSet from "@/components/AllFunc/AllSet.vue";
|
||||
|
||||
const status = statusStore();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.main-box {
|
||||
position: absolute;
|
||||
width: 80%;
|
||||
height: 400px;
|
||||
max-height: 400px;
|
||||
max-width: 1200px;
|
||||
background-color: var(--main-background-light-color);
|
||||
backdrop-filter: blur(20px);
|
||||
color: var(--main-text-color);
|
||||
border-radius: 8px;
|
||||
transition: opacity 0.3s, transform 0.3s, margin-top 0.3s;
|
||||
z-index: 2;
|
||||
:deep(.set-item) {
|
||||
width: 100%;
|
||||
border-radius: 8px;
|
||||
margin-bottom: 12px;
|
||||
border: none;
|
||||
box-shadow: var(--main-box-shadow);
|
||||
--n-color: var(--main-background-light-color);
|
||||
.n-card__content {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
.name {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.title {
|
||||
font-size: 16px;
|
||||
}
|
||||
.tip {
|
||||
font-size: 13px;
|
||||
opacity: 0.8;
|
||||
}
|
||||
}
|
||||
.set {
|
||||
width: 200px;
|
||||
@media (max-width: 768px) {
|
||||
width: 140px;
|
||||
min-width: 140px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
104
src/components/AllFunc/AllSet.vue
Normal file
104
src/components/AllFunc/AllSet.vue
Normal file
@ -0,0 +1,104 @@
|
||||
<!-- 全局设置 -->
|
||||
<template>
|
||||
<n-tabs class="set" size="large" justify-content="space-evenly" animated>
|
||||
<n-tab-pane name="main" tab="基础设置">
|
||||
<n-scrollbar style="max-height: 316px">
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
<span class="title">壁纸偏好</span>
|
||||
<span class="tip">设置站点背景图片</span>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
<span class="title">壁纸遮罩</span>
|
||||
<span class="tip">壁纸周围是否显示暗色遮罩</span>
|
||||
</div>
|
||||
<n-switch v-model:value="showBackgroundGray" :round="false" />
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
<span class="title">搜索引擎</span>
|
||||
<span class="tip">切换搜索引擎</span>
|
||||
</div>
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
<span class="title">搜索建议</span>
|
||||
<span class="tip">是否显示搜索建议</span>
|
||||
</div>
|
||||
<n-switch v-model:value="showSuggestions" :round="false" />
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
<span class="title">跳转方式</span>
|
||||
<span class="tip">全站链接跳转方式</span>
|
||||
</div>
|
||||
<n-select
|
||||
class="set"
|
||||
v-model:value="urlJumpType"
|
||||
:options="urlJumpTypeOptions"
|
||||
/>
|
||||
</n-card>
|
||||
</n-scrollbar>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="personalization" tab="个性调整">
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
<span class="title">时间显秒</span>
|
||||
<span class="tip">是否在分钟后面显示秒数</span>
|
||||
</div>
|
||||
<n-switch v-model:value="showSeconds" :round="false" />
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
<span class="title">自动聚焦</span>
|
||||
<span class="tip">打开网站时自动聚焦搜索框</span>
|
||||
</div>
|
||||
<n-switch v-model:value="autoFocus" :round="false" />
|
||||
</n-card>
|
||||
<n-card class="set-item">
|
||||
<div class="name">
|
||||
<span class="title">自动失焦</span>
|
||||
<span class="tip">跳转搜索后搜索框自动失焦</span>
|
||||
</div>
|
||||
<n-switch v-model:value="autoInputBlur" :round="false" />
|
||||
</n-card>
|
||||
</n-tab-pane>
|
||||
<n-tab-pane name="other" tab="其他设置"> 其他设置 </n-tab-pane>
|
||||
</n-tabs>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { NTabs, NTabPane, NCard, NSwitch, NSelect, NScrollbar } from "naive-ui";
|
||||
import { storeToRefs } from "pinia";
|
||||
import { setStore } from "@/stores";
|
||||
|
||||
const set = setStore();
|
||||
const {
|
||||
backgroundType,
|
||||
showBackgroundGray,
|
||||
searchEngine,
|
||||
lastSearchEngine,
|
||||
customEngine,
|
||||
showCleanInput,
|
||||
autoFocus,
|
||||
autoInputBlur,
|
||||
timeStyle,
|
||||
showSeconds,
|
||||
showSuggestions,
|
||||
urlJumpType,
|
||||
} = storeToRefs(set);
|
||||
|
||||
// 链接跳转方式
|
||||
const urlJumpTypeOptions = [
|
||||
{
|
||||
label: "新页面打开",
|
||||
value: "open",
|
||||
},
|
||||
{
|
||||
label: "当前页打开",
|
||||
value: "href",
|
||||
},
|
||||
];
|
||||
</script>
|
@ -67,8 +67,9 @@ const imgAnimationEnd = () => {
|
||||
|
||||
// 图片显示失败
|
||||
const imgLoadError = () => {
|
||||
console.error("图片加载失败:", bgUrl.value);
|
||||
bgUrl.value = `/background/bg${bgRandom}.jpg`;
|
||||
console.error("图片加载失败:", bgUrl.value);
|
||||
$message.error("壁纸加载失败,已临时切换回默认");
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
|
@ -13,5 +13,6 @@
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
color: var(--main-text-color);
|
||||
z-index: 1;
|
||||
}
|
||||
</style>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<n-config-provider
|
||||
:locale="zhCN"
|
||||
:date-locale="dateZhCN"
|
||||
:theme="darkTheme"
|
||||
:theme-overrides="themeOverrides"
|
||||
abstract
|
||||
inline-theme-disabled
|
||||
@ -23,6 +24,7 @@ import { defineComponent } from "vue";
|
||||
import {
|
||||
zhCN,
|
||||
dateZhCN,
|
||||
darkTheme,
|
||||
NConfigProvider,
|
||||
NDialogProvider,
|
||||
NNotificationProvider,
|
||||
@ -36,6 +38,10 @@ import {
|
||||
const themeOverrides = {
|
||||
common: {
|
||||
fontFamily: "'HarmonyOS_Regular', sans-serif",
|
||||
primaryColor: "#ffffff",
|
||||
primaryColorHover: "#ffffff70",
|
||||
primaryColorSuppl: "#ffffff30",
|
||||
primaryColorPressed: "#ffffff30",
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
<div
|
||||
v-if="status.siteStatus === 'focus'"
|
||||
class="mask"
|
||||
@click="closeSearchInput"
|
||||
@click="closeSearchInput(false)"
|
||||
/>
|
||||
<div
|
||||
ref="searchAllRef"
|
||||
@ -29,12 +29,12 @@
|
||||
title="请输入搜索内容"
|
||||
autocomplete="false"
|
||||
:placeholder="inputTip"
|
||||
v-model="inputValue"
|
||||
v-model="status.searchInputValue"
|
||||
@focus="status.setSiteStatus('focus')"
|
||||
@click.stop="status.setEngineChangeStatus(false)"
|
||||
@keydown.stop="pressKeyboard"
|
||||
/>
|
||||
<div class="go" title="搜索" @click="toSearch(inputValue)">
|
||||
<div class="go" title="搜索" @click="toSearch(status.searchInputValue)">
|
||||
<SvgIcon iconName="icon-search" className="search" />
|
||||
</div>
|
||||
</div>
|
||||
@ -43,7 +43,7 @@
|
||||
<!-- 搜索建议 -->
|
||||
<Suggestions
|
||||
ref="suggestionsRef"
|
||||
:keyWord="inputValue"
|
||||
:keyWord="status.searchInputValue"
|
||||
@toSearch="toSearch"
|
||||
/>
|
||||
</div>
|
||||
@ -52,8 +52,8 @@
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { statusStore, setStore } from "@/stores";
|
||||
import SearchEngine from "@/components/SearchEngine.vue";
|
||||
import Suggestions from "@/components/Suggestions.vue";
|
||||
import SearchEngine from "@/components/SearchInput/SearchEngine.vue";
|
||||
import Suggestions from "@/components/SearchInput/Suggestions.vue";
|
||||
import defaultEngine from "@/assets/defaultEngine.json";
|
||||
|
||||
const set = setStore();
|
||||
@ -66,17 +66,20 @@ const inputTip = import.meta.env.VITE_INPUT_TIP ?? "想要搜点什么";
|
||||
const searchAllRef = ref(null);
|
||||
const searchInputRef = ref(null);
|
||||
const inputClickable = ref(true);
|
||||
const inputValue = ref("");
|
||||
|
||||
// 搜索建议子组件
|
||||
const suggestionsRef = ref(null);
|
||||
|
||||
// 关闭搜索框
|
||||
const closeSearchInput = () => {
|
||||
const closeSearchInput = (check = false) => {
|
||||
if (check && !set.autoInputBlur) {
|
||||
status.setSiteStatus("focus");
|
||||
} else {
|
||||
status.setSearchInputValue("");
|
||||
status.setSiteStatus("normal");
|
||||
status.setEngineChangeStatus(false);
|
||||
searchInputRef.value?.blur();
|
||||
inputValue.value = "";
|
||||
}
|
||||
status.setEngineChangeStatus(false);
|
||||
};
|
||||
|
||||
// 前往搜索
|
||||
@ -84,9 +87,9 @@ const toSearch = (val, type = 1) => {
|
||||
const searchValue = val?.trim();
|
||||
// 定义跳转方法
|
||||
const jumpLink = (url) => {
|
||||
if (set.urlJumpType === "open") {
|
||||
if (set.urlJumpType === "href") {
|
||||
window.location.href = url;
|
||||
} else if (set.urlJumpType === "href") {
|
||||
} else if (set.urlJumpType === "open") {
|
||||
window.open(url, "_blank");
|
||||
}
|
||||
};
|
||||
@ -124,8 +127,11 @@ const toSearch = (val, type = 1) => {
|
||||
default:
|
||||
break;
|
||||
}
|
||||
closeSearchInput();
|
||||
closeSearchInput(true);
|
||||
} else {
|
||||
if (status.siteStatus === "focus") {
|
||||
$message.info("请输入搜索内容", { duration: 1500 });
|
||||
}
|
||||
status.setSiteStatus("focus");
|
||||
searchInputRef.value?.focus();
|
||||
}
|
||||
@ -159,12 +165,13 @@ const changeEngine = () => {
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.search-input {
|
||||
position: relative;
|
||||
position: absolute;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
max-width: 680px;
|
||||
width: calc(100% - 60px);
|
||||
transition: width 0.3s;
|
||||
.mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
@ -186,7 +193,7 @@ const changeEngine = () => {
|
||||
backdrop-filter: blur(10px);
|
||||
opacity: 1;
|
||||
animation: fade-up-in 0.7s cubic-bezier(0.37, 0.99, 0.36, 1);
|
||||
transition: transform 0.3s, background-color 0.3s;
|
||||
transition: transform 0.3s, background-color 0.3s, opacity 0.5s;
|
||||
z-index: 1;
|
||||
&.focus {
|
||||
transform: translateY(-60px);
|
@ -27,7 +27,7 @@
|
||||
<div
|
||||
v-if="searchKeywordType === 'text'"
|
||||
class="s-result"
|
||||
@click="toSearch(keyWord, 2)"
|
||||
@click.stop="toSearch(keyWord, 2)"
|
||||
>
|
||||
<SvgIcon iconName="icon-translation-two" />
|
||||
<span class="text">快捷翻译:{{ keyWord }}</span>
|
||||
@ -36,7 +36,7 @@
|
||||
<div
|
||||
v-if="searchKeywordType !== 'text'"
|
||||
class="s-result"
|
||||
@click="
|
||||
@click.stop="
|
||||
toSearch(searchKeyword, searchKeywordType === 'email' ? 3 : 4)
|
||||
"
|
||||
>
|
||||
@ -69,7 +69,7 @@
|
||||
v-for="item in searchSuggestionsData"
|
||||
class="s-result"
|
||||
:key="item"
|
||||
@click="toSearch(item, 1)"
|
||||
@click.stop="toSearch(item, 1)"
|
||||
>
|
||||
<SvgIcon iconName="icon-search" className="search" />
|
||||
<span class="text">{{ item }}</span>
|
||||
@ -214,6 +214,7 @@ const toSearch = (val, type = 1) => {
|
||||
watch(
|
||||
() => props.keyWord,
|
||||
(val) => {
|
||||
if (set.showSuggestions) {
|
||||
// 清空结果
|
||||
searchSuggestionsData.value = [];
|
||||
// 判断类型
|
||||
@ -221,6 +222,7 @@ watch(
|
||||
// 调用搜索结果
|
||||
keywordsSearch(val);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
// 暴露方法
|
@ -1,13 +1,17 @@
|
||||
<template>
|
||||
<div :class="`weather-time ${status.siteStatus}`" @click.stop>
|
||||
<div
|
||||
:class="
|
||||
status.siteStatus !== 'normal' ? 'weather-time focus' : 'weather-time'
|
||||
:class="['time', set.timeStyle]"
|
||||
@click.stop="
|
||||
status.setSiteStatus(
|
||||
status.siteStatus !== 'normal' && status.siteStatus !== 'focus'
|
||||
? 'normal'
|
||||
: 'box'
|
||||
)
|
||||
"
|
||||
@click.stop
|
||||
>
|
||||
<div :class="['time', set.timeStyle]">
|
||||
<span class="hour">{{ timeData.hour ?? "00" }}</span>
|
||||
<span class="separator">:</span>
|
||||
<span class="separator" :key="set.showSeconds">:</span>
|
||||
<span class="minute">{{ timeData.minute ?? "00" }}</span>
|
||||
<template v-if="set.showSeconds">
|
||||
<span class="separator">:</span>
|
||||
@ -120,6 +124,10 @@ onBeforeUnmount(() => {
|
||||
&.focus {
|
||||
transform: translateY(-180px);
|
||||
}
|
||||
&.box,
|
||||
&.set {
|
||||
transform: translateY(-220px);
|
||||
}
|
||||
.time {
|
||||
cursor: pointer;
|
||||
font-size: 3rem;
|
||||
@ -160,8 +168,7 @@ onBeforeUnmount(() => {
|
||||
}
|
||||
}
|
||||
.weather {
|
||||
margin-top: 6px;
|
||||
opacity: 0.8;
|
||||
opacity: 0.7;
|
||||
font-size: 1rem;
|
||||
text-shadow: var(--main-text-shadow);
|
||||
.temperature {
|
||||
|
@ -4,16 +4,14 @@ import { createPinia } from "pinia";
|
||||
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
|
||||
// IconFont
|
||||
import SvgIcon from "@/components/SvgIcon.vue";
|
||||
import "@/utils/iconfont.js";
|
||||
// Notification
|
||||
import Notification from "@/components/Notification.vue";
|
||||
|
||||
// 主组件
|
||||
import App from "@/App.vue";
|
||||
|
||||
// 全局样式
|
||||
import "@/style/global.scss";
|
||||
|
||||
// 根组件
|
||||
const app = createApp(App);
|
||||
|
||||
// Pinia
|
||||
|
@ -16,6 +16,8 @@ const useSetDataStore = defineStore("setData", {
|
||||
showCleanInput: true,
|
||||
// 搜索框自动 focus
|
||||
autoFocus: false,
|
||||
// 搜索后搜索框自动失焦
|
||||
autoInputBlur: true,
|
||||
// 时间样式
|
||||
timeStyle: "one",
|
||||
// 是否显秒
|
||||
@ -23,8 +25,8 @@ const useSetDataStore = defineStore("setData", {
|
||||
// 是否显示搜索建议
|
||||
showSuggestions: true,
|
||||
// 跳转方式
|
||||
// open 当前页面 / href 新标签页
|
||||
urlJumpType: "href",
|
||||
// open 新标签页 / href 当前页面
|
||||
urlJumpType: "open",
|
||||
};
|
||||
},
|
||||
actions: {
|
||||
|
@ -10,6 +10,8 @@ const useStatusDataStore = defineStore("statusData", {
|
||||
siteStatus: "normal",
|
||||
// 切换搜索引擎
|
||||
engineChangeStatus: false,
|
||||
// 搜索框文本
|
||||
searchInputValue: "",
|
||||
};
|
||||
},
|
||||
getters: {},
|
||||
@ -19,10 +21,14 @@ const useStatusDataStore = defineStore("statusData", {
|
||||
},
|
||||
setSiteStatus(value) {
|
||||
this.siteStatus = value;
|
||||
this.searchInputValue = "";
|
||||
},
|
||||
setEngineChangeStatus(value) {
|
||||
this.engineChangeStatus = value;
|
||||
},
|
||||
setSearchInputValue(value) {
|
||||
this.searchInputValue = value;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
|
@ -8,6 +8,7 @@
|
||||
--main-background-hover-color: #ffffff70;
|
||||
--main-input-hover-color: #ffffff;
|
||||
--main-notification-background-color: #00000030;
|
||||
--main-box-shadow: 0px 0px 10px 0px #00000020;
|
||||
--main-text-shadow: 0px 0px 8px #00000066;
|
||||
}
|
||||
|
||||
@ -49,10 +50,42 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
// NTabs
|
||||
.n-tabs {
|
||||
height: 100%;
|
||||
--n-bar-color: var(--main-text-color) !important;
|
||||
--n-tab-text-color-active: var(--main-text-color) !important;
|
||||
--n-tab-text-color-hover: var(--main-text-color) !important;
|
||||
.n-tabs-nav {
|
||||
height: 44px;
|
||||
}
|
||||
.n-tabs-pane-wrapper {
|
||||
height: 100%;
|
||||
.n-tab-pane {
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NSwitch
|
||||
.n-switch {
|
||||
--n-rail-color: var(--main-background-light-color) !important;
|
||||
--n-rail-color-active: var(--main-background-hover-color) !important;
|
||||
--n-box-shadow-focus: var(--main-box-shadow) !important;
|
||||
}
|
||||
|
||||
// NScrollbar
|
||||
.n-scrollbar {
|
||||
.n-scrollbar-rail {
|
||||
right: 0 !important;
|
||||
}
|
||||
}
|
||||
|
||||
// Transition 动画
|
||||
.fade-enter-active,
|
||||
.fade-leave-active {
|
||||
transition: opacity 0.3s ease;
|
||||
transition: opacity 0.25s ease-in-out;
|
||||
}
|
||||
|
||||
.fade-enter-from,
|
||||
|
File diff suppressed because one or more lines are too long
@ -11,6 +11,8 @@ const identifyInput = (input) => {
|
||||
*/
|
||||
const urlRegex =
|
||||
/^(?:(?:(?:https?|ftp):\/\/)?(?:www\.)?)?([a-zA-Z0-9.-]+(?:\.[a-zA-Z]{2,})+)(?:\/[^\s]*)?(?:\?[^#\s]*)?(?:#[^\s]*)?$/;
|
||||
const ipv4Regex =
|
||||
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/;
|
||||
|
||||
/**
|
||||
* 邮箱正则
|
||||
@ -19,7 +21,7 @@ const identifyInput = (input) => {
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
|
||||
// 判断是否为网址
|
||||
if (urlRegex.test(input)) return "url";
|
||||
if (urlRegex.test(input) || ipv4Regex.test(input)) return "url";
|
||||
|
||||
// 判断是否为邮件地址
|
||||
if (emailRegex.test(input)) return "email";
|
||||
|
@ -2,6 +2,7 @@ import { defineConfig } from "vite";
|
||||
import { VitePWA } from "vite-plugin-pwa";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import path from "path";
|
||||
import viteCompression from "vite-plugin-compression";
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
@ -48,6 +49,8 @@ export default defineConfig({
|
||||
],
|
||||
},
|
||||
}),
|
||||
// viteCompression
|
||||
viteCompression(),
|
||||
],
|
||||
server: {
|
||||
host: "0.0.0.0",
|
||||
|
Loading…
Reference in New Issue
Block a user