commit
e3af10fb68
9
.env
9
.env
@ -1,8 +1,14 @@
|
||||
# 站点名称
|
||||
VITE_SITE_NAME = "無名の主页"
|
||||
VITE_SITE_ANTHOR = "無名"
|
||||
VITE_SITE_KEYWORDS = "無名,个人主页"
|
||||
VITE_SITE_DES = "一个默默无闻的主页"
|
||||
VITE_SITE_URL = "imsyy.top"
|
||||
VITE_SITE_LOGO = "/images/icon/logo.png"
|
||||
|
||||
# 百度统计(若不需要可不填)
|
||||
VITE_SITE_BAIDUTONGJI = "14e9f35ff8bc67fd4bcb5f07a6e6655a"
|
||||
|
||||
# 简介文本
|
||||
VITE_DESC_HELLO = "Hello World !"
|
||||
VITE_DESC_TEXT = "一个建立于 21 世纪的小站,存活于互联网的边缘"
|
||||
@ -25,8 +31,9 @@ VITE_WEATHER_KEY = "57eaea5833ff1616cfd1ff2c4cf9b58a"
|
||||
VITE_SITE_START = "2020-10-24"
|
||||
|
||||
# 歌曲 API 地址
|
||||
## 请参照 https://github.com/imsyy/Meting-API#cloudflare-workers 进行 API 服务部署
|
||||
## 请参照 https://github.com/xizeyoupan/Meting-API#deno-deploy 进行 API 服务部署
|
||||
## 此处提供的服务可能会超量从而无法访问,请自行部署
|
||||
## 若使用QQ音乐歌单,歌曲数量最好不要超出50首
|
||||
## 备用:https://api.wuenci.com/meting/api/
|
||||
VITE_SONG_API = "https://api-meting.imsyy.top/api"
|
||||
# 歌曲服务器 ( netease-网易云, tencent-qq音乐 )
|
||||
|
@ -40,6 +40,9 @@
|
||||
* 在 `终端` 中输入:
|
||||
|
||||
```bash
|
||||
# 安装 yarn
|
||||
npm install -g yarn
|
||||
|
||||
# 安装依赖
|
||||
yarn install
|
||||
|
||||
|
10
index.html
10
index.html
@ -6,9 +6,9 @@
|
||||
<meta http-equiv="Access-Control-Allow-Origin" content="*">
|
||||
<!-- <meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests"> -->
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<meta name="description" content="一个默默无闻的主页" />
|
||||
<meta name="keywords" content="無名,个人主页" />
|
||||
<meta name="author" content="無名" />
|
||||
<meta name="description" content="<%- description %>" />
|
||||
<meta name="keywords" content="<%- keywords %>" />
|
||||
<meta name="author" content="<%- author %>" />
|
||||
<meta name="theme-color" content="#424242" />
|
||||
<title><%- title %></title>
|
||||
<!-- HarmonyOS Sans -->
|
||||
@ -24,16 +24,18 @@
|
||||
window.location.href =
|
||||
"https://support.dmeng.net/upgrade-your-browser.html?referrer=" + encodeURIComponent(window.location.href)
|
||||
</script>
|
||||
<% if (tongji) { %>
|
||||
<!-- 百度统计 -->
|
||||
<script>
|
||||
var _hmt = _hmt || [];
|
||||
(function () {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?14e9f35ff8bc67fd4bcb5f07a6e6655a";
|
||||
hm.src = "https://hm.baidu.com/hm.js?<%- tongji %>";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
<% } %>
|
||||
</head>
|
||||
|
||||
<body class="loading">
|
||||
|
@ -4,7 +4,7 @@
|
||||
"github": "https://github.com/imsyy/home",
|
||||
"home": "https://imsyy.top",
|
||||
"private": true,
|
||||
"version": "4.0.2",
|
||||
"version": "4.0.3",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --host",
|
||||
@ -16,6 +16,7 @@
|
||||
"aplayer": "^1.10.1",
|
||||
"axios": "^1.1.3",
|
||||
"element-plus": "^2.2.18",
|
||||
"fetch-jsonp": "^1.2.3",
|
||||
"pinia": "^2.0.23",
|
||||
"pinia-plugin-persistedstate": "^3.0.0",
|
||||
"terser": "^5.16.1",
|
||||
|
@ -18,9 +18,14 @@
|
||||
"icon": "/images/icon/email.png",
|
||||
"tip": "来封 Email ~",
|
||||
"url": "mailto:one@imsyy.top"
|
||||
}, {
|
||||
"name": "Twitter",
|
||||
"icon": "/images/icon/twitter.png",
|
||||
"tip": "你懂的 ~",
|
||||
"url": "https://twitter.com/iimmsyy"
|
||||
}, {
|
||||
"name": "Telegram",
|
||||
"icon": "/images/icon/telegram.png",
|
||||
"tip": "你懂的 ~",
|
||||
"url": "https://twitter.com/iimmsyy"
|
||||
"url": "https://t.me/bottom_user"
|
||||
}]
|
@ -1,4 +1,5 @@
|
||||
// import axios from "axios";
|
||||
import fetchJsonp from "fetch-jsonp";
|
||||
|
||||
/**
|
||||
* 音乐播放器
|
||||
@ -6,9 +7,38 @@
|
||||
|
||||
// 获取音乐播放列表
|
||||
export const getPlayerList = async (server, type, id) => {
|
||||
const res = await fetch(`${import.meta.env.VITE_SONG_API}?server=${server}&type=${type}&id=${id}`);
|
||||
return await res.json();
|
||||
}
|
||||
const res = await fetch(
|
||||
`${import.meta.env.VITE_SONG_API}?server=${server}&type=${type}&id=${id}`
|
||||
);
|
||||
const data = await res.json();
|
||||
|
||||
if (data[0].url.startsWith("@")) {
|
||||
const [handle, jsonpCallback, jsonpCallbackFunction, url] = data[0].url
|
||||
.split("@")
|
||||
.slice(1);
|
||||
const jsonpData = await fetchJsonp(url).then((res) => res.json());
|
||||
const domain = (
|
||||
jsonpData.req_0.data.sip.find((i) => !i.startsWith("http://ws")) ||
|
||||
jsonpData.req_0.data.sip[0]
|
||||
).replace("http://", "https://");
|
||||
|
||||
return data.map((v, i) => ({
|
||||
title: v.name || v.title,
|
||||
artist: v.artist || v.author,
|
||||
src: domain + jsonpData.req_0.data.midurlinfo[i].purl,
|
||||
pic: v.pic,
|
||||
lrc: v.lrc,
|
||||
}));
|
||||
} else {
|
||||
return data.map((v) => ({
|
||||
title: v.name || v.title,
|
||||
artist: v.artist || v.author,
|
||||
src: v.url,
|
||||
pic: v.pic,
|
||||
lrc: v.lrc,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 一言
|
||||
@ -16,26 +46,27 @@ export const getPlayerList = async (server, type, id) => {
|
||||
|
||||
// 获取一言数据
|
||||
export const getHitokoto = async () => {
|
||||
const res = await fetch("https://v1.hitokoto.cn");
|
||||
return await res.json();
|
||||
}
|
||||
const res = await fetch("https://v1.hitokoto.cn");
|
||||
return await res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* 天气
|
||||
*/
|
||||
|
||||
|
||||
// 获取高德地理位置信息
|
||||
export const getAdcode = async (key) => {
|
||||
const res = await fetch(`https://restapi.amap.com/v3/ip?key=${key}`);
|
||||
return await res.json();
|
||||
}
|
||||
const res = await fetch(`https://restapi.amap.com/v3/ip?key=${key}`);
|
||||
return await res.json();
|
||||
};
|
||||
|
||||
// 获取高德地理天气信息
|
||||
export const getWeather = async (key, city) => {
|
||||
const res = await fetch(`https://restapi.amap.com/v3/weather/weatherInfo?key=${key}&city=${city}`);
|
||||
return await res.json();
|
||||
}
|
||||
const res = await fetch(
|
||||
`https://restapi.amap.com/v3/weather/weatherInfo?key=${key}&city=${city}`
|
||||
);
|
||||
return await res.json();
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取配置
|
||||
@ -43,6 +74,6 @@ export const getWeather = async (key, city) => {
|
||||
|
||||
// 获取社交链接
|
||||
export const getSocialLinks = async () => {
|
||||
const res = await fetch("/socialLinks.json");
|
||||
return await res.json();
|
||||
}
|
||||
const res = await fetch("/socialLinks.json");
|
||||
return await res.json();
|
||||
};
|
||||
|
@ -1,375 +0,0 @@
|
||||
<template>
|
||||
<!-- 音乐控制面板 -->
|
||||
<div
|
||||
class="music"
|
||||
@mouseenter="volumeShow = true"
|
||||
@mouseleave="volumeShow = false"
|
||||
v-show="store.musicOpenState"
|
||||
>
|
||||
<div class="btns">
|
||||
<span @click="musicListShow = true">音乐列表</span>
|
||||
<span @click="store.musicOpenState = false">回到一言</span>
|
||||
</div>
|
||||
<div class="control">
|
||||
<go-start
|
||||
theme="filled"
|
||||
size="30"
|
||||
fill="#efefef"
|
||||
@click="changeMusicIndex(0)"
|
||||
/>
|
||||
<div class="state" @click="changePlayState">
|
||||
<play-one
|
||||
theme="filled"
|
||||
size="50"
|
||||
fill="#efefef"
|
||||
v-show="!store.playerState"
|
||||
/>
|
||||
<pause
|
||||
theme="filled"
|
||||
size="50"
|
||||
fill="#efefef"
|
||||
v-show="store.playerState"
|
||||
/>
|
||||
</div>
|
||||
<go-end
|
||||
theme="filled"
|
||||
size="30"
|
||||
fill="#efefef"
|
||||
@click="changeMusicIndex(1)"
|
||||
/>
|
||||
</div>
|
||||
<div class="menu">
|
||||
<div class="name" v-show="!volumeShow">
|
||||
<span>{{
|
||||
store.getPlayerData.name
|
||||
? store.getPlayerData.name + " - " + store.getPlayerData.artist
|
||||
: "未播放音乐"
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="volume" v-show="volumeShow">
|
||||
<div class="icon">
|
||||
<volume-mute
|
||||
theme="filled"
|
||||
size="24"
|
||||
fill="#efefef"
|
||||
v-if="volumeNum == 0"
|
||||
/>
|
||||
<volume-small
|
||||
theme="filled"
|
||||
size="24"
|
||||
fill="#efefef"
|
||||
v-else-if="volumeNum > 0 && volumeNum < 0.7"
|
||||
/>
|
||||
<volume-notice theme="filled" size="24" fill="#efefef" v-else />
|
||||
</div>
|
||||
<el-slider
|
||||
v-model="volumeNum"
|
||||
:show-tooltip="false"
|
||||
:min="0"
|
||||
:max="1"
|
||||
:step="0.01"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 音乐列表弹窗 -->
|
||||
<Transition name="fade">
|
||||
<div
|
||||
class="music-list"
|
||||
v-show="musicListShow"
|
||||
@click="musicListShow = false"
|
||||
>
|
||||
<Transition name="zoom">
|
||||
<div class="list" v-show="musicListShow" @click.stop>
|
||||
<close-one
|
||||
class="close"
|
||||
theme="filled"
|
||||
size="28"
|
||||
fill="#ffffff60"
|
||||
@click="musicListShow = false"
|
||||
/>
|
||||
<Player
|
||||
:songServer="playerData.server"
|
||||
:songType="playerData.type"
|
||||
:songId="playerData.id"
|
||||
ref="playerRef"
|
||||
/>
|
||||
<div class="error" v-if="!store.musicIsOk">
|
||||
<play-wrong theme="outline" size="60" />
|
||||
<span>音乐播放器加载失败</span>
|
||||
</div>
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
</Transition>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, watch, onMounted } from "vue";
|
||||
import {
|
||||
GoStart,
|
||||
PlayOne,
|
||||
Pause,
|
||||
GoEnd,
|
||||
CloseOne,
|
||||
VolumeMute,
|
||||
VolumeSmall,
|
||||
VolumeNotice,
|
||||
PlayWrong,
|
||||
} from "@icon-park/vue-next";
|
||||
import Player from "@/components/Player/old.vue";
|
||||
import { mainStore } from "@/store";
|
||||
const store = mainStore();
|
||||
|
||||
// 音量条数据
|
||||
let volumeShow = ref(false);
|
||||
let volumeNum = ref(
|
||||
localStorage.getItem("aplayer-setting")
|
||||
? JSON.parse(localStorage.getItem("aplayer-setting")).volume
|
||||
: 0.7
|
||||
);
|
||||
|
||||
// 播放列表数据
|
||||
let musicListShow = ref(false);
|
||||
const playerRef = ref(null);
|
||||
const musicDialog = ref(null);
|
||||
const playerData = reactive({
|
||||
server: import.meta.env.VITE_SONG_SERVER,
|
||||
type: import.meta.env.VITE_SONG_TYPE,
|
||||
id: import.meta.env.VITE_SONG_ID,
|
||||
});
|
||||
// 音乐播放暂停
|
||||
const changePlayState = () => {
|
||||
playerRef.value.playToggle();
|
||||
};
|
||||
// 音乐上下曲
|
||||
const changeMusicIndex = (type) => {
|
||||
if (type) {
|
||||
playerRef.value.changeSongPrev();
|
||||
} else {
|
||||
playerRef.value.changeSongNext();
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 空格键事件
|
||||
window.addEventListener("keydown", (e) => {
|
||||
if (e.code == "Space" && store.musicIsOk) {
|
||||
changePlayState();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 监听音量变化
|
||||
watch(
|
||||
() => volumeNum.value,
|
||||
(value) => {
|
||||
console.log(value);
|
||||
playerRef.value.changeVolume(value);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.music {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: #00000040;
|
||||
backdrop-filter: blur(10px);
|
||||
border-radius: 6px;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
animation: fade;
|
||||
-webkit-animation: fade 0.5s;
|
||||
|
||||
.btns {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 6px;
|
||||
|
||||
span {
|
||||
background: #ffffff26;
|
||||
padding: 2px 8px;
|
||||
border-radius: 6px;
|
||||
margin: 0px 6px;
|
||||
text-overflow: ellipsis;
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
|
||||
&:hover {
|
||||
background: #ffffff4d;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.control {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: space-evenly;
|
||||
width: 100%;
|
||||
|
||||
.state {
|
||||
.i-icon {
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
.i-icon {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
border-radius: 6px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 6px;
|
||||
transform: scale(1);
|
||||
|
||||
&:hover {
|
||||
background: #ffffff33;
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu {
|
||||
height: 26px;
|
||||
width: 100%;
|
||||
line-height: 26px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.name {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
overflow-x: hidden;
|
||||
white-space: nowrap;
|
||||
// font-size: 1.1rem;
|
||||
animation: fade;
|
||||
-webkit-animation: fade 0.3s;
|
||||
}
|
||||
|
||||
.volume {
|
||||
width: 100%;
|
||||
padding: 0 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: row;
|
||||
animation: fade;
|
||||
-webkit-animation: fade 0.3s;
|
||||
|
||||
.icon {
|
||||
margin-right: 12px;
|
||||
|
||||
span {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: block;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(*) {
|
||||
transition: none;
|
||||
}
|
||||
|
||||
:deep(.el-slider__button) {
|
||||
transition: 0.3s;
|
||||
}
|
||||
|
||||
.el-slider {
|
||||
margin-right: 12px;
|
||||
--el-slider-main-bg-color: #efefef;
|
||||
--el-slider-runway-bg-color: #ffffff40;
|
||||
--el-slider-button-size: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.music-list {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #00000080;
|
||||
backdrop-filter: blur(20px);
|
||||
z-index: 1;
|
||||
|
||||
.list {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
top: calc(50% - 300px);
|
||||
left: calc(50% - 320px);
|
||||
width: 640px;
|
||||
height: 600px;
|
||||
background-color: #ffffff66;
|
||||
border-radius: 6px;
|
||||
z-index: 999;
|
||||
|
||||
.close {
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
display: block;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
&:active {
|
||||
transform: scale(0.95);
|
||||
}
|
||||
}
|
||||
|
||||
.error {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
.i-icon {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 弹窗动画
|
||||
.fade-enter-active {
|
||||
animation: fade 0.3s ease-in-out;
|
||||
}
|
||||
.fade-leave-active {
|
||||
animation: fade 0.3s ease-in-out reverse;
|
||||
}
|
||||
.zoom-enter-active {
|
||||
animation: zoom 0.4s ease-in-out;
|
||||
}
|
||||
.zoom-leave-active {
|
||||
animation: zoom 0.3s ease-in-out reverse;
|
||||
}
|
||||
@keyframes zoom {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0) translateY(-600px);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1) translateY(0);
|
||||
}
|
||||
}
|
||||
</style>
|
@ -20,17 +20,10 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { MusicOne, PlayWrong } from "@icon-park/vue-next";
|
||||
import aplayer from "vue3-aplayer";
|
||||
import {
|
||||
h,
|
||||
ref,
|
||||
reactive,
|
||||
nextTick,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
watch,
|
||||
} from "vue";
|
||||
import fetchJsonp from "fetch-jsonp";
|
||||
import { h, ref, nextTick, onMounted } from "vue";
|
||||
import { MusicOne, PlayWrong } from "@icon-park/vue-next";
|
||||
import { getPlayerList } from "@/api";
|
||||
import { mainStore } from "@/store";
|
||||
|
||||
@ -107,42 +100,46 @@ const props = defineProps({
|
||||
// 初始化播放器
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
getPlayerList(props.songServer, props.songType, props.songId)
|
||||
.then((res) => {
|
||||
// 生成歌单信息
|
||||
playIndex.value = Math.floor(Math.random() * res.length);
|
||||
playListCount.value = res.length;
|
||||
// 更改播放器加载状态
|
||||
store.musicIsOk = true;
|
||||
console.log(
|
||||
"音乐加载完成",
|
||||
res,
|
||||
playIndex.value,
|
||||
playListCount.value,
|
||||
props.volume
|
||||
);
|
||||
// 生成歌单
|
||||
res.forEach((v) => {
|
||||
playList.value.push({
|
||||
title: v.name || v.title,
|
||||
artist: v.artist || v.author,
|
||||
src: v.url,
|
||||
pic: v.pic,
|
||||
lrc: v.lrc,
|
||||
try {
|
||||
getPlayerList(props.songServer, props.songType, props.songId).then(
|
||||
(res) => {
|
||||
console.log(res);
|
||||
// 生成歌单信息
|
||||
playIndex.value = Math.floor(Math.random() * res.length);
|
||||
playListCount.value = res.length;
|
||||
// 更改播放器加载状态
|
||||
store.musicIsOk = true;
|
||||
// 生成歌单
|
||||
res.forEach((v) => {
|
||||
playList.value.push({
|
||||
title: v.name || v.title,
|
||||
artist: v.artist || v.author,
|
||||
src: v.url || v.src,
|
||||
pic: v.pic,
|
||||
lrc: v.lrc,
|
||||
});
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
store.musicIsOk = false;
|
||||
ElMessage({
|
||||
message: "播放器加载失败",
|
||||
grouping: true,
|
||||
icon: h(PlayWrong, {
|
||||
theme: "filled",
|
||||
fill: "#efefef",
|
||||
}),
|
||||
});
|
||||
console.log(
|
||||
"音乐加载完成",
|
||||
playList.value,
|
||||
playIndex.value,
|
||||
playListCount.value,
|
||||
props.volume
|
||||
);
|
||||
}
|
||||
);
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
store.musicIsOk = false;
|
||||
ElMessage({
|
||||
message: "播放器加载失败",
|
||||
grouping: true,
|
||||
icon: h(PlayWrong, {
|
||||
theme: "filled",
|
||||
fill: "#efefef",
|
||||
}),
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@ -214,7 +211,7 @@ const changeSong = (type) => {
|
||||
defineExpose({ playToggle, changeVolume, changeSong });
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
<style lang="scss" scoped>
|
||||
.aplayer {
|
||||
width: 80%;
|
||||
background: transparent;
|
||||
@ -226,7 +223,7 @@ defineExpose({ playToggle, changeVolume, changeSong });
|
||||
.aplayer-info {
|
||||
margin-left: 0;
|
||||
background-color: #ffffff40;
|
||||
border-color: transparent;
|
||||
border-color: transparent !important;
|
||||
.aplayer-music {
|
||||
flex-grow: initial;
|
||||
margin-bottom: 2px;
|
||||
|
@ -1,319 +0,0 @@
|
||||
<template>
|
||||
<div ref="playerRef"></div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { MusicOne, PlayWrong } from "@icon-park/vue-next";
|
||||
import APlayer from "APlayer";
|
||||
import "aplayer/dist/APlayer.min.css";
|
||||
import {
|
||||
h,
|
||||
ref,
|
||||
reactive,
|
||||
nextTick,
|
||||
onMounted,
|
||||
onBeforeUnmount,
|
||||
watch,
|
||||
} from "vue";
|
||||
import { getPlayerList } from "@/api";
|
||||
import { mainStore } from "@/store";
|
||||
const store = mainStore();
|
||||
|
||||
const playerRef = ref();
|
||||
const state = reactive({
|
||||
instance: null,
|
||||
});
|
||||
const playerData = reactive({
|
||||
name: null,
|
||||
artist: null,
|
||||
lrc: null,
|
||||
});
|
||||
|
||||
// APlayer歌曲信息
|
||||
class Audio {
|
||||
// 音频艺术家
|
||||
// artist: String;
|
||||
// 音频名称
|
||||
// name: String;
|
||||
// 音频链接
|
||||
// url: String;
|
||||
// 音频封面
|
||||
// cover: String;
|
||||
// 歌词
|
||||
// lrc: String;
|
||||
|
||||
constructor(artist, name, url, cover, lrc) {
|
||||
this.artist = artist;
|
||||
this.name = name;
|
||||
this.url = url;
|
||||
this.cover = cover;
|
||||
this.lrc = lrc;
|
||||
}
|
||||
}
|
||||
|
||||
const props = defineProps({
|
||||
// 开启吸底模式
|
||||
fixed: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 开启迷你模式
|
||||
mini: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 音频自动播放
|
||||
autoplay: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 主题色
|
||||
theme: {
|
||||
type: String,
|
||||
default: "#efefef",
|
||||
},
|
||||
// 音频循环播放
|
||||
loop: {
|
||||
type: String,
|
||||
default: "all", //'all' | 'one' | 'none'
|
||||
},
|
||||
// 音频循环顺序
|
||||
order: {
|
||||
type: String,
|
||||
default: "random", //'list' | 'random'
|
||||
},
|
||||
// 预加载
|
||||
preload: {
|
||||
type: String,
|
||||
default: "auto", //'auto' | 'metadata' | 'none'
|
||||
},
|
||||
// 默认音量
|
||||
volume: {
|
||||
type: Number,
|
||||
default: 0.7,
|
||||
validator: (value) => {
|
||||
return value >= 0 && value <= 1;
|
||||
},
|
||||
},
|
||||
// 歌曲服务器 ( netease-网易云, tencent-qq音乐, kugou-酷狗, xiami-小米音乐, baidu-百度音乐 )
|
||||
songServer: {
|
||||
type: String,
|
||||
default: "netease", //'netease' | 'tencent' | 'kugou' | 'xiami' | 'baidu'
|
||||
},
|
||||
// 播放类型 ( song-歌曲, playlist-播放列表, album-专辑, search-搜索, artist-艺术家 )
|
||||
songType: {
|
||||
type: String,
|
||||
default: "playlist",
|
||||
},
|
||||
// id
|
||||
songId: {
|
||||
type: String,
|
||||
default: "7452421335",
|
||||
},
|
||||
// 互斥,阻止多个播放器同时播放,当前播放器播放时暂停其他播放器
|
||||
mutex: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
// 传递歌词方式
|
||||
lrcType: {
|
||||
type: Number,
|
||||
default: 3,
|
||||
},
|
||||
// 列表是否默认折叠
|
||||
listFolded: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
// 列表最大高度
|
||||
listMaxHeight: {
|
||||
type: String,
|
||||
default: "420px",
|
||||
},
|
||||
// 存储播放器设置的 localStorage key
|
||||
storageName: {
|
||||
type: String,
|
||||
default: "aplayer-setting",
|
||||
},
|
||||
});
|
||||
|
||||
// 初始化播放器
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
getPlayerList(props.songServer, props.songType, props.songId)
|
||||
.then((res) => {
|
||||
// console.log(res);
|
||||
// 更改播放器加载状态
|
||||
store.musicIsOk = true;
|
||||
console.log("音乐加载完成");
|
||||
let audioList = res.map(
|
||||
(value) =>
|
||||
new Audio(value.artist, value.name, value.url, value.pic, value.lrc)
|
||||
);
|
||||
state.instance = new APlayer({
|
||||
container: playerRef.value,
|
||||
fixed: props.fixed,
|
||||
mini: props.mini,
|
||||
autoplay: props.autoplay,
|
||||
theme: props.theme,
|
||||
loop: props.loop,
|
||||
order: props.order,
|
||||
preload: props.preload,
|
||||
volume: props.volume,
|
||||
mutex: props.mutex,
|
||||
lrcType: props.lrcType,
|
||||
listFolded: props.listFolded,
|
||||
listMaxHeight: props.listMaxHeight,
|
||||
storageName: props.storageName,
|
||||
audio: audioList,
|
||||
});
|
||||
|
||||
state.instance.on("play", () => {
|
||||
// 播放状态
|
||||
store.setPlayerState(state.instance.audio.paused);
|
||||
// 储存播放器信息
|
||||
store.setPlayerData(
|
||||
playerRef.value.getElementsByClassName("aplayer-title")[0]
|
||||
.innerHTML,
|
||||
playerRef.value
|
||||
.getElementsByClassName("aplayer-author")[0]
|
||||
.innerHTML.split("-")[1]
|
||||
.trim()
|
||||
);
|
||||
ElMessage({
|
||||
message:
|
||||
store.getPlayerData.name + " - " + store.getPlayerData.artist,
|
||||
grouping: true,
|
||||
icon: h(MusicOne, {
|
||||
theme: "filled",
|
||||
fill: "#efefef",
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
state.instance.on("pause", () => {
|
||||
// 播放状态
|
||||
store.setPlayerState(state.instance.audio.paused);
|
||||
});
|
||||
|
||||
state.instance.on("timeupdate", () => {
|
||||
if (playerRef.value) {
|
||||
playerData.lrc = playerRef.value.getElementsByClassName(
|
||||
"aplayer-lrc-current"
|
||||
)[0].innerHTML;
|
||||
}
|
||||
});
|
||||
})
|
||||
.catch(() => {
|
||||
store.musicIsOk = false;
|
||||
ElMessage({
|
||||
message: "播放器加载失败",
|
||||
grouping: true,
|
||||
icon: h(PlayWrong, {
|
||||
theme: "filled",
|
||||
fill: "#efefef",
|
||||
}),
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// 销毁播放器
|
||||
onBeforeUnmount(() => {
|
||||
state.instance.destroy();
|
||||
});
|
||||
|
||||
// 切换播放暂停事件
|
||||
const playToggle = () => {
|
||||
state.instance.toggle();
|
||||
};
|
||||
|
||||
// 切换音量事件
|
||||
const changeVolume = (value) => {
|
||||
state.instance.volume(value);
|
||||
};
|
||||
|
||||
// 切换上下曲
|
||||
const changeSongPrev = () => {
|
||||
state.instance.skipBack();
|
||||
};
|
||||
const changeSongNext = () => {
|
||||
state.instance.skipForward();
|
||||
};
|
||||
|
||||
// 暴露子组件方法
|
||||
defineExpose({ playToggle, changeVolume, changeSongPrev, changeSongNext });
|
||||
|
||||
// 监听歌词变化
|
||||
watch(
|
||||
() => playerData.lrc,
|
||||
(value) => {
|
||||
console.log(value);
|
||||
// 储存至 pinia
|
||||
store.setPlayerLrc(value);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang='scss' scoped>
|
||||
.aplayer {
|
||||
width: 80%;
|
||||
background: transparent;
|
||||
border-radius: 6px;
|
||||
:deep(.aplayer-body) {
|
||||
.aplayer-pic {
|
||||
display: none;
|
||||
}
|
||||
.aplayer-info {
|
||||
margin-left: 0;
|
||||
background-color: #ffffff40;
|
||||
border-color: transparent;
|
||||
.aplayer-title {
|
||||
font-size: 16px;
|
||||
}
|
||||
.aplayer-author {
|
||||
color: #efefef;
|
||||
}
|
||||
.aplayer-lrc {
|
||||
text-align: left;
|
||||
margin: 4px 0 0 6px;
|
||||
height: 38px;
|
||||
&::before,
|
||||
&::after {
|
||||
display: none;
|
||||
}
|
||||
p {
|
||||
color: #efefef;
|
||||
}
|
||||
.aplayer-lrc-current {
|
||||
font-size: 0.95rem;
|
||||
margin-bottom: 4px !important;
|
||||
}
|
||||
}
|
||||
.aplayer-controller {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
:deep(.aplayer-list) {
|
||||
margin-top: 6px;
|
||||
ol {
|
||||
li {
|
||||
border-color: transparent;
|
||||
&.aplayer-list-light {
|
||||
background: #ffffff40;
|
||||
border-radius: 6px;
|
||||
}
|
||||
&:hover {
|
||||
background: #ffffff26 !important;
|
||||
border-radius: 6px !important;
|
||||
}
|
||||
.aplayer-list-index,
|
||||
.aplayer-list-author {
|
||||
color: #efefef;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -15,3 +15,10 @@ pinia.use(piniaPluginPersistedstate);
|
||||
|
||||
app.use(pinia);
|
||||
app.mount('#app')
|
||||
|
||||
// PWA
|
||||
navigator.serviceWorker.addEventListener('controllerchange', () => {
|
||||
// 弹出更新提醒
|
||||
console.log("站点已更新,刷新后生效");
|
||||
ElMessage("站点已更新,刷新后生效");
|
||||
})
|
@ -36,6 +36,10 @@ export default ({
|
||||
inject: {
|
||||
data: {
|
||||
title: loadEnv(mode, process.cwd()).VITE_SITE_NAME,
|
||||
author: loadEnv(mode, process.cwd()).VITE_SITE_ANTHOR,
|
||||
keywords: loadEnv(mode, process.cwd()).VITE_SITE_KEYWORDS,
|
||||
description: loadEnv(mode, process.cwd()).VITE_SITE_DES,
|
||||
tongji: loadEnv(mode, process.cwd()).VITE_SITE_BAIDUTONGJI,
|
||||
},
|
||||
},
|
||||
}),
|
||||
@ -46,6 +50,8 @@ export default ({
|
||||
navigateFallbackAllowlist: [/^index.html$/]
|
||||
},
|
||||
workbox: {
|
||||
skipWaiting: true,
|
||||
clientsClaim: true,
|
||||
runtimeCaching: [{
|
||||
urlPattern: /(.*?)\.(js|css|woff2|woff|ttf)/, // js / css 静态资源缓存
|
||||
handler: 'CacheFirst',
|
||||
@ -63,9 +69,9 @@ export default ({
|
||||
],
|
||||
},
|
||||
manifest: {
|
||||
"name": "無名の主页",
|
||||
"short_name": "無名の主页",
|
||||
"description": "一个默默无闻的主页",
|
||||
"name": loadEnv(mode, process.cwd()).VITE_SITE_NAME,
|
||||
"short_name": loadEnv(mode, process.cwd()).VITE_SITE_NAME,
|
||||
"description": loadEnv(mode, process.cwd()).VITE_SITE_DES,
|
||||
"display": "standalone",
|
||||
"start_url": "/",
|
||||
"theme_color": "#424242",
|
||||
|
@ -2024,6 +2024,11 @@ fastq@^1.6.0:
|
||||
dependencies:
|
||||
reusify "^1.0.4"
|
||||
|
||||
fetch-jsonp@^1.2.3:
|
||||
version "1.2.3"
|
||||
resolved "https://registry.yarnpkg.com/fetch-jsonp/-/fetch-jsonp-1.2.3.tgz#ae99a867095cb1ce5c39fac601d70d1084db122f"
|
||||
integrity sha512-C13k1o7R9JTN1wmhKkrW5bU/00LwixXnkufQUR6Rbf4KCS0i8mycQaovt4WVbHnA2NKgi7Ryp9Whpy/CGcij6Q==
|
||||
|
||||
filelist@^1.0.1:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.npmmirror.com/filelist/-/filelist-1.0.4.tgz"
|
||||
|
Loading…
Reference in New Issue
Block a user