# Conflicts:
#	.env
#	index.html
#	src/assets/socialLinks.json
#	src/components/Message.vue
#	yarn.lock
This commit is contained in:
轩辕龙儿 2024-02-05 15:20:27 +08:00
commit bb8b62b5a6
73 changed files with 3759 additions and 6971 deletions

15
.dockerignore Normal file
View File

@ -0,0 +1,15 @@
node_modules
npm-debug.log
Dockerfile*
docker-compose*
.dockerignore
.git
.github
.gitignore
README.md
LICENSE
.vscode
dist
build
images
script

3
.eslintignore Normal file
View File

@ -0,0 +1,3 @@
node_modules
dist
.gitignore

35
.eslintrc.json Normal file
View File

@ -0,0 +1,35 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": ["eslint:recommended", "plugin:vue/vue3-essential"],
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": ["vue"],
"rules": {
"vue/multi-word-component-names": "off"
},
"globals": {
"defineProps": true,
"defineEmits": true,
"withDefaults": true,
"h": true,
"vue": true,
"ref": true,
"reactive": true,
"computed": true,
"watch": true,
"provide": true,
"inject": true,
"defineComponent": true,
"onBeforeMount": true,
"onMounted": true,
"onBeforeUnmount": true,
"nextTick": true,
"ElMessage": true,
"$openList": true
}
}

38
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,38 @@
# Dev 分支推送部署预览
## 仅部署 Win 端
name: Build Dev
on:
push:
branches:
- dev
- master
jobs:
release:
name: Build Website
runs-on: windows-latest
steps:
# 检出 Git 仓库
- name: Check out git repository
uses: actions/checkout@v4.1.1
# 安装 Node.js
- name: Install Node.js
uses: actions/setup-node@v4.0.0
with:
node-version: "18.x"
# 安装项目依赖
- name: Install Dependencies
run: npm install
# 构建程序
- name: Build Website
run: npm run build
env:
GITHUB_TOKEN: ${{ secrets.ACCESS_TOKEN }}
# 上传构建产物
- name: Upload artifacts
uses: actions/upload-artifact@v3.1.3
with:
name: Home
path: dist

8
.prettierrc.json Normal file
View File

@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"singleQuote": false,
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"printWidth": 100
}

128
CODE_OF_CONDUCT.md Normal file
View File

