Compare commits

...

41 Commits
v4.1.0 ... 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
imsyy
3c2bc226ff feat: 修改部分样式 2023-11-13 09:19:31 +08:00
底层用户
015a139ce0
Merge pull request #214 from first19326/dev
feat: 优化 logo 区域样式
2023-11-13 09:15:21 +08:00
first19326
704d446328 feat: 优化字体加载方式 2023-11-11 21:17:29 +08:00
first19326
b7947467e1 feat: 优化 logo 区域样式 2023-11-10 23:54:42 +08:00
imsyy
e879efa7d4 fix: 首字母裁切 #82
> 域名过长的样式还没想好怎么调整 😂
2023-11-09 09:59:38 +08:00
底层用户
df854f3f19
Merge pull request #211 from first19326/dev
feat: 新增加载音乐失败的处理方法
2023-11-08 15:50:46 +08:00
first19326
5097e18e31 feat: 优化了一言数据加载 2023-11-04 20:28:41 +08:00
first19326
46b187d01f feat: 新增加载音乐失败的处理方法 2023-11-04 20:21:50 +08:00
imsyy
be94711dc8 feat: 支持配置音乐播放器开关 #209 2023-11-03 09:23:06 +08:00
底层用户
6c58920f77
Merge pull request #210 from first19326/dev
修复了一些小问题
2023-11-03 09:10:57 +08:00
first19326
17b278f467 fix: 修复壁纸展示状态无法点击下载按钮的问题 2023-11-03 00:49:56 +08:00
first19326
1f8afa7268 fix: 修复在Safari浏览器关闭音乐列表屏幕闪烁问题 2023-11-03 00:49:18 +08:00
first19326
418d73caba fix: 修复720px宽度的视窗下,移动端按钮未显示错误 2023-11-03 00:48:17 +08:00
first19326
9c03de74f8 fix: 修复壁纸展示状态下移动端菜单按钮未隐藏错误 2023-11-03 00:47:07 +08:00
imsyy
1716f53864 docs: update docs 2023-09-18 09:34:47 +08:00
imsyy
8cb8aba71f fix: 修复站点链接问题 #197 2023-09-18 09:32:23 +08:00
imsyy
802cca0f44 feat(dependencies): add ESLint #190 2023-08-26 16:03:07 +08:00
底层用户
75c75fff0f
Merge pull request #190 from OrzMiku/dev
添加 Prettier 并应用代码格式化
2023-08-26 15:47:01 +08:00
神奇のDz
40e45a539c feat(dependencies): add Prettier package 2023-08-23 13:39:30 +08:00
神奇のDz
981320feb2 style(formatting): apply Prettier code formatting 2023-08-23 13:38:17 +08:00
imsyy
8d23deeb40 fix: update README.md 2023-08-17 14:08:11 +08:00
51 changed files with 5089 additions and 7579 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

