[Add] 添加功能

# 添加独立的壁纸配置,增加壁纸时无需重新编译项目
# 允许为移动端设置不同分辨率的壁纸
# 允许在移动端点击 网站列表  以打开设置
# 依赖更新
This commit is contained in:
NanoRocky 2025-03-10 20:10:42 +08:00
parent a3480ad75b
commit 3cfb290fc5
14 changed files with 1499 additions and 1174 deletions

View File

@ -44,7 +44,7 @@ VITE_SITE_MPS = ""
## 如果要使用逐字歌词功能,请使用 https://github.com/NanoRocky/meting-api 进行部署
## 此处提供的服务可能会超量从而无法访问,请自行部署或前往搜索引擎使用其他站长搭建的
## 若使用 QQ 音乐歌单,歌曲数量最好不要超出 50 首
VITE_SONG_API = "https://api.wuenci.com/meting/api/"
VITE_SONG_API = "https://metingapi.nanorocky.top/"
# 歌曲服务器 ( netease-网易云, tencent-qq音乐 )
VITE_SONG_SERVER = "netease"
# 播放类型 ( song-歌曲, playlist-播放列表, album-专辑, search-搜索, artist-艺术家 )

View File

@ -1,10 +1,13 @@
{
"name": "home",
"author": "imsyy",
"efua": "NanoRocky",
"github": "https://github.com/imsyy/home",
"efug": "https://github.com/NanoRocky/home",
"home": "https://imsyy.top",
"efuh": "https://nanorocky.top",
"private": true,
"version": "4.1.4",
"version": "4.2.1 [EFU]",
"type": "module",
"scripts": {
"dev": "vite --host",
@ -16,16 +19,16 @@
"dependencies": {
"@worstone/vue-aplayer": "^1.0.7",
"aplayer": "^1.10.1",
"axios": "^1.7.9",
"axios": "^1.8.2",
"dayjs": "^1.11.13",
"element-plus": "^2.9.3",
"element-plus": "^2.9.6",
"fetch-jsonp": "^1.3.0",
"jparticles": "^3.5.0",
"lodash-es": "^4.17.21",
"pinia": "^2.3.0",
"pinia": "^3.0.1",
"pinia-plugin-persistedstate-2": "^2.0.28",
"swiper": "^11.2.1",
"three": "^0.172.0",
"swiper": "^11.2.5",
"three": "^0.174.0",
"vue": "^3.5.13",
"vuex": "^4.1.0"
},
@ -38,14 +41,14 @@
"@vicons/tabler": "^0.13.0",
"@vicons/utils": "^0.1.4",
"@vitejs/plugin-vue": "^5.2.1",
"eslint": "^9.18.0",
"eslint-plugin-vue": "^9.32.0",
"prettier": "^3.4.2",
"sass": "^1.83.4",
"terser": "^5.37.0",
"unplugin-auto-import": "^19.0.0",
"unplugin-vue-components": "^28.0.0",
"vite": "^6.0.9",
"eslint": "^9.21.0",
"eslint-plugin-vue": "^9.33.0",
"prettier": "^3.5.3",
"sass": "^1.85.1",
"terser": "^5.39.0",
"unplugin-auto-import": "^19.1.1",
"unplugin-vue-components": "^28.4.1",
"vite": "^6.2.1",
"vite-plugin-compression2": "^1.3.3",
"vite-plugin-pwa": "^0.21.1"
},

2214
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,4 @@
{
"bgImageCount": 10,
"bgImageCountP": 2
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 609 KiB

View File

@ -73,3 +73,28 @@ export const getOtherWeather = async () => {
const res = await fetch("https://api.oioweb.cn/api/weather/GetWeather");
return await res.json();
};
// 这是一个待完善的新免 token 天气模块
// // 获取位置信息
// export const getLocation = async () => {
// const res = await fetch("http://inip.in/ip.json");
// return await res.json();
// };
// // 转换城市 ID
// export const getCityId = async (city) => {
// const res = await fetch(
// `https://api.songzixian.com/api/china-city?dataSource=LOCAL_CHINA_CITY&district=${city}`,
// );
// return await res.json();
// };
// // 获取小米天气 API
// export const getOtherWeather = async () => {
// const res = await fetch(
// `https://weatherapi.market.xiaomi.com/wtr-v3/weather/all?latitude=0&longitude=0&isLocated=true&locationKey=weathercn%3A${city}&days=2&appKey=weather20151024&sign=zUFJoAR2ZVrDy1vF3D07&locale=zh_cn&alpha=false&isGlobal=false`,
// );
// return await res.json();
// };

View File

@ -23,14 +23,60 @@ const bgUrl = ref(null);
const imgTimeout = ref(null);
const emit = defineEmits(["loadComplete"]);
//
// Math.random()
const bgRandom = Math.floor(Math.random() * 10 + 1);
//
// 使 json
// JSON
let bgImageCount = 10; // PC
let bgImageCountP = 2; //
let bgRandom = 0;
let bgRandomp = 0;
// config.json
async function loadConfig() {
try {
const response = await fetch('/images/config.json');
const data = await response.json();
bgImageCount = Math.max(data.bgImageCount, 1);
bgImageCountP = Math.max(data.bgImageCountP, 1);
bgRandom = Math.floor(Math.random() * bgImageCount + 1);
bgRandomp = Math.floor(Math.random() * bgImageCountP + 1);
} catch (error) {
console.error('无法加载壁纸配置文件:', error);
bgRandom = Math.floor(Math.random() * bgImageCount + 1);
bgRandomp = Math.floor(Math.random() * bgImageCountP + 1);
};
};
//
const detectDevice = () => {
const userAgent = navigator.userAgent.toLowerCase();
if (/mobile|android|iphone|ipad|ipod|windows phone/.test(userAgent)) {
if (/ipad|tablet|playbook|silk|kindle/.test(userAgent)) {
return 'tablet'; //
} else {
return 'mobile'; //
};
} else {
return 'pc'; // PC
};
};
//
const changeBg = (type) => {
(async () => {
await loadConfig(); //
const deviceType = detectDevice(); //
if (type == 0) {
// webp png
//
// bgUrl.value = `/images/background${bgRandom}.jpg`;
if (deviceType === 'mobile') {
bgUrl.value = `/images/phone/backgroundphone${bgRandomp}.jpg`;
} else if (deviceType === 'tablet' || deviceType === 'pc') {
bgUrl.value = `/images/background${bgRandom}.jpg`;
} else {
bgUrl.value = `/images/background${bgRandom}.jpg`;
};
} else if (type == 1) {
bgUrl.value = "https://api.dujin.org/bing/1920.php";
} else if (type == 2) {
@ -38,6 +84,7 @@ const changeBg = (type) => {
} else if (type == 3) {
bgUrl.value = "https://api.vvhan.com/api/wallpaper/acg";
};
})();
};
//

View File

@ -47,9 +47,9 @@
:key="store.playerLrc.length != 0 ? `lrc-line-${store.playerLrc[0][2]}` : `lrc-line-null`">
<music-one theme="filled" size="18" fill="#efefef" />
&nbsp;
<!-- <Icon size="20" style="transform: rotate(-18deg);" class="paws-1">
<Icon size="20" style="transform: rotate(-18deg);" class="paws-1">
<paw />
</Icon> -->
</Icon>
<span class="yrc-box">
<span class="yrc-2 lrc-text text-hidden" id="yrc-2-wrap">
<span v-for="i in store.playerLrc" :key="`lrc-over-char-${i[2]}-${i[3]}`" v-html="i[4]">
@ -66,9 +66,9 @@
</span>
</span>
</span>
<!-- <Icon size="20" style="transform: rotate(18deg);" class="paws-2">
<Icon size="20" style="transform: rotate(18deg);" class="paws-2">
<paw />
</Icon> -->
</Icon>
&nbsp;
<music-one theme="filled" size="18" fill="#efefef" />
</div>
@ -78,13 +78,13 @@
<div class="lrc-all" :key="store.getPlayerLrc">
<music-one theme="filled" size="18" fill="#efefef" />
&nbsp;
<!-- <Icon size="20" style="transform: rotate(-18deg);" class="paws-3">
<Icon size="20" style="transform: rotate(-18deg);" class="paws-3">
<paw />
</Icon> -->
</Icon>
<span class="lrc-text text-hidden" v-html="store.getPlayerLrc[0][4]" :class="`lrc-char`" />
<!-- <Icon size="20" style="transform: rotate(18deg);" class="paws-4">
<Icon size="20" style="transform: rotate(18deg);" class="paws-4">
<paw />
</Icon> -->
</Icon>
&nbsp;
<music-one theme="filled" size="18" fill="#efefef" />
</div>

View File

@ -4,29 +4,20 @@
<Icon size="20">
<Link />
</Icon>
<span class="title">网站列表</span>
<span class="title text-hidden" v-if="store.mobileOpenState" @click="store.setOpenState = !store.setOpenState">网站列表</span>
<span class="title" v-else>网站列表</span>
</div>
<!-- 网站列表 -->
<Swiper
v-if="siteLinks[0]"
:modules="[Pagination, Mousewheel]"
:slides-per-view="1"
:space-between="40"
<Swiper v-if="siteLinks[0]" :modules="[Pagination, Mousewheel]" :slides-per-view="1" :space-between="40"
:pagination="{
el: '.swiper-pagination',
clickable: true,
bulletElement: 'div',
}"
:mousewheel="true"
>
}" :mousewheel="true">
<SwiperSlide v-for="site in siteLinksList" :key="site">
<el-row class="link-all" :gutter="20">
<el-col v-for="(item, index) in site" :span="8" :key="item">
<div
class="item cards"
:style="index < 3 ? 'margin-bottom: 20px' : null"
@click="jumpLink(item)"
>
<div class="item cards" :style="index < 3 ? 'margin-bottom: 20px' : null" @click="jumpLink(item)">
<Icon size="26">
<component :is="siteIcon[item.icon]" />
</Icon>
@ -90,26 +81,31 @@ const jumpLink = (data) => {
display: flex;
align-items: center;
animation: fade 0.5s;
.title {
margin-left: 8px;
font-size: 1.15rem;
text-shadow: 0 0 5px #00000050;
}
}
.swiper {
left: -10px;
width: calc(100% + 20px);
padding: 5px 10px 0;
z-index: 0;
.swiper-slide {
height: 100%;
}
.swiper-pagination {
margin-top: 12px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
:deep(.swiper-pagination-bullet) {
background-color: #fff;
width: 20px;
@ -118,17 +114,21 @@ const jumpLink = (data) => {
border-radius: 4px;
opacity: 0.2;
transition: opacity 0.3s;
&.swiper-pagination-bullet-active {
opacity: 1;
}
&:hover {
opacity: 1;
}
}
}
}
.link-all {
height: 220px;
.item {
height: 100px;
width: 100%;
@ -153,16 +153,20 @@ const jumpLink = (data) => {
font-size: 1.1rem;
margin-left: 8px;
}
@media (min-width: 720px) and (max-width: 820px) {
.name {
display: none;
}
}
@media (max-width: 720px) {
height: 80px;
}
@media (max-width: 460px) {
flex-direction: column;
.name {
font-size: 1rem;
margin-left: 0;
@ -170,6 +174,7 @@ const jumpLink = (data) => {
}
}
}
@media (max-width: 720px) {
height: 180px;
}

View File

@ -258,8 +258,8 @@ const fetchYrc = async (yrcUrl) => {
};
const songUrlInfUrl = store.playerYrcATDBF
? {
netease: `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.yrc`,
tencent: `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songId}.qrc`,
netease: `https://ghfast.top/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.yrc`,
tencent: `https://ghfast.top/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songId}.qrc`,
}
: {
netease: `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songId}.yrc`,
@ -351,8 +351,8 @@ function syncYrcLrc() {
} else {
const songUrlInfwurl = store.playerYrcATDBF
? {
netease: `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songIdlrc}.lrc`,
tencent: `https://ghp.ci/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songIdlrc}.lrc`,
netease: `https://ghfast.top/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songIdlrc}.lrc`,
tencent: `https://ghfast.top/https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/qq-lyrics/${songIdlrc}.lrc`,
}
: {
netease: `https://raw.githubusercontent.com/Steve-xmh/amll-ttml-db/main/ncm-lyrics/${songIdlrc}.lrc`,
@ -390,13 +390,19 @@ function syncYrcLrc() {
};
});
};
})
.catch(() => {
lrc = "歌词加载失败";
});
};
};
} else {
const output = [[true, 1, playerLyricIndex, 0, lrc]];
if (store.playerLrc.toString() != output.toString()) {
store.setPlayerLrc(output);
};
};
};
} else {
//
const now = player.value.audioStatus.playedTime * 1000;

View File

@ -1,14 +1,40 @@
<template>
<div class="set" @mouseenter="closeShow = true" @mouseleave="closeShow = false" @click.stop>
<div class="mobileset" v-if="store.mobileOpenState" @mouseenter="closeShow = true" @mouseleave="closeShow = false"
@click.stop>
<transition name="el-fade-in-linear">
<close-one
class="close"
theme="filled"
size="28"
fill="#ffffff60"
v-show="closeShow"
@click="store.setOpenState = false"
/>
<close-one class="close" theme="filled" size="28" fill="#ffffff60" v-show="closeShow"
@click="store.setOpenState = false" />
</transition>
<el-row>
<el-col class="left">
<div class="logo text-hidden">
<span class="bg">{{ siteUrl[0] }}</span>
<span class="sm">.{{ siteUrl[1] }}</span>
</div>
<!-- 移动端设置菜单 -->
<div class="title">
<setting-two theme="filled" size="28" fill="#ffffff60" />
<span class="name">全局设置</span>
</div>
<div class="mobileset-scrollable">
<Set />
</div>
<div class="version">
<div class="num">v&nbsp;{{ config.version }}</div>
<el-tooltip content="Powered by imsyy" placement="right" :show-arrow="false">
<github-one class="github" theme="outline" size="24" @click="jumpTo(config.github)" />
</el-tooltip>
<el-tooltip content="Extension Function Updates by NanoRocky" placement="right" :show-arrow="false">
<file-editing-one class="github" theme="outline" size="24" @click="jumpTo(config.efug)" />
</el-tooltip>
</div>
</el-col>
</el-row>
</div>
<div class="set" v-else @mouseenter="closeShow = true" @mouseleave="closeShow = false" @click.stop>
<transition name="el-fade-in-linear">
<close-one class="close" theme="filled" size="28" fill="#ffffff60" v-show="closeShow"
@click="store.setOpenState = false" />
</transition>
<el-row :gutter="40">
<el-col :span="12" class="left">
@ -21,6 +47,9 @@
<el-tooltip content="Github 源代码仓库" placement="right" :show-arrow="false">
<github-one class="github" theme="outline" size="24" @click="jumpTo(config.github)" />
</el-tooltip>
<el-tooltip content="扩展功能更新仓库" placement="right" :show-arrow="false">
<file-editing-one class="github" theme="outline" size="24" @click="jumpTo(config.efug)" />
</el-tooltip>
</div>
<el-card class="update">
<template #header>
@ -40,6 +69,7 @@
</div>
</el-card>
</el-col>
<!-- 桌面端设置菜单 -->
<el-col :span="12" class="right">
<div class="title">
<setting-two theme="filled" size="28" fill="#ffffff60" />
@ -52,7 +82,7 @@
</template>
<script setup>
import { CloseOne, SettingTwo, GithubOne, AddOne, Bug } from "@icon-park/vue-next";
import { CloseOne, SettingTwo, GithubOne, AddOne, Bug, FileEditingOne } from "@icon-park/vue-next";
import { mainStore } from "@/store";
import Set from "@/components/Set.vue";
import config from "@/../package.json";
@ -75,12 +105,15 @@ const siteUrl = computed(() => {
//
const upData = reactive({
new: [
"采用 Vue 进行重构",
"音乐歌单支持快速自定义",
"壁纸支持个性化设置",
"音乐播放器支持音量控制",
"增加逐字歌词功能",
"依赖组件更新",
"支持在移动端打开设置",
"添加音乐进度条",
"动效优化",
"交互优化",
"支持在移动端显示不同的壁纸",
],
fix: ["修复天气 API", "时光胶囊显示错误", "移动端动画及细节", "图标更换为 IconPark"],
fix: ["消除依赖及功能弃用提示", "增强网页兼容性", "修复 Player 模块的故障"],
});
//
@ -137,6 +170,7 @@ const jumpTo = (url) => {
width: 100%;
height: 260px;
min-height: 140px;
.bg {
font-size: 5rem;
}
@ -150,14 +184,17 @@ const jumpTo = (url) => {
.bg {
font-size: 4.5rem;
}
.sm {
font-size: 1.7rem;
}
}
@media (max-width: 825px) {
.bg {
font-size: 3.8rem;
}
.sm {
font-size: 1.3rem;
}
@ -242,4 +279,139 @@ const jumpTo = (url) => {
}
}
}
.mobileset {
position: absolute;
top: 50%;
left: 50%;
-webkit-transform: translate(-50%, -50%);
transform: translate(-50%, -50%);
width: 82%;
height: 86%;
background: rgb(255 255 255 / 40%);
border-radius: 6px;
padding: 36px;
.close {
position: absolute;
top: 14px;
right: 14px;
width: 28px;
height: 28px;
&:hover {
transform: scale(1.2);
}
&:active {
transform: scale(1);
}
}
.el-row {
height: 100%;
flex-wrap: nowrap;
.left {
height: 100%;
padding-left: 24px !important;
padding-bottom: 24px;
display: flex;
flex-direction: column;
// justify-content: space-between;
.logo {
transform: translateY(-8%);
font-family: "Pacifico-Regular";
padding-left: 6px;
width: 72%;
height: auto;
.bg {
font-size: 2rem;
}
.sm {
margin-left: 6px;
font-size: 1.2rem;
}
}
.title {
display: flex;
align-items: center;
flex-direction: row;
font-size: 18px;
margin-bottom: 16px;
padding-top: 18px;
.i-icon {
width: 28px;
height: 28px;
margin-right: 6px;
}
}
.mobileset-scrollable {
flex: 1;
overflow-y: auto;
}
.version {
display: flex;
flex-direction: row;
align-items: center;
margin-top: auto;
.num {
font-size: 1rem;
font-family: "Pacifico-Regular";
}
.github {
width: 24px;
height: 24px;
margin-left: 12px;
margin-top: 6px;
&:hover {
transform: scale(1.2);
}
}
}
.update {
margin-top: 30px;
height: 100%;
:deep(.el-card__body) {
height: 100%;
.upnote {
padding: 20px;
height: calc(100% - 56px);
overflow-y: auto;
.uptext {
display: flex;
flex-direction: row;
align-items: center;
padding-bottom: 16px;
&:nth-last-of-type(1) {
padding: 0;
}
.i-icon {
width: 22px;
height: 22px;
margin-right: 8px;
}
}
}
}
}
}
}
}
</style>

View File

@ -22,6 +22,9 @@ export default ({ mode }) =>
}),
VitePWA({
registerType: "autoUpdate",
// 酪灰的小批注:如果遇到了子页面自动跳转主页等问题,或不需要客户端浏览器缓存,可尝试取消注释这两行代码,而不需要完全移除 PWA ~
// selfDestroying: true,
// injectRegister: false,
workbox: {
skipWaiting: true,
clientsClaim: true,
@ -106,8 +109,10 @@ export default ({ mode }) =>
css: {
preprocessorOptions: {
scss: {
api: 'modern',
charset: false,
additionalData: `@use "@/style/global.scss" as global;`,
silenceDeprecations: ["legacy-js-api"],
},
},
},