Compare commits

...

20 Commits
master ... dev

Author SHA1 Message Date
imsyy
52602e91a5
🐞 fix: 更新壁纸接口 #302 2024-06-05 13:52:04 +08:00
imsyy
e7a4cf7171
🐞 fix: 修复移动端样式异常 2024-04-28 11:19:36 +08:00
imsyy
777979f0e3
🐞 fix: 修正时间统计错误 2024-04-28 10:00:45 +08:00
imsyy
70e7fa534f
🦄 refactor: 移除 env 2024-04-24 10:37:29 +08:00
imsyy
462aab20af
🐞 fix: 修复一系列问题 2024-04-24 10:36:23 +08:00
imsyy
e8b69c5ab5
feat: 优化部分样式 #259 2024-03-11 10:38:21 +08:00
底层用户
4b60e7edf1
Merge pull request #252 from L1bw/dev
fix: 修复备用接口获取天气时显示失败的问题
2024-03-11 09:42:42 +08:00
Libw·I
550545fa3e
fix: 修复备用接口获取天气时显示失败的问题 2024-02-24 21:18:09 +08:00
底层用户
c068f676d1
Merge pull request #230 from first19326/dev
fix: 修复了播放器未加载完成或加载失败时,键盘控制事件仍旧生效的问题
2024-01-03 13:48:13 +08:00
first19326
c94b230d45 feat: 更新 vue-aplayer 版本 2024-01-02 23:10:37 +08:00
first19326
27780c6558 fix: 修复了播放器未加载完成或加载失败时,键盘控制事件仍旧生效的问题 2024-01-02 23:09:43 +08:00
imsyy
f016348b97
feat: 新增 GitHub 仓库列表
- 更换播放器为 @worstone/vue-aplayer #158
2024-01-02 18:11:48 +08:00
底层用户
e2290042f7
Merge pull request #229 from first19326/dev
feat: 替换 vue3-aplayer 插件,并添加播放器设置选项
2024-01-02 15:59:16 +08:00
first19326
d085ccc832 feat: 替换 vue3-aplayer 插件,并添加播放器设置选项 2024-01-02 02:58:01 +08:00
底层用户
78e0d0505a
Merge pull request #226 from nova1751/dev
fix: fix the logo be cut in small screen #157
2023-12-19 14:52:54 +08:00
nova1751
f3dd5b8dc5 fix: fix the logo be cut in small screen #157 2023-12-16 13:31:19 +08:00
imsyy
a8b1d66330 fix: 修复教书先生API接口调用失败 #224 2023-12-11 16:11:22 +08:00
imsyy
b28f92b99f feat: Add Docker #107 2023-12-06 13:41:45 +08:00
imsyy
1e0fe3e724 feat: 添加部署说明 2023-12-02 09:36:47 +08:00
imsyy
c93d7d206b feat: add Actions 2023-12-02 09:11:18 +08:00
34 changed files with 4528 additions and 3841 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

View File

@ -26,7 +26,7 @@ VITE_DESC_TEXT_OTHER = "哎呀,这都被你发现了( 再点击一次可关
## 请注意不是 Web端 (JS API),免费申请,每日上限 5000 次
## 此处提供的服务可能会超量从而无法访问,请自行申请!请自行申请!请自行申请!
## 若此处设为空则调用 教书先生 API https://api.oioweb.cn/doc/weather/GetWeather
VITE_WEATHER_KEY = "6c13af6fc30868bee488faf2cc652ab4"
VITE_WEATHER_KEY = ""
# 建站日期
## 若不需要,请设为空即可
@ -48,4 +48,4 @@ VITE_SONG_SERVER = "netease"
# 播放类型 ( song-歌曲, playlist-播放列表, album-专辑, search-搜索, artist-艺术家 )
VITE_SONG_TYPE = "playlist"
# 播放 ID ( 若无需播放器,请设为空即可 )
VITE_SONG_ID = "7452421335"
VITE_SONG_ID = "9379831714"

3
.eslintignore Normal file
View File

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

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

@ -0,0 +1,46 @@
# 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: Copy .env.example
run: |
if (-not (Test-Path .env)) {
Copy-Item .env.example .env
} else {
Write-Host ".env file already exists. Skipping the copy step."
}
# 安装项目依赖
- 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

1
.gitignore vendored
View File

