基本完成歌曲详情
This commit is contained in:
parent
2701d0cfe7
commit
25af6cfedb
@ -22,6 +22,7 @@
|
||||
"core-js": "^3.6.5",
|
||||
"dayjs": "^1.10.7",
|
||||
"element-plus": "^1.1.0-beta.19",
|
||||
"lodash": "^4.17.21",
|
||||
"pubsub-js": "^1.9.3",
|
||||
"unplugin-vue-components": "^0.15.6",
|
||||
"vue": "^3.2.16",
|
||||
|
@ -228,7 +228,7 @@ body {
|
||||
}
|
||||
}
|
||||
#wpSongDetail {
|
||||
|
||||
z-index: 100;
|
||||
}
|
||||
#wpPlayingList {
|
||||
// position: absolute;
|
||||
|
27
src/components/AlbumSpan.vue
Normal file
27
src/components/AlbumSpan.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<script setup>
|
||||
import { ref, defineProps, onUnmounted, onDeactivated } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const props = defineProps({
|
||||
album: Object,
|
||||
onLeave: Function,
|
||||
});
|
||||
const router = useRouter();
|
||||
// console.log(props.artists);
|
||||
|
||||
const click = (id) => {
|
||||
props.onLeave?.();
|
||||
router.push("/album/" + id);
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<span
|
||||
@click="click(album.id)"
|
||||
style="margin-right: 4px; cursor: pointer"
|
||||
>{{ album.name }}</span
|
||||
>
|
||||
</template>
|
||||
|
||||
<script></script>
|
||||
|
||||
<style></style>
|
@ -2,14 +2,17 @@
|
||||
import { ref, defineProps, onUnmounted, onDeactivated } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
|
||||
const props = defineProps({ artists: Array, onLeave: Function });
|
||||
const router = useRouter()
|
||||
const props = defineProps({
|
||||
artists: Array,
|
||||
onLeave: Function,
|
||||
});
|
||||
const router = useRouter();
|
||||
// console.log(props.artists);
|
||||
|
||||
const click = (id) => {
|
||||
props.onLeave?.()
|
||||
router.push('/singer/' + id)
|
||||
}
|
||||
props.onLeave?.();
|
||||
router.push("/singer/" + id);
|
||||
};
|
||||
</script>
|
||||
<template>
|
||||
<template v-for="(ar, idx) of artists" key="idx">
|
||||
@ -22,9 +25,7 @@ const click = (id) => {
|
||||
</template>
|
||||
|
||||
<script>
|
||||
// export default {
|
||||
// props: ["artists"],
|
||||
// };
|
||||
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
|
@ -1,42 +1,105 @@
|
||||
<script setup>
|
||||
import { ref, onUnmounted, watch } from "vue";
|
||||
import { ref, onUnmounted, watch, toRaw, nextTick } from "vue";
|
||||
import { NButton, NIcon } from "naive-ui";
|
||||
import { useStore } from "vuex";
|
||||
import svgChevrongDown from "@/assets/svgs/ChevronDown.svg";
|
||||
import pubsub from "pubsub-js";
|
||||
import ArtistsSpan from "@/components/ArtistsSpan.vue";
|
||||
import AlbumSpan from "@/components/AlbumSpan.vue";
|
||||
import _ from 'lodash';
|
||||
|
||||
const store = useStore();
|
||||
|
||||
const songInfo = ref(null);
|
||||
const song = ref(null);
|
||||
const lyric = ref(null);
|
||||
const lyArr = ref([]);
|
||||
const coverAngle = ref(0);
|
||||
const currTime = ref(0);
|
||||
const totalTime = ref(0);
|
||||
const currentLyricLine = ref(false);
|
||||
const currentLyricIdx = ref(-1);
|
||||
const lyList = ref('')
|
||||
|
||||
let interval;
|
||||
//#region 唱机
|
||||
let interval = [];
|
||||
watch(
|
||||
() => store.state.settings.playing,
|
||||
(val) => {
|
||||
if(val){
|
||||
interval = setInterval(() => {
|
||||
coverAngle.value += .5
|
||||
}, 100);
|
||||
() => [store.state.settings.playing, store.state.showSongDetail],
|
||||
([playing, showSongDetail]) => {
|
||||
if (playing && showSongDetail) {
|
||||
interval.push(
|
||||
setInterval(() => {
|
||||
coverAngle.value += 0.5;
|
||||
}, 40)
|
||||
);
|
||||
} else {
|
||||
clearInterval(interval)
|
||||
interval.map((item) => clearInterval(item));
|
||||
interval = [];
|
||||
}
|
||||
|
||||
},
|
||||
{ immediate: true }
|
||||
);
|
||||
//#endregion
|
||||
|
||||
//#region 歌词
|
||||
|
||||
//处理歌词
|
||||
const handleLyric = (ly) => {
|
||||
lyArr.value = [];
|
||||
const lines = ly.split("\n");
|
||||
const re = /^\[(\d+):(\d+(.\d+)?)\](.*)$/;
|
||||
lines.forEach((line) => {
|
||||
// console.log(line);
|
||||
// let match = re.exec(line)
|
||||
// console.log(match);
|
||||
// console.log(Number(line.replace(re,'$1')))
|
||||
if (re.test(line))
|
||||
lyArr.value.push({
|
||||
time:
|
||||
Number(line.replace(re, "$1")) * 60 * 1000 +
|
||||
Number(line.replace(re, "$2")) * 1000,
|
||||
lyric: line.replace(re, "$4"),
|
||||
origin: line,
|
||||
});
|
||||
});
|
||||
// lyList.value.scrollTop = 0;
|
||||
// console.log(lyArr.value);
|
||||
};
|
||||
|
||||
watch(
|
||||
()=> currentLyricIdx.value,
|
||||
(val)=>{
|
||||
if(val>0){
|
||||
// let elContent = document.getElementsByClassName('ly-content')[0]
|
||||
let el = document.getElementsByClassName('ly-key-lines')[val]
|
||||
lyList.value.scrollTop = el.offsetTop - 100
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
)
|
||||
|
||||
//#endregion
|
||||
|
||||
//#region 处理消息订阅
|
||||
|
||||
const token = pubsub.subscribe("zp", (msg, data) => {
|
||||
switch (msg) {
|
||||
case "zp.songInfo":
|
||||
console.log("SongDetail: 收到歌曲详细信息。", data);
|
||||
songInfo.value = data;
|
||||
// console.log("SongDetail: 收到歌曲详细信息。", data);
|
||||
song.value = data;
|
||||
break;
|
||||
case "zp.lyric":
|
||||
lyric.value = data;
|
||||
handleLyric(data);
|
||||
break;
|
||||
case "zp.progress":
|
||||
totalTime.value = data.total * 1000;
|
||||
currTime.value = data.progress * 1000;
|
||||
currentLyricIdx.value = _.findLastIndex(lyArr.value,(item) => {
|
||||
return item.time < currTime.value;
|
||||
});
|
||||
// console.log(currentLyricIdx.value);
|
||||
break;
|
||||
}
|
||||
});
|
||||
@ -61,8 +124,9 @@ onUnmounted(() => {
|
||||
</template>
|
||||
</n-button>
|
||||
</div>
|
||||
<div id="sdContent" v-if="songInfo">
|
||||
<div id="sdContent" v-if="song">
|
||||
<div class="detail">
|
||||
<!-- 唱机 -->
|
||||
<div class="disk">
|
||||
<div class="styli">
|
||||
<img
|
||||
@ -74,21 +138,58 @@ onUnmounted(() => {
|
||||
<img class="disk" src="@/assets/images/disk.png" />
|
||||
<img
|
||||
class="cover"
|
||||
:src="songInfo.album.picUrl"
|
||||
:src="song.album.picUrl"
|
||||
:style="{ transform: 'rotateZ(' + coverAngle + 'deg)' }"
|
||||
/>
|
||||
</div>
|
||||
<div class="pic"></div>
|
||||
</div>
|
||||
<div class="song">
|
||||
歌曲
|
||||
<div class="name"></div>
|
||||
<div class="others"></div>
|
||||
<div class="ly"></div>
|
||||
<div class="name">{{ song.name }}</div>
|
||||
<div class="alias">
|
||||
{{ song.alias.length > 0 ? song.alias.join(" ") : "" }}
|
||||
</div>
|
||||
<div class="others">
|
||||
<div class="album">
|
||||
专辑:
|
||||
<AlbumSpan
|
||||
:album="song.album"
|
||||
:onLeave="
|
||||
() => {
|
||||
pubsub.publish('zp.toggleSongDetail');
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
<div class="artists">
|
||||
歌手:
|
||||
<ArtistsSpan
|
||||
:artists="song.artists"
|
||||
:onLeave="
|
||||
() => {
|
||||
pubsub.publish('zp.toggleSongDetail');
|
||||
}
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ly" v-if="lyric" ref="lyList">
|
||||
<div class="ly-content" >
|
||||
<div
|
||||
class="ly-line"
|
||||
v-for="(line, idx) in lyArr"
|
||||
key="idx"
|
||||
:class="{ current: currentLyricIdx == idx, ['ly-key-lines']: true, }"
|
||||
>
|
||||
{{ line.lyric }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="ly-right"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="comments"></div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
@ -115,6 +216,7 @@ export default {};
|
||||
right: 0;
|
||||
bottom: 64px;
|
||||
background-color: #f6f6f6;
|
||||
overflow-y: auto;
|
||||
|
||||
.styli {
|
||||
position: relative;
|
||||
@ -138,7 +240,7 @@ export default {};
|
||||
height: 50px;
|
||||
}
|
||||
.disk {
|
||||
--back-color: #eee;
|
||||
--back-color: #ddd;
|
||||
--width-num: 340;
|
||||
--padding-num: 10;
|
||||
--width: calc(var(--width-num) * 1px);
|
||||
@ -170,7 +272,57 @@ export default {};
|
||||
}
|
||||
.song {
|
||||
position: relative;
|
||||
width: 400px;
|
||||
width: 330px;
|
||||
margin-left: 30px;
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
|
||||
.name {
|
||||
font-size: 24px;
|
||||
font-weight: 500;
|
||||
margin-top: 10px;
|
||||
}
|
||||
.others {
|
||||
// display: flex;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
.album {
|
||||
flex: 1;
|
||||
}
|
||||
.artists {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.ly {
|
||||
position: relative;
|
||||
margin-top: 15px;
|
||||
height: 290px;
|
||||
overflow-y: auto;
|
||||
|
||||
// background-image : linear-gradient(180deg,hsla(255,0%,100%,0),rgba(255, 255, 255, 0.521));
|
||||
|
||||
.ly-content {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin-bottom: 100px;
|
||||
// display: flex;
|
||||
// flex-direction: column;
|
||||
// justify-items: center;
|
||||
|
||||
.ly-line {
|
||||
min-height: 32px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.current {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ const info = reactive({
|
||||
name: "",
|
||||
artists: [],
|
||||
album: {},
|
||||
alias: [],
|
||||
duration: 0,
|
||||
mv: 0,
|
||||
});
|
||||
@ -33,9 +34,11 @@ const songInfo = (id, im) => {
|
||||
getSongDetial(id)
|
||||
.then((res) => {
|
||||
showInfo.value = true;
|
||||
console.log('SongInfo: 获取歌曲详细信息',res.data.songs[0]);
|
||||
info.name = res.data.songs[0].name;
|
||||
info.artists = res.data.songs[0].ar;
|
||||
info.album = res.data.songs[0].al;
|
||||
info.alias = res.data.songs[0].alia;
|
||||
info.duration = res.data.songs[0].dt;
|
||||
info.mv = res.data.songs[0].mv;
|
||||
currTime.value = "00:00";
|
||||
@ -105,9 +108,9 @@ onUnmounted(() => {
|
||||
//#endregion
|
||||
|
||||
const toggleSongDetail = () => {
|
||||
console.log(
|
||||
"显示/隐藏歌曲详细界面,包括歌词、模拟唱机以及其他操作。"
|
||||
);
|
||||
// console.log(
|
||||
// "显示/隐藏歌曲详细界面,包括歌词、模拟唱机以及其他操作。"
|
||||
// );
|
||||
pubsub.publish("zp.toggleSongDetail");
|
||||
};
|
||||
</script>
|
||||
|
Loading…
x
Reference in New Issue
Block a user