@ -3,7 +3,7 @@ VITE_SITE_NAME = "無名の主页" # 名称
VITE_SITE_ANTHOR = "無名" # 作者 VITE_SITE_ANTHOR = "無名" # 作者
VITE_SITE_KEYWORDS = "無名,个人主页" # 关键词 VITE_SITE_KEYWORDS = "無名,个人主页" # 关键词
VITE_SITE_DES = "一个默默无闻的主页" # 站点简介 VITE_SITE_DES = "一个默默无闻的主页" # 站点简介
VITE_SITE_URL = "imsyy.top" # 作者地址 VITE_SITE_URL = "imsyy.top" # 站点地址
VITE_SITE_LOGO = "/images/icon/favicon.ico" # 站点主图标 VITE_SITE_LOGO = "/images/icon/favicon.ico" # 站点主图标
VITE_SITE_MAIN_LOGO = "/images/icon/logo.png" # 主页图标 VITE_SITE_MAIN_LOGO = "/images/icon/logo.png" # 主页图标
VITE_SITE_APPLE_LOGO = "/images/logo/apple-touch-icon.png" # Apple 端图标 VITE_SITE_APPLE_LOGO = "/images/logo/apple-touch-icon.png" # Apple 端图标
@ -26,7 +26,7 @@ VITE_DESC_TEXT_OTHER = "哎呀,这都被你发现了( 再点击一次可关
## 请注意不是 Web端 (JS API),免费申请,每日上限 5000 次 ## 请注意不是 Web端 (JS API),免费申请,每日上限 5000 次
## 此处提供的服务可能会超量从而无法访问,请自行申请!请自行申请!请自行申请! ## 此处提供的服务可能会超量从而无法访问,请自行申请!请自行申请!请自行申请!
## 若此处设为空则调用 教书先生 API https://api.oioweb.cn/doc/weather/GetWeather ## 若此处设为空则调用 教书先生 API https://api.oioweb.cn/doc/weather/GetWeather
VITE_WEATHER_KEY = "6c13af6fc30868bee488faf2cc652ab4" VITE_WEATHER_KEY = ""
# 建站日期 # 建站日期
## 若不需要,请设为空即可 ## 若不需要,请设为空即可
@ -47,5 +47,5 @@ VITE_SONG_API = "https://api-meting.imsyy.top/api"
VITE_SONG_SERVER = "netease" VITE_SONG_SERVER = "netease"
# 播放类型 ( song-歌曲, playlist-播放列表, album-专辑, search-搜索, artist-艺术家 ) # 播放类型 ( song-歌曲, playlist-播放列表, album-专辑, search-搜索, artist-艺术家 )
VITE_SONG_TYPE = "playlist" VITE_SONG_TYPE = "playlist"
# 播放 ID # 播放 ID ( 若无需播放器,请设为空即可 )
VITE_SONG_ID = "7452421335" VITE_SONG_ID = "9379831714"

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

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
dist-ssr dist-ssr
*.local *.local
.env
# Editor directories and files # Editor directories and files
.vscode/* .vscode/*

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
}

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"]

View File

@ -5,19 +5,19 @@
简单的小主页,原来的看够了,重新弄了一个 简单的小主页,原来的看够了,重新弄了一个
</p> </p>
![無名の主页](https://s2.loli.net/2022/07/14/K5JigfvDoNewtuS.webp) ![無名の主页](/screenshots/main.jpg)
>主页的 Logo 字体已经过压缩,若用本站 Logo 以外的字母会变回默认字体,这里是 [完整字体](https://file.imsyy.top/font/Other/Pacifico-Regular.ttf) > 主页的 Logo 字体已经过压缩,若用本站 Logo 以外的字母会变回默认字体,这里是 [完整字体](https://file.imsyy.top/font/Other/Pacifico-Regular.ttf),若无法下载,可将字体目录下的 `Pacifico-Regular-all.ttf` 进行替换
### Demo ### 👀 Demo
>由于 CDN 缓存原因,查看最新效果可能需要 `Ctrl` + `F5` 强制刷新浏览器缓存 > 由于 CDN 缓存原因,查看最新效果可能需要 `Ctrl` + `F5` 强制刷新浏览器缓存
- [無名の主页](https://www.imsyy.top) - [無名の主页](https://www.imsyy.top)
- [無名の主页 - Dev](https://dev.home-5iw.pages.dev/) - [無名の主页 - Dev](https://home-imsyy.vercel.app)
- [無名の主页 - 备用线路](https://home-imsyy.vercel.app/) - [無名の主页 - 备用线路](https://home-5iw.pages.dev)
### 功能 ### 🎉 功能
- [x] 载入动画 - [x] 载入动画
- [x] 站点简介 - [x] 站点简介
@ -28,15 +28,27 @@
- [x] 音乐播放器 - [x] 音乐播放器
- [x] 移动端适配 - [x] 移动端适配
### 部署 ### ⚙️ 自动部署
* **安装** [node.js](https://nodejs.org/zh-cn/) **环境** 如果遇到构建环境或者打包过程出现错误,则可以采用 `Github Actions` 来进行自动构建
- 在成功 `fork` 仓库后,前往 `Actions` 页面,若您是首次开启,则会出现下面的提示,点击开启
![步骤1](/screenshots/step1.jpg)
- 然后在仓库中进行任意修改后均会触发工作流的运行,在工作流完成后,会在下方生成一个可供下载的压缩包,这就是构建出的静态文件,可自行上传至服务器
![步骤2](/screenshots/step2.jpg)
### ⚙️ 手动部署
- **安装** [node.js](https://nodejs.org/zh-cn/) **环境**
> node > 16.16.0 > node > 16.16.0
> npm > 8.15.0 > npm > 8.15.0
* 然后以 **管理员权限** 运行 `cmd` 终端,并 `cd` 到 项目根目录 - 然后以 **管理员权限** 运行 `cmd` 终端,并 `cd` 到 项目根目录
* 在 `终端` 中输入: -`终端` 中输入:
```bash ```bash
# 安装 pnpm # 安装 pnpm
@ -51,8 +63,29 @@ pnpm dev
# 构建 # 构建
pnpm build pnpm build
``` ```
> 构建完成后,静态资源会在 **`dist` 目录** 中生成,可将 **`dist` 文件夹下的文件**上传至服务器,也可使用 `Vercel` 等托管平台一键导入并自动部署 > 构建完成后,静态资源会在 **`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` 中可以自定义网站链接(以指向自己的网站): `src/assets/siteLinks.json` 中可以自定义网站链接(以指向自己的网站):
@ -107,11 +140,10 @@ const siteIcon = {
也可自行更换其他方式 也可自行更换其他方式
### 音乐 ### 音乐
>本项目采用了基于 `MetingJS``Aplayer` 音乐播放器,可实现快速自定义歌单 > 本项目采用了基于 `MetingJS``Aplayer` 音乐播放器,可实现快速自定义歌单
>*仅支持 **中国大陆地区** > \*仅支持 **中国大陆地区**
请在 `.env` 文件中更改歌曲相关参数即可实现自定义歌单列表 请在 `.env` 文件中更改歌曲相关参数即可实现自定义歌单列表
@ -130,14 +162,14 @@ VITE_SONG_ID = "7452421335"
现采用 `HarmonyOS Sans` 开源字体,采用字体拆分,提升加载速度 现采用 `HarmonyOS Sans` 开源字体,采用字体拆分,提升加载速度
>由于本站 `CDN` 已开启防盗链,**非本站域名不可访问**,请将字体引入链接更改为下方内容,否则 **自定义字体将失效** > 由于本站 `CDN` 已开启防盗链,**非本站域名不可访问**,请将字体引入链接更改为下方内容,否则 **自定义字体将失效**
> >
>`https://s1.hdslb.com/bfs/static/jinkela/long/font/regular.css` > `https://s1.hdslb.com/bfs/static/jinkela/long/font/regular.css`
<details> <details>
<summary>旧版方式</summary> <summary>旧版方式</summary>
>由于本项目引入了中文字体,需要压缩中文字体以提高网页加载速度( 也可以取消使用中文字体 > 由于本项目引入了中文字体,需要压缩中文字体以提高网页加载速度( 也可以取消使用中文字体
#### 中文字体去除繁体 #### 中文字体去除繁体
@ -165,7 +197,7 @@ make clean all
- 最终可对原字体进行缓加载,**先行加载压缩后的字体** - 最终可对原字体进行缓加载,**先行加载压缩后的字体**
>详细信息可前往 [虹墨空间站](https://www.imaegoo.com/2020/chinese-font-compress/) 查看原文 > 详细信息可前往 [虹墨空间站](https://www.imaegoo.com/2020/chinese-font-compress/) 查看原文
</details> </details>
@ -178,12 +210,9 @@ make clean all
如果想要添加更多的本地图片作为网站背景,可以将图片重命名 `background+数字` 的形式,并在 `src/components/Background/index.vue` 中进行修改: 如果想要添加更多的本地图片作为网站背景,可以将图片重命名 `background+数字` 的形式,并在 `src/components/Background/index.vue` 中进行修改:
```js ```js
if (type == 0) { if (type == 0) {
// 修改此处 Math.random() 后面的第一个数字为图片的数量 // 修改此处 Math.random() 后面的第一个数字为图片的数量
bgUrl.value = `/images/background${Math.floor( bgUrl.value = `/images/background${Math.floor(Math.random() * 10 + 1)}.webp`;
Math.random() * 10 + 1
)}.webp`;
} }
``` ```
@ -193,20 +222,21 @@ if (type == 0) {
### 技术栈 ### 技术栈
* [Vue](https://cn.vuejs.org/) - [Vue](https://cn.vuejs.org/)
* [Vite](https://vitejs.cn/vite3-cn/) - [Vite](https://vitejs.cn/vite3-cn/)
* [Pinia](https://pinia.vuejs.org/zh/) - [Pinia](https://pinia.vuejs.org/zh/)
* [IconPark](https://iconpark.oceanengine.com/official) - [IconPark](https://iconpark.oceanengine.com/official)
* [xicons](https://xicons.org/) - [xicons](https://xicons.org/)
* [Aplayer](https://aplayer.js.org/) - [Aplayer](https://aplayer.js.org/)
### API ### API
* [MetingAPI By 武恩赐](https://api.wuenci.com/meting/api/) - [韩小韩 WebAPI 接口](https://api.vvhan.com/)
* [搏天 API](https://api.btstu.cn/doc/sjbz.php) - [搏天 API](https://api.btstu.cn/doc/sjbz.php)
* [高德开放平台](https://lbs.amap.com/) - [教书先生 API](https://api.oioweb.cn/doc/weather/GetWeather)
* [Hitokoto 一言](https://hitokoto.cn/) - [高德开放平台](https://lbs.amap.com/)
* - [Hitokoto 一言](https://hitokoto.cn/)
## Star History ## Star History
[![Star History Chart](https://api.star-history.com/svg?repos=imsyy/home&type=Date)](https://star-history.com/#imsyy/home&Date) [![Star History Chart](https://api.star-history.com/svg?repos=imsyy/home&type=Date)](https://star-history.com/#imsyy/home&Date)

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 >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://www.imsyy.top)
- [無名の主页 - 备用线路](https://home-imsyy.vercel.app/) - [無名の主页 - Dev](https://home-imsyy.vercel.app)
- [無名の主页 - Standby](https://home-5iw.pages.dev)
### Functions ### Functions
@ -135,7 +136,7 @@ make clean all
### API ### API
* [MetingAPI By 武恩赐](https://api.wuenci.com/meting/api/) * [韩小韩 WebAPI 接口](https://api.vvhan.com/)
* [搏天 API](https://api.btstu.cn/doc/sjbz.php) * [搏天 API](https://api.btstu.cn/doc/sjbz.php)
* [高德开放平台](https://lbs.amap.com/) * [高德开放平台](https://lbs.amap.com/)
* [Hitokoto 一言](https://hitokoto.cn/) * [Hitokoto 一言](https://hitokoto.cn/)

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

@ -1,12 +1,11 @@
<!DOCTYPE html> <!doctype html>
<html lang="zh-CN"> <html lang="zh-CN">
<head>
<head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<meta http-equiv="Access-Control-Allow-Origin" content="*"> <meta http-equiv="Access-Control-Allow-Origin" content="*" />
<!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> --> <!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> -->
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="icon" href="%VITE_SITE_LOGO%"> <link rel="icon" href="%VITE_SITE_LOGO%" />
<link rel="apple-touch-icon" href="%VITE_SITE_APPLE_LOGO%" /> <link rel="apple-touch-icon" href="%VITE_SITE_APPLE_LOGO%" />
<link rel="bookmark" href="%VITE_SITE_APPLE_LOGO%" /> <link rel="bookmark" href="%VITE_SITE_APPLE_LOGO%" />
<link rel="apple-touch-icon-precomposed" sizes="200x200" href="%VITE_SITE_APPLE_LOGO%" /> <link rel="apple-touch-icon-precomposed" sizes="200x200" href="%VITE_SITE_APPLE_LOGO%" />
@ -21,13 +20,14 @@
<!-- <link rel="stylesheet" href="https://cdn.imsyy.top/gh/imsyy/file/font/HarmonyOS_Sans/regular.min.css" /> --> <!-- <link rel="stylesheet" href="https://cdn.imsyy.top/gh/imsyy/file/font/HarmonyOS_Sans/regular.min.css" /> -->
<!-- IE Out --> <!-- IE Out -->
<script> <script>
if ( /*@cc_on!@*/ false || (!!window.MSInputMethodContext && !!document.documentMode)) if (/*@cc_on!@*/ false || (!!window.MSInputMethodContext && !!document.documentMode))
window.location.href = window.location.href =
"https://support.dmeng.net/upgrade-your-browser.html?referrer=" + encodeURIComponent(window.location.href) 'https://support.dmeng.net/upgrade-your-browser.html?referrer=' +
encodeURIComponent(window.location.href);
</script> </script>
</head> </head>
<body> <body>
<!-- 主体内容 --> <!-- 主体内容 -->
<div id="app"></div> <div id="app"></div>
<!-- noscript --> <!-- noscript -->
@ -35,6 +35,5 @@
<div style="text-align: center">请开启 JavaScript</div> <div style="text-align: center">请开启 JavaScript</div>
</noscript> </noscript>
<script type="module" src="/src/main.js"></script> <script type="module" src="/src/main.js"></script>
</body> </body>
</html> </html>