@ -11,6 +11,7 @@ node_modules
dist
dist-ssr
*.local
.env
# Editor directories and files
.vscode/*

17
Dockerfile Normal file
View File

@ -0,0 +1,17 @@
# 构建应用
FROM node:18 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
RUN [ ! -e ".env" ] && cp .env.example .env || true
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"]

459
README.md
View File

@ -1,215 +1,244 @@
简体中文 | [English](./README_EN.md)
<p>
<strong><h2>無名の主页</h2></strong>
简单的小主页,原来的看够了,重新弄了一个
</p>
![無名の主页](https://s2.loli.net/2022/07/14/K5JigfvDoNewtuS.webp)
>主页的 Logo 字体已经过压缩,若用本站 Logo 以外的字母会变回默认字体,这里是 [完整字体](https://file.imsyy.top/font/Other/Pacifico-Regular.ttf),若无法下载,可将字体目录下的 `Pacifico-Regular-all.ttf` 进行替换
### Demo
>由于 CDN 缓存原因,查看最新效果可能需要 `Ctrl` + `F5` 强制刷新浏览器缓存
- [無名の主页](https://www.imsyy.top)
- [無名の主页 - Dev](https://home-imsyy.vercel.app)
- [無名の主页 - 备用线路](https://home-5iw.pages.dev)
### 功能
- [x] 载入动画
- [x] 站点简介
- [x] Hitokoto 一言
- [x] 日期及时间
- [x] 实时天气
- [x] 时光进度条
- [x] 音乐播放器
- [x] 移动端适配
### 部署
* **安装** [node.js](https://nodejs.org/zh-cn/) **环境**
> node > 16.16.0
> npm > 8.15.0
* 然后以 **管理员权限** 运行 `cmd` 终端,并 `cd` 到 项目根目录
* 在 `终端` 中输入:
```bash
# 安装 pnpm
npm install -g pnpm
# 安装依赖
pnpm install
# 预览
pnpm dev
# 构建
pnpm build
```
> 构建完成后,静态资源会在 **`dist` 目录** 中生成,可将 **`dist` 文件夹下的文件**上传至服务器,也可使用 `Vercel` 等托管平台一键导入并自动部署
### 网站链接
`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
- 前往 [高德开放平台控制台](https://console.amap.com/dev/index) 创建一个 `Web 服务` 类型的 `Key`,并将 `Key` 填入 `.env` 中的 `VITE_WEATHER_KEY`
也可自行更换其他方式
### 音乐
>本项目采用了基于 `MetingJS``Aplayer` 音乐播放器,可实现快速自定义歌单
>*仅支持 **中国大陆地区**
请在 `.env` 文件中更改歌曲相关参数即可实现自定义歌单列表
```bash
# 歌曲 API 地址
VITE_SONG_API = "https://api-meting.imsyy.top"
# 歌曲服务器 ( netease-网易云, tencent-qq音乐 )
VITE_SONG_SERVER = "netease"
# 播放类型 ( song-歌曲, playlist-播放列表, album-专辑, search-搜索, artist-艺术家 )
VITE_SONG_TYPE = "playlist"
# 播放 ID
VITE_SONG_ID = "7452421335"
```
### 字体
现采用 `HarmonyOS Sans` 开源字体,采用字体拆分,提升加载速度
>由于本站 `CDN` 已开启防盗链,**非本站域名不可访问**,请将字体引入链接更改为下方内容,否则 **自定义字体将失效**
>
>`https://s1.hdslb.com/bfs/static/jinkela/long/font/regular.css`
<details>
<summary>旧版方式</summary>
>由于本项目引入了中文字体,需要压缩中文字体以提高网页加载速度( 也可以取消使用中文字体
#### 中文字体去除繁体
- 安装 `Python 3.7``pip`
- 运行 `pip install fonttools`
- 下载 [sc_unicode.txt](https://gist.githubusercontent.com/imaegoo/d64e5088b723c2e02c40985f55ff12db/raw/5ebd2ce49418c73459a9dfe050483409306a6c1d/sc_unicode.txt)
- 运行 `pyftsubset 字体名称.ttf --unicodes-file=sc_unicode.txt`
#### 字体进一步压缩
- 编译安装 `Google woff2`
```bash
sudo apt-get install -y git g++ make
git clone --recursive https://github.com/google/woff2.git
cd woff2
make clean all
```
- 再压缩字体
```
./woff2_compress ./字体名称.ttf
```
- 最终可对原字体进行缓加载,**先行加载压缩后的字体**
>详细信息可前往 [虹墨空间站](https://www.imaegoo.com/2020/chinese-font-compress/) 查看原文
</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/)
* [Vite](https://vitejs.cn/vite3-cn/)
* [Pinia](https://pinia.vuejs.org/zh/)
* [IconPark](https://iconpark.oceanengine.com/official)
* [xicons](https://xicons.org/)
* [Aplayer](https://aplayer.js.org/)
### API
* [小歪 API](https://api.aixiaowai.cn)
* [搏天 API](https://api.btstu.cn/doc/sjbz.php)
* [高德开放平台](https://lbs.amap.com/)
* [Hitokoto 一言](https://hitokoto.cn/)
* [MetingAPI By 武恩赐](https://api.wuenci.com/meting/api/)
## 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>
简体中文 | [English](./README_EN.md)
<p>
<strong><h2>無名の主页</h2></strong>
简单的小主页,原来的看够了,重新弄了一个
</p>
![無名の主页](/screenshots/main.jpg)
> 主页的 Logo 字体已经过压缩,若用本站 Logo 以外的字母会变回默认字体,这里是 [完整字体](https://file.imsyy.top/font/Other/Pacifico-Regular.ttf),若无法下载,可将字体目录下的 `Pacifico-Regular-all.ttf` 进行替换
### 👀 Demo
> 由于 CDN 缓存原因,查看最新效果可能需要 `Ctrl` + `F5` 强制刷新浏览器缓存
- [無名の主页](https://www.imsyy.top)
- [無名の主页 - Dev](https://home-imsyy.vercel.app)
- [無名の主页 - 备用线路](https://home-5iw.pages.dev)
### 🎉 功能
- [x] 载入动画
- [x] 站点简介
- [x] Hitokoto 一言
- [x] 日期及时间
- [x] 实时天气
- [x] 时光进度条
- [x] 音乐播放器
- [x] 移动端适配
### ⚙️ 自动部署
如果遇到构建环境或者打包过程出现错误,则可以采用 `Github Actions` 来进行自动构建
- 在成功 `fork` 仓库后,前往 `Actions` 页面,若您是首次开启,则会出现下面的提示,点击开启
![步骤1](/screenshots/step1.jpg)
- 然后在仓库中进行任意修改后均会触发工作流的运行,在工作流完成后,会在下方生成一个可供下载的压缩包,这就是构建出的静态文件,可自行上传至服务器
![步骤2](/screenshots/step2.jpg)
### ⚙️ 手动部署
- **安装** [node.js](https://nodejs.org/zh-cn/) **环境**
> node > 16.16.0
> npm > 8.15.0
- 然后以 **管理员权限** 运行 `cmd` 终端,并 `cd` 到 项目根目录
- 在 `终端` 中输入:
```bash
# 安装 pnpm
npm install -g pnpm
# 安装依赖
pnpm install
# 预览
pnpm dev
# 构建
pnpm build
```
> 构建完成后,静态资源会在 **`dist` 目录** 中生成,可将 **`dist` 文件夹下的文件**上传至服务器,也可使用 `Vercel` 等托管平台一键导入并自动部署
### ⚙️ Docker 部署
> 安装及配置 Docker 将不在此处说明,请自行解决
```bash
# 构建
docker build -t home .
# 运行
docker run -p 12445:12445 -d home
```
### ⚙️ Vercel 部署
> 其他部署平台大致相同,在此不做说明
1. 点击本仓库右上角的 `Fork`,复制本仓库到你的 `GitHub` 账号
2. 复制 `/.env.example` 文件并重命名为 `/.env` 重要
3. 按需修改 `/.env` 文件中的配置
4. 点击 `Deploy`,即可成功部署
### 网站链接
`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
- 前往 [高德开放平台控制台](https://console.amap.com/dev/index) 创建一个 `Web 服务` 类型的 `Key`,并将 `Key` 填入 `.env` 中的 `VITE_WEATHER_KEY`
也可自行更换其他方式
### 音乐
> 本项目采用了基于 `MetingJS``Aplayer` 音乐播放器,可实现快速自定义歌单
> \*仅支持 **中国大陆地区**
请在 `.env` 文件中更改歌曲相关参数即可实现自定义歌单列表
```bash
# 歌曲 API 地址
VITE_SONG_API = "https://api-meting.imsyy.top"
# 歌曲服务器 ( netease-网易云, tencent-qq音乐 )
VITE_SONG_SERVER = "netease"
# 播放类型 ( song-歌曲, playlist-播放列表, album-专辑, search-搜索, artist-艺术家 )
VITE_SONG_TYPE = "playlist"
# 播放 ID
VITE_SONG_ID = "7452421335"
```
### 字体
现采用 `HarmonyOS Sans` 开源字体,采用字体拆分,提升加载速度
> 由于本站 `CDN` 已开启防盗链,**非本站域名不可访问**,请将字体引入链接更改为下方内容,否则 **自定义字体将失效**
>
> `https://s1.hdslb.com/bfs/static/jinkela/long/font/regular.css`
<details>
<summary>旧版方式</summary>
> 由于本项目引入了中文字体,需要压缩中文字体以提高网页加载速度( 也可以取消使用中文字体
#### 中文字体去除繁体
- 安装 `Python 3.7``pip`
- 运行 `pip install fonttools`
- 下载 [sc_unicode.txt](https://gist.githubusercontent.com/imaegoo/d64e5088b723c2e02c40985f55ff12db/raw/5ebd2ce49418c73459a9dfe050483409306a6c1d/sc_unicode.txt)
- 运行 `pyftsubset 字体名称.ttf --unicodes-file=sc_unicode.txt`
#### 字体进一步压缩
- 编译安装 `Google woff2`
```bash
sudo apt-get install -y git g++ make
git clone --recursive https://github.com/google/woff2.git
cd woff2
make clean all
```
- 再压缩字体
```
./woff2_compress ./字体名称.ttf
```
- 最终可对原字体进行缓加载,**先行加载压缩后的字体**
> 详细信息可前往 [虹墨空间站](https://www.imaegoo.com/2020/chinese-font-compress/) 查看原文
</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/)
- [Vite](https://vitejs.cn/vite3-cn/)
- [Pinia](https://pinia.vuejs.org/zh/)
- [IconPark](https://iconpark.oceanengine.com/official)
- [xicons](https://xicons.org/)
- [Aplayer](https://aplayer.js.org/)
### API
- [韩小韩 WebAPI 接口](https://api.vvhan.com/)
- [搏天 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

@ -1,144 +1,144 @@
English | [Chinese](./README.md)
<p>
<strong><h2>無名の主页</h2></strong>
Simple little homepage, had enough of the original one and made a new one
</p>
![無名の主页](https://s2.loli.net/2022/07/14/K5JigfvDoNewtuS.webp)
>The logo font on the home page has been compressed, so if you use a font other than this logo, it will change back to the default font, Here is the [full font](https://file.4everland.app/font/Other/Pacifico-Regular.ttf)
### Demo
>Due to CDN caching, you may need `Ctrl` + `F5` to force a browser cache refresh to see the latest results
- [無名の主页](https://www.imsyy.top)
- [無名の主页 - Dev](https://home-imsyy.vercel.app)
- [無名の主页 - Standby](https://home-5iw.pages.dev)
### Functions
- [x] Loading animation
- [x] Site description
- [x] Hitokoto
- [x] Date and time
- [x] Live weather
- [x] Time progress bar
- [x] Music player
- [x] Mobile adaptation
* [ ] Player cancels using Aplayer
### Deployment
* **Installation** [node.js](https://nodejs.org/zh-cn/) **Environment**
> node > 16.16.0
> npm > 8.15.0
* Then run the `cmd` terminal with **administrator privileges** and `cd` to the project root directory
* In the `terminal` type:
```bash
# Install pnpm
npm install -g pnpm
# Install the dependencies
pnpm install
# Preview
pnpm dev
# Build
pnpm build
```
> Once the build is complete, the files in the `dist` folder can be uploaded to the server or imported and automatically deployed with one click using a hosting platform such as `Vercel`.
### Weather
Weather and area access requires `高德开放平台` related API
- Go to [高德开放平台控制台](https://console.amap.com/dev/index) to create a `Key` of type `Web Service` and fill the `Key` into `VITE_WEATHER_KEY` in `.env`
It can also be replaced by other methods
### Music
>This project uses the `Aplayer` music player based on `MetingJS` for quick song list customization
>*Only supported in **Mainland China**
Please change the song related parameters in the `.env` file to customize the song list
```bash
# Songs API address
VITE_SONG_API = "https://api-meting.imsyy.top"
# Song server ( netease-netease, tencent-qq music )
VITE_SONG_SERVER = "netease"
# Playback type ( song-song, playlist-playlist, album-album, search-search, artist-artist )
VITE_SONG_TYPE = "playlist"
# Playback ID
VITE_SONG_ID = "7452421335"
```
### Fonts
Now using `HarmonyOS Sans` open source font, using font splitting to improve loading speed
>Because this site's `CDN` has opened anti-leech, **non-site domain name is not accessible**, please change the font import link to the following content, otherwise **custom fonts will be invalid**
>
>`https://cdn.jsdelivr.net/gh/imsyy/file/font/HarmonyOS_Sans/regular.min.css`
<details>
<summary>old way</summary>
>As Chinese fonts are introduced in this project, Chinese fonts need to be compressed to improve the loading speed of the page (you can also cancel the use of Chinese fonts)
#### Chinese font removal traditional
- Install `Python 3.7` and `pip`
- Run `pip install fonttools`
- Download [sc_unicode.txt](https://gist.githubusercontent.com/imaegoo/d64e5088b723c2e02c40985f55ff12db/raw/5ebd2ce49418c73459a9dfe050483409306a6c1d/sc_unicode.txt)
- Run `pyftsubset font-name.ttf --unicodes-file=sc_unicode.txt`
#### fonts further compressed
- Compile and install ``Google woff2``
```bash
sudo apt-get install -y git g++ make
git clone --recursive https://github.com/google/woff2.git
cd woff2
make clean all
```
- Compress the font again
```
. /woff2_compress . /font_name.ttf
```
- Eventually the original font can be slow loaded, **load the compressed font first**
>For more information, please go to [虹墨空间站](https://www.imaegoo.com/2020/chinese-font-compress/) to view the original article
</details>
### Technology Stack
* [Vue](https://cn.vuejs.org/)
* [Vite](https://vitejs.cn/vite3-cn/)
* [Pinia](https://pinia.vuejs.org/zh/)
* [IconPark](https://iconpark.oceanengine.com/official)
* [xicons](https://xicons.org/)
* [Aplayer](https://aplayer.js.org/)
### API
* [MetingAPI By 武恩赐](https://api.wuenci.com/meting/api/)
* [搏天 API](https://api.btstu.cn/doc/sjbz.php)
* [高德开放平台](https://lbs.amap.com/)
* [Hitokoto 一言](https://hitokoto.cn/)
<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>
English | [Chinese](./README.md)
<p>
<strong><h2>無名の主页</h2></strong>
Simple little homepage, had enough of the original one and made a new one
</p>
![無名の主页](https://s2.loli.net/2022/07/14/K5JigfvDoNewtuS.webp)
>The logo font on the home page has been compressed, so if you use a font other than this logo, it will change back to the default font, Here is the [full font](https://file.4everland.app/font/Other/Pacifico-Regular.ttf)
### Demo
>Due to CDN caching, you may need `Ctrl` + `F5` to force a browser cache refresh to see the latest results
- [無名の主页](https://www.imsyy.top)
- [無名の主页 - Dev](https://home-imsyy.vercel.app)
- [無名の主页 - Standby](https://home-5iw.pages.dev)
### Functions
- [x] Loading animation
- [x] Site description
- [x] Hitokoto
- [x] Date and time
- [x] Live weather
- [x] Time progress bar
- [x] Music player
- [x] Mobile adaptation
* [ ] Player cancels using Aplayer
### Deployment
* **Installation** [node.js](https://nodejs.org/zh-cn/) **Environment**
> node > 16.16.0
> npm > 8.15.0
* Then run the `cmd` terminal with **administrator privileges** and `cd` to the project root directory
* In the `terminal` type:
```bash
# Install pnpm
npm install -g pnpm
# Install the dependencies
pnpm install
# Preview
pnpm dev
# Build
pnpm build
```
> Once the build is complete, the files in the `dist` folder can be uploaded to the server or imported and automatically deployed with one click using a hosting platform such as `Vercel`.
### Weather
Weather and area access requires `高德开放平台` related API
- Go to [高德开放平台控制台](https://console.amap.com/dev/index) to create a `Key` of type `Web Service` and fill the `Key` into `VITE_WEATHER_KEY` in `.env`
It can also be replaced by other methods
### Music
>This project uses the `Aplayer` music player based on `MetingJS` for quick song list customization
>*Only supported in **Mainland China**
Please change the song related parameters in the `.env` file to customize the song list
```bash
# Songs API address
VITE_SONG_API = "https://api-meting.imsyy.top"
# Song server ( netease-netease, tencent-qq music )
VITE_SONG_SERVER = "netease"
# Playback type ( song-song, playlist-playlist, album-album, search-search, artist-artist )
VITE_SONG_TYPE = "playlist"
# Playback ID
VITE_SONG_ID = "7452421335"
```
### Fonts
Now using `HarmonyOS Sans` open source font, using font splitting to improve loading speed
>Because this site's `CDN` has opened anti-leech, **non-site domain name is not accessible**, please change the font import link to the following content, otherwise **custom fonts will be invalid**
>
>`https://cdn.jsdelivr.net/gh/imsyy/file/font/HarmonyOS_Sans/regular.min.css`
<details>
<summary>old way</summary>
>As Chinese fonts are introduced in this project, Chinese fonts need to be compressed to improve the loading speed of the page (you can also cancel the use of Chinese fonts)
#### Chinese font removal traditional
- Install `Python 3.7` and `pip`
- Run `pip install fonttools`
- Download [sc_unicode.txt](https://gist.githubusercontent.com/imaegoo/d64e5088b723c2e02c40985f55ff12db/raw/5ebd2ce49418c73459a9dfe050483409306a6c1d/sc_unicode.txt)
- Run `pyftsubset font-name.ttf --unicodes-file=sc_unicode.txt`
#### fonts further compressed
- Compile and install ``Google woff2``
```bash
sudo apt-get install -y git g++ make
git clone --recursive https://github.com/google/woff2.git
cd woff2
make clean all
```
- Compress the font again
```
. /woff2_compress . /font_name.ttf
```
- Eventually the original font can be slow loaded, **load the compressed font first**
>For more information, please go to [虹墨空间站](https://www.imaegoo.com/2020/chinese-font-compress/) to view the original article
</details>
### Technology Stack
* [Vue](https://cn.vuejs.org/)
* [Vite](https://vitejs.cn/vite3-cn/)
* [Pinia](https://pinia.vuejs.org/zh/)
* [IconPark](https://iconpark.oceanengine.com/official)
* [xicons](https://xicons.org/)
* [Aplayer](https://aplayer.js.org/)
### API
* [韩小韩 WebAPI 接口](https://api.vvhan.com/)
* [搏天 API](https://api.btstu.cn/doc/sjbz.php)
* [高德开放平台](https://lbs.amap.com/)
* [Hitokoto 一言](https://hitokoto.cn/)
<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>

57
auto-imports.d.ts vendored Normal file
View File

@ -0,0 +1,57 @@
// Generated by 'unplugin-auto-import'
export {}
declare global {
const EffectScope: typeof import('vue')['EffectScope']
const ElMessage: typeof import('element-plus/es')['ElMessage']
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isProxy: typeof import('vue')['isProxy']
const isReactive: typeof import('vue')['isReactive']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const resolveDirective: typeof import('vue')['resolveDirective']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useSlots: typeof import('vue')['useSlots']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
const watchPostEffect: typeof import('vue')['watchPostEffect']
const watchSyncEffect: typeof import('vue')['watchSyncEffect']
}

35
components.d.ts vendored Normal file
View File

@ -0,0 +1,35 @@
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/core/pull/3399
import '@vue/runtime-core'
export {}
declare module '@vue/runtime-core' {
export interface GlobalComponents {
Background: typeof import('./src/components/Background.vue')['default']
ElCard: typeof import('element-plus/es')['ElCard']
ElCol: typeof import('element-plus/es')['ElCol']
ElCollapse: typeof import('element-plus/es')['ElCollapse']
ElCollapseItem: typeof import('element-plus/es')['ElCollapseItem']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElSlider: typeof import('element-plus/es')['ElSlider']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTooltip: typeof import('element-plus/es')['ElTooltip']
Footer: typeof import('./src/components/Footer.vue')['default']
Hitokoto: typeof import('./src/components/Hitokoto.vue')['default']
Links: typeof import('./src/components/Links.vue')['default']
Loading: typeof import('./src/components/Loading.vue')['default']
Message: typeof import('./src/components/Message.vue')['default']
MoreContent: typeof import('./src/components/MoreContent.vue')['default']
Music: typeof import('./src/components/Music.vue')['default']
Player: typeof import('./src/components/Player.vue')['default']
Set: typeof import('./src/components/Set.vue')['default']
SocialLinks: typeof import('./src/components/SocialLinks.vue')['default']
TimeCapsule: typeof import('./src/components/TimeCapsule.vue')['default']
Weather: typeof import('./src/components/Weather.vue')['default']
}
}

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,39 +4,41 @@
"github": "https://github.com/imsyy/home",
"home": "https://imsyy.top",
"private": true,
"version": "4.1.3",
"version": "4.1.4",
"type": "module",
"scripts": {
"dev": "vite --host",
"build": "vite build",
"preview": "vite preview",
"format": "prettier --write src/"
"format": "prettier --write src/",
"lint": "eslint . --ext .js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts,.vue --fix"
},
"dependencies": {
"@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",
"vue": "^3.3.4",
"vue3-aplayer": "^1.7.3"
"axios": "^1.6.8",
"dayjs": "^1.11.10",
"element-plus": "^2.7.1",
"fetch-jsonp": "^1.3.0",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"swiper": "^11.1.1",
"vue": "^3.4.24"
},
"devDependencies": {
"@icon-park/vue-next": "^1.4.2",
"@vicons/fa": "^0.12.0",
"@vicons/utils": "^0.1.4",
"@vitejs/plugin-vue": "^4.2.3",
"eslint": "^8.48.0",
"eslint-plugin-vue": "^9.17.0",
"prettier": "^3.0.2",
"sass": "^1.55.0",
"terser": "^5.16.1",
"unplugin-auto-import": "^0.11.2",
"unplugin-vue-components": "^0.22.8",
"vite": "^4.4.5",
"@vitejs/plugin-vue": "^4.6.2",
"eslint": "^8.57.0",
"eslint-plugin-vue": "^9.25.0",
"prettier": "^3.2.5",
"sass": "^1.75.0",
"terser": "^5.30.4",
"unplugin-auto-import": "^0.11.5",
"unplugin-vue-components": "^0.22.12",
"vite": "^4.5.3",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-pwa": "^0.14.1"
"vite-plugin-pwa": "^0.14.7"
}
}

File diff suppressed because it is too large Load Diff

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

@ -13,6 +13,7 @@ export const getPlayerList = async (server, type, id) => {
const data = await res.json();
if (data[0].url.startsWith("@")) {
// 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 = (
@ -21,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,
}));
}

View File

@ -2,9 +2,9 @@
<div :class="store.backgroundShow ? 'cover show' : 'cover'">
<img
v-show="store.imgLoadStatus"
:src="bgUrl"
class="bg"
alt="cover"
:src="bgUrl"
@load="imgLoadComplete"
@error.once="imgLoadError"
@animationend="imgAnimationEnd"
@ -43,9 +43,9 @@ const changeBg = (type) => {
} 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";
bgUrl.value = "https://api.vvhan.com/api/wallpaper/views";
} else if (type == 3) {
bgUrl.value = "https://api.aixiaowai.cn/api/api.php";
bgUrl.value = "https://api.vvhan.com/api/wallpaper/acg";
}
};
@ -79,6 +79,14 @@ const imgLoadError = () => {
bgUrl.value = `/images/background${bgRandom}.jpg`;
};
//
watch(
() => store.coverType,
(value) => {
changeBg(value);
},
);
onMounted(() => {
//
changeBg(store.coverType);
@ -115,7 +123,7 @@ onBeforeUnmount(() => {
transition:
filter 0.3s,
transform 0.3s;
animation: fade-blur-in 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
animation: fade-blur-in 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
animation-delay: 0.45s;
}
.gray {

View File

@ -45,23 +45,22 @@ const hitokotoData = reactive({
});
//
const getHitokotoData = () => {
getHitokoto()
.then((res) => {
hitokotoData.text = res.hitokoto;
hitokotoData.from = res.from;
})
.catch(() => {
ElMessage({
message: "一言获取失败",
icon: h(Error, {
theme: "filled",
fill: "#efefef",
}),
});
hitokotoData.text = "这里应该显示一句话";
hitokotoData.from = "無名";
const getHitokotoData = async () => {
try {
const result = await getHitokoto();
hitokotoData.text = result.hitokoto;
hitokotoData.from = result.from;
} catch (error) {
ElMessage({
message: "一言获取失败",
icon: h(Error, {
theme: "filled",
fill: "#efefef",
}),
});
hitokotoData.text = "这里应该显示一句话";
hitokotoData.from = "無名";
}
};
//

View File

@ -46,10 +46,8 @@ import { Icon } from "@vicons/utils";
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 { Pagination, Mousewheel } from "swiper/modules";
import siteLinks from "@/assets/siteLinks.json";
import "swiper/scss";
import "swiper/scss/pagination";
const store = mainStore();
@ -111,14 +109,22 @@ onMounted(() => {
height: 100%;
}
.swiper-pagination {
position: static;
margin-top: 4px;
margin-top: 12px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
:deep(.swiper-pagination-bullet) {
background-color: #fff;
width: 18px;
width: 20px;
height: 4px;
margin: 0 4px;
border-radius: 4px;
opacity: 0.2;
transition: opacity 0.3s;
&.swiper-pagination-bullet-active {
opacity: 1;
}
&:hover {
opacity: 1;
}

View File

@ -15,10 +15,12 @@
<Icon size="16">
<QuoteLeft />
</Icon>
<div class="text">
<p>{{ descriptionText.hello }}</p>
<p>{{ descriptionText.text }}</p>
</div>
<Transition name="fade" mode="out-in">
<div :key="descriptionText.hello + descriptionText.text" class="text">
<p>{{ descriptionText.hello }}</p>
<p>{{ descriptionText.text }}</p>
</div>
</Transition>
<Icon size="16">
<QuoteRight />
</Icon>
@ -146,6 +148,7 @@ watch(
margin: 0.75rem 1rem;
line-height: 2rem;
margin-right: auto;
transition: opacity 0.2s;
p {
&:nth-of-type(1) {

View File

@ -0,0 +1,14 @@
<template>
<div class="more-content">您可在此编写任意内容</div>
</template>
<style lang="scss" scoped>
.more-content {
display: flex;
align-items: center;
justify-content: center;
margin-top: 20px;
width: 100%;
height: 100%;
}
</style>

View File

@ -12,10 +12,12 @@
</div>
<div class="control">
<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" />
</div>
<Transition name="fade" mode="out-in">
<div :key="store.playerState" 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" />
</div>
</Transition>
<go-end theme="filled" size="30" fill="#efefef" @click="changeMusicIndex(1)" />
</div>
<div class="menu">
@ -43,7 +45,7 @@
</div>
<!-- 音乐列表弹窗 -->
<Transition name="fade" mode="out-in">
<div class="music-list" v-show="musicListShow" @click="musicListShow = false">
<div class="music-list" v-show="musicListShow" @click="closeMusicList()">
<Transition name="zoom">
<div class="list" v-show="musicListShow" @click.stop>
<close-one
@ -51,15 +53,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="false"
ref="playerRef"
/>
</div>
</Transition>
@ -98,6 +99,13 @@ const playerData = reactive({
//
const openMusicList = () => {
musicListShow.value = true;
playerRef.value.toggleList();
};
//
const closeMusicList = () => {
musicListShow.value = false;
playerRef.value.toggleList();
};
//
@ -113,6 +121,9 @@ const changeMusicIndex = (type) => {
onMounted(() => {
//
window.addEventListener("keydown", (e) => {
if (!store.musicIsOk) {
return;
}
if (e.code == "Space") {
changePlayState();
}
@ -168,6 +179,7 @@ watch(
justify-content: space-evenly;
width: 100%;
.state {
transition: opacity 0.1s;
.i-icon {
width: 50px;
height: 50px;

View File

@ -1,21 +1,21 @@
<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>
@ -24,7 +24,7 @@
import { MusicOne, PlayWrong } from "@icon-park/vue-next";
import { getPlayerList } from "@/api";
import { mainStore } from "@/store";
import aplayer from "vue3-aplayer";
import APlayer from "@worstone/vue-aplayer";
const store = mainStore();
@ -36,32 +36,14 @@ const playList = ref([]);
//
const playIndex = ref(0);
const playListCount = ref(0);
const skipTimeout = ref(null);
//
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,
@ -92,39 +74,28 @@ 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) => {
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,
});
});
console.log(
"音乐加载完成",
playList.value,
playIndex.value,
playListCount.value,
props.volume,
);
playList.value = res;
console.log("音乐加载完成");
console.log(playList.value);
console.log(playIndex.value, playList.value.length, props.volume);
});
} catch (err) {
console.error(err);
@ -144,10 +115,11 @@ 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,
@ -160,19 +132,23 @@ 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) {
const currentLrcElement = playerRef.querySelector(".aplayer-lrc-current");
const previousLrcElement = currentLrcElement?.previousElementSibling;
const lrcContent =
currentLrcElement?.innerHTML || previousLrcElement?.innerHTML || "这句没有歌词";
store.setPlayerLrc(lrcContent);
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);
};
//
@ -182,41 +158,27 @@ 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 后进行下一首";
//
skipTimeout.value = setTimeout(() => {
changeSong(1);
if (!player.value.audio.paused) {
onPlay();
}
}, 2000);
} else {
notice = "播放歌曲出现错误";
}
@ -229,24 +191,22 @@ const loadMusicError = () => {
duration: 2000,
}),
});
console.error("播放歌曲: " + player.value.currentMusic.title + " 出现错误");
console.error(
"播放歌曲: " + player.value.aplayer.audio[player.value.aplayer.index].name + " 出现错误",
);
};
//
defineExpose({ playToggle, changeVolume, changeSong });
onBeforeUnmount(() => {
clearTimeout(skipTimeout.value);
});
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;
}
@ -268,8 +228,8 @@ onBeforeUnmount(() => {
}
.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%,
@ -301,6 +261,8 @@ onBeforeUnmount(() => {
}
:deep(.aplayer-list) {
margin-top: 6px;
height: v-bind(listHeight);
background-color: transparent;
ol {
&::-webkit-scrollbar-track {
background-color: transparent;

View File

@ -4,10 +4,10 @@
<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 value="0" size="large" border>默认壁纸</el-radio>
<el-radio value="1" size="large" border>每日一图</el-radio>
<el-radio value="2" size="large" border>随机风景</el-radio>
<el-radio value="3" size="large" border>随机动漫</el-radio>
</el-radio-group>
</div>
</el-collapse-item>
@ -50,7 +50,34 @@
</div>
</el-collapse-item>
<el-collapse-item title="播放器配置" name="3">
<div>设置内容待增加</div>
<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 value="all" border>列表</el-radio>
<el-radio value="one" border>单曲</el-radio>
<el-radio value="none" border>不循环</el-radio>
</el-radio-group>
</div>
</el-collapse-item>
<el-collapse-item title="其他设置" name="4">
<div>设置内容待增加</div>
@ -65,7 +92,16 @@ import { mainStore } from "@/store";
import { storeToRefs } from "pinia";
const store = mainStore();
const { coverType, siteStartShow, musicClick, playerLrcShow, footerBlur } = storeToRefs(store);
const {
coverType,
siteStartShow,
musicClick,
playerLrcShow,
footerBlur,
playerAutoplay,
playerOrder,
playerLoop,
} = storeToRefs(store);
//
const activeName = ref("1");
@ -73,7 +109,7 @@ const activeName = ref("1");
//
const radioChange = () => {
ElMessage({
message: "壁纸设置成功,刷新后生效",
message: "壁纸更换成功",
icon: h(SuccessPicture, {
theme: "filled",
fill: "#efefef",
@ -107,45 +143,54 @@ const radioChange = () => {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
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;
margin: 2px 10px 2px 0;
border-radius: 5px;
.el-radio__label {
color: #fff;
&: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: #ffffff06 !important;
border: 2px solid #eeeeee !important;
background-color: #ffffff30 !important;
border-color: #fff !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;
}
& + .el-radio__label {
color: #fff !important;
}
}
}

View File

@ -4,23 +4,24 @@
<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" />
<div v-if="startDate?.length >= 4 && store.siteStartShow">
<span class="text" v-html="startDateText" />
<!-- <el-progress
:show-text="false"
:indeterminate="true"
:stroke-width="6"
:percentage="80"
:duration="2"
/> -->
<div v-if="timeData" class="all-capsule">
<div v-for="(item, tag, index) in timeData" :key="index" class="capsule-item">
<div class="item-title">
<span class="percentage">
{{ item.name }}已度过
<strong>{{ item.passed }}</strong>
{{ tag === "day" ? "小时" : "天" }}
</span>
<span class="remaining">
剩余&nbsp;{{ item.remaining }}&nbsp;{{ tag === "day" ? "小时" : "天" }}
</span>
</div>
<el-progress :text-inside="true" :stroke-width="20" :percentage="item.percentage" />
</div>
<!-- 建站日期 -->
<div v-if="store.siteStartShow" class="capsule-item start">
<div class="item-title">{{ startDateText }}</div>
</div>
</div>
</div>
</template>
@ -65,10 +66,33 @@ onBeforeUnmount(() => {
margin-right: 6px;
}
}
.text {
display: block;
margin: 1rem 0rem 0.5rem 0rem;
font-size: 0.95rem;
.all-capsule {
.capsule-item {
margin-bottom: 1rem;
.item-title {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin: 1rem 0rem 0.5rem 0rem;
font-size: 0.95rem;
.remaining {
opacity: 0.6;
font-size: 0.85rem;
font-style: oblique;
}
}
&:last-child {
margin-bottom: 0;
}
&.start {
.item-title {
justify-content: center;
opacity: 0.8;
font-size: 0.85rem;
}
}
}
}
}
</style>

View File

@ -38,55 +38,60 @@ const weatherData = reactive({
},
});
//
const getTemperature = (min, max) => {
try {
//
const average = (Number(min) + Number(max)) / 2;
return Math.round(average);
} catch (error) {
console.error("计算温度出现错误:", error);
return "NaN";
}
};
//
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 getWeatherData = async () => {
try {
//
if (!mainKey) {
console.log("未配置,使用备用天气接口");
const result = await getOtherWeather();
console.log(result);
const data = result.result;
weatherData.adCode = {
city: data.city.City || "未知地区",
// adcode: data.city.cityId,
};
weatherData.weather = {
weather: data.condition.day_weather,
temperature: getTemperature(data.condition.min_degree, data.condition.max_degree),
winddirection: data.condition.day_wind_direction,
windpower: data.condition.day_wind_power,
};
} 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("天气信息获取失败");
}
};

View File

@ -4,6 +4,8 @@ import App from "@/App.vue";
// 引入 pinia
import { createPinia } from "pinia";
import piniaPluginPersistedstate from "pinia-plugin-persistedstate";
// swiper
import "swiper/css";
const app = createApp(App);
const pinia = createPinia();

View File

@ -22,6 +22,9 @@ export const mainStore = defineStore("main", {
playerLrc: "歌词加载中", // 当前播放歌词
playerLrcShow: true, // 是否显示底栏歌词
footerBlur: true, // 底栏模糊
playerAutoplay: false, // 是否自动播放
playerLoop: "all", // 循环播放 "all", "one", "none"
playerOrder: "list", // 循环顺序 "list", "random"
};
},
getters: {
@ -82,6 +85,9 @@ export const mainStore = defineStore("main", {
"musicClick",
"playerLrcShow",
"footerBlur",
"playerAutoplay",
"playerLoop",
"playerOrder",
],
},
});

View File

@ -13,6 +13,8 @@ html,
body {
width: 100%;
height: 100%;
// width: 100dvh;
// height: 100dvh;
margin: 0;
padding: 0;
background-color: #333;
@ -53,8 +55,8 @@ p {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
width: 100%;
height: 100%;
z-index: 0;
}
@ -168,7 +170,7 @@ p {
}
to {
filter: blur(0) brightness(1);
transform: scale(1.2);
transform: scale(1);
}
}

View File

@ -1,5 +1,6 @@
import { h } from "vue";
import { SpaCandle } from "@icon-park/vue-next";
import dayjs from "dayjs";
// 时钟
export const getCurrentTime = () => {
@ -25,47 +26,48 @@ export const getCurrentTime = () => {
// 时光胶囊
export const getTimeCapsule = () => {
// 日进度
const todayStartDate = new Date(new Date().toLocaleDateString()).getTime();
const todayPassHours = (new Date() - todayStartDate) / 1000 / 60 / 60;
const todayPassHoursPercent = (todayPassHours / 24) * 100;
// 周进度
const weeks = [7, 1, 2, 3, 4, 5, 6];
const weekDay = weeks[new Date().getDay()];
const weekDayPassPercent = (weekDay / 7) * 100;
// 月进度
const year = new Date().getFullYear();
const date = new Date().getDate();
const month = new Date().getMonth() + 1;
const monthAll = new Date(year, month, 0).getDate();
const monthPassPercent = (date / monthAll) * 100;
// 年进度
const yearStartDate = new Date(year, 0, 1).getTime();
const yearEndDate = new Date(year + 1, 0, 1).getTime();
const yearPassHours = (new Date() - yearStartDate) / 1000 / 60 / 60;
const yearTotalHours = (yearEndDate - yearStartDate) / 1000 / 60 / 60;
const yearPassPercent = (yearPassHours / yearTotalHours) * 100;
const now = dayjs();
const dayText = {
day: "今日",
week: "本周",
month: "本月",
year: "本年",
};
/**
* 计算时间差的函数
* @param {String} unit 时间单位可以是 'day', 'week', 'month', 'year'
*/
const getDifference = (unit) => {
// 获取当前时间单位的开始时间
const start = now.startOf(unit);
// 获取当前时间单位的结束时间
const end = now.endOf(unit);
// 计算总的天数或小时数
const total = end.diff(start, unit === "day" ? "hour" : "day") + 1;
// 计算已经过去的天数或小时数
let passed;
if (unit === "week" && now.day() === 0) {
// 如果是星期日
passed = total - 1;
} else {
passed = now.diff(start, unit === "day" ? "hour" : "day");
}
const remaining = total - passed;
const percentage = (passed / total) * 100;
// 返回数据
return {
name: dayText[unit],
total: total,
passed: passed,
remaining: remaining,
percentage: percentage.toFixed(2),
};
};
return {
day: {
elapsed: Math.floor(todayPassHours),
pass: Math.floor(todayPassHoursPercent),
},
week: {
elapsed: weekDay,
pass: Math.floor(weekDayPassPercent),
},
month: {
elapsed: date,
pass: Math.floor(monthPassPercent),
},
year: {
elapsed: month - 1,
pass: Math.floor(yearPassPercent),
},
day: getDifference("day"),
week: getDifference("week"),
month: getDifference("month"),
year: getDifference("year"),
};
};

View File

@ -21,7 +21,9 @@
/>
</transition>
<div class="content">
<!-- 可在此处自定义任意内容 -->
<TimeCapsule />
<MoreContent />
</div>
</div>
</template>
@ -30,6 +32,7 @@
import { CloseOne, SettingTwo } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import TimeCapsule from "@/components/TimeCapsule.vue";
import MoreContent from "@/components/MoreContent.vue";
const store = mainStore();
const closeShow = ref(false);
@ -40,6 +43,7 @@ const closeShow = ref(false);
flex: 1 0 0%;
margin-left: 0.75rem;
height: 80%;
max-width: 50%;
position: relative;
animation: fade 0.5s;
@ -72,6 +76,8 @@ const closeShow = ref(false);
}
.content {
display: flex;
flex-direction: column;
padding: 30px;
width: 100%;
height: 100%;

View File

@ -136,7 +136,7 @@ const jumpTo = (url) => {
padding-left: 22px;
width: 100%;
height: 260px;
min-height: 140px;
.bg {
font-size: 5rem;
}

View File

@ -1,3 +1,4 @@
/* eslint-disable no-undef */
import { defineConfig, loadEnv } from "vite";
import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
import { resolve } from "path";