feat: 设置及盒子页面构建

This commit is contained in:
imsyy 2023-08-07 18:09:18 +08:00
parent 1b016cb037
commit c184efb8ce
24 changed files with 451 additions and 87 deletions

View File

@ -1,6 +1,7 @@
<!doctype html> <!doctype html>
<html lang="zh-CN"> <html lang="zh-CN">
<head>
<head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="favicon.png" /> <link rel="icon" type="image/svg+xml" href="favicon.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
@ -18,9 +19,12 @@
"https://support.dmeng.net/upgrade-your-browser.html?referrer=" + "https://support.dmeng.net/upgrade-your-browser.html?referrer=" +
encodeURIComponent(window.location.href); encodeURIComponent(window.location.href);
</script> </script>
</head> </head>
<body>
<body>
<div id="app"></div> <div id="app"></div>
<script src="/lib/iconfont.js" async></script>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
</body> </body>
</html> </html>

View File

@ -1,7 +1,7 @@
{ {
"name": "snavigation", "name": "snavigation",
"private": true, "private": true,
"version": "2.0.0 beta 2", "version": "2.0.0 beta 3",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --host", "dev": "vite --host",
@ -21,6 +21,7 @@
"@vitejs/plugin-vue": "^4.2.3", "@vitejs/plugin-vue": "^4.2.3",
"naive-ui": "^2.34.4", "naive-ui": "^2.34.4",
"terser": "^5.19.2", "terser": "^5.19.2",
"vite": "^4.4.5" "vite": "^4.4.5",
"vite-plugin-compression": "^0.5.1"
} }
} }

View File

@ -40,6 +40,9 @@ devDependencies:
vite: vite:
specifier: ^4.4.5 specifier: ^4.4.5
version: 4.4.6(sass@1.64.1)(terser@5.19.2) 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: packages:
@ -1691,7 +1694,6 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dependencies: dependencies:
color-convert: 2.0.1 color-convert: 2.0.1
dev: false
/anymatch@3.1.3: /anymatch@3.1.3:
resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==}
@ -1859,7 +1861,6 @@ packages:
dependencies: dependencies:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
supports-color: 7.2.0 supports-color: 7.2.0
dev: false
/chokidar@3.5.3: /chokidar@3.5.3:
resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==}
@ -1886,7 +1887,6 @@ packages:
engines: {node: '>=7.0.0'} engines: {node: '>=7.0.0'}
dependencies: dependencies:
color-name: 1.1.4 color-name: 1.1.4
dev: false
/color-name@1.1.3: /color-name@1.1.3:
resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==}
@ -1894,7 +1894,6 @@ packages:
/color-name@1.1.4: /color-name@1.1.4:
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
dev: false
/combined-stream@1.0.8: /combined-stream@1.0.8:
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
@ -1969,7 +1968,6 @@ packages:
optional: true optional: true
dependencies: dependencies:
ms: 2.1.2 ms: 2.1.2
dev: false
/deepmerge@4.3.1: /deepmerge@4.3.1:
resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
@ -2185,6 +2183,15 @@ packages:
mime-types: 2.1.35 mime-types: 2.1.35
dev: false 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: /fs-extra@9.1.0:
resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@ -2287,7 +2294,6 @@ packages:
/graceful-fs@4.2.11: /graceful-fs@4.2.11:
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
dev: false
/has-bigints@1.0.2: /has-bigints@1.0.2:
resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==} resolution: {integrity: sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==}
@ -2301,7 +2307,6 @@ packages:
/has-flag@4.0.0: /has-flag@4.0.0:
resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
engines: {node: '>=8'} engines: {node: '>=8'}
dev: false
/has-property-descriptors@1.0.0: /has-property-descriptors@1.0.0:
resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==} resolution: {integrity: sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==}
@ -2556,7 +2561,6 @@ packages:
universalify: 2.0.0 universalify: 2.0.0
optionalDependencies: optionalDependencies:
graceful-fs: 4.2.11 graceful-fs: 4.2.11
dev: false
/jsonpointer@5.0.1: /jsonpointer@5.0.1:
resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==}
@ -2645,7 +2649,6 @@ packages:
/ms@2.1.2: /ms@2.1.2:
resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==}
dev: false
/naive-ui@2.34.4(vue@3.3.4): /naive-ui@2.34.4(vue@3.3.4):
resolution: {integrity: sha512-aPG8PDfhSzIzn/jSC9y3Jb3Pe2wHJ7F0cFV1EWlbImSrZECeUmoc+fIcOSWbizoztkKfaUAeKwYdMl09MKkj1g==} resolution: {integrity: sha512-aPG8PDfhSzIzn/jSC9y3Jb3Pe2wHJ7F0cFV1EWlbImSrZECeUmoc+fIcOSWbizoztkKfaUAeKwYdMl09MKkj1g==}
@ -3040,7 +3043,6 @@ packages:
engines: {node: '>=8'} engines: {node: '>=8'}
dependencies: dependencies:
has-flag: 4.0.0 has-flag: 4.0.0
dev: false
/supports-preserve-symlinks-flag@1.0.0: /supports-preserve-symlinks-flag@1.0.0:
resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
@ -3177,7 +3179,6 @@ packages:
/universalify@2.0.0: /universalify@2.0.0:
resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==} resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
engines: {node: '>= 10.0.0'} engines: {node: '>= 10.0.0'}
dev: false
/upath@1.2.0: /upath@1.2.0:
resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==} resolution: {integrity: sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==}
@ -3210,6 +3211,19 @@ packages:
vue: 3.3.4 vue: 3.3.4
dev: true 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): /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==} resolution: {integrity: sha512-lmwHFIs9zI2H9bXJld/zVTbCqCQHZ9WrpyDMqosICDV0FVnCJwniX1NMDB79HGTIZzOQkY4gSZaVTJTw6maz/Q==}
engines: {node: '>=16.0.0'} engines: {node: '>=16.0.0'}

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