3691
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -4,35 +4,41 @@
"github": "https://github.com/imsyy/home", "github": "https://github.com/imsyy/home",
"home": "https://imsyy.top", "home": "https://imsyy.top",
"private": true, "private": true,
"version": "4.1.0", "version": "4.1.4",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite --host", "dev": "vite --host",
"build": "vite build", "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": { "dependencies": {
"@worstone/vue-aplayer": "^1.0.6",
"aplayer": "^1.10.1", "aplayer": "^1.10.1",
"axios": "^1.1.3", "axios": "^1.6.8",
"element-plus": "^2.2.18", "dayjs": "^1.11.10",
"fetch-jsonp": "^1.2.3", "element-plus": "^2.7.1",
"pinia": "^2.0.23", "fetch-jsonp": "^1.3.0",
"pinia-plugin-persistedstate": "^3.0.0", "pinia": "^2.1.7",
"swiper": "^9.3.2", "pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.3.4", "swiper": "^11.1.1",
"vue3-aplayer": "^1.7.3" "vue": "^3.4.24"
}, },
"devDependencies": { "devDependencies": {
"@icon-park/vue-next": "^1.4.2", "@icon-park/vue-next": "^1.4.2",
"@vicons/fa": "^0.12.0", "@vicons/fa": "^0.12.0",
"@vicons/utils": "^0.1.4", "@vicons/utils": "^0.1.4",
"@vitejs/plugin-vue": "^4.2.3", "@vitejs/plugin-vue": "^4.6.2",
"sass": "^1.55.0", "eslint": "^8.57.0",
"terser": "^5.16.1", "eslint-plugin-vue": "^9.25.0",
"unplugin-auto-import": "^0.11.2", "prettier": "^3.2.5",
"unplugin-vue-components": "^0.22.8", "sass": "^1.75.0",
"vite": "^4.4.5", "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-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

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`

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

@ -12,11 +12,7 @@
<MainRight v-show="!store.boxOpenState" /> <MainRight v-show="!store.boxOpenState" />
<Box v-show="store.boxOpenState" /> <Box v-show="store.boxOpenState" />
</section> </section>
<section <section class="more" v-show="store.setOpenState" @click="store.setOpenState = false">
class="more"
v-show="store.setOpenState"
@click="store.setOpenState = false"
>
<MoreSet /> <MoreSet />
</section> </section>
</div> </div>
@ -24,6 +20,7 @@
<Icon <Icon
class="menu" class="menu"
size="24" size="24"
v-show="!store.backgroundShow"
@click="store.mobileOpenState = !store.mobileOpenState" @click="store.mobileOpenState = !store.mobileOpenState"
> >
<component :is="store.mobileOpenState ? CloseSmall : HamburgerButton" /> <component :is="store.mobileOpenState ? CloseSmall : HamburgerButton" />
@ -74,7 +71,7 @@ watch(
if (value < 990) { if (value < 990) {
store.boxOpenState = false; store.boxOpenState = false;
} }
} },
); );
onMounted(() => { onMounted(() => {
@ -107,8 +104,7 @@ onMounted(() => {
window.addEventListener("resize", getWidth); window.addEventListener("resize", getWidth);
// //
const styleTitle1 = const styleTitle1 = "font-size: 20px;font-weight: 600;color: rgb(244,167,89);";
"font-size: 20px;font-weight: 600;color: rgb(244,167,89);";
const styleTitle2 = "font-size:12px;color: rgb(244,167,89);"; const styleTitle2 = "font-size:12px;color: rgb(244,167,89);";
const styleContent = "color: rgb(30,152,255);"; const styleContent = "color: rgb(30,152,255);";
const title1 = "無名の主页"; const title1 = "無名の主页";
@ -120,12 +116,7 @@ onMounted(() => {
_| |_| | | |____) | | | | | _| |_| | | |____) | | | | |
|_____|_| |_|_____/ |_| |_|`; |_____|_| |_|_____/ |_| |_|`;
const content = `\n\n版本: ${config.version}\n主页: ${config.home}\nGithub: ${config.github}`; const content = `\n\n版本: ${config.version}\n主页: ${config.home}\nGithub: ${config.github}`;
console.info( console.info(`%c${title1} %c${title2} %c${content}`, styleTitle1, styleTitle2, styleContent);
`%c${title1} %c${title2} %c${content}`,
styleTitle1,
styleTitle2,
styleContent
);
}); });
onBeforeUnmount(() => { onBeforeUnmount(() => {
@ -142,8 +133,7 @@ onBeforeUnmount(() => {
height: 100%; height: 100%;
transform: scale(1.2); transform: scale(1.2);
transition: transform 0.3s; transition: transform 0.3s;
animation: fade-blur-main-in 0.65s cubic-bezier(0.25, 0.46, 0.45, 0.94) animation: fade-blur-main-in 0.65s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
forwards;
animation-delay: 0.5s; animation-delay: 0.5s;
.container { .container {
width: 100%; width: 100%;
@ -193,7 +183,7 @@ onBeforeUnmount(() => {
.i-icon { .i-icon {
transform: translateY(2px); transform: translateY(2px);
} }
@media (min-width: 720px) { @media (min-width: 721px) {
display: none; display: none;
} }
} }

View File

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

View File

@ -1,31 +1,38 @@
[{ [
{
"name": "Github", "name": "Github",
"icon": "/images/icon/github.png", "icon": "/images/icon/github.png",
"tip": "去 Github 看看", "tip": "去 Github 看看",
"url": "https://github.com/imsyy" "url": "https://github.com/imsyy"
}, { },
{
"name": "BiliBili", "name": "BiliBili",
"icon": "/images/icon/bilibili.png", "icon": "/images/icon/bilibili.png",
"tip": "(゜-゜)つロ 干杯 ~", "tip": "(゜-゜)つロ 干杯 ~",
"url": "https://space.bilibili.com/98544142" "url": "https://space.bilibili.com/98544142"
}, { },
{
"name": "QQ", "name": "QQ",
"icon": "/images/icon/qq.png", "icon": "/images/icon/qq.png",
"tip": "有什么事吗", "tip": "有什么事吗",
"url": "https://res.abeim.cn/api/qq/?qq=1539250352" "url": "https://res.abeim.cn/api/qq/?qq=1539250352"
}, { },
{
"name": "Email", "name": "Email",
"icon": "/images/icon/email.png", "icon": "/images/icon/email.png",
"tip": "来封 Email ~", "tip": "来封 Email ~",
"url": "mailto:one@imsyy.top" "url": "mailto:one@imsyy.top"
}, { },
{
"name": "Twitter", "name": "Twitter",
"icon": "/images/icon/twitter.png", "icon": "/images/icon/twitter.png",
"tip": "你懂的 ~", "tip": "你懂的 ~",
"url": "https://twitter.com/iimmsyy" "url": "https://twitter.com/iimmsyy"
}, { },
{
"name": "Telegram", "name": "Telegram",
"icon": "/images/icon/telegram.png", "icon": "/images/icon/telegram.png",
"tip": "你懂的 ~", "tip": "你懂的 ~",
"url": "https://t.me/bottom_user" "url": "https://t.me/bottom_user"
}] }
]

View File

@ -1,10 +1,10 @@
<template> <template>
<div class="cover"> <div :class="store.backgroundShow ? 'cover show' : 'cover'">
<img <img
v-show="store.imgLoadStatus" v-show="store.imgLoadStatus"
:src="bgUrl"
class="bg" class="bg"
alt="cover" alt="cover"
:src="bgUrl"
@load="imgLoadComplete" @load="imgLoadComplete"
@error.once="imgLoadError" @error.once="imgLoadError"
@animationend="imgAnimationEnd" @animationend="imgAnimationEnd"
@ -43,17 +43,20 @@ const changeBg = (type) => {
} else if (type == 1) { } else if (type == 1) {
bgUrl.value = "https://api.dujin.org/bing/1920.php"; bgUrl.value = "https://api.dujin.org/bing/1920.php";
} else if (type == 2) { } else if (type == 2) {
bgUrl.value = "https://api.btstu.cn/sjbz/api.php?lx=fengjing&format=images"; bgUrl.value = "https://api.vvhan.com/api/wallpaper/views";
} else if (type == 3) { } else if (type == 3) {
bgUrl.value = "https://api.btstu.cn/sjbz/api.php?lx=dongman&format=images"; bgUrl.value = "https://api.vvhan.com/api/wallpaper/acg";
} }
}; };
// //
const imgLoadComplete = () => { const imgLoadComplete = () => {
imgTimeout.value = setTimeout(() => { imgTimeout.value = setTimeout(
() => {
store.setImgLoadStatus(true); store.setImgLoadStatus(true);
}, Math.floor(Math.random() * (600 - 300 + 1)) + 300); },
Math.floor(Math.random() * (600 - 300 + 1)) + 300,
);
}; };
// //
@ -76,6 +79,14 @@ const imgLoadError = () => {
bgUrl.value = `/images/background${bgRandom}.jpg`; bgUrl.value = `/images/background${bgRandom}.jpg`;
}; };
//
watch(
() => store.coverType,
(value) => {
changeBg(value);
},
);
onMounted(() => { onMounted(() => {
// //
changeBg(store.coverType); changeBg(store.coverType);
@ -96,6 +107,10 @@ onBeforeUnmount(() => {
transition: 0.25s; transition: 0.25s;
z-index: -1; z-index: -1;
&.show {
z-index: 1;
}
.bg { .bg {
position: absolute; position: absolute;
left: 0; left: 0;
@ -105,8 +120,10 @@ onBeforeUnmount(() => {
object-fit: cover; object-fit: cover;
backface-visibility: hidden; backface-visibility: hidden;
filter: blur(20px) brightness(0.3); filter: blur(20px) brightness(0.3);
transition: filter 0.3s, transform 0.3s; transition:
animation: fade-blur-in 1s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards; filter 0.3s,
transform 0.3s;
animation: fade-blur-in 0.8s cubic-bezier(0.25, 0.46, 0.45, 0.94) forwards;
animation-delay: 0.45s; animation-delay: 0.45s;
} }
.gray { .gray {
@ -116,10 +133,7 @@ onBeforeUnmount(() => {
top: 0; top: 0;
width: 100%; width: 100%;
height: 100%; height: 100%;
background-image: radial-gradient( background-image: radial-gradient(rgba(0, 0, 0, 0) 0, rgba(0, 0, 0, 0.5) 100%),
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%); radial-gradient(rgba(0, 0, 0, 0) 33%, rgba(0, 0, 0, 0.3) 166%);
transition: 1.5s; transition: 1.5s;

View File

@ -9,7 +9,7 @@
- -
</span> </span>
{{ fullYear }} {{ fullYear }}
<a :href="SiteUrl">{{ SiteAnthor }}</a> <a :href="siteUrl">{{ siteAnthor }}</a>
</span> </span>
<!-- 以下信息请不要修改哦 --> <!-- 以下信息请不要修改哦 -->
<span class="hidden"> <span class="hidden">
@ -48,8 +48,16 @@ const fullYear = new Date().getFullYear();
// //
const siteStartDate = ref(import.meta.env.VITE_SITE_START); const siteStartDate = ref(import.meta.env.VITE_SITE_START);
const siteIcp = ref(import.meta.env.VITE_SITE_ICP); const siteIcp = ref(import.meta.env.VITE_SITE_ICP);
const SiteAnthor = ref(import.meta.env.VITE_SITE_ANTHOR); const siteAnthor = ref(import.meta.env.VITE_SITE_ANTHOR);
const SiteUrl = ref(import.meta.env.VITE_SITE_URL); 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>

View File

@ -18,10 +18,12 @@
</div> </div>
</Transition> </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="text">{{ hitokotoData.text }}</span>
<span class="from">-&nbsp;{{ hitokotoData.from }}&nbsp;</span> <span class="from">-&nbsp;{{ hitokotoData.from }}&nbsp;</span>
</div> </div>
</Transition>
</div> </div>
</template> </template>
@ -43,13 +45,12 @@ const hitokotoData = reactive({
}); });
// //
const getHitokotoData = () => { const getHitokotoData = async () => {
getHitokoto() try {
.then((res) => { const result = await getHitokoto();
hitokotoData.text = res.hitokoto; hitokotoData.text = result.hitokoto;
hitokotoData.from = res.from; hitokotoData.from = result.from;
}) } catch (error) {
.catch(() => {
ElMessage({ ElMessage({
message: "一言获取失败", message: "一言获取失败",
icon: h(Error, { icon: h(Error, {
@ -57,13 +58,13 @@ const getHitokotoData = () => {
fill: "#efefef", fill: "#efefef",
}), }),
}); });
}); hitokotoData.text = "这里应该显示一句话";
hitokotoData.from = "無名";
}
}; };
// //
const updateHitokoto = () => { const updateHitokoto = () => {
hitokotoData.text = "新的一言正在赶来的路上";
hitokotoData.from = "来源加载中";
// //
debounce(() => { debounce(() => {
getHitokotoData(); getHitokotoData();

View File

@ -43,22 +43,11 @@
<script setup> <script setup>
import { Icon } from "@vicons/utils"; import { Icon } from "@vicons/utils";
// https://www.xicons.org // https://www.xicons.org
import { import { Link, Blog, CompactDisc, Cloud, Compass, Book, Fire, LaptopCode } from "@vicons/fa"; // 使
Link,
Blog,
CompactDisc,
Cloud,
Compass,
Book,
Fire,
LaptopCode,
} from "@vicons/fa"; // 使
import { mainStore } from "@/store"; import { mainStore } from "@/store";
import { Swiper, SwiperSlide } from "swiper/vue"; import { Swiper, SwiperSlide } from "swiper/vue";
import { Pagination, Mousewheel } from "swiper"; import { Pagination, Mousewheel } from "swiper/modules";
import siteLinks from "@/assets/siteLinks.json"; import siteLinks from "@/assets/siteLinks.json";
import "swiper/scss";
import "swiper/scss/pagination";
const store = mainStore(); const store = mainStore();
@ -120,14 +109,22 @@ onMounted(() => {
height: 100%; height: 100%;
} }
.swiper-pagination { .swiper-pagination {
position: static; margin-top: 12px;
margin-top: 4px; display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
:deep(.swiper-pagination-bullet) { :deep(.swiper-pagination-bullet) {
background-color: #fff; background-color: #fff;
width: 18px; width: 20px;
height: 4px; height: 4px;
margin: 0 4px;
border-radius: 4px; border-radius: 4px;
opacity: 0.2;
transition: opacity 0.3s; transition: opacity 0.3s;
&.swiper-pagination-bullet-active {
opacity: 1;
}
&:hover { &:hover {
opacity: 1; opacity: 1;
} }

View File

@ -109,7 +109,9 @@ const siteName = import.meta.env.VITE_SITE_NAME;
&.loaded { &.loaded {
visibility: hidden; visibility: hidden;
transform: translateY(-100%); transform: translateY(-100%);
transition: transform 0.3s 1s ease-out, visibility 0.3s 1s ease-out; transition:
transform 0.3s 1s ease-out,
visibility 0.3s 1s ease-out;
.loader { .loader {
.loader-circle, .loader-circle,
.loader-text { .loader-text {

View File

@ -4,7 +4,7 @@
<!-- Logo --> <!-- Logo -->
<div class="logo"> <div class="logo">
<img class="logo-img" :src="siteLogo" alt="logo" /> <img class="logo-img" :src="siteLogo" alt="logo" />
<div class="name text-hidden"> <div :class="{ name: true, 'text-hidden': true, long: siteUrl[0].length >= 6 }">
<span class="bg">{{ siteUrl[0] }}</span> <span class="bg">{{ siteUrl[0] }}</span>
<span class="sm">.{{ siteUrl[1] }}</span> <span class="sm">.{{ siteUrl[1] }}</span>
</div> </div>
@ -15,10 +15,12 @@
<Icon size="16"> <Icon size="16">
<QuoteLeft /> <QuoteLeft />
</Icon> </Icon>
<div class="text"> <Transition name="fade" mode="out-in">
<div :key="descriptionText.hello + descriptionText.text" class="text">
<p>{{ descriptionText.hello }}</p> <p>{{ descriptionText.hello }}</p>
<p>{{ descriptionText.text }}</p> <p>{{ descriptionText.text }}</p>
</div> </div>
</Transition>
<Icon size="16"> <Icon size="16">
<QuoteRight /> <QuoteRight />
</Icon> </Icon>
@ -37,7 +39,16 @@ const store = mainStore();
// logo // logo
const siteLogo = import.meta.env.VITE_SITE_MAIN_LOGO; const siteLogo = import.meta.env.VITE_SITE_MAIN_LOGO;
// //
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 descriptionText = reactive({ const descriptionText = reactive({
@ -72,7 +83,7 @@ watch(
descriptionText.hello = import.meta.env.VITE_DESC_HELLO; descriptionText.hello = import.meta.env.VITE_DESC_HELLO;
descriptionText.text = import.meta.env.VITE_DESC_TEXT; descriptionText.text = import.meta.env.VITE_DESC_TEXT;
} }
} },
); );
</script> </script>
@ -83,14 +94,14 @@ watch(
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
animation: fade 0.5s; animation: fade 0.5s;
max-width: 460px;
.logo-img { .logo-img {
border-radius: 50%; border-radius: 50%;
width: 120px; width: 120px;
} }
.name { .name {
width: 100%; width: 100%;
height: 142px; padding-left: 22px;
margin-left: 12px;
transform: translateY(-8px); transform: translateY(-8px);
font-family: "Pacifico-Regular"; font-family: "Pacifico-Regular";
@ -117,6 +128,10 @@ watch(
} }
} }
} }
@media (max-width: 720px) {
max-width: 100%;
}
} }
.description { .description {
@ -133,6 +148,7 @@ watch(
margin: 0.75rem 1rem; margin: 0.75rem 1rem;
line-height: 2rem; line-height: 2rem;
margin-right: auto; margin-right: auto;
transition: opacity 0.2s;
p { p {
&:nth-of-type(1) { &: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

@ -11,32 +11,14 @@
<span @click="store.musicOpenState = false">回到一言</span> <span @click="store.musicOpenState = false">回到一言</span>
</div> </div>
<div class="control"> <div class="control">
<go-start <go-start theme="filled" size="30" fill="#efefef" @click="changeMusicIndex(0)" />
theme="filled" <Transition name="fade" mode="out-in">
size="30" <div :key="store.playerState" class="state" @click="changePlayState">
fill="#efefef" <play-one theme="filled" size="50" fill="#efefef" v-show="!store.playerState" />
@click="changeMusicIndex(0)" <pause theme="filled" size="50" fill="#efefef" v-show="store.playerState" />
/>
<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> </div>
<go-end </Transition>
theme="filled" <go-end theme="filled" size="30" fill="#efefef" @click="changeMusicIndex(1)" />
size="30"
fill="#efefef"
@click="changeMusicIndex(1)"
/>
</div> </div>
<div class="menu"> <div class="menu">
<div class="name" v-show="!volumeShow"> <div class="name" v-show="!volumeShow">
@ -48,12 +30,7 @@
</div> </div>
<div class="volume" v-show="volumeShow"> <div class="volume" v-show="volumeShow">
<div class="icon"> <div class="icon">
<volume-mute <volume-mute theme="filled" size="24" fill="#efefef" v-if="volumeNum == 0" />
theme="filled"
size="24"
fill="#efefef"
v-if="volumeNum == 0"
/>
<volume-small <volume-small
theme="filled" theme="filled"
size="24" size="24"
@ -62,23 +39,13 @@
/> />
<volume-notice theme="filled" size="24" fill="#efefef" v-else /> <volume-notice theme="filled" size="24" fill="#efefef" v-else />
</div> </div>
<el-slider <el-slider v-model="volumeNum" :show-tooltip="false" :min="0" :max="1" :step="0.01" />
v-model="volumeNum"
:show-tooltip="false"
:min="0"
:max="1"
:step="0.01"
/>
</div> </div>
</div> </div>
</div> </div>
<!-- 音乐列表弹窗 --> <!-- 音乐列表弹窗 -->
<Transition name="fade"> <Transition name="fade" mode="out-in">
<div <div class="music-list" v-show="musicListShow" @click="closeMusicList()">
class="music-list"
v-show="musicListShow"
@click="musicListShow = false"
>
<Transition name="zoom"> <Transition name="zoom">
<div class="list" v-show="musicListShow" @click.stop> <div class="list" v-show="musicListShow" @click.stop>
<close-one <close-one
@ -86,15 +53,14 @@
theme="filled" theme="filled"
size="28" size="28"
fill="#ffffff60" fill="#ffffff60"
@click="musicListShow = false" @click="closeMusicList()"
/> />
<Player <Player
ref="playerRef"
:songServer="playerData.server" :songServer="playerData.server"
:songType="playerData.type" :songType="playerData.type"
:songId="playerData.id" :songId="playerData.id"
:volume="volumeNum" :volume="volumeNum"
:shuffle="false"
ref="playerRef"
/> />
</div> </div>
</Transition> </Transition>
@ -133,6 +99,13 @@ const playerData = reactive({
// //
const openMusicList = () => { const openMusicList = () => {
musicListShow.value = true; musicListShow.value = true;
playerRef.value.toggleList();
};
//
const closeMusicList = () => {
musicListShow.value = false;
playerRef.value.toggleList();
}; };
// //
@ -148,6 +121,9 @@ const changeMusicIndex = (type) => {
onMounted(() => { onMounted(() => {
// //
window.addEventListener("keydown", (e) => { window.addEventListener("keydown", (e) => {
if (!store.musicIsOk) {
return;
}
if (e.code == "Space") { if (e.code == "Space") {
changePlayState(); changePlayState();
} }
@ -162,7 +138,7 @@ watch(
(value) => { (value) => {
store.musicVolume = value; store.musicVolume = value;
playerRef.value.changeVolume(store.musicVolume); playerRef.value.changeVolume(store.musicVolume);
} },
); );
</script> </script>
@ -203,6 +179,7 @@ watch(
justify-content: space-evenly; justify-content: space-evenly;
width: 100%; width: 100%;
.state { .state {
transition: opacity 0.1s;
.i-icon { .i-icon {
width: 50px; width: 50px;
height: 50px; height: 50px;
@ -316,12 +293,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 { .zoom-enter-active {
animation: zoom 0.4s ease-in-out; animation: zoom 0.4s ease-in-out;
} }

View File

@ -1,21 +1,22 @@
<template> <template>
<aplayer <APlayer
showLrc
ref="player"
v-if="playList[0]" v-if="playList[0]"
:music="playList[playIndex]" ref="player"
:list="playList" :audio="playList"
:autoplay="autoplay" :autoplay="store.playerAutoplay"
:theme="theme" :theme="theme"
:repeat="repeat" :autoSwitch="false"
:shuffle="shuffle" :loop="store.playerLoop"
:listMaxHeight="listMaxHeight" :order="store.playerOrder"
:listFolded="listFolded"
:volume="volume" :volume="volume"
:showLrc="true"
:listFolded="listFolded"
:listMaxHeight="listMaxHeight"
:noticeSwitch="false"
@play="onPlay" @play="onPlay"
@pause="onPause" @pause="onPause"
@timeupdate="onTimeUp" @timeupdate="onTimeUp"
@onSelectSong="onSelectSong" @error="loadMusicError"
/> />
</template> </template>
@ -23,7 +24,7 @@
import { MusicOne, PlayWrong } from "@icon-park/vue-next"; import { MusicOne, PlayWrong } from "@icon-park/vue-next";
import { getPlayerList } from "@/api"; import { getPlayerList } from "@/api";
import { mainStore } from "@/store"; import { mainStore } from "@/store";
import aplayer from "vue3-aplayer"; import APlayer from "@worstone/vue-aplayer";
const store = mainStore(); const store = mainStore();
@ -35,30 +36,14 @@ const playList = ref([]);
// //
const playIndex = ref(0); const playIndex = ref(0);
const playListCount = ref(0);
// //
const props = defineProps({ const props = defineProps({
//
autoplay: {
type: Boolean,
default: false,
},
// //
theme: { theme: {
type: String, type: String,
default: "#efefef", default: "#efefef",
}, },
//
repeat: {
type: String,
default: "list", //'list' | 'music' | 'none'
},
//
shuffle: {
type: Boolean,
default: false,
},
// //
volume: { volume: {
type: Number, type: Number,
@ -89,42 +74,29 @@ const props = defineProps({
}, },
// //
listMaxHeight: { listMaxHeight: {
type: String, type: Number,
default: "420px", default: 420,
}, },
}); });
const listHeight = computed(() => {
return props.listMaxHeight + "px";
});
// //
onMounted(() => { onMounted(() => {
nextTick(() => { nextTick(() => {
try { try {
getPlayerList(props.songServer, props.songType, props.songId).then( getPlayerList(props.songServer, props.songType, props.songId).then((res) => {
(res) => {
console.log(res); console.log(res);
//
playIndex.value = Math.floor(Math.random() * res.length);
playListCount.value = res.length;
// //
store.musicIsOk = true; store.musicIsOk = true;
// //
res.forEach((v) => { playList.value = res;
playList.value.push({ console.log("音乐加载完成");
title: v.name || v.title, console.log(playList.value);
artist: v.artist || v.author, console.log(playIndex.value, playList.value.length, props.volume);
src: v.url || v.src,
pic: v.pic,
lrc: v.lrc,
}); });
});
console.log(
"音乐加载完成",
playList.value,
playIndex.value,
playListCount.value,
props.volume
);
}
);
} catch (err) { } catch (err) {
console.error(err); console.error(err);
store.musicIsOk = false; store.musicIsOk = false;
@ -143,13 +115,11 @@ onMounted(() => {
// //
const onPlay = () => { const onPlay = () => {
console.log("播放"); console.log("播放");
playIndex.value = player.value.aplayer.index;
// //
store.setPlayerState(player.value.audio.paused); store.setPlayerState(player.value.audioRef.paused);
// //
store.setPlayerData( store.setPlayerData(playList.value[playIndex.value].name, playList.value[playIndex.value].artist);
player.value.currentMusic.title,
player.value.currentMusic.artist
);
ElMessage({ ElMessage({
message: store.getPlayerData.name + " - " + store.getPlayerData.artist, message: store.getPlayerData.name + " - " + store.getPlayerData.artist,
grouping: true, grouping: true,
@ -162,21 +132,23 @@ const onPlay = () => {
// //
const onPause = () => { const onPause = () => {
store.setPlayerState(player.value.audio.paused); store.setPlayerState(player.value.audioRef.paused);
}; };
// //
const onTimeUp = () => { const onTimeUp = () => {
let playerRef = player.value.$.vnode.el; let lyrics = player.value.aplayer.lyrics[playIndex.value];
if (playerRef) { let lyricIndex = player.value.aplayer.lyricIndex;
const currentLrcElement = playerRef.querySelector(".aplayer-lrc-current"); if (!lyrics || !lyrics[lyricIndex]) {
const previousLrcElement = currentLrcElement?.previousElementSibling; return;
const lrcContent =
currentLrcElement?.innerHTML ||
previousLrcElement?.innerHTML ||
"这句没有歌词";
store.setPlayerLrc(lrcContent);
} }
let lrc = lyrics[lyricIndex][1];
if (lrc === "Loading") {
lrc = "歌词加载中";
} else if (lrc === "Not available") {
lrc = "歌词加载失败";
}
store.setPlayerLrc(lrc);
}; };
// //
@ -186,40 +158,55 @@ const playToggle = () => {
// //
const changeVolume = (value) => { const changeVolume = (value) => {
player.value.audio.volume = value; player.value.setVolume(value, false);
};
const onSelectSong = (val) => {
console.log(val);
}; };
// //
const changeSong = (type) => { const changeSong = (type) => {
playIndex.value = player.value.playIndex; type === 0 ? player.value.skipBack() : player.value.skipForward();
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]);
nextTick(() => { nextTick(() => {
player.value.play(); 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.aplayer { .aplayer {
width: 80%; width: 80%;
background: transparent;
border-radius: 6px; border-radius: 6px;
font-family: "HarmonyOS_Regular", sans-serif !important; font-family: "HarmonyOS_Regular", sans-serif !important;
:deep(.aplayer-body) { :deep(.aplayer-body) {
background-color: transparent;
.aplayer-pic { .aplayer-pic {
display: none; display: none;
} }
@ -241,8 +228,8 @@ defineExpose({ playToggle, changeVolume, changeSong });
} }
.aplayer-lrc { .aplayer-lrc {
text-align: left; text-align: left;
margin: 4px 0 6px 6px; margin: 7px 0 6px 6px;
height: 100%; height: 44px;
mask: linear-gradient( mask: linear-gradient(
#fff 15%, #fff 15%,
#fff 85%, #fff 85%,
@ -274,6 +261,8 @@ defineExpose({ playToggle, changeVolume, changeSong });
} }
:deep(.aplayer-list) { :deep(.aplayer-list) {
margin-top: 6px; margin-top: 6px;
height: v-bind(listHeight);
background-color: transparent;
ol { ol {
&::-webkit-scrollbar-track { &::-webkit-scrollbar-track {
background-color: transparent; background-color: transparent;

View File

@ -3,15 +3,11 @@
<el-collapse class="collapse" v-model="activeName" accordion> <el-collapse class="collapse" v-model="activeName" accordion>
<el-collapse-item title="个性壁纸" name="1"> <el-collapse-item title="个性壁纸" name="1">
<div class="bg-set"> <div class="bg-set">
<el-radio-group <el-radio-group v-model="coverType" text-color="#ffffff" @change="radioChange">
v-model="coverType" <el-radio value="0" size="large" border>默认壁纸</el-radio>
text-color="#ffffff" <el-radio value="1" size="large" border>每日一图</el-radio>
@change="radioChange" <el-radio value="2" size="large" border>随机风景</el-radio>
> <el-radio value="3" size="large" border>随机动漫</el-radio>
<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> </el-radio-group>
</div> </div>
</el-collapse-item> </el-collapse-item>
@ -54,7 +50,34 @@
</div> </div>
</el-collapse-item> </el-collapse-item>
<el-collapse-item title="播放器配置" name="3"> <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>
<el-collapse-item title="其他设置" name="4"> <el-collapse-item title="其他设置" name="4">
<div>设置内容待增加</div> <div>设置内容待增加</div>
@ -69,8 +92,16 @@ import { mainStore } from "@/store";
import { storeToRefs } from "pinia"; import { storeToRefs } from "pinia";
const store = mainStore(); const store = mainStore();
const { coverType, siteStartShow, musicClick, playerLrcShow, footerBlur } = const {
storeToRefs(store); coverType,
siteStartShow,
musicClick,
playerLrcShow,
footerBlur,
playerAutoplay,
playerOrder,
playerLoop,
} = storeToRefs(store);
// //
const activeName = ref("1"); const activeName = ref("1");
@ -78,7 +109,7 @@ const activeName = ref("1");
// //
const radioChange = () => { const radioChange = () => {
ElMessage({ ElMessage({
message: "壁纸设置成功,刷新后生效", message: "壁纸更换成功",
icon: h(SuccessPicture, { icon: h(SuccessPicture, {
theme: "filled", theme: "filled",
fill: "#efefef", fill: "#efefef",
@ -112,13 +143,23 @@ const radioChange = () => {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
flex-wrap: wrap;
font-size: 14px; font-size: 14px;
.el-switch__core { .el-switch__core {
border-color: transparent; border-color: transparent;
background-color: #ffffff30; background-color: #ffffff30;
} }
.el-radio-group {
.el-radio {
margin: 2px 10px 2px 0;
border-radius: 5px;
&:last-child {
margin-right: 0;
}
}
}
} }
.bg-set {
.el-radio-group { .el-radio-group {
justify-content: space-between; justify-content: space-between;
@ -157,6 +198,5 @@ const radioChange = () => {
} }
} }
} }
}
} }
</style> </style>

View File

@ -37,7 +37,9 @@ const socialTip = ref("通过这里联系我吧");
border-radius: 6px; border-radius: 6px;
backdrop-filter: blur(0); backdrop-filter: blur(0);
animation: fade 0.5s; animation: fade 0.5s;
transition: background-color 0.3s, backdrop-filter 0.3s; transition:
background-color 0.3s,
backdrop-filter 0.3s;
@media (max-width: 840px) { @media (max-width: 840px) {
max-width: 100%; max-width: 100%;
justify-content: center; justify-content: center;

View File

@ -1,54 +1,27 @@
<template> <template>
<div class="time-capsule"> <div class="time-capsule">
<div class="title"> <div class="title">
<hourglass-full <hourglass-full theme="two-tone" size="24" :fill="['#efefef', '#00000020']" />
theme="two-tone"
size="24"
:fill="['#efefef', '#00000020']"
/>
<span>时光胶囊</span> <span>时光胶囊</span>
</div> </div>
<span class="text" <div v-if="timeData" class="all-capsule">
>今日已经度过了&nbsp;{{ timeData.day.elapsed }}&nbsp;小时</span <div v-for="(item, tag, index) in timeData" :key="index" class="capsule-item">
> <div class="item-title">
<el-progress <span class="percentage">
:text-inside="true" {{ item.name }}已度过
:stroke-width="20" <strong>{{ item.passed }}</strong>
:percentage="timeData.day.pass" {{ tag === "day" ? "小时" : "天" }}
/> </span>
<span class="text" <span class="remaining">
>本周已经度过了&nbsp;{{ timeData.week.elapsed }}&nbsp;</span 剩余&nbsp;{{ item.remaining }}&nbsp;{{ tag === "day" ? "小时" : "天" }}
> </span>
<el-progress </div>
:text-inside="true" <el-progress :text-inside="true" :stroke-width="20" :percentage="item.percentage" />
:stroke-width="20" </div>
:percentage="timeData.week.pass" <!-- 建站日期 -->
/> <div v-if="store.siteStartShow" class="capsule-item start">
<span class="text" <div class="item-title">{{ startDateText }}</div>
>本月已经度过了&nbsp;{{ timeData.month.elapsed }}&nbsp;</span </div>
>
<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> </div>
</div> </div>
</template> </template>
@ -68,8 +41,7 @@ const timeInterval = ref(null);
onMounted(() => { onMounted(() => {
timeInterval.value = setInterval(() => { timeInterval.value = setInterval(() => {
timeData.value = getTimeCapsule(); timeData.value = getTimeCapsule();
if (startDate.value) if (startDate.value) startDateText.value = siteDateStatistics(new Date(startDate.value));
startDateText.value = siteDateStatistics(new Date(startDate.value));
}, 1000); }, 1000);
}); });
@ -94,10 +66,33 @@ onBeforeUnmount(() => {
margin-right: 6px; margin-right: 6px;
} }
} }
.text { .all-capsule {
display: block; .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; margin: 1rem 0rem 0.5rem 0rem;
font-size: 0.95rem; 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> </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 = () => { const getWeatherData = async () => {
try {
// //
if (!mainKey) { if (!mainKey) {
getOtherWeather() console.log("未配置,使用备用天气接口");
.then((res) => { const result = await getOtherWeather();
console.log(res); console.log(result);
const data = res.result; const data = result.result;
weatherData.adCode = { weatherData.adCode = {
city: data.city.name, city: data.city.City || "未知地区",
adcode: data.city.cityId, // adcode: data.city.cityId,
}; };
weatherData.weather = { weatherData.weather = {
weather: data.condition.condition, weather: data.condition.day_weather,
temperature: data.condition.temp, temperature: getTemperature(data.condition.min_degree, data.condition.max_degree),
winddirection: data.condition.windDir, winddirection: data.condition.day_wind_direction,
windpower: data.condition.windLevel, windpower: data.condition.day_wind_power,
}; };
})
.catch((err) => {
console.error("天气信息获取失败:" + err);
onError("天气信息获取失败");
});
} else { } else {
getAdcode(mainKey) // Adcode
.then((res) => { const adCode = await getAdcode(mainKey);
console.log(adCode);
if (adCode.infocode !== "10000") {
throw "地区查询失败";
}
weatherData.adCode = { weatherData.adCode = {
city: res.city, city: adCode.city,
adcode: res.adcode, adcode: adCode.adcode,
}; };
// //
getWeather(mainKey, weatherData.adCode.adcode) const result = await getWeather(mainKey, weatherData.adCode.adcode);
.then((res) => {
weatherData.weather = { weatherData.weather = {
weather: res.lives[0].weather, weather: result.lives[0].weather,
temperature: res.lives[0].temperature, temperature: result.lives[0].temperature,
winddirection: res.lives[0].winddirection, winddirection: result.lives[0].winddirection,
windpower: res.lives[0].windpower, windpower: result.lives[0].windpower,
}; };
}) }
.catch((err) => { } catch (error) {
console.error("天气信息获取失败:" + err); console.error("天气信息获取失败:" + error);
onError("天气信息获取失败"); onError("天气信息获取失败");
});
})
.catch((err) => {
console.error("地理位置获取失败:" + err);
onError("地理位置获取失败");
});
} }
}; };

View File

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

View File

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

View File

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

View File

@ -13,6 +13,8 @@ html,
body { body {
width: 100%; width: 100%;
height: 100%; height: 100%;
// width: 100dvh;
// height: 100dvh;
margin: 0; margin: 0;
padding: 0; padding: 0;
background-color: #333; background-color: #333;
@ -38,11 +40,13 @@ p {
// 字体文件 // 字体文件
@font-face { @font-face {
font-family: "Pacifico-Regular"; font-family: "Pacifico-Regular";
font-display: swap;
src: url("/font/Pacifico-Regular.ttf") format("truetype"); src: url("/font/Pacifico-Regular.ttf") format("truetype");
} }
@font-face { @font-face {
font-family: "UnidreamLED"; font-family: "UnidreamLED";
font-display: swap;
src: url("/font/UnidreamLED.ttf") format("truetype"); src: url("/font/UnidreamLED.ttf") format("truetype");
} }
@ -51,8 +55,8 @@ p {
position: fixed; position: fixed;
top: 0; top: 0;
left: 0; left: 0;
width: 100vw; width: 100%;
height: 100vh; height: 100%;
z-index: 0; z-index: 0;
} }
@ -62,7 +66,9 @@ p {
background-color: #00000040; background-color: #00000040;
backdrop-filter: blur(10px); backdrop-filter: blur(10px);
transform: scale(1); transform: scale(1);
transition: backdrop-filter 0.3s, transform 0.3s; transition:
backdrop-filter 0.3s,
transform 0.3s;
&:hover { &:hover {
transform: scale(1.01); transform: scale(1.01);
} }
@ -164,7 +170,7 @@ p {
} }
to { to {
filter: blur(0) brightness(1); filter: blur(0) brightness(1);
transform: scale(1.2); transform: scale(1);
} }
} }

View File

@ -1,21 +1,26 @@
var CURSOR; let mainCursor;
Math.lerp = (a, b, n) => (1 - n) * a + n * b; Math.lerp = (a, b, n) => (1 - n) * a + n * b;
const getStyle = (el, attr) => { const getStyle = (el, attr) => {
try { try {
return window.getComputedStyle ? return window.getComputedStyle ? window.getComputedStyle(el)[attr] : el.currentStyle[attr];
window.getComputedStyle(el)[attr] : } catch (e) {
el.currentStyle[attr]; console.error(e);
} catch (e) {} }
return ""; return false;
};
const cursorInit = () => {
mainCursor = new Cursor();
return mainCursor;
}; };
class Cursor { class Cursor {
constructor() { constructor() {
this.pos = { this.pos = {
curr: null, curr: null,
prev: null prev: null,
}; };
this.pt = []; this.pt = [];
this.create(); this.create();
@ -37,10 +42,9 @@ class Cursor {
document.body.append(this.cursor); document.body.append(this.cursor);
} }
var el = document.getElementsByTagName('*'); var el = document.getElementsByTagName("*");
for (let i = 0; i < el.length; i++) for (let i = 0; i < el.length; i++)
if (getStyle(el[i], "cursor") == "pointer") if (getStyle(el[i], "cursor") == "pointer") this.pt.push(el[i].outerHTML);
this.pt.push(el[i].outerHTML);
document.body.appendChild((this.scr = document.createElement("style"))); 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}`; 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.cursor.classList.remove("active");
this.pos = { this.pos = {
curr: null, curr: null,
prev: null prev: null,
}; };
this.pt = []; this.pt = [];
@ -61,18 +65,18 @@ class Cursor {
} }
init() { init() {
document.onmousemove = e => { document.onmousemove = (e) => {
(this.pos.curr == null) && this.move(e.clientX - 8, e.clientY - 8); this.pos.curr == null && this.move(e.clientX - 8, e.clientY - 8);
this.pos.curr = { this.pos.curr = {
x: e.clientX - 8, x: e.clientX - 8,
y: e.clientY - 8 y: e.clientY - 8,
}; };
this.cursor.classList.remove("hidden"); this.cursor.classList.remove("hidden");
}; };
document.onmouseenter = e => this.cursor.classList.remove("hidden"); document.onmouseenter = () => this.cursor.classList.remove("hidden");
document.onmouseleave = e => this.cursor.classList.add("hidden"); document.onmouseleave = () => this.cursor.classList.add("hidden");
document.onmousedown = e => this.cursor.classList.add("active"); document.onmousedown = () => this.cursor.classList.add("active");
document.onmouseup = e => this.cursor.classList.remove("active"); document.onmouseup = () => this.cursor.classList.remove("active");
} }
render() { render() {
@ -87,8 +91,4 @@ class Cursor {
} }
} }
const cursorInit = () => {
CURSOR = new Cursor();
};
export default cursorInit; export default cursorInit;

