mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-14 14:50:50 +08:00
首页 歌手推荐 歌单分类 最热音乐没写完
This commit is contained in:
@@ -6,7 +6,13 @@
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite App</title>
|
||||
<link rel="stylesheet" href="//at.alicdn.com/t/font_2685283_d0nzj20hrzk.css">
|
||||
<link rel="stylesheet" href="//at.alicdn.com/t/font_2685283_m5ii4umo6k8.css">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/4.1.1/animate.min.css" />
|
||||
<style>
|
||||
:root {
|
||||
--animate-delay: 0.5s;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
13
package-lock.json
generated
13
package-lock.json
generated
@@ -465,6 +465,14 @@
|
||||
"postcss-value-parser": "^4.1.0"
|
||||
}
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.21.1",
|
||||
"resolved": "https://registry.npm.taobao.org/axios/download/axios-0.21.1.tgz",
|
||||
"integrity": "sha1-IlY0gZYvTWvemnbVFu8OXTwJsrg=",
|
||||
"requires": {
|
||||
"follow-redirects": "^1.10.0"
|
||||
}
|
||||
},
|
||||
"balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npm.taobao.org/balanced-match/download/balanced-match-1.0.2.tgz",
|
||||
@@ -866,6 +874,11 @@
|
||||
"to-regex-range": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"follow-redirects": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.nlark.com/follow-redirects/download/follow-redirects-1.14.1.tgz?cache=0&sync_timestamp=1620555300559&other_urls=https%3A%2F%2Fregistry.nlark.com%2Ffollow-redirects%2Fdownload%2Ffollow-redirects-1.14.1.tgz",
|
||||
"integrity": "sha1-2RFN7Qoc/dM04WTmZirQK/2R/0M="
|
||||
},
|
||||
"fs-extra": {
|
||||
"version": "10.0.0",
|
||||
"resolved": "https://registry.nlark.com/fs-extra/download/fs-extra-10.0.0.tgz",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"dependencies": {
|
||||
"@tailwindcss/postcss7-compat": "^2.2.4",
|
||||
"autoprefixer": "^9.8.6",
|
||||
"axios": "^0.21.1",
|
||||
"postcss": "^7.0.36",
|
||||
"sass": "^1.35.2",
|
||||
"tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.4",
|
||||
|
||||
34
src/api/home.ts
Normal file
34
src/api/home.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
import request from "@/utils/request";
|
||||
import { IHotSinger } from "@/type/singer";
|
||||
import { ISearchKeyword } from "@/type/search";
|
||||
import { IPlayListSort } from "@/type/playlist";
|
||||
import { IRecommendMusic } from "@/type/music";
|
||||
|
||||
interface IHotSingerParams {
|
||||
offset: number;
|
||||
limit: number;
|
||||
}
|
||||
|
||||
interface IRecommendMusicParams {
|
||||
limit: number;
|
||||
}
|
||||
|
||||
// 获取热门歌手
|
||||
export const getHotSinger = (params: IHotSingerParams) => {
|
||||
return request.get<IHotSinger>("/top/artists", { params });
|
||||
};
|
||||
|
||||
// 获取搜索推荐词
|
||||
export const getSearchKeyword = () => {
|
||||
return request.get<ISearchKeyword>("/search/default");
|
||||
};
|
||||
|
||||
// 获取歌单分类
|
||||
export const getPlaylistCategory = () => {
|
||||
return request.get<IPlayListSort>("/playlist/catlist");
|
||||
};
|
||||
|
||||
// 获取推荐音乐
|
||||
export const getRecommendMusic = (params: IRecommendMusicParams) => {
|
||||
return request.get<IRecommendMusic>("/personalized/newsong", { params });
|
||||
};
|
||||
@@ -2,31 +2,80 @@
|
||||
<div class="layout-page">
|
||||
<div class="layout-main">
|
||||
<app-menu class="menu" :menus="menus" />
|
||||
<router-view class="main"></router-view>
|
||||
<div class="main">
|
||||
<div class="search-box flex">
|
||||
<div class="search-box-input flex-1">
|
||||
<n-input
|
||||
size="large"
|
||||
round
|
||||
:placeholder="searchKeyword"
|
||||
class="border border-gray-600"
|
||||
>
|
||||
<template #prefix>
|
||||
<i class="iconfont icon-search"></i>
|
||||
</template>
|
||||
</n-input>
|
||||
</div>
|
||||
<div class="user-box">
|
||||
<n-popselect
|
||||
v-model:value="value"
|
||||
:options="options"
|
||||
trigger="click"
|
||||
size="small"
|
||||
>
|
||||
<i class="iconfont icon-xiasanjiaoxing"></i>
|
||||
</n-popselect>
|
||||
<n-avatar
|
||||
class="ml-2"
|
||||
circle
|
||||
size="large"
|
||||
src="https://picsum.photos/200/300?random=1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<router-view></router-view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref } from '@vue/reactivity';
|
||||
import { useStore } from 'vuex';
|
||||
import { AppMenu } from './components';
|
||||
let menus = ref([
|
||||
import { getSearchKeyword } from '@/api/home';
|
||||
import { ref, onMounted } from 'vue';
|
||||
|
||||
const store = useStore();
|
||||
const menus = store.state.menus;
|
||||
|
||||
|
||||
const value = 'Drive My Car'
|
||||
const options = [
|
||||
{
|
||||
href: '/',
|
||||
icon: "icon-homefill",
|
||||
text: "hello"
|
||||
label: 'Girl',
|
||||
value: 'Girl'
|
||||
},
|
||||
{
|
||||
href: '/main',
|
||||
icon: "icon-peoplefill",
|
||||
text: "hello"
|
||||
label: 'In My Life',
|
||||
value: 'In My Life'
|
||||
},
|
||||
{
|
||||
href: '/',
|
||||
icon: "icon-videofill",
|
||||
text: "hello"
|
||||
},
|
||||
])
|
||||
label: 'Wait',
|
||||
value: 'Wait'
|
||||
}
|
||||
]
|
||||
|
||||
const searchKeyword = ref<String>("搜索点什么吧...")
|
||||
|
||||
const loadSearchKeyword = async () => {
|
||||
const { data } = await getSearchKeyword();
|
||||
searchKeyword.value = data.data.showKeyword
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadSearchKeyword()
|
||||
})
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -38,8 +87,10 @@ let menus = ref([
|
||||
|
||||
.layout-main {
|
||||
@apply bg-black rounded-lg mb-10 text-white shadow-xl flex;
|
||||
height: 800px;
|
||||
width: 1400px;
|
||||
height: 900px;
|
||||
width: 1500px;
|
||||
overflow: hidden;
|
||||
min-width: 1500px;
|
||||
.menu {
|
||||
width: 90px;
|
||||
}
|
||||
@@ -47,5 +98,10 @@ let menus = ref([
|
||||
@apply pt-6 pr-6 pb-6;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.user-box {
|
||||
@apply ml-6 flex text-lg justify-center items-center rounded-full pl-3 border border-gray-600;
|
||||
background: #1a1a1a;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -4,12 +4,12 @@
|
||||
<div class="app-menu">
|
||||
<div class="app-menu-header">
|
||||
<div class="app-menu-logo">
|
||||
<img src="@/assets/logo.png" class="w-10 h-10" alt="logo" />
|
||||
<img src="@/assets/logo.png" class="w-9 h-9 mt-2" alt="logo" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="app-menu-list">
|
||||
<div class="app-menu-item" v-for="(item,index) in menus" :key="index">
|
||||
<router-link class="app-menu-item-link mb-4 mt-4" :to="item.href">
|
||||
<router-link class="app-menu-item-link" :to="item.href">
|
||||
<i class="iconfont app-menu-item-icon" :style="iconStyle" :class="item.icon"></i>
|
||||
<span v-if="isText" class="app-menu-item-text ml-3">{{ item.text }}</span>
|
||||
</router-link>
|
||||
@@ -29,15 +29,15 @@ const props = defineProps({
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: '30px'
|
||||
default: '26px'
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#fff'
|
||||
default: '#aaa'
|
||||
},
|
||||
menus: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
default: []
|
||||
}
|
||||
})
|
||||
|
||||
@@ -61,7 +61,13 @@ onMounted(() => {
|
||||
@apply flex items-center justify-center;
|
||||
}
|
||||
|
||||
img {
|
||||
color: #fff;
|
||||
.app-menu-item-link {
|
||||
@apply mb-6 mt-6;
|
||||
}
|
||||
|
||||
.app-menu-item-icon:hover {
|
||||
color: #fff !important;
|
||||
transform: scale(1.05);
|
||||
transition: 0.2s ease-in-out;
|
||||
}
|
||||
</style>
|
||||
@@ -10,7 +10,10 @@ import "./index.css";
|
||||
|
||||
import router from "@/router";
|
||||
|
||||
import store from "@/store";
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(router);
|
||||
app.use(store);
|
||||
app.use(naive);
|
||||
app.mount("#app");
|
||||
|
||||
30
src/store/index.ts
Normal file
30
src/store/index.ts
Normal file
@@ -0,0 +1,30 @@
|
||||
import { createStore } from "vuex";
|
||||
|
||||
let state = {
|
||||
menus: [
|
||||
{
|
||||
href: "/",
|
||||
icon: "icon-homefill",
|
||||
text: "hello",
|
||||
},
|
||||
{
|
||||
href: "/main",
|
||||
icon: "icon-peoplefill",
|
||||
text: "hello",
|
||||
},
|
||||
{
|
||||
href: "/",
|
||||
icon: "icon-videofill",
|
||||
text: "hello",
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
let mutations = {};
|
||||
|
||||
const store = createStore({
|
||||
state: state,
|
||||
mutations: mutations,
|
||||
});
|
||||
|
||||
export default store;
|
||||
0
src/type/home.ts
Normal file
0
src/type/home.ts
Normal file
156
src/type/music.ts
Normal file
156
src/type/music.ts
Normal file
@@ -0,0 +1,156 @@
|
||||
export interface IRecommendMusic {
|
||||
code: number;
|
||||
category: number;
|
||||
result: Result[];
|
||||
}
|
||||
|
||||
interface Result {
|
||||
id: number;
|
||||
type: number;
|
||||
name: string;
|
||||
copywriter?: any;
|
||||
picUrl: string;
|
||||
canDislike: boolean;
|
||||
trackNumberUpdateTime?: any;
|
||||
song: Song;
|
||||
alg: string;
|
||||
}
|
||||
|
||||
interface Song {
|
||||
name: string;
|
||||
id: number;
|
||||
position: number;
|
||||
alias: string[];
|
||||
status: number;
|
||||
fee: number;
|
||||
copyrightId: number;
|
||||
disc: string;
|
||||
no: number;
|
||||
artists: Artist[];
|
||||
album: Album;
|
||||
starred: boolean;
|
||||
popularity: number;
|
||||
score: number;
|
||||
starredNum: number;
|
||||
duration: number;
|
||||
playedNum: number;
|
||||
dayPlays: number;
|
||||
hearTime: number;
|
||||
ringtone: string;
|
||||
crbt?: any;
|
||||
audition?: any;
|
||||
copyFrom: string;
|
||||
commentThreadId: string;
|
||||
rtUrl?: any;
|
||||
ftype: number;
|
||||
rtUrls: any[];
|
||||
copyright: number;
|
||||
transName?: any;
|
||||
sign?: any;
|
||||
mark: number;
|
||||
originCoverType: number;
|
||||
originSongSimpleData?: any;
|
||||
single: number;
|
||||
noCopyrightRcmd?: any;
|
||||
rtype: number;
|
||||
rurl?: any;
|
||||
mvid: number;
|
||||
bMusic: BMusic;
|
||||
mp3Url?: any;
|
||||
hMusic: BMusic;
|
||||
mMusic: BMusic;
|
||||
lMusic: BMusic;
|
||||
exclusive: boolean;
|
||||
privilege: Privilege;
|
||||
}
|
||||
|
||||
interface Privilege {
|
||||
id: number;
|
||||
fee: number;
|
||||
payed: number;
|
||||
st: number;
|
||||
pl: number;
|
||||
dl: number;
|
||||
sp: number;
|
||||
cp: number;
|
||||
subp: number;
|
||||
cs: boolean;
|
||||
maxbr: number;
|
||||
fl: number;
|
||||
toast: boolean;
|
||||
flag: number;
|
||||
preSell: boolean;
|
||||
playMaxbr: number;
|
||||
downloadMaxbr: number;
|
||||
rscl?: any;
|
||||
freeTrialPrivilege: FreeTrialPrivilege;
|
||||
chargeInfoList: ChargeInfoList[];
|
||||
}
|
||||
|
||||
interface ChargeInfoList {
|
||||
rate: number;
|
||||
chargeUrl?: any;
|
||||
chargeMessage?: any;
|
||||
chargeType: number;
|
||||
}
|
||||
|
||||
interface FreeTrialPrivilege {
|
||||
resConsumable: boolean;
|
||||
userConsumable: boolean;
|
||||
}
|
||||
|
||||
interface BMusic {
|
||||
name?: any;
|
||||
id: number;
|
||||
size: number;
|
||||
extension: string;
|
||||
sr: number;
|
||||
dfsId: number;
|
||||
bitrate: number;
|
||||
playTime: number;
|
||||
volumeDelta: number;
|
||||
}
|
||||
|
||||
interface Album {
|
||||
name: string;
|
||||
id: number;
|
||||
type: string;
|
||||
size: number;
|
||||
picId: number;
|
||||
blurPicUrl: string;
|
||||
companyId: number;
|
||||
pic: number;
|
||||
picUrl: string;
|
||||
publishTime: number;
|
||||
description: string;
|
||||
tags: string;
|
||||
company: string;
|
||||
briefDesc: string;
|
||||
artist: Artist;
|
||||
songs: any[];
|
||||
alias: string[];
|
||||
status: number;
|
||||
copyrightId: number;
|
||||
commentThreadId: string;
|
||||
artists: Artist[];
|
||||
subType: string;
|
||||
transName?: any;
|
||||
onSale: boolean;
|
||||
mark: number;
|
||||
picId_str: string;
|
||||
}
|
||||
|
||||
interface Artist {
|
||||
name: string;
|
||||
id: number;
|
||||
picId: number;
|
||||
img1v1Id: number;
|
||||
briefDesc: string;
|
||||
picUrl: string;
|
||||
img1v1Url: string;
|
||||
albumSize: number;
|
||||
alias: any[];
|
||||
trans: string;
|
||||
musicSize: number;
|
||||
topicPerson: number;
|
||||
}
|
||||
26
src/type/playlist.ts
Normal file
26
src/type/playlist.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
export interface IPlayListSort {
|
||||
code: number;
|
||||
all: SortAll;
|
||||
sub: SortAll[];
|
||||
categories: SortCategories;
|
||||
}
|
||||
|
||||
interface SortCategories {
|
||||
"0": string;
|
||||
"1": string;
|
||||
"2": string;
|
||||
"3": string;
|
||||
"4": string;
|
||||
}
|
||||
|
||||
interface SortAll {
|
||||
name: string;
|
||||
resourceCount: number;
|
||||
imgId: number;
|
||||
imgUrl?: any;
|
||||
type: number;
|
||||
category: number;
|
||||
resourceType: number;
|
||||
hot: boolean;
|
||||
activity: boolean;
|
||||
}
|
||||
16
src/type/search.ts
Normal file
16
src/type/search.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
export interface ISearchKeyword {
|
||||
code: number;
|
||||
message?: any;
|
||||
data: SearchKeywordData;
|
||||
}
|
||||
|
||||
interface SearchKeywordData {
|
||||
showKeyword: string;
|
||||
realkeyword: string;
|
||||
searchType: number;
|
||||
action: number;
|
||||
alg: string;
|
||||
gap: number;
|
||||
source?: any;
|
||||
bizQueryInfo: string;
|
||||
}
|
||||
32
src/type/singer.ts
Normal file
32
src/type/singer.ts
Normal file
@@ -0,0 +1,32 @@
|
||||
export interface IHotSinger {
|
||||
code: number;
|
||||
more: boolean;
|
||||
artists: Artist[];
|
||||
}
|
||||
|
||||
interface Artist {
|
||||
name: string;
|
||||
id: number;
|
||||
picId: number;
|
||||
img1v1Id: number;
|
||||
briefDesc: string;
|
||||
picUrl: string;
|
||||
img1v1Url: string;
|
||||
albumSize: number;
|
||||
alias: string[];
|
||||
trans: string;
|
||||
musicSize: number;
|
||||
topicPerson: number;
|
||||
showPrivateMsg?: any;
|
||||
isSubed?: any;
|
||||
accountId?: number;
|
||||
picId_str?: string;
|
||||
img1v1Id_str: string;
|
||||
transNames?: string[];
|
||||
followed: boolean;
|
||||
mvSize?: any;
|
||||
publishTime?: any;
|
||||
identifyTag?: any;
|
||||
alg?: any;
|
||||
fansCount?: any;
|
||||
}
|
||||
8
src/utils/request.ts
Normal file
8
src/utils/request.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
import axios from "axios";
|
||||
|
||||
const request = axios.create({
|
||||
baseURL: "http://123.56.226.179:3000",
|
||||
timeout: 10000,
|
||||
});
|
||||
|
||||
export default request;
|
||||
@@ -1,22 +1,156 @@
|
||||
<template>
|
||||
<n-layout class="h-full bg-black" :native-scrollbar="false">
|
||||
<div class="main-page">
|
||||
<!--音乐搜索框 -->
|
||||
<div class="search-box">
|
||||
<div class="search-box-input">
|
||||
<n-input size="large" round placeholder="搜索歌曲或歌手...">
|
||||
<template #prefix>
|
||||
<i class="iconfont icon-search"></i>
|
||||
</template>
|
||||
</n-input>
|
||||
<!-- 推荐歌手 -->
|
||||
<div class="recommend-singer">
|
||||
<div class="recommend-singer-list">
|
||||
<div
|
||||
class="recommend-singer-item relative"
|
||||
v-for="(item,index) in hotSingerData?.artists"
|
||||
:key="item.id"
|
||||
>
|
||||
<div :style="getStyle(item)" class="recommend-singer-item-bg"></div>
|
||||
<div
|
||||
class="recommend-singer-item-count p-2 text-base text-gray-200 z-10"
|
||||
>{{ item.musicSize }}首</div>
|
||||
<div class="recommend-singer-item-info z-10">
|
||||
<div class="recommend-singer-item-info-play">
|
||||
<i class="iconfont icon-playfill text-xl"></i>
|
||||
</div>
|
||||
<div class="ml-4">
|
||||
<div class="recommend-singer-item-info-name">{{ item.name }}</div>
|
||||
<div class="recommend-singer-item-info-name">{{ item.name }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main-content">
|
||||
<!-- 歌单分类列表 -->
|
||||
<div class="play-list-type">
|
||||
<div class="title">歌单分类</div>
|
||||
<n-layout class="bg-black">
|
||||
<template v-for="(item,index) in playlistCategory?.sub" :key="item.name">
|
||||
<span
|
||||
class="play-list-type-item animate__animated animate__bounceIn animate__repeat-1"
|
||||
:style="getPlaylistTypeStyle(index <= 8 ? index : index - 8)"
|
||||
v-if="isShowAllPlaylistCategory || index <= 8"
|
||||
>{{ item.name }}</span>
|
||||
</template>
|
||||
<div
|
||||
class="play-list-type-showall animate__animated animate__bounceIn animate__repeat-1"
|
||||
:style="getPlaylistTypeStyle(!isShowAllPlaylistCategory ? 25 : playlistCategory?.sub.length + 30)"
|
||||
@click="isShowAllPlaylistCategory = !isShowAllPlaylistCategory"
|
||||
>{{ !isShowAllPlaylistCategory ? '显示全部' : '隐藏一些' }}</div>
|
||||
</n-layout>
|
||||
</div>
|
||||
<div class="recommend-music">
|
||||
<div class="title">本周最热音乐</div>
|
||||
<n-layout class="bg-black">
|
||||
<img v-for="item in recommendMusic?.result" :src="item.picUrl" width="100" height="100" />
|
||||
</n-layout>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</n-layout>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, ref } from "vue";
|
||||
import { getHotSinger, getPlaylistCategory, getRecommendMusic } from "@/api/home"
|
||||
import type { IHotSinger } from "@/type/singer";
|
||||
import type { IPlayListSort } from "@/type/playlist";
|
||||
import type { IRecommendMusic } from "@/type/music";
|
||||
|
||||
// 歌手信息
|
||||
const hotSingerData = ref<IHotSinger>()
|
||||
// 歌单分类
|
||||
const playlistCategory = ref<IPlayListSort>()
|
||||
// 是否显示全部歌单分类
|
||||
const isShowAllPlaylistCategory = ref<boolean>(false)
|
||||
// 推荐歌曲
|
||||
const recommendMusic = ref<IRecommendMusic>()
|
||||
|
||||
|
||||
// 设置歌手背景图片
|
||||
const getStyle = (item: any) => {
|
||||
return {
|
||||
"background-image": "url(" + item.picUrl + ")"
|
||||
}
|
||||
}
|
||||
// 设置歌单分类样式
|
||||
const getPlaylistTypeStyle = (index: number) => {
|
||||
return {
|
||||
"animation-delay": index * 25 + "ms"
|
||||
}
|
||||
}
|
||||
//加载推荐歌手
|
||||
const loadSingerList = async () => {
|
||||
const { data } = await getHotSinger({ offset: 0, limit: 5 })
|
||||
hotSingerData.value = data
|
||||
}
|
||||
// 加载歌单分类
|
||||
const loadPlaylistCategory = async () => {
|
||||
const { data } = await getPlaylistCategory()
|
||||
playlistCategory.value = data
|
||||
}
|
||||
// 加载推荐歌曲
|
||||
const loadRecommendMusic = async () => {
|
||||
const { data } = await getRecommendMusic({ limit: 6 })
|
||||
recommendMusic.value = data
|
||||
}
|
||||
|
||||
// 页面初始化
|
||||
onMounted(() => {
|
||||
loadSingerList()
|
||||
loadPlaylistCategory()
|
||||
loadRecommendMusic()
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.recommend-singer {
|
||||
&-list {
|
||||
@apply flex mt-5;
|
||||
height: 350px;
|
||||
}
|
||||
&-item {
|
||||
@apply flex-1 h-full rounded-3xl p-5 mr-5 flex flex-col justify-between;
|
||||
&-bg {
|
||||
@apply bg-gray-900 bg-no-repeat bg-cover bg-center rounded-3xl absolute w-full h-full top-0 left-0 z-0;
|
||||
filter: brightness(80%);
|
||||
}
|
||||
&-info {
|
||||
@apply flex items-center p-2;
|
||||
&-play {
|
||||
@apply w-12 h-12 bg-green-500 rounded-full flex justify-center items-center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.main-content {
|
||||
@apply mt-6 flex;
|
||||
.title {
|
||||
@apply text-lg font-bold mb-4;
|
||||
}
|
||||
.play-list-type {
|
||||
width: 250px;
|
||||
@apply mr-6;
|
||||
&-item,
|
||||
&-showall {
|
||||
@apply py-2 px-3 mr-3 mb-3 inline-block border border-gray-700 rounded-xl cursor-pointer hover:bg-green-600 transition;
|
||||
background-color: #1a1a1a;
|
||||
}
|
||||
&-showall {
|
||||
@apply block text-center;
|
||||
}
|
||||
}
|
||||
|
||||
.recommend-music {
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user