1
public/lib/iconfont.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -4,10 +4,43 @@
<Cover @loadComplete="loadComplete" /> <Cover @loadComplete="loadComplete" />
<!-- 主界面 --> <!-- 主界面 -->
<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"
tabindex="0"
id="main"
:class="`main-${status.siteStatus}`"
@click="status.setSiteStatus('normal')"
@contextmenu="mainContextmenu"
@keydown="mainPressKeyboard"
>
<WeatherTime /> <WeatherTime />
<SearchInp /> <SearchInp @contextmenu.stop />
<AllFunc @contextmenu.stop />
<Footer /> <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 -->
<Notification /> <Notification />
</main> </main>
@ -26,7 +59,8 @@ import { getGreeting } from "@/utils/timeTools";
import Provider from "@/components/Provider.vue"; import Provider from "@/components/Provider.vue";
import Cover from "@/components/Cover.vue"; import Cover from "@/components/Cover.vue";
import WeatherTime from "@/components/WeatherTime.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"; import Footer from "@/components/Footer.vue";
const status = statusStore(); const status = statusStore();
@ -34,9 +68,10 @@ const status = statusStore();
// //
const welcomeText = import.meta.env.VITE_WELCOME_TEXT ?? "欢迎访问本站"; const welcomeText = import.meta.env.VITE_WELCOME_TEXT ?? "欢迎访问本站";
// //
const mainClick = () => { const mainContextmenu = (event) => {
status.setSiteStatus("normal"); 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
@ -62,6 +109,53 @@ const loadComplete = () => {
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
align-items: 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 { #loading {
color: var(--main-text-color); color: var(--main-text-color);

View 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>

View 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>

View 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>

View File

@ -67,8 +67,9 @@ const imgAnimationEnd = () => {
// //
const imgLoadError = () => { const imgLoadError = () => {
console.error("图片加载失败:", bgUrl.value);
bgUrl.value = `/background/bg${bgRandom}.jpg`; bgUrl.value = `/background/bg${bgRandom}.jpg`;
console.error("图片加载失败:", bgUrl.value);
$message.error("壁纸加载失败,已临时切换回默认");
}; };
onMounted(() => { onMounted(() => {

View File

@ -13,5 +13,6 @@
width: 100%; width: 100%;
font-size: 14px; font-size: 14px;
color: var(--main-text-color); color: var(--main-text-color);
z-index: 1;
} }
</style> </style>

View File

@ -3,6 +3,7 @@
<n-config-provider <n-config-provider
:locale="zhCN" :locale="zhCN"
:date-locale="dateZhCN" :date-locale="dateZhCN"
:theme="darkTheme"
:theme-overrides="themeOverrides" :theme-overrides="themeOverrides"
abstract abstract
inline-theme-disabled inline-theme-disabled
@ -23,6 +24,7 @@ import { defineComponent } from "vue";
import { import {
zhCN, zhCN,
dateZhCN, dateZhCN,
darkTheme,
NConfigProvider, NConfigProvider,
NDialogProvider, NDialogProvider,
NNotificationProvider, NNotificationProvider,
@ -36,6 +38,10 @@ import {
const themeOverrides = { const themeOverrides = {
common: { common: {
fontFamily: "'HarmonyOS_Regular', sans-serif", fontFamily: "'HarmonyOS_Regular', sans-serif",
primaryColor: "#ffffff",
primaryColorHover: "#ffffff70",
primaryColorSuppl: "#ffffff30",
primaryColorPressed: "#ffffff30",
}, },
}; };

View File

@ -3,7 +3,7 @@
<div <div
v-if="status.siteStatus === 'focus'" v-if="status.siteStatus === 'focus'"
class="mask" class="mask"
@click="closeSearchInput" @click="closeSearchInput(false)"
/> />
<div <div
ref="searchAllRef" ref="searchAllRef"
@ -29,12 +29,12 @@
title="请输入搜索内容" title="请输入搜索内容"
autocomplete="false" autocomplete="false"
:placeholder="inputTip" :placeholder="inputTip"
v-model="inputValue" v-model="status.searchInputValue"
@focus="status.setSiteStatus('focus')" @focus="status.setSiteStatus('focus')"
@click.stop="status.setEngineChangeStatus(false)" @click.stop="status.setEngineChangeStatus(false)"
@keydown.stop="pressKeyboard" @keydown.stop="pressKeyboard"
/> />
<div class="go" title="搜索" @click="toSearch(inputValue)"> <div class="go" title="搜索" @click="toSearch(status.searchInputValue)">
<SvgIcon iconName="icon-search" className="search" /> <SvgIcon iconName="icon-search" className="search" />
</div> </div>
</div> </div>
@ -43,7 +43,7 @@
<!-- 搜索建议 --> <!-- 搜索建议 -->
<Suggestions <Suggestions
ref="suggestionsRef" ref="suggestionsRef"
:keyWord="inputValue" :keyWord="status.searchInputValue"
@toSearch="toSearch" @toSearch="toSearch"
/> />
</div> </div>
@ -52,8 +52,8 @@
<script setup> <script setup>
import { ref } from "vue"; import { ref } from "vue";
import { statusStore, setStore } from "@/stores"; import { statusStore, setStore } from "@/stores";
import SearchEngine from "@/components/SearchEngine.vue"; import SearchEngine from "@/components/SearchInput/SearchEngine.vue";
import Suggestions from "@/components/Suggestions.vue"; import Suggestions from "@/components/SearchInput/Suggestions.vue";
import defaultEngine from "@/assets/defaultEngine.json"; import defaultEngine from "@/assets/defaultEngine.json";
const set = setStore(); const set = setStore();
@ -66,17 +66,20 @@ const inputTip = import.meta.env.VITE_INPUT_TIP ?? "想要搜点什么";
const searchAllRef = ref(null); const searchAllRef = ref(null);
const searchInputRef = ref(null); const searchInputRef = ref(null);
const inputClickable = ref(true); const inputClickable = ref(true);
const inputValue = ref("");
// //
const suggestionsRef = ref(null); const suggestionsRef = ref(null);
// //
const closeSearchInput = () => { const closeSearchInput = (check = false) => {
if (check && !set.autoInputBlur) {
status.setSiteStatus("focus");
} else {
status.setSearchInputValue("");
status.setSiteStatus("normal"); status.setSiteStatus("normal");
status.setEngineChangeStatus(false);
searchInputRef.value?.blur(); searchInputRef.value?.blur();
inputValue.value = ""; }
status.setEngineChangeStatus(false);
}; };
// //
@ -84,9 +87,9 @@ const toSearch = (val, type = 1) => {
const searchValue = val?.trim(); const searchValue = val?.trim();
// //
const jumpLink = (url) => { const jumpLink = (url) => {
if (set.urlJumpType === "open") { if (set.urlJumpType === "href") {
window.location.href = url; window.location.href = url;
} else if (set.urlJumpType === "href") { } else if (set.urlJumpType === "open") {
window.open(url, "_blank"); window.open(url, "_blank");
} }
}; };
@ -124,8 +127,11 @@ const toSearch = (val, type = 1) => {
default: default:
break; break;
} }
closeSearchInput(); closeSearchInput(true);
} else { } else {
if (status.siteStatus === "focus") {
$message.info("请输入搜索内容", { duration: 1500 });
}
status.setSiteStatus("focus"); status.setSiteStatus("focus");
searchInputRef.value?.focus(); searchInputRef.value?.focus();
} }
@ -159,12 +165,13 @@ const changeEngine = () => {
<style lang="scss" scoped> <style lang="scss" scoped>
.search-input { .search-input {
position: relative; position: absolute;
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
max-width: 680px; max-width: 680px;
width: calc(100% - 60px); width: calc(100% - 60px);
transition: width 0.3s;
.mask { .mask {
position: fixed; position: fixed;
top: 0; top: 0;
@ -186,7 +193,7 @@ const changeEngine = () => {
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
opacity: 1; opacity: 1;
animation: fade-up-in 0.7s cubic-bezier(0.37, 0.99, 0.36, 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; z-index: 1;
&.focus { &.focus {
transform: translateY(-60px); transform: translateY(-60px);

View File

@ -27,7 +27,7 @@
<div <div
v-if="searchKeywordType === 'text'" v-if="searchKeywordType === 'text'"
class="s-result" class="s-result"
@click="toSearch(keyWord, 2)" @click.stop="toSearch(keyWord, 2)"
> >
<SvgIcon iconName="icon-translation-two" /> <SvgIcon iconName="icon-translation-two" />
<span class="text">快捷翻译{{ keyWord }}</span> <span class="text">快捷翻译{{ keyWord }}</span>
@ -36,7 +36,7 @@
<div <div
v-if="searchKeywordType !== 'text'" v-if="searchKeywordType !== 'text'"
class="s-result" class="s-result"
@click=" @click.stop="
toSearch(searchKeyword, searchKeywordType === 'email' ? 3 : 4) toSearch(searchKeyword, searchKeywordType === 'email' ? 3 : 4)
" "
> >
@ -69,7 +69,7 @@
v-for="item in searchSuggestionsData" v-for="item in searchSuggestionsData"
class="s-result" class="s-result"
:key="item" :key="item"
@click="toSearch(item, 1)" @click.stop="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>
@ -214,6 +214,7 @@ const toSearch = (val, type = 1) => {
watch( watch(
() => props.keyWord, () => props.keyWord,
(val) => { (val) => {
if (set.showSuggestions) {
// //
searchSuggestionsData.value = []; searchSuggestionsData.value = [];
// //
@ -221,6 +222,7 @@ watch(
// //
keywordsSearch(val); keywordsSearch(val);
} }
}
); );
// //

View File

@ -1,13 +1,17 @@
<template> <template>
<div :class="`weather-time ${status.siteStatus}`" @click.stop>
<div <div
:class=" :class="['time', set.timeStyle]"
status.siteStatus !== 'normal' ? 'weather-time focus' : 'weather-time' @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="hour">{{ timeData.hour ?? "00" }}</span>
<span class="separator">:</span> <span class="separator" :key="set.showSeconds">:</span>
<span class="minute">{{ timeData.minute ?? "00" }}</span> <span class="minute">{{ timeData.minute ?? "00" }}</span>
<template v-if="set.showSeconds"> <template v-if="set.showSeconds">
<span class="separator">:</span> <span class="separator">:</span>
@ -120,6 +124,10 @@ onBeforeUnmount(() => {
&.focus { &.focus {
transform: translateY(-180px); transform: translateY(-180px);
} }
&.box,
&.set {
transform: translateY(-220px);
}
.time { .time {
cursor: pointer; cursor: pointer;
font-size: 3rem; font-size: 3rem;
@ -160,8 +168,7 @@ onBeforeUnmount(() => {
} }
} }
.weather { .weather {
margin-top: 6px; opacity: 0.7;
opacity: 0.8;
font-size: 1rem; font-size: 1rem;
text-shadow: var(--main-text-shadow); text-shadow: var(--main-text-shadow);
.temperature { .temperature {

View File

@ -4,16 +4,14 @@ import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate"; import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
// IconFont // IconFont
import SvgIcon from "@/components/SvgIcon.vue"; import SvgIcon from "@/components/SvgIcon.vue";
import "@/utils/iconfont.js";
// Notification // Notification
import Notification from "@/components/Notification.vue"; import Notification from "@/components/Notification.vue";
// 主组件 // 主组件
import App from "@/App.vue"; import App from "@/App.vue";
// 全局样式 // 全局样式
import "@/style/global.scss"; import "@/style/global.scss";
// 根组件
const app = createApp(App); const app = createApp(App);
// Pinia // Pinia

View File

@ -16,6 +16,8 @@ const useSetDataStore = defineStore("setData", {
showCleanInput: true, showCleanInput: true,
// 搜索框自动 focus // 搜索框自动 focus
autoFocus: false, autoFocus: false,
// 搜索后搜索框自动失焦
autoInputBlur: true,
// 时间样式 // 时间样式
timeStyle: "one", timeStyle: "one",
// 是否显秒 // 是否显秒
@ -23,8 +25,8 @@ const useSetDataStore = defineStore("setData", {
// 是否显示搜索建议 // 是否显示搜索建议
showSuggestions: true, showSuggestions: true,
// 跳转方式 // 跳转方式
// open 当前页面 / href 新标签页 // open 新标签页 / href 当前页面
urlJumpType: "href", urlJumpType: "open",
}; };
}, },
actions: { actions: {

View File

@ -10,6 +10,8 @@ const useStatusDataStore = defineStore("statusData", {
siteStatus: "normal", siteStatus: "normal",
// 切换搜索引擎 // 切换搜索引擎
engineChangeStatus: false, engineChangeStatus: false,
// 搜索框文本
searchInputValue: "",
}; };
}, },
getters: {}, getters: {},
@ -19,10 +21,14 @@ const useStatusDataStore = defineStore("statusData", {
}, },
setSiteStatus(value) { setSiteStatus(value) {
this.siteStatus = value; this.siteStatus = value;
this.searchInputValue = "";
}, },
setEngineChangeStatus(value) { setEngineChangeStatus(value) {
this.engineChangeStatus = value; this.engineChangeStatus = value;
}, },
setSearchInputValue(value) {
this.searchInputValue = value;
},
}, },
}); });

View File

@ -8,6 +8,7 @@
--main-background-hover-color: #ffffff70; --main-background-hover-color: #ffffff70;
--main-input-hover-color: #ffffff; --main-input-hover-color: #ffffff;
--main-notification-background-color: #00000030; --main-notification-background-color: #00000030;
--main-box-shadow: 0px 0px 10px 0px #00000020;
--main-text-shadow: 0px 0px 8px #00000066; --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 动画 // Transition 动画
.fade-enter-active, .fade-enter-active,
.fade-leave-active { .fade-leave-active {
transition: opacity 0.3s ease; transition: opacity 0.25s ease-in-out;
} }
.fade-enter-from, .fade-enter-from,

File diff suppressed because one or more lines are too long

View File

@ -11,6 +11,8 @@ const identifyInput = (input) => {
*/ */
const urlRegex = const urlRegex =
/^(?:(?:(?:https?|ftp):\/\/)?(?:www\.)?)?([a-zA-Z0-9.-]+(?:\.[a-zA-Z]{2,})+)(?:\/[^\s]*)?(?:\?[^#\s]*)?(?:#[^\s]*)?$/; /^(?:(?:(?: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@]+$/; 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"; if (emailRegex.test(input)) return "email";

View File

@ -2,6 +2,7 @@ import { defineConfig } from "vite";
import { VitePWA } from "vite-plugin-pwa"; import { VitePWA } from "vite-plugin-pwa";
import vue from "@vitejs/plugin-vue"; import vue from "@vitejs/plugin-vue";
import path from "path"; import path from "path";
import viteCompression from "vite-plugin-compression";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
@ -48,6 +49,8 @@ export default defineConfig({
], ],
}, },
}), }),
// viteCompression
viteCompression(),
], ],
server: { server: {
host: "0.0.0.0", host: "0.0.0.0",