@ -0,0 +1,128 @@
# Contributor Covenant Code of Conduct
## Our Pledge
We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, religion, or sexual identity
and orientation.
We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.
## Our Standards
Examples of behavior that contributes to a positive environment for our
community include:
* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
and learning from the experience
* Focusing on what is best not just for us as individuals, but for the
overall community
Examples of unacceptable behavior include:
* The use of sexualized language or imagery, and sexual attention or
advances of any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email
address, without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
professional setting
## Enforcement Responsibilities
Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.
Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.
## Scope
This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official e-mail address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.
## Enforcement
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
one@imsyy.top.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
reporter of any incident.
## Enforcement Guidelines
Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:
### 1. Correction
**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.
**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.
### 2. Warning
**Community Impact**: A violation through a single incident or series
of actions.
**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or
permanent ban.
### 3. Temporary Ban
**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.
**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.
### 4. Permanent Ban
**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.
**Consequence**: A permanent ban from any sort of public interaction within
the community.
## Attribution
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.0, available at
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
Community Impact Guidelines were inspired by [Mozilla's code of conduct
enforcement ladder](https://github.com/mozilla/diversity).
[homepage]: https://www.contributor-covenant.org
For answers to common questions about this code of conduct, see the FAQ at
https://www.contributor-covenant.org/faq. Translations are available at
https://www.contributor-covenant.org/translations.

16
Dockerfile Normal file
View File

@ -0,0 +1,16 @@
# 构建应用
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
# 最小化镜像
FROM node:18-alpine
WORKDIR /app
COPY --from=builder /app/dist ./dist
RUN npm install -g http-server
EXPOSE 12445
CMD ["http-server", "dist", "-p", "12445"]

107
README.md
View File

@ -5,16 +5,17 @@
简单的小主页,原来的看够了,重新弄了一个
</p>
![無名の主页](https://s2.loli.net/2022/07/14/K5JigfvDoNewtuS.webp)
![無名の主页](/screenshots/main.jpg)
>主页的 Logo 字体已经过压缩,若用本站 Logo 以外的字母会变回默认字体,这里是 [完整字体](https://file.4everland.app/font/Other/Pacifico-Regular.ttf)
>主页的 Logo 字体已经过压缩,若用本站 Logo 以外的字母会变回默认字体,这里是 [完整字体](https://file.imsyy.top/font/Other/Pacifico-Regular.ttf),若无法下载,可将字体目录下的 `Pacifico-Regular-all.ttf` 进行替换
### Demo
>由于 CDN 缓存原因,查看最新效果可能需要 `Ctrl` + `F5` 强制刷新浏览器缓存
- [無名の主页](https://www.imsyy.top)
- [無名の主页 - 备用线路](https://home-imsyy.vercel.app/)
- [無名の主页 - Dev](https://home-imsyy.vercel.app)
- [無名の主页 - 备用线路](https://home-5iw.pages.dev)
### 功能
@ -27,7 +28,19 @@
- [x] 音乐播放器
- [x] 移动端适配
### 部署
### 自动部署
如果遇到构建环境或者打包过程出现错误,则可以采用 `Github Actions` 来进行自动构建
- 在成功 `fork` 仓库后,前往 `Actions` 页面,若您是首次开启,则会出现下面的提示,点击开启
![步骤1](/screenshots/step1.jpg)
- 然后在仓库中进行任意修改后均会触发工作流的运行,在工作流完成后,会在下方生成一个可供下载的压缩包,这就是构建出的静态文件,可自行上传至服务器
![步骤2](/screenshots/step2.jpg)
### 手动部署
* **安装** [node.js](https://nodejs.org/zh-cn/) **环境**
@ -52,6 +65,63 @@ pnpm build
```
> 构建完成后,静态资源会在 **`dist` 目录** 中生成,可将 **`dist` 文件夹下的文件**上传至服务器,也可使用 `Vercel` 等托管平台一键导入并自动部署
### Docker 部署
> 安装及配置 Docker 将不在此处说明,请自行解决
```bash
# 构建
docker build -t home .
# 运行
docker run -p 12445:12445 -d home
```
### 网站链接
`src/assets/siteLinks.json` 中可以自定义网站链接(以指向自己的网站):
```json
{
"icon": "Blog",
"name": "博客",
"link": "https://blog.imsyy.top/"
},
```
其中 `icon` 网站链接的图标可以在 `src/components/Links/index.vue` 中添加:
```js
// 可前往 https://www.xicons.org 自行挑选并在此处引入
// 此处引入的是 fa 类型
import {
Link,
Blog,
CompactDisc,
Cloud,
Compass,
Book,
Fire,
LaptopCode,
} from "@vicons/fa";
...
// 网站链接图标
const siteIcon = {
Blog,
Cloud,
CompactDisc,
Compass,
Book,
Fire,
LaptopCode,
};
```
### 社交链接
`src/assets/socialLinks.json` 中可以自定义社交链接。
### 天气
天气及地区获取需要 `高德开放平台` 相关 API
@ -122,6 +192,28 @@ make clean all
</details>
### 网站图标及网站背景
#### 网站背景
可以在 `public/images` 中修改网站背景
如果想要添加更多的本地图片作为网站背景,可以将图片重命名 `background+数字` 的形式,并在 `src/components/Background/index.vue` 中进行修改:
```js
if (type == 0) {
// 修改此处 Math.random() 后面的第一个数字为图片的数量
bgUrl.value = `/images/background${Math.floor(
Math.random() * 10 + 1
)}.webp`;
}
```
#### 网站图标
可以在 `public/images/icon` 中修改网站图标。
### 技术栈
* [Vue](https://cn.vuejs.org/)
@ -133,9 +225,14 @@ make clean all
### API
* [MetingAPI By 武恩赐](https://api.wuenci.com/meting/api/)
* [小歪 API](https://api.aixiaowai.cn)
* [搏天 API](https://api.btstu.cn/doc/sjbz.php)
* [教书先生 API](https://api.oioweb.cn/doc/weather/GetWeather)
* [高德开放平台](https://lbs.amap.com/)
* [Hitokoto 一言](https://hitokoto.cn/)
## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=imsyy/home&type=Date)](https://star-history.com/#imsyy/home&Date)
<a title="SSL" target="_blank" href="https://myssl.com/seal/detail?domain=blog.imsyy.top"><img src="https://img.shields.io/badge/MySSL-安全认证-brightgreen"></a>&nbsp;<a title="CDN" target="_blank" href="https://cdnjs.com/"><img src="https://img.shields.io/badge/CDN-Cloudflare-blue"></a>&nbsp;<a title="Copyright" target="_blank" href="https://imsyy.top/"><img src="https://img.shields.io/badge/Copyright%20%C2%A9%202020--2023-%E7%84%A1%E5%90%8D-red"></a>

View File

@ -14,7 +14,8 @@ Simple little homepage, had enough of the original one and made a new one
>Due to CDN caching, you may need `Ctrl` + `F5` to force a browser cache refresh to see the latest results
- [無名の主页](https://www.imsyy.top)
- [無名の主页 - 备用线路](https://home-imsyy.vercel.app/)
- [無名の主页 - Dev](https://home-imsyy.vercel.app)
- [無名の主页 - Standby](https://home-5iw.pages.dev)
### Functions

9
docker-compose.yml Normal file
View File

@ -0,0 +1,9 @@
version: '3'
services:
app:
build:
context: .
dockerfile: Dockerfile
ports:
- "12445:12445"

View File

@ -4,36 +4,40 @@
"github": "https://github.com/imsyy/home",
"home": "https://imsyy.top",
"private": true,
"version": "4.0.5",
"version": "4.1.4",
"type": "module",
"scripts": {
"dev": "vite --host",
"build": "vite build",
"preview": "vite preview"
"preview": "vite preview",
"format": "prettier --write src/",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix"
},
"dependencies": {
"@icon-park/vue-next": "^1.4.2",
"@worstone/vue-aplayer": "^1.0.6",
"aplayer": "^1.10.1",
"axios": "^1.1.3",
"element-plus": "^2.2.18",
"fetch-jsonp": "^1.2.3",
"pinia": "^2.0.23",
"pinia-plugin-persistedstate": "^3.0.0",
"swiper": "^9.3.2",
"terser": "^5.16.1",
"vue": "^3.2.37",
"vue3-aplayer": "^1.7.3"
"axios": "^1.6.3",
"element-plus": "^2.4.4",
"fetch-jsonp": "^1.3.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"swiper": "^9.4.1",
"vue": "^3.4.3"
},
"devDependencies": {
"@icon-park/vue-next": "^1.4.2",
"@vicons/fa": "^0.12.0",
"@vicons/material": "^0.12.0",
"@vicons/utils": "^0.1.4",
"@vitejs/plugin-vue": "^3.1.0",
"sass": "^1.55.0",
"unplugin-auto-import": "^0.11.2",
"unplugin-vue-components": "^0.22.8",
"vite": "^3.1.0",
"vite-plugin-html": "^3.2.0",
"vite-plugin-pwa": "^0.14.1"
"@vitejs/plugin-vue": "^4.6.2",
"eslint": "^8.56.0",
"eslint-plugin-vue": "^9.19.2",
"prettier": "^3.1.1",
"sass": "^1.69.6",
"terser": "^5.26.0",
"unplugin-auto-import": "^0.11.5",
"unplugin-vue-components": "^0.22.12",
"vite": "^4.5.1",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-pwa": "^0.14.7"
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

3
public/font/README.md Normal file
View File

@ -0,0 +1,3 @@
## Logo 字体替换
可将该文件夹下的 `Pacifico-Regular-all.ttf` 替换原来的 `Pacifico-Regular.ttf`

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

View File

@ -1,141 +0,0 @@
* {
margin: 0;
padding: 0;
}
#loading-box .loading-left-bg,
#loading-box .loading-right-bg {
position: fixed;
z-index: 999;
width: 50%;
height: 100%;
background-color: #515151e0;
transition: all 0.75s cubic-bezier(0.42, 0, 0, 1.01);
-webkit-backdrop-filter: blur(10px);
backdrop-filter: blur(10px);
}
#loading-box .loading-right-bg {
right: 0;
}
#loading-box > .spinner-box {
position: fixed;
z-index: 999999;
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100vh;
}
#loading-box .spinner-box .loading-word {
position: absolute;
color: #ffffff;
font-size: 0.95rem;
transform: translateY(64px);
text-align: center;
}
.loading-title {
font-size: 1.25rem;
margin: 20px 10px 4px 10px;
}
#loading-box .spinner-box .configure-core {
width: 100%;
height: 100%;
background-color: #37474f;
}
/* 加载完成 */
.loaded .loading-left-bg {
transform: translate(-100%, 0);
}
.loaded .loading-right-bg {
transform: translate(100%, 0);
}
.loaded .spinner-box {
display: none !important;
}
.loader {
position: absolute;
top: calc(50% - 32px);
left: calc(50% - 32px);
width: 64px;
height: 64px;
border-radius: 50%;
perspective: 800px;
transition: all 0.7s cubic-bezier(0.42, 0, 0, 1.01);
}
.inner {
position: absolute;
box-sizing: border-box;
width: 100%;
height: 100%;
border-radius: 50%;
}
.inner.one {
left: 0%;
top: 0%;
animation: rotate-one 1s linear infinite;
border-bottom: 3px solid #efeffa;
}
.inner.two {
right: 0%;
top: 0%;
animation: rotate-two 1s linear infinite;
border-right: 3px solid #efeffa;
}
.inner.three {
right: 0%;
bottom: 0%;
animation: rotate-three 1s linear infinite;
border-top: 3px solid #efeffa;
}
noscript {
z-index: 1000;
position: absolute;
display: flex;
justify-content: center;
width: 100%;
margin-top: 26px;
}
@keyframes rotate-one {
0% {
transform: rotateX(35deg) rotateY(-45deg) rotateZ(0deg);
}
100% {
transform: rotateX(35deg) rotateY(-45deg) rotateZ(360deg);
}
}
@keyframes rotate-two {
0% {
transform: rotateX(50deg) rotateY(10deg) rotateZ(0deg);
}
100% {
transform: rotateX(50deg) rotateY(10deg) rotateZ(360deg);
}
}
@keyframes rotate-three {
0% {
transform: rotateX(35deg) rotateY(55deg) rotateZ(0deg);
}
100% {
transform: rotateX(35deg) rotateY(55deg) rotateZ(360deg);
}
}

View File

@ -1 +0,0 @@
*{margin:0;padding:0}#loading-box .loading-left-bg,#loading-box .loading-right-bg{position:fixed;z-index:999;width:50%;height:100%;background-color:#515151e0;transition:all .75s cubic-bezier(.42,0,0,1.01);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}#loading-box .loading-right-bg{right:0}#loading-box>.spinner-box{position:fixed;z-index:999999;display:flex;justify-content:center;align-items:center;width:100%;height:100vh}#loading-box .spinner-box .loading-word{position:absolute;color:#fff;font-size:.95rem;transform:translateY(64px);text-align:center}.loading-title{font-size:1.25rem;margin:20px 10px 4px 10px}#loading-box .spinner-box .configure-core{width:100%;height:100%;background-color:#37474f}.loaded .loading-left-bg{transform:translate(-100%,0)}.loaded .loading-right-bg{transform:translate(100%,0)}.loaded .spinner-box{display:none!important}.loader{position:absolute;top:calc(50% - 32px);left:calc(50% - 32px);width:64px;height:64px;border-radius:50%;perspective:800px;transition:all .7s cubic-bezier(.42,0,0,1.01)}.inner{position:absolute;box-sizing:border-box;width:100%;height:100%;border-radius:50%}.inner.one{left:0;top:0;animation:rotate-one 1s linear infinite;border-bottom:3px solid #efeffa}.inner.two{right:0;top:0;animation:rotate-two 1s linear infinite;border-right:3px solid #efeffa}.inner.three{right:0;bottom:0;animation:rotate-three 1s linear infinite;border-top:3px solid #efeffa}noscript{z-index:1000;position:absolute;display:flex;justify-content:center;width:100%;margin-top:26px}@keyframes rotate-one{0%{transform:rotateX(35deg) rotateY(-45deg) rotateZ(0)}100%{transform:rotateX(35deg) rotateY(-45deg) rotateZ(360deg)}}@keyframes rotate-two{0%{transform:rotateX(50deg) rotateY(10deg) rotateZ(0)}100%{transform:rotateX(50deg) rotateY(10deg) rotateZ(360deg)}}@keyframes rotate-three{0%{transform:rotateX(35deg) rotateY(55deg) rotateZ(0)}100%{transform:rotateX(35deg) rotateY(55deg) rotateZ(360deg)}}

BIN
screenshots/main.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

BIN
screenshots/step1.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
screenshots/step2.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

View File

@ -1,18 +1,18 @@
<template>
<div class="animate">
<Background />
<main>
<!-- 加载 -->
<Loading />
<!-- 壁纸 -->
<Background @loadComplete="loadComplete" />
<!-- 主界面 -->
<Transition name="fade" mode="out-in">
<main id="main" v-if="store.imgLoadStatus">
<div class="container" v-show="!store.backgroundShow">
<section class="main" v-show="!store.setOpenState">
<section class="all" v-show="!store.setOpenState">
<MainLeft />
<MainRight v-show="!store.boxOpenState" />
<Box v-show="store.boxOpenState" />
</section>
<section
class="more"
v-show="store.setOpenState"
@click="store.setOpenState = false"
>
<section class="more" v-show="store.setOpenState" @click="store.setOpenState = false">
<MoreSet />
</section>
</div>
@ -20,30 +20,32 @@
<Icon
class="menu"
size="24"
v-show="!store.backgroundShow"
@click="store.mobileOpenState = !store.mobileOpenState"
>
<component :is="store.mobileOpenState ? CloseSmall : HamburgerButton" />
</Icon>
</main>
<!-- 页脚 -->
<Transition name="fade" mode="out-in">
<Footer v-show="!store.backgroundShow && !store.setOpenState" />
</div>
</Transition>
</main>
</Transition>
</template>
<script setup>
import { onMounted, onBeforeUnmount, watch } from "vue";
import { helloInit, checkDays } from "@/utils/getTime.js";
import { HamburgerButton, CloseSmall } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import { Icon } from "@vicons/utils";
import { HamburgerButton, CloseSmall } from "@icon-park/vue-next";
import Loading from "@/components/Loading.vue";
import MainLeft from "@/views/Main/Left.vue";
import MainRight from "@/views/Main/Right.vue";
import Background from "@/components/Background/index.vue";
import Footer from "@/components/Footer/index.vue";
import Background from "@/components/Background.vue";
import Footer from "@/components/Footer.vue";
import Box from "@/views/Box/index.vue";
import MoreSet from "@/views/MoreSet/index.vue";
import cursorInit from "@/utils/cursor.js";
import config from "@/../package.json";
//
// import "@/utils/lantern.js";
const store = mainStore();
@ -52,22 +54,29 @@ const getWidth = () => {
store.setInnerWidth(window.innerWidth);
};
onMounted(() => {
//
cursorInit();
//
window.addEventListener("load", () => {
console.log("加载完成");
//
document.getElementsByTagName("body")[0].className = "";
//
const loadingBox = document.getElementById("loading-box");
loadingBox.classList.add("loaded");
//
const loadComplete = () => {
nextTick(() => {
//
helloInit();
//
checkDays();
});
};
//
watch(
() => store.innerWidth,
(value) => {
if (value < 990) {
store.boxOpenState = false;
}
},
);
onMounted(() => {
//
cursorInit();
//
document.oncontextmenu = () => {
@ -95,8 +104,7 @@ onMounted(() => {
window.addEventListener("resize", getWidth);
//
const styleTitle1 =
"font-size: 20px;font-weight: 600;color: rgb(244,167,89);";
const styleTitle1 = "font-size: 20px;font-weight: 600;color: rgb(244,167,89);";
const styleTitle2 = "font-size:12px;color: rgb(244,167,89);";
const styleContent = "color: rgb(30,152,255);";
const title1 = "無名の主页";
@ -108,39 +116,30 @@ onMounted(() => {
_| |_| | | |____) | | | | |
|_____|_| |_|_____/ |_| |_|`;
const content = `\n\n版本: ${config.version}\n主页: ${config.home}\nGithub: ${config.github}`;
console.info(
`%c${title1} %c${title2} %c${content}`,
styleTitle1,
styleTitle2,
styleContent
);
console.info(`%c${title1} %c${title2} %c${content}`, styleTitle1, styleTitle2, styleContent);
});
//
watch(
() => store.innerWidth,
(value) => {
if (value < 990) {
store.boxOpenState = false;
}
}
);
onBeforeUnmount(() => {
window.removeEventListener("resize", getWidth);
});
</script>
<style lang="scss" scoped>
main {
#main {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transform: scale(1.2);
transition: transform 0.3s;
animation: fade-blur-main-in 0.65s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
animation-delay: 0.5s;
.container {
width: 100%;
height: 100vh;
margin: 0 auto;
@media (max-width: 1200px) {
padding: 0 2vw;
}
.main {
.all {
width: 100%;
height: 100%;
padding: 0 0.75rem;
@ -158,8 +157,10 @@ main {
background-color: #00000080;
backdrop-filter: blur(20px);
z-index: 2;
animation: fade;
-webkit-animation: fade 0.5s;
animation: fade 0.5s;
}
@media (max-width: 1200px) {
padding: 0 2vw;
}
}
.menu {
@ -174,37 +175,17 @@ main {
background: rgb(0 0 0 / 20%);
backdrop-filter: blur(10px);
border-radius: 6px;
transition: all 0.3s;
animation: fade;
-webkit-animation: fade 0.5s;
transition: transform 0.3s;
animation: fade 0.5s;
&:active {
transform: scale(0.95);
}
.i-icon {
transform: translateY(2px);
}
@media (min-width: 720px) {
@media (min-width: 721px) {
display: none;
}
}
}
//
.animate {
transform: scale(1);
transition: all ease 1.25s;
opacity: 1;
filter: blur(0);
width: 100%;
height: 100%;
}
.loading {
.animate {
transform: scale(1.2);
transition: all ease 1.25s;
opacity: 0;
filter: blur(10px);
}
}
</style>

View File

@ -8,14 +8,13 @@ import fetchJsonp from "fetch-jsonp";
// 获取音乐播放列表
export const getPlayerList = async (server, type, id) => {
const res = await fetch(
`${import.meta.env.VITE_SONG_API}?server=${server}&type=${type}&id=${id}`
`${import.meta.env.VITE_SONG_API}?server=${server}&type=${type}&id=${id}`,
);
const data = await res.json();
if (data[0].url.startsWith("@")) {
const [handle, jsonpCallback, jsonpCallbackFunction, url] = data[0].url
.split("@")
.slice(1);
// eslint-disable-next-line no-unused-vars
const [handle, jsonpCallback, jsonpCallbackFunction, url] = data[0].url.split("@").slice(1);
const jsonpData = await fetchJsonp(url).then((res) => res.json());
const domain = (
jsonpData.req_0.data.sip.find((i) => !i.startsWith("http://ws")) ||
@ -23,18 +22,18 @@ export const getPlayerList = async (server, type, id) => {
).replace("http://", "https://");
return data.map((v, i) => ({
title: v.name || v.title,
name: v.name || v.title,
artist: v.artist || v.author,
src: domain + jsonpData.req_0.data.midurlinfo[i].purl,
pic: v.pic,
url: domain + jsonpData.req_0.data.midurlinfo[i].purl,
cover: v.cover || v.pic,
lrc: v.lrc,
}));
} else {
return data.map((v) => ({
title: v.name || v.title,
name: v.name || v.title,
artist: v.artist || v.author,
src: v.url,
pic: v.pic,
url: v.url,
cover: v.cover || v.pic,
lrc: v.lrc,
}));
}
@ -63,7 +62,7 @@ export const getAdcode = async (key) => {
// 获取高德地理天气信息
export const getWeather = async (key, city) => {
const res = await fetch(
`https://restapi.amap.com/v3/weather/weatherInfo?key=${key}&city=${city}`
`https://restapi.amap.com/v3/weather/weatherInfo?key=${key}&city=${city}`,
);
return await res.json();
};

View File

@ -0,0 +1,163 @@
<template>
<div :class="store.backgroundShow ? 'cover show' : 'cover'">
<img
v-show="store.imgLoadStatus"
class="bg"
alt="cover"
:src="bgUrl"
@load="imgLoadComplete"
@error.once="imgLoadError"
@animationend="imgAnimationEnd"
/>
<div :class="store.backgroundShow ? 'gray hidden' : 'gray'" />
<Transition name="fade" mode="out-in">
<a
v-if="store.backgroundShow && store.coverType != '3'"
class="down"
:href="bgUrl"
target="_blank"
>
下载壁纸
</a>
</Transition>
</div>
</template>
<script setup>
import { mainStore } from "@/store";
import { Error } from "@icon-park/vue-next";
const store = mainStore();
const bgUrl = ref(null);
const imgTimeout = ref(null);
const emit = defineEmits(["loadComplete"]);
//
// Math.random()
const bgRandom = Math.floor(Math.random() * 10 + 1);
//
const changeBg = (type) => {
if (type == 0) {
bgUrl.value = `/images/background${bgRandom}.jpg`;
} else if (type == 1) {
bgUrl.value = "https://api.dujin.org/bing/1920.php";
} else if (type == 2) {
bgUrl.value = "https://api.aixiaowai.cn/gqapi/gqapi.php";
} else if (type == 3) {
bgUrl.value = "https://api.aixiaowai.cn/api/api.php";
}
};
//
const imgLoadComplete = () => {
imgTimeout.value = setTimeout(
() => {
store.setImgLoadStatus(true);
},
Math.floor(Math.random() * (600 - 300 + 1)) + 300,
);
};
//
const imgAnimationEnd = () => {
console.log("壁纸加载且动画完成");
//
emit("loadComplete");
};
//
const imgLoadError = () => {
console.error("壁纸加载失败:", bgUrl.value);
ElMessage({
message: "壁纸加载失败,已临时切换回默认",
icon: h(Error, {
theme: "filled",
fill: "#efefef",
}),
});
bgUrl.value = `/images/background${bgRandom}.jpg`;
};
onMounted(() => {
//
changeBg(store.coverType);
});
onBeforeUnmount(() => {
clearTimeout(imgTimeout.value);
});
</script>
<style lang="scss" scoped>
.cover {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transition: 0.25s;
z-index: -1;
&.show {
z-index: 1;
}
.bg {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
object-fit: cover;
backface-visibility: hidden;
filter: blur(20px) brightness(0.3);
transition:
filter 0.3s,
transform 0.3s;
animation: fade-blur-in 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
animation-delay: 0.45s;
}
.gray {
opacity: 1;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-image: radial-gradient(rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 0.5) 100%),
radial-gradient(rgba(0, 0, 0, 0) 33%, rgba(0, 0, 0, 0.3) 166%);
transition: 1.5s;
&.hidden {
opacity: 0;
transition: 1.5s;
}
}
.down {
font-size: 16px;
color: white;
position: absolute;
bottom: 30px;
left: 0;
right: 0;
margin: 0 auto;
display: block;
padding: 20px 26px;
border-radius: 8px;
background-color: #00000030;
width: 120px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
&:hover {
transform: scale(1.05);
background-color: #00000060;
}
&:active {
transform: scale(1);
}
}
}
</style>

View File

@ -1,144 +0,0 @@
<template>
<div class="cover">
<img class="bg" :src="bgUrl" alt="cover" />
<div :class="store.backgroundShow ? 'gray sm' : 'gray'" />
<transition name="el-fade-in-linear">
<a
class="down"
:href="bgUrl"
target="_blank"
v-show="store.backgroundShow && store.coverType != '3'"
>下载壁纸</a
>
</transition>
</div>
</template>
<script setup>
import { onMounted, ref, watch, h } from "vue";
import { SuccessPicture } from "@icon-park/vue-next";
import { mainStore } from "@/store";
const store = mainStore();
const bgUrl = ref(null); //
//
const changeBg = (type) => {
if (type == 0) {
bgUrl.value = `/images/background${Math.floor(
Math.random() * 10 + 1
)}.webp`;
} else if (type == 1) {
bgUrl.value = "https://api.dujin.org/bing/1920.php";
} else if (type == 2) {
bgUrl.value = "https://api.btstu.cn/sjbz/api.php?lx=fengjing&format=images";
} else if (type == 3) {
bgUrl.value = "https://api.btstu.cn/sjbz/api.php?lx=dongman&format=images";
}
};
onMounted(() => {
//
changeBg(store.coverType);
});
//
watch(
() => store.coverType,
(value) => {
changeBg(value);
ElMessage({
message: "壁纸设置成功",
icon: h(SuccessPicture, {
theme: "filled",
fill: "#efefef",
}),
});
}
);
</script>
<style lang="scss" scoped>
.cover {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
transition: 0.25s;
z-index: -1;
.bg {
transform: scale(1);
filter: blur(0);
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
object-fit: cover;
transition: all 1.5s ease 0s;
backface-visibility: hidden;
}
.gray {
opacity: 1;
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-image: radial-gradient(
rgba(0, 0, 0, 0) 0,
rgba(0, 0, 0, 0.5) 100%
),
radial-gradient(rgba(0, 0, 0, 0) 33%, rgba(0, 0, 0, 0.3) 166%);
transition: 1.5s;
&.sm {
opacity: 0;
transition: 1.5s;
}
}
.down {
font-size: 16px;
color: white;
position: absolute;
bottom: 30px;
left: 0;
right: 0;
margin: 0 auto;
display: block;
padding: 20px 26px;
border-radius: 8px;
background-color: #00000030;
width: 120px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
&:hover {
transform: scale(1.05);
background-color: #00000060;
}
&:active {
transform: scale(1);
}
}
}
//
.loading {
.cover {
.bg {
transition: 1.5s;
transform: scale(1.1);
filter: blur(10px);
}
.gray {
transition: 1.5s;
opacity: 0;
}
}
}
</style>

120
src/components/Footer.vue Normal file
View File

@ -0,0 +1,120 @@
<template>
<footer id="footer" :class="store.footerBlur ? 'blur' : null">
<Transition name="fade" mode="out-in">
<div v-if="!store.playerState || !store.playerLrcShow" class="power">
<span>
Copyright&nbsp;&copy;
<span v-if="siteStartDate?.length >= 4" class="site-start">
{{ siteStartDate.substring(0, 4) }}
-
</span>
{{ fullYear }}
<a :href="siteUrl">{{ siteAnthor }}</a>
</span>
<!-- 以下信息请不要修改哦 -->
<span class="hidden">
&amp;&nbsp;Made&nbsp;by
<a :href="config.github" target="_blank">
{{ config.author }}
</a>
</span>
<!-- 站点备案 -->
<a v-if="siteIcp" href="https://beian.miit.gov.cn" target="_blank">
&amp;
{{ siteIcp }}
</a>
</div>
<div v-else class="lrc">
<Transition name="fade" mode="out-in">
<div class="lrc-all" :key="store.getPlayerLrc">
<music-one theme="filled" size="18" fill="#efefef" />
<span class="lrc-text text-hidden" v-html="store.getPlayerLrc" />
<music-one theme="filled" size="18" fill="#efefef" />
</div>
</Transition>
</div>
</Transition>
</footer>
</template>
<script setup>
import { MusicOne } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import config from "@/../package.json";
const store = mainStore();
const fullYear = new Date().getFullYear();
//
const siteStartDate = ref(import.meta.env.VITE_SITE_START);
const siteIcp = ref(import.meta.env.VITE_SITE_ICP);
const siteAnthor = ref(import.meta.env.VITE_SITE_ANTHOR);
const siteUrl = computed(() => {
const url = import.meta.env.VITE_SITE_URL;
if (!url) return "https://www.imsyy.top";
//
if (!url.startsWith("http://") && !url.startsWith("https://")) {
return "//" + url;
}
return url;
});
</script>
<style lang="scss" scoped>
#footer {
width: 100%;
position: absolute;
bottom: 0;
left: 0;
height: 46px;
line-height: 46px;
text-align: center;
z-index: 0;
font-size: 14px;
.power {
animation: fade 0.3s;
}
.lrc {
padding: 0 20px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
.lrc-all {
width: 98%;
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
.lrc-text {
margin: 0 8px;
}
.i-icon {
width: 18px;
height: 18px;
display: inherit;
}
}
}
&.blur {
backdrop-filter: blur(10px);
background: rgb(0 0 0 / 25%);
font-size: 16px;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.15s ease-in-out;
}
@media (max-width: 720px) {
font-size: 0.85rem;
&.blur {
font-size: 0.85rem;
}
}
@media (max-width: 480px) {
.hidden {
display: none;
}
}
}
</style>

View File

@ -1,101 +0,0 @@
<template>
<footer>
<div class="power" v-show="!store.playerState">
<span>
Copyright&nbsp;&copy;
<span v-if="siteStartDate?.length >= 4" class="site-start">
{{ siteStartDate.substring(0, 4) }}
-
</span>
{{ fullYear }}
<a :href="SiteUrl">{{ SiteAnthor }}</a>
</span>
<!-- 以下信息请不要修改哦 -->
<span class="hidden">
&amp;&nbsp;Made&nbsp;by
<a :href="config.github" target="_blank">
{{ config.author }}
</a>
</span>
<!-- 站点备案 -->
<a v-if="siteIcp" href="https://beian.miit.gov.cn" target="_blank">
&amp;
{{ siteIcp }}
</a>
</div>
<div class="lrc" v-show="store.playerState">
<music-one theme="filled" size="18" fill="#efefef" />
<span class="lrc-text">
{{ store.getPlayerLrc ? store.getPlayerLrc : "这句没有歌词" }}
</span>
<music-one theme="filled" size="18" fill="#efefef" />
</div>
</footer>
</template>
<script setup>
import { ref } from "vue";
import { MusicOne } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import config from "@/../package.json";
const store = mainStore();
const fullYear = new Date().getFullYear();
//
const siteStartDate = ref(import.meta.env.VITE_SITE_START);
const siteIcp = ref(import.meta.env.VITE_SITE_ICP);
const SiteAnthor = ref(import.meta.env.VITE_SITE_ANTHOR);
const SiteUrl = ref(import.meta.env.VITE_SITE_URL);
</script>
<style lang="scss" scoped>
footer {
width: 100%;
position: absolute;
bottom: 0;
left: 0;
height: 46px;
line-height: 46px;
text-align: center;
backdrop-filter: blur(10px);
background: rgb(0 0 0 / 25%);
z-index: 0;
animation: fade;
-webkit-animation: fade 0.5s;
@media (max-width: 720px) {
font-size: 0.85rem;
}
@media (max-width: 480px) {
.hidden {
display: none;
}
}
.power {
animation: fade;
-webkit-animation: fade 0.3s;
}
.lrc {
padding: 0 20px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
animation: fade;
-webkit-animation: fade 0.3s;
.lrc-text {
margin: 0 8px;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 1;
overflow: hidden;
word-break: break-all;
}
.i-icon {
width: 18px;
height: 18px;
display: inherit;
}
}
}
</style>

View File

@ -0,0 +1,169 @@
<template>
<div class="github-project">
<div class="title">
<github-one theme="two-tone" size="24" :fill="['#efefef', '#00000020']" />
<span>Github</span>
</div>
<Swiper
:modules="[Pagination, Mousewheel]"
:slides-per-view="1"
:space-between="40"
:pagination="{
el: '.swiper-pagination',
clickable: true,
bulletElement: 'div',
}"
:mousewheel="true"
>
<SwiperSlide v-for="list in projectList" :key="list">
<el-row class="all-project" :gutter="20">
<el-col v-for="(item, index) in list" :span="12" :key="index">
<div class="project cards" @click="toGithub(item)">
<div class="name">
<bookmark theme="outline" size="22" fill="#efefef" />
<div class="name-text">
<span class="author">{{ item.author }}</span>
<span>{{ item.name }}</span>
</div>
</div>
<span class="desc">{{ item.desc }}</span>
</div>
</el-col>
</el-row>
</SwiperSlide>
<div class="swiper-pagination" />
</Swiper>
</div>
</template>
<script setup>
import { GithubOne, Bookmark } from "@icon-park/vue-next";
import { Swiper, SwiperSlide } from "swiper/vue";
import { Pagination, Mousewheel } from "swiper";
//
const projectData = [
{
name: "home",
author: "imsyy",
desc: "个人主页我的个人主页个人主页源码主页模板homepage",
},
{
name: "SPlayer",
author: "imsyy",
desc: "🎉 一个简约的音乐播放器,支持网易云音乐账号登录,逐字歌词,下载歌曲,展示评论区,音乐云盘及歌单管理,音乐频谱,移动端基础适配 | A minimalist music player",
},
{
name: "Snavigation",
author: "imsyy",
desc: "Snavigation 一个简约的起始页 | 支持自定义搜索引擎,自定义快捷方式,自定义壁纸以及数据备份",
},
{
name: "DailyHotApi",
author: "imsyy",
desc: "今日热榜 API一个聚合热门数据的 API 接口,支持 Vercel 部署 | 前端页面https://github.com/imsyy/DailyHot",
},
{
name: "site-status",
author: "imsyy",
desc: "📺 一个基于 UptimeRobot API 的在线状态面板 | 站点监测 | 状态检测 | An online status panel based on the UptimeRobot API | UptimeRobot, status, site",
},
];
//
const projectList = computed(() => {
const result = [];
for (let i = 0; i < projectData.length; i += 4) {
const subArr = projectData.slice(i, i + 4);
result.push(subArr);
}
return result;
});
// Github
const toGithub = (data) => {
window.open(`https://github.com/${data.author}/${data.name}`);
};
</script>
<style lang="scss" scoped>
.github-project {
width: 100%;
margin-top: 20px;
.title {
display: flex;
flex-direction: row;
align-items: center;
margin: 0.2rem 0 1.5rem;
font-size: 1.1rem;
.i-icon {
display: flex;
justify-content: center;
align-items: center;
margin-right: 6px;
}
}
.swiper {
left: -10px;
width: calc(100% + 20px);
padding: 5px 10px 0;
z-index: 0;
.swiper-slide {
height: 100%;
}
.swiper-pagination {
position: static;
margin-top: -8px;
:deep(.swiper-pagination-bullet) {
background-color: #fff;
width: 18px;
height: 4px;
border-radius: 4px;
transition: opacity 0.3s;
&:hover {
opacity: 1;
}
}
}
}
.all-project {
width: calc(100% + 20px);
.project {
display: flex;
flex-direction: column;
justify-content: center;
margin-bottom: 20px;
padding: 12px;
height: 100px;
background-color: transparent;
.name {
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 8px;
.i-icon {
display: flex;
margin-right: 6px;
}
.author {
opacity: 0.8;
&::after {
content: "/";
margin: 0 4px;
}
}
}
.desc {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
word-break: break-all;
font-size: 13px;
opacity: 0.8;
line-height: 20px;
}
}
}
}
</style>

View File

@ -18,19 +18,21 @@
</div>
</Transition>
<!-- 一言内容 -->
<div class="content" @click="updateHitokoto">
<Transition name="el-fade-in-linear" mode="out-in">
<div :key="hitokotoData.text" class="content" @click="updateHitokoto">
<span class="text">{{ hitokotoData.text }}</span>
<span class="from">-&nbsp;{{ hitokotoData.from }}&nbsp;</span>
</div>
</Transition>
</div>
</template>
<script setup>
import { reactive, ref, onMounted, h } from "vue";
import { MusicMenu, Error } from "@icon-park/vue-next";
import { getHitokoto } from "@/api";
import debounce from "@/utils/debounce.js";
import { mainStore } from "@/store";
import debounce from "@/utils/debounce.js";
const store = mainStore();
//
@ -43,13 +45,12 @@ const hitokotoData = reactive({
});
//
const getHitokotoData = () => {
getHitokoto()
.then((res) => {
hitokotoData.text = res.hitokoto;
hitokotoData.from = res.from;
})
.catch(() => {
const getHitokotoData = async () => {
try {
const result = await getHitokoto();
hitokotoData.text = result.hitokoto;
hitokotoData.from = result.from;
} catch (error) {
ElMessage({
message: "一言获取失败",
icon: h(Error, {
@ -57,13 +58,13 @@ const getHitokotoData = () => {
fill: "#efefef",
}),
});
});
hitokotoData.text = "这里应该显示一句话";
hitokotoData.from = "無名";
}
};
//
const updateHitokoto = () => {
hitokotoData.text = "新的一言正在赶来的路上";
hitokotoData.from = "来源加载中";
//
debounce(() => {
getHitokotoData();
@ -80,8 +81,7 @@ onMounted(() => {
width: 100%;
height: 100%;
padding: 20px;
animation: fade;
-webkit-animation: fade 0.5s;
animation: fade 0.5s;
.open-music {
width: 100%;
position: absolute;

View File

@ -21,20 +21,16 @@
>
<SwiperSlide v-for="site in siteLinksList" :key="site">
<el-row class="link-all" :gutter="20">
<el-col
v-for="(item, index) in site"
:span="8"
:key="item"
@click="jumpLink(item)"
>
<el-col v-for="(item, index) in site" :span="8" :key="item">
<div
class="item cards"
:style="index < 3 ? 'margin-bottom: 20px' : null"
@click="jumpLink(item)"
>
<Icon size="26">
<component :is="siteIcon[item.icon]" />
</Icon>
<span class="name">{{ item.name }}</span>
<span class="name text-hidden">{{ item.name }}</span>
</div>
</el-col>
</el-row>
@ -45,25 +41,13 @@
</template>
<script setup>
import { onMounted, computed } from "vue";
import { Icon } from "@vicons/utils";
// https://www.xicons.org
import {
Link,
Blog,
CompactDisc,
Cloud,
Compass,
Book,
Fire,
LaptopCode,
} from "@vicons/fa";
import { Link, Blog, CompactDisc, Cloud, Compass, Book, Fire, LaptopCode } from "@vicons/fa"; // 使
import { mainStore } from "@/store";
import { Swiper, SwiperSlide } from "swiper/vue";
import { Pagination, Mousewheel } from "swiper";
import siteLinks from "@/assets/siteLinks.json";
import "swiper/scss";
import "swiper/scss/pagination";
const store = mainStore();
@ -109,8 +93,7 @@ onMounted(() => {
font-size: 1.1rem;
display: flex;
align-items: center;
animation: fade;
-webkit-animation: fade 0.5s;
animation: fade 0.5s;
.title {
margin-left: 8px;
font-size: 1.15rem;
@ -149,8 +132,8 @@ onMounted(() => {
align-items: center;
flex-direction: row;
justify-content: center;
animation: fade;
-webkit-animation: fade 0.5s;
padding: 0 10px;
animation: fade 0.5s;
&:hover {
transform: scale(1.02);
@ -183,6 +166,9 @@ onMounted(() => {
}
}
}
@media (max-width: 720px) {
height: 180px;
}
}
}
</style>

152
src/components/Loading.vue Normal file
View File

@ -0,0 +1,152 @@
<template>
<div id="loader-wrapper" :class="store.imgLoadStatus ? 'loaded' : null">
<div class="loader">
<div class="loader-circle" />
<div class="loader-text">
<span class="name">
{{ siteName }}
</span>
<span class="tip"> 加载中 </span>
</div>
</div>
<div class="loader-section section-left" />
<div class="loader-section section-right" />
</div>
</template>
<script setup>
import { mainStore } from "@/store";
const store = mainStore();
//
const siteName = import.meta.env.VITE_SITE_NAME;
</script>
<style lang="scss" scoped>
#loader-wrapper {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 999;
overflow: hidden;
.loader {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.loader-circle {
width: 150px;
height: 150px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #fff;
animation: spin 1.8s linear infinite;
z-index: 2;
&:before {
content: "";
position: absolute;
top: 5px;
left: 5px;
right: 5px;
bottom: 5px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #a4a4a4;
animation: spin-reverse 0.6s linear infinite;
}
&:after {
content: "";
position: absolute;
top: 15px;
left: 15px;
right: 15px;
bottom: 15px;
border-radius: 50%;
border: 3px solid transparent;
border-top-color: #d3d3d3;
animation: spin 1s linear infinite;
}
}
.loader-text {
display: flex;
flex-direction: column;
align-items: center;
color: #fff;
z-index: 2;
margin-top: 40px;
font-size: 24px;
.tip {
margin-top: 6px;
font-size: 18px;
opacity: 0.6;
}
}
}
.loader-section {
position: fixed;
top: 0;
width: 51%;
height: 100%;
background: #333;
z-index: 1;
&.section-left {
left: 0;
}
&.section-right {
right: 0;
}
}
&.loaded {
visibility: hidden;
transform: translateY(-100%);
transition:
transform 0.3s 1s ease-out,
visibility 0.3s 1s ease-out;
.loader {
.loader-circle,
.loader-text {
opacity: 0;
transition: opacity 0.3s ease-out;
}
}
.loader-section {
&.section-left {
transform: translateX(-100%);
transition: transform 0.5s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
&.section-right {
transform: translateX(100%);
transition: transform 0.5s 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
}
}
}
}
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes spin-reverse {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(-360deg);
}
}
</style>

View File

@ -73,7 +73,7 @@ watch(
descriptionText.hello = import.meta.env.VITE_DESC_HELLO;
descriptionText.text = import.meta.env.VITE_DESC_TEXT;
}
}
},
);
</script>
@ -83,8 +83,8 @@ watch(
display: flex;
flex-direction: row;
align-items: center;
animation: fade;
-webkit-animation: fade 0.5s;
animation: fade 0.5s;
max-width: 460px;
.logo-img {
border-radius: 50%;
width: 120px;
@ -98,7 +98,7 @@ watch(
font-family: "Pacifico-Regular";
.bg {
font-size: 4rem;
font-size: 5rem;
}
.sm {
@ -120,14 +120,17 @@ watch(
}
}
}
@media (max-width: 720px) {
max-width: 100%;
}
}
.description {
padding: 1rem;
margin-top: 3.5rem;
max-width: 460px;
animation: fade;
-webkit-animation: fade 0.5s;
animation: fade 0.5s;
.content {
display: flex;

View File

@ -11,32 +11,12 @@
<span @click="store.musicOpenState = false">回到一言</span>
</div>
<div class="control">
<go-start
theme="filled"
size="30"
fill="#efefef"
@click="changeMusicIndex(0)"
/>
<go-start theme="filled" size="30" fill="#efefef" @click="changeMusicIndex(0)" />
<div class="state" @click="changePlayState">
<play-one
theme="filled"
size="50"
fill="#efefef"
v-show="!store.playerState"
/>
<pause
theme="filled"
size="50"
fill="#efefef"
v-show="store.playerState"
/>
<play-one theme="filled" size="50" fill="#efefef" v-show="!store.playerState" />
<pause theme="filled" size="50" fill="#efefef" v-show="store.playerState" />
</div>
<go-end
theme="filled"
size="30"
fill="#efefef"
@click="changeMusicIndex(1)"
/>
<go-end theme="filled" size="30" fill="#efefef" @click="changeMusicIndex(1)" />
</div>
<div class="menu">
<div class="name" v-show="!volumeShow">
@ -48,12 +28,7 @@
</div>
<div class="volume" v-show="volumeShow">
<div class="icon">
<volume-mute
theme="filled"
size="24"
fill="#efefef"
v-if="volumeNum == 0"
/>
<volume-mute theme="filled" size="24" fill="#efefef" v-if="volumeNum == 0" />
<volume-small
theme="filled"
size="24"
@ -62,23 +37,13 @@
/>
<volume-notice theme="filled" size="24" fill="#efefef" v-else />
</div>
<el-slider
v-model="volumeNum"
:show-tooltip="false"
:min="0"
:max="1"
:step="0.01"
/>
<el-slider v-model="volumeNum" :show-tooltip="false" :min="0" :max="1" :step="0.01" />
</div>
</div>
</div>
<!-- 音乐列表弹窗 -->
<Transition name="fade">
<div
class="music-list"
v-show="musicListShow"
@click="musicListShow = false"
>
<Transition name="fade" mode="out-in">
<div class="music-list" v-show="musicListShow" @click="closeMusicList()">
<Transition name="zoom">
<div class="list" v-show="musicListShow" @click.stop>
<close-one
@ -86,15 +51,14 @@
theme="filled"
size="28"
fill="#ffffff60"
@click="musicListShow = false"
@click="closeMusicList()"
/>
<Player
ref="playerRef"
:songServer="playerData.server"
:songType="playerData.type"
:songId="playerData.id"
:volume="volumeNum"
:shuffle="true"
ref="playerRef"
/>
</div>
</Transition>
@ -103,7 +67,6 @@
</template>
<script setup>
import { ref, reactive, watch, onMounted } from "vue";
import {
GoStart,
PlayOne,
@ -114,7 +77,7 @@ import {
VolumeSmall,
VolumeNotice,
} from "@icon-park/vue-next";
import Player from "@/components/Player/index.vue";
import Player from "@/components/Player.vue";
import { mainStore } from "@/store";
const store = mainStore();
@ -134,6 +97,13 @@ const playerData = reactive({
//
const openMusicList = () => {
musicListShow.value = true;
playerRef.value.toggleList();
};
//
const closeMusicList = () => {
musicListShow.value = false;
playerRef.value.toggleList();
};
//
@ -149,6 +119,9 @@ const changeMusicIndex = (type) => {
onMounted(() => {
//
window.addEventListener("keydown", (e) => {
if (!store.musicIsOk) {
return ;
}
if (e.code == "Space") {
changePlayState();
}
@ -163,7 +136,7 @@ watch(
(value) => {
store.musicVolume = value;
playerRef.value.changeVolume(store.musicVolume);
}
},
);
</script>
@ -179,8 +152,7 @@ watch(
justify-content: space-between;
align-items: center;
flex-direction: column;
animation: fade;
-webkit-animation: fade 0.5s;
animation: fade 0.5s;
.btns {
display: flex;
align-items: center;
@ -242,8 +214,7 @@ watch(
text-overflow: ellipsis;
overflow-x: hidden;
white-space: nowrap;
animation: fade;
-webkit-animation: fade 0.3s;
animation: fade 0.3s;
}
.volume {
width: 100%;
@ -251,8 +222,7 @@ watch(
display: flex;
align-items: center;
flex-direction: row;
animation: fade;
-webkit-animation: fade 0.3s;
animation: fade 0.3s;
.icon {
margin-right: 12px;
span {
@ -320,12 +290,6 @@ watch(
}
//
.fade-enter-active {
animation: fade 0.3s ease-in-out;
}
.fade-leave-active {
animation: fade 0.3s ease-in-out reverse;
}
.zoom-enter-active {
animation: zoom 0.4s ease-in-out;
}

View File

@ -1,31 +1,30 @@
<template>
<aplayer
showLrc
ref="player"
<APlayer
v-if="playList[0]"
:music="playList[playIndex]"
:list="playList"
:autoplay="autoplay"
ref="player"
:audio="playList"
:autoplay="store.playerAutoplay"
:theme="theme"
:repeat="repeat"
:shuffle="shuffle"
:listMaxHeight="listMaxHeight"
:listFolded="listFolded"
:autoSwitch="false"
:loop="store.playerLoop"
:order="store.playerOrder"
:volume="volume"
:showLrc="true"
:listFolded="listFolded"
:listMaxHeight="listMaxHeight"
:noticeSwitch="false"
@play="onPlay"
@pause="onPause"
@timeupdate="onTimeUp"
@onSelectSong="onSelectSong"
@error="loadMusicError"
/>
</template>
<script setup>
import aplayer from "vue3-aplayer";
import fetchJsonp from "fetch-jsonp";
import { h, ref, nextTick, onMounted } from "vue";
import { MusicOne, PlayWrong } from "@icon-park/vue-next";
import { getPlayerList } from "@/api";
import { mainStore } from "@/store";
import APlayer from "@worstone/vue-aplayer";
const store = mainStore();
@ -34,34 +33,17 @@ const player = ref(null);
//
const playList = ref([]);
const playerLrc = ref("");
//
const playIndex = ref(0);
const playListCount = ref(0);
//
const props = defineProps({
//
autoplay: {
type: Boolean,
default: false,
},
//
theme: {
type: String,
default: "#efefef",
},
//
repeat: {
type: String,
default: "list", //'list' | 'music' | 'none'
},
//
shuffle: {
type: Boolean,
default: false,
},
//
volume: {
type: Number,
@ -70,10 +52,10 @@ const props = defineProps({
return value >= 0 && value <= 1;
},
},
// ( netease-, tencent-qq, kugou-, xiami-, baidu- )
// ( netease-, tencent-qq )
songServer: {
type: String,
default: "netease", //'netease' | 'tencent' | 'kugou' | 'xiami' | 'baidu'
default: "netease", //'netease' | 'tencent'
},
// ( song-, playlist-, album-, search-, artist- )
songType: {
@ -92,42 +74,29 @@ const props = defineProps({
},
//
listMaxHeight: {
type: String,
default: "420px",
type: Number,
default: 420,
},
});
const listHeight = computed(() => {
return props.listMaxHeight + "px";
});
//
onMounted(() => {
nextTick(() => {
try {
getPlayerList(props.songServer, props.songType, props.songId).then(
(res) => {
getPlayerList(props.songServer, props.songType, props.songId).then((res) => {
console.log(res);
//
playIndex.value = Math.floor(Math.random() * res.length);
playListCount.value = res.length;
//
store.musicIsOk = true;
//
res.forEach((v) => {
playList.value.push({
title: v.name || v.title,
artist: v.artist || v.author,
src: v.url || v.src,
pic: v.pic,
lrc: v.lrc,
playList.value = res;
console.log("音乐加载完成");
console.log(playList.value);
console.log(playIndex.value, playList.value.length, props.volume);
});
});
console.log(
"音乐加载完成",
playList.value,
playIndex.value,
playListCount.value,
props.volume
);
}
);
} catch (err) {
console.error(err);
store.musicIsOk = false;
@ -143,16 +112,14 @@ onMounted(() => {
});
});
//
//
const onPlay = () => {
console.log("播放");
playIndex.value = player.value.aplayer.index;
//
store.setPlayerState(player.value.audio.paused);
store.setPlayerState(player.value.audioRef.paused);
//
store.setPlayerData(
player.value.currentMusic.title,
player.value.currentMusic.artist
);
store.setPlayerData(playList.value[playIndex.value].name, playList.value[playIndex.value].artist);
ElMessage({
message: store.getPlayerData.name + " - " + store.getPlayerData.artist,
grouping: true,
@ -162,19 +129,26 @@ const onPlay = () => {
}),
});
};
//
const onPause = () => {
store.setPlayerState(player.value.audio.paused);
store.setPlayerState(player.value.audioRef.paused);
};
//
const onTimeUp = () => {
let playerRef = player.value.$.vnode.el;
if (playerRef) {
playerLrc.value = playerRef.getElementsByClassName(
"aplayer-lrc-current"
)[0].innerHTML;
store.setPlayerLrc(playerLrc.value);
let lyrics = player.value.aplayer.lyrics[playIndex.value];
let lyricIndex = player.value.aplayer.lyricIndex;
if (!lyrics || !lyrics[lyricIndex]) {
return;
}
let lrc = lyrics[lyricIndex][1];
if (lrc === "Loading") {
lrc = "歌词加载中";
} else if (lrc === "Not available") {
lrc = "歌词加载失败";
}
store.setPlayerLrc(lrc);
};
//
@ -184,40 +158,55 @@ const playToggle = () => {
//
const changeVolume = (value) => {
player.value.audio.volume = value;
};
const onSelectSong = (val) => {
console.log(val);
player.value.setVolume(value, false);
};
//
const changeSong = (type) => {
playIndex.value = player.value.playIndex;
playIndex.value += type ? 1 : -1;
// /
if (playIndex.value < 0) {
playIndex.value = playListCount.value - 1;
} else if (playIndex.value >= playListCount.value) {
playIndex.value = 0;
}
// console.log(playIndex.value, playList.value[playIndex.value]);
type === 0 ? player.value.skipBack() : player.value.skipForward();
nextTick(() => {
player.value.play();
});
};
//
const toggleList = () => {
player.value.toggleList();
};
//
const loadMusicError = () => {
let notice = "";
if (playList.value.length > 1) {
notice = "播放歌曲出现错误,播放器将在 2s 后进行下一首";
} else {
notice = "播放歌曲出现错误";
}
ElMessage({
message: notice,
grouping: true,
icon: h(PlayWrong, {
theme: "filled",
fill: "#EFEFEF",
duration: 2000,
}),
});
console.error(
"播放歌曲: " + player.value.aplayer.audio[player.value.aplayer.index].name + " 出现错误",
);
};
//
defineExpose({ playToggle, changeVolume, changeSong });
defineExpose({ playToggle, changeVolume, changeSong, toggleList });
</script>
<style lang="scss" scoped>
.aplayer {
width: 80%;
background: transparent;
border-radius: 6px;
font-family: "HarmonyOS_Regular", sans-serif !important;
:deep(.aplayer-body) {
background-color: transparent;
.aplayer-pic {
display: none;
}
@ -239,8 +228,8 @@ defineExpose({ playToggle, changeVolume, changeSong });
}
.aplayer-lrc {
text-align: left;
margin: 4px 0 6px 6px;
height: 100%;
margin: 7px 0 6px 6px;
height: 44px;
mask: linear-gradient(
#fff 15%,
#fff 85%,
@ -272,6 +261,8 @@ defineExpose({ playToggle, changeVolume, changeSong });
}
:deep(.aplayer-list) {
margin-top: 6px;
height: v-bind(listHeight);
background-color: transparent;
ol {
&::-webkit-scrollbar-track {
background-color: transparent;

202
src/components/Set.vue Normal file
View File

@ -0,0 +1,202 @@
<template>
<div class="setting">
<el-collapse class="collapse" v-model="activeName" accordion>
<el-collapse-item title="个性壁纸" name="1">
<div class="bg-set">
<el-radio-group v-model="coverType" text-color="#ffffff" @change="radioChange">
<el-radio label="0" size="large" border>默认壁纸</el-radio>
<el-radio label="1" size="large" border>每日一图</el-radio>
<el-radio label="2" size="large" border>随机风景</el-radio>
<el-radio label="3" size="large" border>随机动漫</el-radio>
</el-radio-group>
</div>
</el-collapse-item>
<el-collapse-item title="个性化调整" name="2">
<div class="item">
<span class="text">建站日期显示</span>
<el-switch
v-model="siteStartShow"
inline-prompt
:active-icon="CheckSmall"
:inactive-icon="CloseSmall"
/>
</div>
<div class="item">
<span class="text">音乐点击是否打开面板</span>
<el-switch
v-model="musicClick"
inline-prompt
:active-icon="CheckSmall"
:inactive-icon="CloseSmall"
/>
</div>
<div class="item">
<span class="text">底栏歌词显示</span>
<el-switch
v-model="playerLrcShow"
inline-prompt
:active-icon="CheckSmall"
:inactive-icon="CloseSmall"
/>
</div>
<div class="item">
<span class="text">底栏背景模糊</span>
<el-switch
v-model="footerBlur"
inline-prompt
:active-icon="CheckSmall"
:inactive-icon="CloseSmall"
/>
</div>
</el-collapse-item>
<el-collapse-item title="播放器配置" name="3">
<div class="item">
<span class="text">自动播放</span>
<el-switch
v-model="playerAutoplay"
inline-prompt
:active-icon="CheckSmall"
:inactive-icon="CloseSmall"
/>
</div>
<div class="item">
<span class="text">随机播放</span>
<el-switch
v-model="playerOrder"
inline-prompt
:active-icon="CheckSmall"
:inactive-icon="CloseSmall"
active-value="random"
inactive-value="list"
/>
</div>
<div class="item">
<span class="text">循环模式</span>
<el-radio-group v-model="playerLoop" size="small" text-color="#FFFFFF">
<el-radio label="all" border>列表</el-radio>
<el-radio label="one" border>单曲</el-radio>
<el-radio label="none" border>不循环</el-radio>
</el-radio-group>
</div>
</el-collapse-item>
<el-collapse-item title="其他设置" name="4">
<div>设置内容待增加</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup>
import { CheckSmall, CloseSmall, SuccessPicture } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import { storeToRefs } from "pinia";
const store = mainStore();
const {
coverType,
siteStartShow,
musicClick,
playerLrcShow,
footerBlur,
playerAutoplay,
playerOrder,
playerLoop,
} = storeToRefs(store);
//
const activeName = ref("1");
//
const radioChange = () => {
ElMessage({
message: "壁纸设置成功,刷新后生效",
icon: h(SuccessPicture, {
theme: "filled",
fill: "#efefef",
}),
});
};
</script>
<style lang="scss" scoped>
.setting {
.collapse {
border-radius: 8px;
--el-collapse-content-bg-color: #ffffff10;
border-color: transparent;
overflow: hidden;
:deep(.el-collapse-item__header) {
background-color: #ffffff30;
color: #fff;
font-size: 15px;
padding-left: 18px;
border-color: transparent;
}
:deep(.el-collapse-item__wrap) {
border-color: transparent;
.el-collapse-item__content {
padding: 20px;
.item {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
font-size: 14px;
.el-switch__core {
border-color: transparent;
background-color: #ffffff30;
}
.el-radio-group {
.el-radio {
margin: 2px 10px 2px 0;
border-radius: 5px;
&:last-child {
margin-right: 0;
}
}
}
}
.el-radio-group {
justify-content: space-between;
.el-radio {
margin: 10px 16px;
background: #ffffff26;
border: 2px solid transparent;
border-radius: 8px;
.el-radio__label {
color: #fff;
}
.el-radio__inner {
background: #ffffff06 !important;
border: 2px solid #eeeeee !important;
}
&.is-checked {
background: #ffffff06 !important;
border: 2px solid #eeeeee !important;
}
.is-checked {
.el-radio__inner {
background-color: #ffffff30 !important;
border-color: #fff !important;
}
& + .el-radio__label {
color: #fff !important;
}
}
}
}
}
}
}
}
</style>

View File

@ -1,142 +0,0 @@
<template>
<div class="setting">
<el-collapse class="collapse" v-model="activeName" accordion>
<el-collapse-item title="壁纸设置" name="1">
<div class="bg-set">
<el-radio-group v-model="bgSet" text-color="#ffffff">
<el-radio label="0" size="large" border>默认壁纸</el-radio>
<el-radio label="1" size="large" border>每日一图</el-radio>
<el-radio label="2" size="large" border>随机风景</el-radio>
<el-radio label="3" size="large" border>随机动漫</el-radio>
</el-radio-group>
</div>
</el-collapse-item>
<el-collapse-item title="其他设置" name="2">
<div class="item">
<span class="text">建站日期显示</span>
<el-switch
v-model="siteStartShow"
inline-prompt
:active-icon="CheckSmall"
:inactive-icon="CloseSmall"
/>
</div>
<div class="item">
<span class="text">音乐点击是否打开面板</span>
<el-switch
v-model="musicClick"
inline-prompt
:active-icon="CheckSmall"
:inactive-icon="CloseSmall"
/>
</div>
</el-collapse-item>
<el-collapse-item title="其他设置" name="3">
<div>设置内容待增加</div>
</el-collapse-item>
<el-collapse-item title="其他设置" name="4">
<div>设置内容待增加</div>
</el-collapse-item>
</el-collapse>
</div>
</template>
<script setup>
import { ref, onMounted, watch } from "vue";
import { mainStore } from "@/store";
import { CheckSmall, CloseSmall } from "@icon-park/vue-next";
import { storeToRefs } from "pinia";
const store = mainStore();
const { siteStartShow, musicClick } = storeToRefs(store);
//
const activeName = ref("1");
const bgSet = ref("0");
onMounted(() => {
bgSet.value = store.coverType.toString();
});
//
watch(
() => bgSet.value,
(value) => {
store.coverType = value;
}
);
</script>
<style lang="scss" scoped>
.setting {
.collapse {
border-radius: 8px;
--el-collapse-content-bg-color: #ffffff10;
border-color: transparent;
overflow: hidden;
:deep(.el-collapse-item__header) {
background-color: #ffffff30;
color: #fff;
font-size: 15px;
padding-left: 18px;
border-color: transparent;
}
:deep(.el-collapse-item__wrap) {
border-color: transparent;
.el-collapse-item__content {
padding: 20px;
.item {
display: flex;
align-items: center;
justify-content: space-between;
font-size: 14px;
.el-switch__core {
border-color: transparent;
background-color: #ffffff30;
}
}
.bg-set {
.el-radio-group {
justify-content: space-between;
.el-radio {
margin: 10px 16px;
background: #ffffff26;
border: 2px solid transparent;
border-radius: 8px;
.el-radio__label {
color: #fff;
}
.el-radio__inner {
background: #ffffff06 !important;
border: 2px solid #eeeeee !important;
}
&.is-checked {
background: #ffffff06 !important;
border: 2px solid #eeeeee !important;
}
.is-checked {
.el-radio__inner {
background-color: #ffffff30 !important;
border-color: #fff !important;
}
& + .el-radio__label {
color: #fff !important;
}
}
}
}
}
}
}
}
}
</style>

View File

@ -18,7 +18,6 @@
</template>
<script setup>
import { ref } from "vue";
import socialLinks from "@/assets/socialLinks.json";
//
@ -37,9 +36,10 @@ const socialTip = ref("通过这里联系我吧");
background-color: transparent;
border-radius: 6px;
backdrop-filter: blur(0);
animation: fade;
-webkit-animation: fade 0.5s;
transition: all 0.5s;
animation: fade 0.5s;
transition:
background-color 0.3s,
backdrop-filter 0.3s;
@media (max-width: 840px) {
max-width: 100%;
justify-content: center;
@ -60,9 +60,12 @@ const socialTip = ref("通过这里联系我吧");
display: inherit;
.icon {
margin: 0 12px;
transition: all 0.3s;
transition: transform 0.3s;
&:hover {
transform: scale(1.1);
}
&:active {
transform: scale(0.9);
transform: scale(1);
}
}
}
@ -70,8 +73,7 @@ const socialTip = ref("通过这里联系我吧");
.tip {
display: none;
margin-right: 12px;
animation: fade;
-webkit-animation: fade 0.5s;
animation: fade 0.5s;
}
@media (min-width: 768px) {
&:hover {

View File

@ -1,45 +1,17 @@
<template>
<div class="time-capsule">
<div class="title">
<hourglass-full
theme="two-tone"
size="24"
:fill="['#efefef', '#00000020']"
/>
<hourglass-full theme="two-tone" size="24" :fill="['#efefef', '#00000020']" />
<span>时光胶囊</span>
</div>
<span class="text"
>今日已经度过了&nbsp;{{ timeData.day.elapsed }}&nbsp;小时</span
>
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage="timeData.day.pass"
/>
<span class="text"
>本周已经度过了&nbsp;{{ timeData.week.elapsed }}&nbsp;</span
>
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage="timeData.week.pass"
/>
<span class="text"
>本月已经度过了&nbsp;{{ timeData.month.elapsed }}&nbsp;</span
>
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage="timeData.month.pass"
/>
<span class="text"
>今年已经度过了&nbsp;{{ timeData.year.elapsed }}&nbsp;个月</span
>
<el-progress
:text-inside="true"
:stroke-width="20"
:percentage="timeData.year.pass"
/>
<span class="text">今日已经度过了&nbsp;{{ timeData.day.elapsed }}&nbsp;小时</span>
<el-progress :text-inside="true" :stroke-width="20" :percentage="timeData.day.pass" />
<span class="text">本周已经度过了&nbsp;{{ timeData.week.elapsed }}&nbsp;</span>
<el-progress :text-inside="true" :stroke-width="20" :percentage="timeData.week.pass" />
<span class="text">本月已经度过了&nbsp;{{ timeData.month.elapsed }}&nbsp;</span>
<el-progress :text-inside="true" :stroke-width="20" :percentage="timeData.month.pass" />
<span class="text">今年已经度过了&nbsp;{{ timeData.year.elapsed }}&nbsp;个月</span>
<el-progress :text-inside="true" :stroke-width="20" :percentage="timeData.year.pass" />
<div v-if="startDate?.length >= 4 && store.siteStartShow">
<span class="text" v-html="startDateText" />
<!-- <el-progress
@ -54,7 +26,6 @@
</template>
<script setup>
import { onMounted, onBeforeUnmount, ref } from "vue";
import { HourglassFull } from "@icon-park/vue-next";
import { getTimeCapsule, siteDateStatistics } from "@/utils/getTime.js";
import { mainStore } from "@/store";
@ -69,8 +40,7 @@ const timeInterval = ref(null);
onMounted(() => {
timeInterval.value = setInterval(() => {
timeData.value = getTimeCapsule();
if (startDate.value)
startDateText.value = siteDateStatistics(new Date(startDate.value));
if (startDate.value) startDateText.value = siteDateStatistics(new Date(startDate.value));
}, 1000);
});

102
src/components/Weather.vue Normal file
View File

@ -0,0 +1,102 @@
<template>
<div class="weather" v-if="weatherData.adCode.city && weatherData.weather.weather">
<span>{{ weatherData.adCode.city }}&nbsp;</span>
<span>{{ weatherData.weather.weather }}&nbsp;</span>
<span>{{ weatherData.weather.temperature }}</span>
<span class="sm-hidden">
&nbsp;{{
weatherData.weather.winddirection?.endsWith("风")
? weatherData.weather.winddirection
: weatherData.weather.winddirection + "风"
}}&nbsp;
</span>
<span class="sm-hidden">{{ weatherData.weather.windpower }}&nbsp;</span>
</div>
<div class="weather" v-else>
<span>天气数据获取失败</span>
</div>
</template>
<script setup>
import { getAdcode, getWeather, getOtherWeather } from "@/api";
import { Error } from "@icon-park/vue-next";
// Key
const mainKey = import.meta.env.VITE_WEATHER_KEY;
//
const weatherData = reactive({
adCode: {
city: null, //
adcode: null, //
},
weather: {
weather: null, //
temperature: null, //
winddirection: null, //
windpower: null, //
},
});
//
const getWeatherData = async () => {
try {
//
if (!mainKey) {
console.log("未配置,使用备用天气接口");
const result = await getOtherWeather();
console.log(result);
const data = result.result;
weatherData.adCode = {
city: data.city.city_name || "未知地区",
// adcode: data.city.cityId,
};
weatherData.weather = {
weather: data.condition.condition,
temperature: data.condition.temp,
winddirection: data.condition.windDir,
windpower: data.condition.windLevel,
};
} else {
// Adcode
const adCode = await getAdcode(mainKey);
console.log(adCode);
if (adCode.infocode !== "10000") {
throw "地区查询失败";
}
weatherData.adCode = {
city: adCode.city,
adcode: adCode.adcode,
};
//
const result = await getWeather(mainKey, weatherData.adCode.adcode);
weatherData.weather = {
weather: result.lives[0].weather,
temperature: result.lives[0].temperature,
winddirection: result.lives[0].winddirection,
windpower: result.lives[0].windpower,
};
}
} catch (error) {
console.error("天气信息获取失败:" + error);
onError("天气信息获取失败");
}
};
//
const onError = (message) => {
ElMessage({
message,
icon: h(Error, {
theme: "filled",
fill: "#efefef",
}),
});
console.error(message);
};
onMounted(() => {
//
getWeatherData();
});
</script>

View File

@ -1,110 +0,0 @@
<template>
<div class="weather" v-if="weatherData.adCode.city && weatherData.weather.weather">
<span>{{ weatherData.adCode.city }}&nbsp;</span>
<span>{{ weatherData.weather.weather }}&nbsp;</span>
<span>{{ weatherData.weather.temperature }}</span>
<span class="sm-hidden">
&nbsp;{{
weatherData.weather.winddirection?.endsWith("风")
? weatherData.weather.winddirection
: weatherData.weather.winddirection + "风"
}}&nbsp;
</span>
<span class="sm-hidden">{{ weatherData.weather.windpower }}&nbsp;</span>
</div>
<div class="weather" v-else>
<span>天气数据获取失败</span>
</div>
</template>
<script setup>
import { onMounted, reactive, h } from "vue";
import { getAdcode, getWeather, getOtherWeather } from "@/api";
import { Error } from "@icon-park/vue-next";
// Key
const mainKey = import.meta.env.VITE_WEATHER_KEY;
//
const weatherData = reactive({
adCode: {
city: null, //
adcode: null, //
},
weather: {
weather: null, //
temperature: null, //
winddirection: null, //
windpower: null, //
},
});
//
const getWeatherData = () => {
//
if (!mainKey) {
getOtherWeather()
.then((res) => {
console.log(res);
const data = res.result;
weatherData.adCode = {
city: data.city.name,
adcode: data.city.cityId,
};
weatherData.weather = {
weather: data.condition.condition,
temperature: data.condition.temp,
winddirection: data.condition.windDir,
windpower: data.condition.windLevel,
};
})
.catch((err) => {
console.error("天气信息获取失败:" + err);
onError("天气信息获取失败");
});
} else {
getAdcode(mainKey)
.then((res) => {
weatherData.adCode = {
city: res.city,
adcode: res.adcode,
};
//
getWeather(mainKey, weatherData.adCode.adcode)
.then((res) => {
weatherData.weather = {
weather: res.lives[0].weather,
temperature: res.lives[0].temperature,
winddirection: res.lives[0].winddirection,
windpower: res.lives[0].windpower,
};
})
.catch((err) => {
console.error("天气信息获取失败:" + err);
onError("天气信息获取失败");
});
})
.catch((err) => {
console.error("地理位置获取失败:" + err);
onError("地理位置获取失败");
});
}
};
//
const onError = (message) => {
ElMessage({
message,
icon: h(Error, {
theme: "filled",
fill: "#efefef",
}),
});
console.error(message);
};
onMounted(() => {
//
getWeatherData();
});
</script>

View File

@ -1,24 +1,23 @@
import {
createApp
} from 'vue';
import '@/style/style.scss';
import App from '@/App.vue';
import { createApp } from "vue";
import "@/style/style.scss";
import App from "@/App.vue";
// 引入 pinia
import {
createPinia
} from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
// swiper
import "swiper/scss";
import "swiper/scss/pagination";
const app = createApp(App);
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
app.use(pinia);
app.mount('#app')
app.mount("#app");
// PWA
navigator.serviceWorker.addEventListener('controllerchange', () => {
navigator.serviceWorker.addEventListener("controllerchange", () => {
// 弹出更新提醒
console.log("站点已更新,刷新后生效");
ElMessage("站点已更新,刷新后生效");
})
});

View File

@ -3,9 +3,11 @@ import { defineStore } from "pinia";
export const mainStore = defineStore("main", {
state: () => {
return {
imgLoadStatus: false, // 壁纸加载状态
innerWidth: null, // 当前窗口宽度
coverType: "0", // 壁纸种类
siteStartShow: true, // 建站日期显示
siteStartShow: false, // 建站日期显示
musicClick: false, // 音乐链接是否跳转
musicIsOk: false, // 音乐是否加载完成
musicVolume: 0, // 音乐音量;
musicOpenState: false, // 音乐面板开启状态
@ -18,7 +20,11 @@ export const mainStore = defineStore("main", {
playerTitle: null, // 当前播放歌曲名
playerArtist: null, // 当前播放歌手名
playerLrc: "歌词加载中", // 当前播放歌词
musicClick: false, // 音乐链接是否跳转
playerLrcShow: true, // 是否显示底栏歌词
footerBlur: true, // 底栏模糊
playerAutoplay: false, // 是否自动播放
playerLoop: "all", // 循环播放 "all", "one", "none"
playerOrder: "list", // 循环顺序 "list", "random"
};
},
getters: {
@ -64,10 +70,24 @@ export const mainStore = defineStore("main", {
this.playerTitle = title;
this.playerArtist = artist;
},
// 更改壁纸加载状态
setImgLoadStatus(value) {
this.imgLoadStatus = value;
},
},
persist: {
key: "data",
storage: window.localStorage,
paths: ["coverType", "musicVolume", "siteStartShow", "musicClick"],
paths: [
"coverType",
"musicVolume",
"siteStartShow",
"musicClick",
"playerLrcShow",
"footerBlur",
"playerAutoplay",
"playerLoop",
"playerOrder",
],
},
});

View File

@ -1,7 +1,7 @@
// scss 全局变量
// 响应式布局
@mixin changeWidth($maxWidth:1200px) {
@mixin changeWidth($maxWidth: 1200px) {
.container {
max-width: $maxWidth;
}
@ -17,19 +17,18 @@
}
}
/* 小于1200px时 */
@media (max-width: 1200px) {
@include changeWidth($maxWidth:1000px);
/* 小于1280px时 */
@media (max-width: 1280px) {
@include changeWidth($maxWidth: 1100px);
}
/* 小于992px时 */
@media (max-width: 992px) {
@include changeWidth($maxWidth:900px);
@include changeWidth($maxWidth: 900px);
}
/* 小于720px时 */
@media (max-width: 720px) {
// 隐藏元素
.xs-hidden {
display: none;

View File

@ -13,6 +13,8 @@ html,
body {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
background-color: #333;
overflow: hidden;
font-family: "HarmonyOS_Regular", sans-serif;
@ -23,8 +25,6 @@ a,
p {
margin: 0;
padding: 0;
-webkit-user-select: none;
-webkit-user-drag: none;
user-select: none;
text-decoration: none;
color: #fff;
@ -38,11 +38,13 @@ p {
// 字体文件
@font-face {
font-family: "Pacifico-Regular";
font-display: swap;
src: url("/font/Pacifico-Regular.ttf") format("truetype");
}
@font-face {
font-family: "UnidreamLED";
font-display: swap;
src: url("/font/UnidreamLED.ttf") format("truetype");
}
@ -59,19 +61,18 @@ p {
// 卡片样式
.cards {
border-radius: 6px;
background: #00000040;
-webkit-backdrop-filter: blur(10px);
background-color: #00000040;
backdrop-filter: blur(10px);
transform: scale(1);
transition: all 0.3s;
}
.cards:hover {
transition:
backdrop-filter 0.3s,
transform 0.3s;
&:hover {
transform: scale(1.01);
}
.cards:active {
}
&:active {
transform: scale(0.98);
}
}
// 文字超出
@ -103,13 +104,11 @@ p {
.el-progress-bar__outer {
border-radius: 6px;
background-color: #00000020;
.el-progress-bar__inner {
background-color: #efefef;
border-radius: 6px;
text-align: center;
font-family: "UnidreamLED";
span {
color: #564d59;
font-size: 0.9rem;
@ -129,31 +128,59 @@ p {
border-radius: 8px !important;
border: 1px solid transparent !important;
background-color: transparent !important;
.el-card__header {
font-weight: bold;
padding: 16px 20px !important;
background-color: #ffffff30 !important;
border-bottom: 1px solid transparent !important;
}
.el-card__body {
padding: 0 !important;
background-color: #ffffff10 !important;
}
}
// 渐入动画
// Transition 动画
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s ease-in-out;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
// keyframes 动画
@keyframes fade {
0% {
from {
opacity: 0;
}
100% {
to {
opacity: 1;
}
}
@keyframes fade-blur-in {
from {
filter: blur(20px) brightness(0.3);
transform: scale(1.6);
}
to {
filter: blur(0) brightness(1);
transform: scale(1.2);
}
}
@keyframes fade-blur-main-in {
from {
transform: scale(1.2);
}
to {
transform: scale(1);
}
}
// 隐藏元素
@media (min-width: 910px) and (max-width: 1200px) {
.sm-hidden {

View File

@ -1,21 +1,26 @@
var CURSOR;
let mainCursor;
Math.lerp = (a, b, n) => (1 - n) * a + n * b;
const getStyle = (el, attr) => {
try {
return window.getComputedStyle ?
window.getComputedStyle(el)[attr] :
el.currentStyle[attr];
} catch (e) {}
return "";
return window.getComputedStyle ? window.getComputedStyle(el)[attr] : el.currentStyle[attr];
} catch (e) {
console.error(e);
}
return false;
};
const cursorInit = () => {
mainCursor = new Cursor();
return mainCursor;
};
class Cursor {
constructor() {
this.pos = {
curr: null,
prev: null
prev: null,
};
this.pt = [];
this.create();
@ -37,10 +42,9 @@ class Cursor {
document.body.append(this.cursor);
}
var el = document.getElementsByTagName('*');
var el = document.getElementsByTagName("*");
for (let i = 0; i < el.length; i++)
if (getStyle(el[i], "cursor") == "pointer")
this.pt.push(el[i].outerHTML);
if (getStyle(el[i], "cursor") == "pointer") this.pt.push(el[i].outerHTML);
document.body.appendChild((this.scr = document.createElement("style")));
this.scr.innerHTML = `* {cursor: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8' width='10px' height='10px'><circle cx='4' cy='4' r='4' fill='white' /></svg>") 4 4, auto !important}`;
@ -51,7 +55,7 @@ class Cursor {
this.cursor.classList.remove("active");
this.pos = {
curr: null,
prev: null
prev: null,
};
this.pt = [];
@ -61,18 +65,18 @@ class Cursor {
}
init() {
document.onmousemove = e => {
(this.pos.curr == null) && this.move(e.clientX - 8, e.clientY - 8);
document.onmousemove = (e) => {
this.pos.curr == null && this.move(e.clientX - 8, e.clientY - 8);
this.pos.curr = {
x: e.clientX - 8,
y: e.clientY - 8
y: e.clientY - 8,
};
this.cursor.classList.remove("hidden");
};
document.onmouseenter = e => this.cursor.classList.remove("hidden");
document.onmouseleave = e => this.cursor.classList.add("hidden");
document.onmousedown = e => this.cursor.classList.add("active");
document.onmouseup = e => this.cursor.classList.remove("active");
document.onmouseenter = () => this.cursor.classList.remove("hidden");
document.onmouseleave = () => this.cursor.classList.add("hidden");
document.onmousedown = () => this.cursor.classList.add("active");
document.onmouseup = () => this.cursor.classList.remove("active");
}
render() {
@ -87,8 +91,4 @@ class Cursor {
}
}
const cursorInit = () => {
CURSOR = new Cursor();
};
export default cursorInit;

View File

@ -12,10 +12,10 @@ function debounce(func, wait = 300, immediate = false) {
timeout = setTimeout(function () {
timeout = null;
}, wait);
if (callNow) typeof func === 'function' && func();
if (callNow) typeof func === "function" && func();
} else {
timeout = setTimeout(function () {
typeof func === 'function' && func();
typeof func === "function" && func();
}, wait);
}
}

View File

@ -5,25 +5,12 @@ import { SpaCandle } from "@icon-park/vue-next";
export const getCurrentTime = () => {
let time = new Date();
let year = time.getFullYear();
let month =
time.getMonth() + 1 < 10
? "0" + (time.getMonth() + 1)
: time.getMonth() + 1;
let month = time.getMonth() + 1 < 10 ? "0" + (time.getMonth() + 1) : time.getMonth() + 1;
let day = time.getDate() < 10 ? "0" + time.getDate() : time.getDate();
let hour = time.getHours() < 10 ? "0" + time.getHours() : time.getHours();
let minute =
time.getMinutes() < 10 ? "0" + time.getMinutes() : time.getMinutes();
let second =
time.getSeconds() < 10 ? "0" + time.getSeconds() : time.getSeconds();
let weekday = [
"星期日",
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
];
let minute = time.getMinutes() < 10 ? "0" + time.getMinutes() : time.getMinutes();
let second = time.getSeconds() < 10 ? "0" + time.getSeconds() : time.getSeconds();
let weekday = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
let currentTime = {
year,
month,
@ -122,7 +109,7 @@ export const checkDays = () => {
const mon = myDate.getMonth() + 1;
const date = myDate.getDate();
const key = `${mon}.${date}`;
if (anniversaries.hasOwnProperty(key)) {
if (Object.prototype.hasOwnProperty.call(anniversaries, key)) {
console.log(`今天是${anniversaries[key]}`);
const gray = document.createElement("style");
gray.innerHTML = "html{filter: grayscale(100%)}";
@ -144,11 +131,11 @@ export const siteDateStatistics = (startDate) => {
const differenceInYears = differenceInMonths / 12;
if (differenceInYears >= 1) {
return `本站已经苟活了 ${Math.floor(differenceInYears)}${Math.floor(
differenceInMonths % 12
differenceInMonths % 12,
)} ${Math.round(differenceInDays % 30)} `;
} else if (differenceInMonths >= 1) {
return `本站已经苟活了 ${Math.floor(differenceInMonths)}${Math.round(
differenceInDays % 30
differenceInDays % 30,
)} `;
} else {
return `本站已经苟活了 ${Math.round(differenceInDays)}`;

View File

@ -1,6 +0,0 @@
/*!
* china-lantern v1.6.0
* (c) 2020-2021 fz6m
* Released under the MIT License.
*/
!function(t){"function"==typeof define&&define.amd?define(t):t()}((function(){"use strict";!function(t,e){void 0===e&&(e={});var n=e.insertAt;if(t&&"undefined"!=typeof document){var r=document.head||document.getElementsByTagName("head")[0],a=document.createElement("style");a.type="text/css","top"===n&&r.firstChild?r.insertBefore(a,r.firstChild):r.appendChild(a),a.styleSheet?a.styleSheet.cssText=t:a.appendChild(document.createTextNode(t))}}('@charset "UTF-8";.lantern__warpper{position:fixed;top:12px;left:40px;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:999}.lantern__warpper.lantern__secondary{left:calc(100% - 130px)}.lantern__warpper.lantern__secondary .lantern__box{-webkit-animation-duration:3s;animation-duration:3s;animation-delay:1s;}.lantern__box{position:relative;display:inline-block;width:90px;height:70px;background:rgba(216,0,15,.8);border-radius:50% 50%;animation:lantern-swing 3s ease-in-out infinite alternate-reverse;-webkit-transform-origin:50% -70px;-ms-transform-origin:50% -70px;transform-origin:50% -70px;-webkit-box-shadow:-5px 5px 50px 4px #fa6c00;box-shadow:-5px 5px 50px 4px #fa6c00}.lantern__box:after,.lantern__box:before{content:"";position:absolute;height:8px;width:45px;left:50%;border:1px solid #dc8f03;background:-webkit-gradient(linear,left top,right top,from(#dc8f03),color-stop(orange),color-stop(#dc8f03),color-stop(orange),to(#dc8f03));background:-o-linear-gradient(left,#dc8f03,orange,#dc8f03,orange,#dc8f03);background:linear-gradient(90deg,#dc8f03,orange,#dc8f03,orange,#dc8f03)}.lantern__box:before{top:0;border-radius:5px 5px 0 0;-webkit-transform:translate(-50%,-50%);-ms-transform:translate(-50%,-50%);transform:translate(-50%,-50%)}.lantern__box:after{bottom:0;border-radius:0 0 5px 5px;-webkit-transform:translate(-50%,50%);-ms-transform:translate(-50%,50%);transform:translate(-50%,50%)}.lantern__line{position:absolute;width:2px;height:12px;top:0;left:50%;-webkit-transform:translate(-50%,-100%);-ms-transform:translate(-50%,-100%);transform:translate(-50%,-100%);background:#dc8f03}.lantern__circle{width:80%;-webkit-box-sizing:border-box;box-sizing:border-box}.lantern__circle,.lantern__circle .lantern__ellipse{height:100%;margin:0 auto;border-radius:50%;border:2px solid #dc8f03}.lantern__circle .lantern__ellipse{width:50%}.lantern__circle .lantern__text{font-family:华文行楷,Microsoft YaHei,sans-serif;font-size:24.3px;color:#dc8f03;font-weight:700;line-height:66px;text-align:center}.lantern__tail{position:relative;width:4px;height:12px;margin:0 auto;animation:lantern-swing 4s ease-in-out infinite alternate-reverse;background:orange;border-radius:0 0 5px 5px}.lantern__tail .lantern__junction{position:absolute;top:0;left:50%;width:8px;height:8px;-webkit-transform:translate(-50%,8.4px);-ms-transform:translate(-50%,8.4px);transform:translate(-50%,8.4px);background:#e69603;border-radius:50%}.lantern__tail .lantern__rect{position:absolute;top:0;left:50%;-webkit-transform:translate(-50%,10.8px);-ms-transform:translate(-50%,10.8px);transform:translate(-50%,10.8px);width:8px;height:24px;background:orange;border-radius:5px 5px 0 5px}@-webkit-keyframes lantern-swing{0%{-webkit-transform:rotate(-8deg);transform:rotate(-8deg)}to{-webkit-transform:rotate(8deg);transform:rotate(8deg)}}@keyframes lantern-swing{0%{-webkit-transform:rotate(-8deg);transform:rotate(-8deg)}to{-webkit-transform:rotate(8deg);transform:rotate(8deg)}}@media (max-width:520px){.j-china-lantern{display: none;}');var t;(t=document.createElement("div")).className="j-china-lantern",t.innerHTML='<div class="lantern__warpper"><div class="lantern__box"><div class="lantern__line"></div><div class="lantern__circle"><div class="lantern__ellipse"><div class="lantern__text">新</div></div></div><div class="lantern__tail"><div class="lantern__rect"></div><div class="lantern__junction"></div></div></div></div><div class="lantern__warpper lantern__secondary"><div class="lantern__box"><div class="lantern__line"></div><div class="lantern__circle"><div class="lantern__ellipse"><div class="lantern__text">年</div></div></div><div class="lantern__tail"><div class="lantern__rect"></div><div class="lantern__junction"></div></div></div></div>',document.body.appendChild(t)}));

View File

@ -1,9 +1,5 @@
<template>
<div
class="box cards"
@mouseenter="closeShow = true"
@mouseleave="closeShow = false"
>
<div class="box cards" @mouseenter="closeShow = true" @mouseleave="closeShow = false">
<transition name="el-fade-in-linear">
<close-one
class="close"
@ -25,16 +21,18 @@
/>
</transition>
<div class="content">
<!-- 可在此处自定义任意内容 -->
<TimeCapsule />
<GithubProject />
</div>
</div>
</template>
<script setup>
import { ref } from "vue";
import { CloseOne, SettingTwo } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import TimeCapsule from "@/components/TimeCapsule/index.vue";
import TimeCapsule from "@/components/TimeCapsule.vue";
import GithubProject from "@/components/GithubProject.vue";
const store = mainStore();
const closeShow = ref(false);
@ -45,9 +43,9 @@ const closeShow = ref(false);
flex: 1 0 0%;
margin-left: 0.75rem;
height: 80%;
max-width: 50%;
position: relative;
animation: fade;
-webkit-animation: fade 0.5s;
animation: fade 0.5s;
&:hover {
transform: scale(1);
@ -60,7 +58,9 @@ const closeShow = ref(false);
right: 14px;
width: 28px;
height: 28px;
transition: transform 0.3s, opacity 0.3s;
transition:
transform 0.3s,
opacity 0.3s;
&:hover {
transform: scale(1.2);

View File

@ -5,7 +5,7 @@
<el-col :span="12">
<div class="left">
<Hitokoto />
<Music />
<Music v-if="playerHasId" />
</div>
</el-col>
<el-col :span="12">
@ -18,11 +18,7 @@
<span class="sm-hidden">{{ currentTime.weekday }}</span>
</div>
<div class="text">
<span>
{{ currentTime.hour }}:{{ currentTime.minute }}:{{
currentTime.second
}}</span
>
<span> {{ currentTime.hour }}:{{ currentTime.minute }}:{{ currentTime.second }}</span>
</div>
</div>
<Weather />
@ -33,12 +29,11 @@
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue";
import { getCurrentTime } from "@/utils/getTime";
import { mainStore } from "@/store";
import Music from "@/components/Music/index.vue";
import Hitokoto from "@/components/Hitokoto/index.vue";
import Weather from "@/components/Weather/index.vue";
import Music from "@/components/Music.vue";
import Hitokoto from "@/components/Hitokoto.vue";
import Weather from "@/components/Weather.vue";
const store = mainStore();
@ -46,6 +41,9 @@ const store = mainStore();
const currentTime = ref({});
const timeInterval = ref(null);
// id
const playerHasId = import.meta.env.VITE_SONG_ID;
//
const updateTimeData = () => {
currentTime.value = getCurrentTime();
@ -114,8 +112,7 @@ onBeforeUnmount(() => {
flex-direction: column;
align-items: center;
justify-content: space-between;
animation: fade;
-webkit-animation: fade 0.5s;
animation: fade 0.5s;
.time {
font-size: 1.1rem;
text-align: center;

View File

@ -7,8 +7,8 @@
<script setup>
import { mainStore } from "@/store";
import Message from "@/components/Message/index.vue";
import SocialLinks from "@/components/SocialLinks/index.vue";
import Message from "@/components/Message.vue";
import SocialLinks from "@/components/SocialLinks.vue";
const store = mainStore();
</script>

View File

@ -15,14 +15,22 @@
<script setup>
import { mainStore } from "@/store";
import Func from "@/views/Func/index.vue";
import Link from "@/components/Links/index.vue";
import Link from "@/components/Links.vue";
const store = mainStore();
//
const siteUrl = import.meta.env.VITE_SITE_URL.split(".");
const siteUrl = computed(() => {
const url = import.meta.env.VITE_SITE_URL;
if (!url) return "imsyy.top".split(".");
//
if (url.startsWith("http://") || url.startsWith("https://")) {
const urlFormat = url.replace(/^(https?:\/\/)/, "");
return urlFormat.split(".");
}
return url.split(".");
});
</script>
<style lang="scss" scoped>
.right {
// flex: 1 0 0%;
@ -36,9 +44,8 @@ const siteUrl = import.meta.env.VITE_SITE_URL.split(".");
top: 6%;
left: 0;
text-align: center;
transition: all 0.3s;
animation: fade;
-webkit-animation: fade 0.5s;
transition: transform 0.3s;
animation: fade 0.5s;
&:active {
transform: scale(0.95);
}

View File

@ -1,10 +1,5 @@
<template>
<div
class="set"
@mouseenter="closeShow = true"
@mouseleave="closeShow = false"
@click.stop
>
<div class="set" @mouseenter="closeShow = true" @mouseleave="closeShow = false" @click.stop>
<transition name="el-fade-in-linear">
<close-one
class="close"
@ -23,17 +18,8 @@
</div>
<div class="version">
<div class="num">v&nbsp;{{ config.version }}</div>
<el-tooltip
content="Github 源代码仓库"
placement="right"
:show-arrow="false"
>
<github-one
class="github"
theme="outline"
size="24"
@click="jumpTo(config.github)"
/>
<el-tooltip content="Github 源代码仓库" placement="right" :show-arrow="false">
<github-one class="github" theme="outline" size="24" @click="jumpTo(config.github)" />
</el-tooltip>
</div>
<el-card class="update">
@ -66,23 +52,25 @@
</template>
<script setup>
import { reactive, ref } from "vue";
import {
CloseOne,
SettingTwo,
GithubOne,
AddOne,
Bug,
} from "@icon-park/vue-next";
import { CloseOne, SettingTwo, GithubOne, AddOne, Bug } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import Set from "@/components/Set/index.vue";
import Set from "@/components/Set.vue";
import config from "@/../package.json";
const store = mainStore();
const store = mainStore();
const closeShow = ref(false);
//
const siteUrl = import.meta.env.VITE_SITE_URL.split(".");
const siteUrl = computed(() => {
const url = import.meta.env.VITE_SITE_URL;
if (!url) return "imsyy.top".split(".");
//
if (url.startsWith("http://") || url.startsWith("https://")) {
const urlFormat = url.replace(/^(https?:\/\/)/, "");
return urlFormat.split(".");
}
return url.split(".");
});
//
const upData = reactive({
@ -92,12 +80,7 @@ const upData = reactive({
"壁纸支持个性化设置",
"音乐播放器支持音量控制",
],
fix: [
"修复天气 API",
"时光胶囊显示错误",
"移动端动画及细节",
"图标更换为 IconPark",
],
fix: ["修复天气 API", "时光胶囊显示错误", "移动端动画及细节", "图标更换为 IconPark"],
});
//
@ -150,10 +133,10 @@ const jumpTo = (url) => {
.logo {
transform: translateY(-8%);
font-family: "Pacifico-Regular";
// line-height: 5rem;
padding-left: 22px;
width: 100%;
height: 260px;
min-height: 140px;
.bg {
font-size: 5rem;
}

View File

@ -1,135 +1,122 @@
import {
defineConfig,
loadEnv
} from 'vite';
import vue from '@vitejs/plugin-vue';
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import {
ElementPlusResolver
} from 'unplugin-vue-components/resolvers'
import {
createHtmlPlugin
} from 'vite-plugin-html';
import {
resolve
} from 'path';
import {
VitePWA
} from 'vite-plugin-pwa';
/* eslint-disable no-undef */
import { defineConfig, loadEnv } from "vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import { resolve } from "path";
import { VitePWA } from "vite-plugin-pwa";
import vue from "@vitejs/plugin-vue";
import AutoImport from "unplugin-auto-import/vite";
import Components from "unplugin-vue-components/vite";
import viteCompression from "vite-plugin-compression";
// https://vitejs.dev/config/
export default ({
mode
}) => defineConfig({
export default ({ mode }) =>
defineConfig({
plugins: [
vue(),
AutoImport({
imports: ["vue"],
resolvers: [ElementPlusResolver()],
}),
Components({
resolvers: [ElementPlusResolver()],
}),
createHtmlPlugin({
minify: true,
inject: {
data: {
logo: loadEnv(mode, process.cwd()).VITE_SITE_LOGO,
appleLogo: loadEnv(mode, process.cwd()).VITE_SITE_APPLE_LOGO,
title: loadEnv(mode, process.cwd()).VITE_SITE_NAME,
author: loadEnv(mode, process.cwd()).VITE_SITE_ANTHOR,
keywords: loadEnv(mode, process.cwd()).VITE_SITE_KEYWORDS,
description: loadEnv(mode, process.cwd()).VITE_SITE_DES,
tongji: loadEnv(mode, process.cwd()).VITE_SITE_BAIDUTONGJI,
},
},
}),
VitePWA({
registerType: 'autoUpdate',
registerType: "autoUpdate",
workbox: {
skipWaiting: true,
clientsClaim: true,
runtimeCaching: [{
runtimeCaching: [
{
urlPattern: /(.*?)\.(js|css|woff2|woff|ttf)/, // js / css 静态资源缓存
handler: 'CacheFirst',
handler: "CacheFirst",
options: {
cacheName: 'js-css-cache',
cacheName: "js-css-cache",
},
},
{
urlPattern: /(.*?)\.(png|jpe?g|svg|gif|bmp|psd|tiff|tga|eps)/, // 图片缓存
handler: 'CacheFirst',
handler: "CacheFirst",
options: {
cacheName: 'image-cache',
cacheName: "image-cache",
},
},
],
},
manifest: {
"name": loadEnv(mode, process.cwd()).VITE_SITE_NAME,
"short_name": loadEnv(mode, process.cwd()).VITE_SITE_NAME,
"description": loadEnv(mode, process.cwd()).VITE_SITE_DES,
"display": "standalone",
"start_url": "/",
"theme_color": "#424242",
"background_color": "#424242",
"icons": [{
"src": "/images/icon/48.png",
"sizes": "48x48",
"type": "image/png"
}, {
"src": "/images/icon/72.png",
"sizes": "72x72",
"type": "image/png"
}, {
"src": "/images/icon/96.png",
"sizes": "96x96",
"type": "image/png"
}, {
"src": "/images/icon/128.png",
"sizes": "128x128",
"type": "image/png"
}, {
"src": "/images/icon/144.png",
"sizes": "144x144",
"type": "image/png"
}, {
"src": "/images/icon/192.png",
"sizes": "192x192",
"type": "image/png"
}, {
"src": "/images/icon/512.png",
"sizes": "512x512",
"type": "image/png"
}]
}
name: loadEnv(mode, process.cwd()).VITE_SITE_NAME,
short_name: loadEnv(mode, process.cwd()).VITE_SITE_NAME,
description: loadEnv(mode, process.cwd()).VITE_SITE_DES,
display: "standalone",
start_url: "/",
theme_color: "#424242",
background_color: "#424242",
icons: [
{
src: "/images/icon/48.png",
sizes: "48x48",
type: "image/png",
},
{
src: "/images/icon/72.png",
sizes: "72x72",
type: "image/png",
},
{
src: "/images/icon/96.png",
sizes: "96x96",
type: "image/png",
},
{
src: "/images/icon/128.png",
sizes: "128x128",
type: "image/png",
},
{
src: "/images/icon/144.png",
sizes: "144x144",
type: "image/png",
},
{
src: "/images/icon/192.png",
sizes: "192x192",
type: "image/png",
},
{
src: "/images/icon/512.png",
sizes: "512x512",
type: "image/png",
},
],
},
}),
viteCompression(),
],
server: {
port: "3000",
hmr: true,
open: true,
},
resolve: {
alias: [{
find: '@',
alias: [
{
find: "@",
replacement: resolve(__dirname, "src"),
}, ]
},
],
},
css: {
preprocessorOptions: {
scss: {
charset: false,
additionalData: `@import "./src/style/global.scss";`
}
}
additionalData: `@import "./src/style/global.scss";`,
},
},
},
build: {
minify: 'terser',
minify: "terser",
terserOptions: {
compress: {
// 生产环境时移除 console
pure_funcs: ['console.log'],
pure_funcs: ["console.log"],
},
},
},
})
});

3815
yarn.lock

File diff suppressed because it is too large Load Diff