View File

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

View File

@ -1,29 +1,17 @@
import { h } from "vue"; import { h } from "vue";
import { SpaCandle } from "@icon-park/vue-next"; import { SpaCandle } from "@icon-park/vue-next";
import dayjs from "dayjs";
// 时钟 // 时钟
export const getCurrentTime = () => { export const getCurrentTime = () => {
let time = new Date(); let time = new Date();
let year = time.getFullYear(); let year = time.getFullYear();
let month = let month = time.getMonth() + 1 < 10 ? "0" + (time.getMonth() + 1) : time.getMonth() + 1;
time.getMonth() + 1 < 10
? "0" + (time.getMonth() + 1)
: time.getMonth() + 1;
let day = time.getDate() < 10 ? "0" + time.getDate() : time.getDate(); let day = time.getDate() < 10 ? "0" + time.getDate() : time.getDate();
let hour = time.getHours() < 10 ? "0" + time.getHours() : time.getHours(); let hour = time.getHours() < 10 ? "0" + time.getHours() : time.getHours();
let minute = let minute = time.getMinutes() < 10 ? "0" + time.getMinutes() : time.getMinutes();
time.getMinutes() < 10 ? "0" + time.getMinutes() : time.getMinutes(); let second = time.getSeconds() < 10 ? "0" + time.getSeconds() : time.getSeconds();
let second = let weekday = ["星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"];
time.getSeconds() < 10 ? "0" + time.getSeconds() : time.getSeconds();
let weekday = [
"星期日",
"星期一",
"星期二",
"星期三",
"星期四",
"星期五",
"星期六",
];
let currentTime = { let currentTime = {
year, year,
month, month,
@ -38,47 +26,48 @@ export const getCurrentTime = () => {
// 时光胶囊 // 时光胶囊
export const getTimeCapsule = () => { export const getTimeCapsule = () => {
// 日进度 const now = dayjs();
const todayStartDate = new Date(new Date().toLocaleDateString()).getTime(); const dayText = {
const todayPassHours = (new Date() - todayStartDate) / 1000 / 60 / 60; day: "今日",
const todayPassHoursPercent = (todayPassHours / 24) * 100; week: "本周",
month: "本月",
// 周进度 year: "本年",
const weeks = [7, 1, 2, 3, 4, 5, 6]; };
const weekDay = weeks[new Date().getDay()]; /**
const weekDayPassPercent = (weekDay / 7) * 100; * 计算时间差的函数
* @param {String} unit 时间单位可以是 'day', 'week', 'month', 'year'
// 月进度 */
const year = new Date().getFullYear(); const getDifference = (unit) => {
const date = new Date().getDate(); // 获取当前时间单位的开始时间
const month = new Date().getMonth() + 1; const start = now.startOf(unit);
const monthAll = new Date(year, month, 0).getDate(); // 获取当前时间单位的结束时间
const monthPassPercent = (date / monthAll) * 100; const end = now.endOf(unit);
// 计算总的天数或小时数
// 年进度 const total = end.diff(start, unit === "day" ? "hour" : "day") + 1;
const yearStartDate = new Date(year, 0, 1).getTime(); // 计算已经过去的天数或小时数
const yearEndDate = new Date(year + 1, 0, 1).getTime(); let passed;
const yearPassHours = (new Date() - yearStartDate) / 1000 / 60 / 60; if (unit === "week" && now.day() === 0) {
const yearTotalHours = (yearEndDate - yearStartDate) / 1000 / 60 / 60; // 如果是星期日
const yearPassPercent = (yearPassHours / yearTotalHours) * 100; passed = total - 1;
} else {
passed = now.diff(start, unit === "day" ? "hour" : "day");
}
const remaining = total - passed;
const percentage = (passed / total) * 100;
// 返回数据
return { return {
day: { name: dayText[unit],
elapsed: Math.floor(todayPassHours), total: total,
pass: Math.floor(todayPassHoursPercent), passed: passed,
}, remaining: remaining,
week: { percentage: percentage.toFixed(2),
elapsed: weekDay, };
pass: Math.floor(weekDayPassPercent), };
}, return {
month: { day: getDifference("day"),
elapsed: date, week: getDifference("week"),
pass: Math.floor(monthPassPercent), month: getDifference("month"),
}, year: getDifference("year"),
year: {
elapsed: month - 1,
pass: Math.floor(yearPassPercent),
},
}; };
}; };
@ -122,7 +111,7 @@ export const checkDays = () => {
const mon = myDate.getMonth() + 1; const mon = myDate.getMonth() + 1;
const date = myDate.getDate(); const date = myDate.getDate();
const key = `${mon}.${date}`; const key = `${mon}.${date}`;
if (anniversaries.hasOwnProperty(key)) { if (Object.prototype.hasOwnProperty.call(anniversaries, key)) {
console.log(`今天是${anniversaries[key]}`); console.log(`今天是${anniversaries[key]}`);
const gray = document.createElement("style"); const gray = document.createElement("style");
gray.innerHTML = "html{filter: grayscale(100%)}"; gray.innerHTML = "html{filter: grayscale(100%)}";
@ -144,11 +133,11 @@ export const siteDateStatistics = (startDate) => {
const differenceInYears = differenceInMonths / 12; const differenceInYears = differenceInMonths / 12;
if (differenceInYears >= 1) { if (differenceInYears >= 1) {
return `本站已经苟活了 ${Math.floor(differenceInYears)}${Math.floor( return `本站已经苟活了 ${Math.floor(differenceInYears)}${Math.floor(
differenceInMonths % 12 differenceInMonths % 12,
)} ${Math.round(differenceInDays % 30)} `; )} ${Math.round(differenceInDays % 30)} `;
} else if (differenceInMonths >= 1) { } else if (differenceInMonths >= 1) {
return `本站已经苟活了 ${Math.floor(differenceInMonths)}${Math.round( return `本站已经苟活了 ${Math.floor(differenceInMonths)}${Math.round(
differenceInDays % 30 differenceInDays % 30,
)} `; )} `;
} else { } else {
return `本站已经苟活了 ${Math.round(differenceInDays)}`; return `本站已经苟活了 ${Math.round(differenceInDays)}`;

View File

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

View File

@ -5,7 +5,7 @@
<el-col :span="12"> <el-col :span="12">
<div class="left"> <div class="left">
<Hitokoto /> <Hitokoto />
<Music /> <Music v-if="playerHasId" />
</div> </div>
</el-col> </el-col>
<el-col :span="12"> <el-col :span="12">
@ -18,11 +18,7 @@
<span class="sm-hidden">{{ currentTime.weekday }}</span> <span class="sm-hidden">{{ currentTime.weekday }}</span>
</div> </div>
<div class="text"> <div class="text">
<span> <span> {{ currentTime.hour }}:{{ currentTime.minute }}:{{ currentTime.second }}</span>
{{ currentTime.hour }}:{{ currentTime.minute }}:{{
currentTime.second
}}</span
>
</div> </div>
</div> </div>
<Weather /> <Weather />
@ -45,6 +41,9 @@ const store = mainStore();
const currentTime = ref({}); const currentTime = ref({});
const timeInterval = ref(null); const timeInterval = ref(null);
// id
const playerHasId = import.meta.env.VITE_SONG_ID;
// //
const updateTimeData = () => { const updateTimeData = () => {
currentTime.value = getCurrentTime(); currentTime.value = getCurrentTime();

View File

@ -19,10 +19,18 @@ import Link from "@/components/Links.vue";
const store = mainStore(); 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> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.right { .right {
// flex: 1 0 0%; // flex: 1 0 0%;

View File

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

View File

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