douyin/src/pages/home/MusicRankList.vue
2024-04-23 18:59:33 +08:00

757 lines
23 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="MusicRankList" @scroll="scroll">
<dy-back mode="light" img="back" @click="router.back()" class="fixed-back" direction="left" />
<div class="fixed-header" :style="fixedStyle">
<span class="f16">抖音音乐榜</span>
</div>
<!-- indicator没有像me页面那样做应该那样做-->
<div class="content">
<div class="l-header">
<img src="../../assets/img/icon/music-rank-list.webp" alt="" />
<div class="update-time">更新于{{ _dateFormat(data.updateTime, 'D') }}</div>
</div>
<Indicator
name="musicRankList"
tabStyleWidth="33%"
:tabTexts="['热歌榜', '飙升樘', '原创榜']"
v-model:active-index="data.contentIndex"
>
</Indicator>
<SlideHorizontal name="musicRankList" v-model:index="data.contentIndex">
<SlideItem>
<div class="list">
<div
class="item"
:key="index"
v-for="(item, index) in data.hotList"
@click="togglePlay(item, data.hotList)"
>
<div class="top">
<div class="rank-wrapper">
<img
v-if="index === 0"
src="../../assets/img/icon/rank1.webp"
alt=""
class="rank"
/>
<img
v-else-if="index === 1"
src="../../assets/img/icon/rank2.webp"
alt=""
class="rank"
/>
<img
v-else-if="index === 2"
src="../../assets/img/icon/rank3.webp"
alt=""
class="rank"
/>
<div v-else class="rank">{{ index + 1 }}</div>
</div>
<div class="right">
<div class="music">
<div class="cover-wrapper">
<img v-lazy="_checkImgUrl(item.cover)" alt="" class="cover" />
<img
v-if="!item.is_play"
src="../../assets/img/icon/play-white.png"
alt=""
class="play"
/>
<img
v-if="item.is_play"
src="../../assets/img/icon/pause-white.png"
alt=""
class="play"
/>
</div>
<div class="desc">
<span class="name">{{ item.name }}</span>
<div class="author">{{ item.author }}</div>
<div class="desc-bottom">
<div class="duration">
{{ _duration(item.duration) }}
</div>
<div class="use_count">{{ _formatNumber(item.use_count) }}人使用</div>
</div>
</div>
</div>
<div class="option">
<img
v-if="item.is_collect"
src="../../assets/img/icon/star-yellow.png"
alt=""
@click.stop="toggleCollect(item)"
/>
<img
v-else
src="../../assets/img/icon/star-white.png"
alt=""
@click.stop="toggleCollect(item)"
/>
<img
src="../../assets/img/icon/menu2-white.png"
alt=""
@click.stop="nav('/home/music')"
/>
</div>
</div>
</div>
<div class="bottom" v-if="item.is_collect">
<div class="left">
<img src="../../assets/img/music-cover/2.jpg" alt="" class="avatar" />
<div class="desc">
<div class="name">{{ item.author }}</div>
<div class="follow_count">粉丝83.4w</div>
</div>
</div>
<dy-button type="primary">关注</dy-button>
<div class="arrow"></div>
</div>
</div>
</div>
</SlideItem>
<SlideItem>
<div class="list">
<div
class="item"
:key="index"
v-for="(item, index) in data.hotList"
@click="togglePlay(item, data.hotList)"
>
<div class="top">
<div class="rank-wrapper">
<img
v-if="index === 0"
src="../../assets/img/icon/rank1.webp"
alt=""
class="rank"
/>
<img
v-else-if="index === 1"
src="../../assets/img/icon/rank2.webp"
alt=""
class="rank"
/>
<img
v-else-if="index === 2"
src="../../assets/img/icon/rank3.webp"
alt=""
class="rank"
/>
<div v-else class="rank">{{ index + 1 }}</div>
</div>
<div class="right">
<div class="music">
<div class="cover-wrapper">
<img v-lazy="_checkImgUrl(item.cover)" alt="" class="cover" />
<img
v-if="!item.is_play"
src="../../assets/img/icon/play-white.png"
alt=""
class="play"
/>
<img
v-if="item.is_play"
src="../../assets/img/icon/pause-white.png"
alt=""
class="play"
/>
</div>
<div class="desc">
<span class="name">{{ item.name }}</span>
<div class="author">{{ item.author }}</div>
<div class="desc-bottom">
<div class="duration">
{{ _duration(item.duration) }}
</div>
<div class="use_count">{{ _formatNumber(item.use_count) }}人使用</div>
</div>
</div>
</div>
<div class="option">
<img
v-if="item.is_collect"
src="../../assets/img/icon/star-yellow.png"
alt=""
@click.stop="toggleCollect(item)"
/>
<img
v-else
src="../../assets/img/icon/star-white.png"
alt=""
@click.stop="toggleCollect(item)"
/>
<img
src="../../assets/img/icon/menu2-white.png"
alt=""
@click.stop="nav('/home/music')"
/>
</div>
</div>
</div>
<div class="bottom" v-if="item.is_collect">
<div class="left">
<img src="../../assets/img/music-cover/2.jpg" alt="" class="avatar" />
<div class="desc">
<div class="name">{{ item.author }}</div>
<div class="follow_count">粉丝83.4w</div>
</div>
</div>
<dy-button type="primary">关注</dy-button>
<div class="arrow"></div>
</div>
</div>
</div>
</SlideItem>
<SlideItem>
<div class="list">
<div
class="item"
:key="index"
v-for="(item, index) in data.hotList"
@click="togglePlay(item, data.hotList)"
>
<div class="top">
<div class="rank-wrapper">
<img
v-if="index === 0"
src="../../assets/img/icon/rank1.webp"
alt=""
class="rank"
/>
<img
v-else-if="index === 1"
src="../../assets/img/icon/rank2.webp"
alt=""
class="rank"
/>
<img
v-else-if="index === 2"
src="../../assets/img/icon/rank3.webp"
alt=""
class="rank"
/>
<div v-else class="rank">{{ index + 1 }}</div>
</div>
<div class="right">
<div class="music">
<div class="cover-wrapper">
<img v-lazy="_checkImgUrl(item.cover)" alt="" class="cover" />
<img
v-if="!item.is_play"
src="../../assets/img/icon/play-white.png"
alt=""
class="play"
/>
<img
v-if="item.is_play"
src="../../assets/img/icon/pause-white.png"
alt=""
class="play"
/>
</div>
<div class="desc">
<span class="name">{{ item.name }}</span>
<div class="author">{{ item.author }}</div>
<div class="desc-bottom">
<div class="duration">
{{ _duration(item.duration) }}
</div>
<div class="use_count">{{ _formatNumber(item.use_count) }}人使用</div>
</div>
</div>
</div>
<div class="option">
<img
v-if="item.is_collect"
src="../../assets/img/icon/star-yellow.png"
alt=""
@click.stop="toggleCollect(item)"
/>
<img
v-else
src="../../assets/img/icon/star-white.png"
alt=""
@click.stop="toggleCollect(item)"
/>
<img
src="../../assets/img/icon/menu2-white.png"
alt=""
@click.stop="nav('/home/music')"
/>
</div>
</div>
</div>
<div class="bottom" v-if="item.is_collect">
<div class="left">
<img src="../../assets/img/music-cover/2.jpg" alt="" class="avatar" />
<div class="desc">
<div class="name">{{ item.author }}</div>
<div class="follow_count">粉丝83.4w</div>
</div>
</div>
<dy-button type="primary">关注</dy-button>
<div class="arrow"></div>
</div>
</div>
</div>
</SlideItem>
</SlideHorizontal>
</div>
</div>
</template>
<script setup lang="ts">
import { useRouter } from 'vue-router'
import { computed, onDeactivated, onMounted, onUnmounted, reactive } from 'vue'
import { _checkImgUrl, _dateFormat, _duration, _formatNumber, _notice } from '@/utils/index.jsx'
import { useNav } from '@/utils/hooks/useNav'
defineOptions({
name: 'MusicRankList'
})
const router = useRouter()
const nav = useNav()
const data = reactive({
contentIndex: 0,
hotList: [
{
name: '龙卷风',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/5605.mp3',
cover: new URL('../../assets/img/music-cover/1.jpg', import.meta.url).href,
author: '周杰伦',
duration: 99,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '爱在西元前',
mp3: 'https://m3.8js.net:99/1916/501204165042405.mp3',
cover: new URL('../../assets/img/music-cover/2.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '蜗牛',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/3684.mp3',
cover: new URL('../../assets/img/music-cover/3.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '半岛铁盒',
mp3: 'https://m3.8js.net:99/2016n/46/94745.mp3',
cover: new URL('../../assets/img/music-cover/4.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '轨迹',
mp3: 'https://m3.8js.net:99/1832/411204324135934.mp3',
cover: new URL('../../assets/img/music-cover/5.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '七里香',
mp3: 'https://m3.8js.net:99/2016n/14/53717.mp3',
cover: new URL('../../assets/img/music-cover/6.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '发如雪',
mp3: 'https://m3.8js.net:99/2014/211204142150965.mp3',
cover: new URL('../../assets/img/music-cover/7.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '霍元甲',
mp3: 'https://m3.8js.net:99/1921/261204212643140.mp3',
cover: new URL('../../assets/img/music-cover/8.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '千里之外(周杰伦/费玉清)',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/121.mp3',
cover: new URL('../../assets/img/music-cover/9.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '菊花台',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/2022.mp3',
cover: new URL('../../assets/img/music-cover/10.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '不能说的秘密',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/165.mp3',
cover: new URL('../../assets/img/music-cover/11.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '牛仔很忙',
mp3: 'http://im5.tongbu.com/rings/singerring/zt_uunGo_1/219.mp3',
cover: new URL('../../assets/img/music-cover/12.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '给我一首歌的时间',
mp3: 'https://m3.8js.net:99/1938/041204380445445.mp3',
cover: new URL('../../assets/img/music-cover/18.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '烟花易冷',
mp3: 'https://m3.8js.net:99/1828/051204280535192.mp3',
cover: new URL('../../assets/img/music-cover/14.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '惊叹号',
mp3: 'https://m3.8js.net:99/20111103/150.mp3',
cover: new URL('../../assets/img/music-cover/15.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '明明就',
mp3: 'https://m3.8js.net:99/2016n/27/96537.mp3',
cover: new URL('../../assets/img/music-cover/16.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '算什么男人',
mp3: 'https://m3.8js.net:99/20150107/429.mp3',
cover: new URL('../../assets/img/music-cover/17.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
},
{
name: '告白气球',
mp3: 'https://m3.8js.net:99/20161016/481.mp3',
cover: new URL('../../assets/img/music-cover/18.jpg', import.meta.url).href,
author: '周杰伦',
duration: 60,
use_count: 37441000,
is_collect: false,
is_play: false
}
],
updateTime: Date.now(),
audio: new Audio(),
scrollTop: -1
})
const fixedStyle = computed(() => {
return {
opacity: data.scrollTop / 120 > 1 ? 1 : data.scrollTop / 120
}
})
onMounted(() => {
data.hotList = data.hotList.concat(data.hotList).concat(data.hotList).concat(data.hotList)
data.hotList = data.hotList.slice(0, 50)
})
onUnmounted(stopPlay)
onDeactivated(stopPlay)
function scroll(e) {
data.scrollTop = e.target.scrollTop
}
function toggleCollect(item) {
item.is_collect = !item.is_collect
if (item.is_collect) {
_notice('收藏成功')
} else {
_notice('取消收藏')
}
}
function togglePlay(item, list) {
list.map((v) => {
if (v.name !== item.name) {
v.is_play = false
}
})
item.is_play = !item.is_play
if (item.is_play) {
data.audio.pause()
data.audio.src = item.mp3
data.audio.currentTime = 0
data.audio.play()
data.audio.addEventListener('ended', () => (item.is_play = false))
} else {
stopPlay()
}
}
function stopPlay() {
data.audio.pause()
data.audio.currentTime = 0
data.audio.removeEventListener('ended', null)
}
</script>
<style scoped lang="less">
@import '../../assets/less/index';
.MusicRankList {
position: fixed;
left: 0;
right: 0;
bottom: 0;
top: 0;
overflow: auto;
color: white;
font-size: 14rem;
.fixed-back {
position: fixed;
left: 10rem;
top: 20rem;
z-index: 3;
}
.fixed-header {
background: var(--main-bg);
width: 100%;
position: fixed;
z-index: 2;
height: 60rem;
display: flex;
align-items: center;
justify-content: center;
}
.l-header {
position: relative;
display: flex;
justify-content: center;
img {
width: 100vw;
}
.update-time {
position: absolute;
background: rgba(172, 107, 251, 0.5);
bottom: 20rem;
border-radius: 1rem;
padding: 2rem 30rem;
}
}
.content {
display: flex;
flex-direction: column;
align-items: center;
.indicator-ctn {
width: 85%;
}
.list {
.item {
padding: 20rem 15rem;
padding-bottom: 0;
display: flex;
flex-direction: column;
.top {
display: flex;
align-items: center;
.rank-wrapper {
.rank {
width: 18rem;
height: 18rem;
line-height: 18rem;
text-align: center;
margin-right: 15rem;
}
}
.right {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
.music {
display: flex;
.cover-wrapper {
margin-right: 10rem;
position: relative;
display: flex;
align-items: center;
justify-content: center;
.play {
width: 30rem;
height: 30rem;
position: absolute;
}
.cover {
border-radius: 2rem;
@width: 60rem;
width: @width;
object-fit: cover;
height: @width;
}
}
.desc {
display: flex;
flex-direction: column;
justify-content: space-between;
.name {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
max-width: 40vw;
}
.author,
.desc-bottom {
font-size: 12rem;
color: var(--second-text-color);
}
.desc-bottom {
display: flex;
.duration {
margin-right: 14rem;
position: relative;
&:after {
content: '';
position: absolute;
width: 1.5px;
height: 1.5px;
border-radius: 50%;
background: white;
right: -7px;
top: 7px;
}
}
}
}
}
.option {
img {
width: 20rem;
height: 20rem;
margin-left: 20rem;
}
}
}
}
.bottom {
background: var(--second-btn-color-tran);
padding: 10rem 15rem;
margin-left: 33rem;
margin-top: 15rem;
display: flex;
justify-content: space-between;
align-items: center;
position: relative;
.arrow {
width: 0;
height: 0;
border: 8rem solid transparent;
border-bottom: 8rem solid var(--second-btn-color-tran);
position: absolute;
left: 20rem;
top: -15rem;
}
.left {
display: flex;
.avatar {
width: 35rem;
height: 35rem;
margin-right: 10rem;
border-radius: 50%;
}
}
.button {
width: 60rem;
height: 25rem;
}
}
}
}
}
}
</style>