mirror of
https://github.com/algerkong/AlgerMusicPlayer.git
synced 2026-04-30 12:07:22 +08:00
添加用户页面, 修改登录 添加手机号登录
This commit is contained in:
@@ -1,31 +1,46 @@
|
|||||||
import request from "@/utils/request";
|
import request from "@/utils/request";
|
||||||
|
|
||||||
|
// 创建二维码key
|
||||||
// /login/qr/key
|
// /login/qr/key
|
||||||
export function getQrKey() {
|
export function getQrKey() {
|
||||||
return request.get("/login/qr/key");
|
return request.get("/login/qr/key");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 创建二维码
|
||||||
// /login/qr/create
|
// /login/qr/create
|
||||||
export function createQr(key: any) {
|
export function createQr(key: any) {
|
||||||
return request.get("/login/qr/create", { params: { key: key, qrimg: true } });
|
return request.get("/login/qr/create", { params: { key: key, qrimg: true } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取二维码状态
|
||||||
// /login/qr/check
|
// /login/qr/check
|
||||||
export function checkQr(key: any) {
|
export function checkQr(key: any) {
|
||||||
return request.get("/login/qr/check", { params: { key: key } });
|
return request.get("/login/qr/check", { params: { key: key } });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取登录状态
|
||||||
// /login/status
|
// /login/status
|
||||||
export function getLoginStatus() {
|
export function getLoginStatus() {
|
||||||
return request.get("/login/status");
|
return request.get("/login/status");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取用户信息
|
||||||
// /user/account
|
// /user/account
|
||||||
export function getUserDetail() {
|
export function getUserDetail() {
|
||||||
return request.get("/user/account");
|
return request.get("/user/account");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 退出登录
|
||||||
// /logout
|
// /logout
|
||||||
export function logout() {
|
export function logout() {
|
||||||
return request.get("/logout");
|
return request.get("/logout");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 手机号登录
|
||||||
|
// /login/cellphone
|
||||||
|
export function loginByCellphone(phone: any, password: any) {
|
||||||
|
return request.post("/login/cellphone", {
|
||||||
|
phone: phone,
|
||||||
|
password: password,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import request from "@/utils/request";
|
||||||
|
|
||||||
|
// /user/detail
|
||||||
|
export function getUserDetail(uid: number) {
|
||||||
|
return request.get("/user/detail", { params: { uid } });
|
||||||
|
}
|
||||||
|
|
||||||
|
// /user/playlist
|
||||||
|
export function getUserPlaylist(uid: number) {
|
||||||
|
return request.get("/user/playlist", { params: { uid } });
|
||||||
|
}
|
||||||
@@ -55,9 +55,13 @@ const loadHotSearchKeyword = async () => {
|
|||||||
hotSearchKeyword.value = data.data.showKeyword
|
hotSearchKeyword.value = data.data.showKeyword
|
||||||
hotSearchValue.value = data.data.realkeyword
|
hotSearchValue.value = data.data.realkeyword
|
||||||
}
|
}
|
||||||
|
|
||||||
|
store.state.user = JSON.parse(localStorage.getItem('user') || '{}')
|
||||||
|
|
||||||
const loadPage = async () => {
|
const loadPage = async () => {
|
||||||
const { data } = await getUserDetail()
|
const { data } = await getUserDetail()
|
||||||
store.state.user = data.profile
|
store.state.user = data.profile
|
||||||
|
localStorage.setItem('user', JSON.stringify(data.profile))
|
||||||
}
|
}
|
||||||
|
|
||||||
const toLogin = () => {
|
const toLogin = () => {
|
||||||
|
|||||||
@@ -29,6 +29,16 @@ const layoutRouter = [
|
|||||||
},
|
},
|
||||||
component: () => import("@/views/list/index.vue"),
|
component: () => import("@/views/list/index.vue"),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/user",
|
||||||
|
name: "user",
|
||||||
|
mate: {
|
||||||
|
title: "用户",
|
||||||
|
keepAlive: true,
|
||||||
|
icon: "icon-Profile",
|
||||||
|
},
|
||||||
|
component: () => import("@/views/user/index.vue"),
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export default layoutRouter;
|
export default layoutRouter;
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
export interface IUserDetail {
|
||||||
|
level: number;
|
||||||
|
listenSongs: number;
|
||||||
|
userPoint: UserPoint;
|
||||||
|
mobileSign: boolean;
|
||||||
|
pcSign: boolean;
|
||||||
|
profile: Profile;
|
||||||
|
peopleCanSeeMyPlayRecord: boolean;
|
||||||
|
bindings: Binding[];
|
||||||
|
adValid: boolean;
|
||||||
|
code: number;
|
||||||
|
createTime: number;
|
||||||
|
createDays: number;
|
||||||
|
profileVillageInfo: ProfileVillageInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ProfileVillageInfo {
|
||||||
|
title: string;
|
||||||
|
imageUrl?: any;
|
||||||
|
targetUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Binding {
|
||||||
|
userId: number;
|
||||||
|
url: string;
|
||||||
|
expiresIn: number;
|
||||||
|
refreshTime: number;
|
||||||
|
bindingTime: number;
|
||||||
|
tokenJsonStr?: any;
|
||||||
|
expired: boolean;
|
||||||
|
id: number;
|
||||||
|
type: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Profile {
|
||||||
|
avatarDetail?: any;
|
||||||
|
userId: number;
|
||||||
|
avatarImgIdStr: string;
|
||||||
|
backgroundImgIdStr: string;
|
||||||
|
description: string;
|
||||||
|
vipType: number;
|
||||||
|
userType: number;
|
||||||
|
createTime: number;
|
||||||
|
nickname: string;
|
||||||
|
avatarUrl: string;
|
||||||
|
experts: Experts;
|
||||||
|
expertTags?: any;
|
||||||
|
djStatus: number;
|
||||||
|
accountStatus: number;
|
||||||
|
birthday: number;
|
||||||
|
gender: number;
|
||||||
|
province: number;
|
||||||
|
city: number;
|
||||||
|
defaultAvatar: boolean;
|
||||||
|
avatarImgId: number;
|
||||||
|
backgroundImgId: number;
|
||||||
|
backgroundUrl: string;
|
||||||
|
mutual: boolean;
|
||||||
|
followed: boolean;
|
||||||
|
remarkName?: any;
|
||||||
|
authStatus: number;
|
||||||
|
detailDescription: string;
|
||||||
|
signature: string;
|
||||||
|
authority: number;
|
||||||
|
followeds: number;
|
||||||
|
follows: number;
|
||||||
|
blacklist: boolean;
|
||||||
|
eventCount: number;
|
||||||
|
allSubscribedCount: number;
|
||||||
|
playlistBeSubscribedCount: number;
|
||||||
|
avatarImgId_str: string;
|
||||||
|
followTime?: any;
|
||||||
|
followMe: boolean;
|
||||||
|
artistIdentity: any[];
|
||||||
|
cCount: number;
|
||||||
|
sDJPCount: number;
|
||||||
|
playlistCount: number;
|
||||||
|
sCount: number;
|
||||||
|
newFollows: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Experts {}
|
||||||
|
|
||||||
|
interface UserPoint {
|
||||||
|
userId: number;
|
||||||
|
balance: number;
|
||||||
|
updateTime: number;
|
||||||
|
version: number;
|
||||||
|
status: number;
|
||||||
|
blockBalance: number;
|
||||||
|
}
|
||||||
@@ -22,7 +22,6 @@ const selectRecommendItem = async (item: IRecommendItem) => {
|
|||||||
showMusic.value = true
|
showMusic.value = true
|
||||||
recommendItem.value = item
|
recommendItem.value = item
|
||||||
listDetail.value = data
|
listDetail.value = data
|
||||||
|
|
||||||
}
|
}
|
||||||
const closeMusic = () => {
|
const closeMusic = () => {
|
||||||
showMusic.value = false
|
showMusic.value = false
|
||||||
|
|||||||
+97
-10
@@ -2,13 +2,16 @@
|
|||||||
import { getQrKey, createQr, checkQr, getLoginStatus } from '@/api/login'
|
import { getQrKey, createQr, checkQr, getLoginStatus } from '@/api/login'
|
||||||
import { onMounted } from '@vue/runtime-core';
|
import { onMounted } from '@vue/runtime-core';
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { getUserDetail } from '@/api/login';
|
import { getUserDetail, loginByCellphone } from '@/api/login';
|
||||||
import { useStore } from 'vuex';
|
import { useStore } from 'vuex';
|
||||||
import { useMessage } from 'naive-ui'
|
import { useMessage } from 'naive-ui'
|
||||||
|
import { setAnimationClass, setAnimationDelay } from "@/utils";
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
const store = useStore();
|
const store = useStore();
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
const qrUrl = ref<string>()
|
const qrUrl = ref<string>()
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
@@ -29,8 +32,6 @@ const timerIsQr = (key: string) => {
|
|||||||
const timer = setInterval(async () => {
|
const timer = setInterval(async () => {
|
||||||
const { data } = await checkQr(key)
|
const { data } = await checkQr(key)
|
||||||
|
|
||||||
console.log(data);
|
|
||||||
|
|
||||||
if (data.code === 800) {
|
if (data.code === 800) {
|
||||||
clearInterval(timer)
|
clearInterval(timer)
|
||||||
}
|
}
|
||||||
@@ -49,15 +50,53 @@ const timerIsQr = (key: string) => {
|
|||||||
}, 5000);
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 是否扫码登陆
|
||||||
|
const isQr = ref(true)
|
||||||
|
const chooseQr = () => {
|
||||||
|
isQr.value = !isQr.value
|
||||||
|
}
|
||||||
|
|
||||||
|
// 手机号登录
|
||||||
|
const phone = ref('')
|
||||||
|
const password = ref('')
|
||||||
|
const loginPhone = async () => {
|
||||||
|
const { data } = await loginByCellphone(phone.value, password.value)
|
||||||
|
if (data.code === 200) {
|
||||||
|
message.success('登录成功')
|
||||||
|
store.state.user = data.profile
|
||||||
|
localStorage.setItem('token', data.cookie)
|
||||||
|
setTimeout(() => {
|
||||||
|
router.push('/')
|
||||||
|
}, 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="login-page">
|
<div class="login-page">
|
||||||
<div class="login-title">扫码登陆</div>
|
<div class="phone-login">
|
||||||
<div class="qr">
|
<div class="bg"></div>
|
||||||
<img class="qr-img" :src="qrUrl" />
|
<div class="content">
|
||||||
|
<div class="phone" v-if="isQr" :class="setAnimationClass('animate__fadeInUp')">
|
||||||
|
<div class="login-title">扫码登陆</div>
|
||||||
|
<img class="qr-img" :src="qrUrl" />
|
||||||
|
<div class="text">使用网易云APP扫码登录</div>
|
||||||
|
</div>
|
||||||
|
<div class="phone" v-else :class="setAnimationClass('animate__fadeInUp')">
|
||||||
|
<div class="login-title">手机号登录</div>
|
||||||
|
<div class="phone-page">
|
||||||
|
<input v-model="phone" class="phone-input" type="text" placeholder="手机号" />
|
||||||
|
<input v-model="password" class="phone-input" type="password" placeholder="密码" />
|
||||||
|
</div>
|
||||||
|
<n-button class="btn-login" @click="loginPhone()">登录</n-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="bottom">
|
||||||
|
<div class="title" @click="chooseQr()">{{ isQr ? '手机号登录' : '扫码登录' }}</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="text">使用网易云APP扫码登录</div>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -67,13 +106,61 @@ const timerIsQr = (key: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.login-title {
|
.login-title {
|
||||||
@apply text-3xl font-bold mb-6;
|
@apply text-2xl font-bold mb-6;
|
||||||
}
|
}
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
@apply mt-4 text-green-500 text-xs;
|
@apply mt-4 text-green-500 text-xs;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr {
|
.phone-login {
|
||||||
|
width: 350px;
|
||||||
|
height: 550px;
|
||||||
|
@apply rounded-2xl bg-cover bg-no-repeat relative overflow-hidden;
|
||||||
|
background-image: url(https://z3.ax1x.com/2021/09/30/4IMyUx.jpg);
|
||||||
|
background-color: #000;
|
||||||
|
|
||||||
|
.bg {
|
||||||
|
@apply absolute w-full h-full bg-black opacity-30;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bottom {
|
||||||
|
width: 200%;
|
||||||
|
height: 250px;
|
||||||
|
bottom: -180px;
|
||||||
|
border-radius: 50%;
|
||||||
|
left: 50%;
|
||||||
|
padding: 10px;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
color: #ffffff99;
|
||||||
|
@apply absolute bg-black flex justify-center text-lg font-bold cursor-pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
@apply absolute w-full h-full p-4 flex flex-col items-center justify-center pb-20 text-center;
|
||||||
|
.qr-img {
|
||||||
|
@apply opacity-80 rounded-2xl cursor-pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.phone {
|
||||||
|
animation-duration: 0.5s;
|
||||||
|
&-page {
|
||||||
|
background-color: #ffffffdd;
|
||||||
|
width: 250px;
|
||||||
|
@apply rounded-2xl overflow-hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-input {
|
||||||
|
height: 40px;
|
||||||
|
border-bottom: 1px solid #e5e5e5;
|
||||||
|
@apply w-full text-black px-4 outline-none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.btn-login {
|
||||||
|
width: 250px;
|
||||||
|
height: 40px;
|
||||||
|
@apply mt-10 text-white rounded-xl bg-black opacity-60;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@@ -1,10 +1,15 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="search-page">
|
<div class="search-page">
|
||||||
<div class="hot-search" :class="setAnimationClass('animate__fadeInDown')">
|
<n-layout
|
||||||
|
class="hot-search"
|
||||||
|
:class="setAnimationClass('animate__fadeInDown')"
|
||||||
|
:native-scrollbar="false"
|
||||||
|
>
|
||||||
|
<div class="title">热搜列表</div>
|
||||||
<template v-for="(item, index) in hotSearchData?.data">
|
<template v-for="(item, index) in hotSearchData?.data">
|
||||||
<div
|
<div
|
||||||
:class="setAnimationClass('animate__bounceInLeft')"
|
:class="setAnimationClass('animate__bounceInLeft')"
|
||||||
:style="setAnimationDelay(index, 100)"
|
:style="setAnimationDelay(index, 10)"
|
||||||
class="hot-search-item"
|
class="hot-search-item"
|
||||||
@click.stop="clickHotKeyword(item.searchWord)"
|
@click.stop="clickHotKeyword(item.searchWord)"
|
||||||
>
|
>
|
||||||
@@ -15,9 +20,14 @@
|
|||||||
{{ item.searchWord }}
|
{{ item.searchWord }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</n-layout>
|
||||||
<!-- 搜索到的歌曲列表 -->
|
<!-- 搜索到的歌曲列表 -->
|
||||||
<div class="search-list">
|
<n-layout
|
||||||
|
class="search-list"
|
||||||
|
:class="setAnimationClass('animate__fadeInUp')"
|
||||||
|
:native-scrollbar="false"
|
||||||
|
>
|
||||||
|
<div class="title">{{ hotKeyword }}</div>
|
||||||
<template v-if="searchDetail">
|
<template v-if="searchDetail">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in searchDetail?.result.song.songs"
|
v-for="(item, index) in searchDetail?.result.song.songs"
|
||||||
@@ -28,7 +38,7 @@
|
|||||||
<SongItem :item="item" />
|
<SongItem :item="item" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</n-layout>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -59,7 +69,9 @@ onMounted(() => {
|
|||||||
loadHotSearch();
|
loadHotSearch();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const hotKeyword = ref(route.query.keyword || "搜索列表");
|
||||||
const clickHotKeyword = (keyword: string) => {
|
const clickHotKeyword = (keyword: string) => {
|
||||||
|
hotKeyword.value = keyword;
|
||||||
router.push({
|
router.push({
|
||||||
path: "/search",
|
path: "/search",
|
||||||
query: {
|
query: {
|
||||||
@@ -92,7 +104,6 @@ loadSearch(route.query.keyword);
|
|||||||
watch(
|
watch(
|
||||||
() => route.query,
|
() => route.query,
|
||||||
async newParams => {
|
async newParams => {
|
||||||
console.log(newParams);
|
|
||||||
loadSearch(newParams.keyword);
|
loadSearch(newParams.keyword);
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@@ -108,6 +119,8 @@ watch(
|
|||||||
}
|
}
|
||||||
.hot-search {
|
.hot-search {
|
||||||
@apply mt-3 mr-4 rounded-xl flex-1 overflow-hidden;
|
@apply mt-3 mr-4 rounded-xl flex-1 overflow-hidden;
|
||||||
|
height: 740px;
|
||||||
|
background-color: #0d0d0d;
|
||||||
animation-duration: 0.2s;
|
animation-duration: 0.2s;
|
||||||
min-width: 400px;
|
min-width: 400px;
|
||||||
&-item {
|
&-item {
|
||||||
@@ -122,6 +135,12 @@ watch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
.search-list {
|
.search-list {
|
||||||
@apply mt-3 flex-1;
|
@apply mt-3 flex-1 rounded-xl;
|
||||||
|
height: 740px;
|
||||||
|
background-color: #0d0d0d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.title {
|
||||||
|
@apply text-gray-200 text-xl font-bold my-2 mx-4;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useStore } from "vuex";
|
||||||
|
import { useRouter } from "vue-router";
|
||||||
|
import { getUserDetail, getUserPlaylist } from "@/api/user";
|
||||||
|
import type { IUserDetail } from "@/type/user";
|
||||||
|
import { ref } from "vue";
|
||||||
|
import { setAnimationClass, setAnimationDelay } from "@/utils";
|
||||||
|
|
||||||
|
const store = useStore()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
|
||||||
|
const userDetail = ref<IUserDetail>()
|
||||||
|
const playList = ref<any[]>([])
|
||||||
|
const user = store.state.user
|
||||||
|
|
||||||
|
const loadPage = async () => {
|
||||||
|
if (!user) {
|
||||||
|
router.push("/login")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const { data: userData } = await getUserDetail(user.userId)
|
||||||
|
userDetail.value = userData
|
||||||
|
|
||||||
|
const { data: playlistData } = await getUserPlaylist(user.userId)
|
||||||
|
playList.value = playlistData.playlist
|
||||||
|
|
||||||
|
}
|
||||||
|
loadPage()
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="user-page" :class="setAnimationClass('animate__fadeInLeft')">
|
||||||
|
<div class="left" v-if="userDetail" :style="{ backgroundImage: `url(${user.backgroundUrl})` }">
|
||||||
|
<div class="page">
|
||||||
|
<div class="user-name">{{ user.nickname }}</div>
|
||||||
|
<div class="user-info">
|
||||||
|
<n-avatar round :size="50" :src="user.avatarUrl" />
|
||||||
|
<div class="user-info-list">
|
||||||
|
<div class="user-info-item">
|
||||||
|
<div class="label">{{ userDetail.profile.followeds }}</div>
|
||||||
|
<div>粉丝</div>
|
||||||
|
</div>
|
||||||
|
<div class="user-info-item">
|
||||||
|
<div class="label">{{ userDetail.profile.follows }}</div>
|
||||||
|
<div>关注</div>
|
||||||
|
</div>
|
||||||
|
<div class="user-info-item">
|
||||||
|
<div class="label">{{ userDetail.level }}</div>
|
||||||
|
<div>等级</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="uesr-signature">{{ userDetail.profile.signature }}</div>
|
||||||
|
|
||||||
|
<div class="play-list" :class="setAnimationClass('animate__fadeInLeft')">
|
||||||
|
<div class="play-list-title">创建的歌单</div>
|
||||||
|
<div class="play-list-item" v-for="(item,index) in playList" :key="index">
|
||||||
|
<img class="play-list-item-img" :src="item.coverImgUrl" />
|
||||||
|
<div class="play-list-item-info">
|
||||||
|
<div class="play-list-item-name">{{ item.name }}</div>
|
||||||
|
<div class="play-list-item-count">{{ item.trackCount }}首,播放{{ item.playCount }}次</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.user-page {
|
||||||
|
@apply flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left {
|
||||||
|
max-width: 600px;
|
||||||
|
background-color: #0d0d0d;
|
||||||
|
background-size: 100%;
|
||||||
|
@apply bg-no-repeat;
|
||||||
|
|
||||||
|
@apply flex-1 rounded-2xl mt-4 overflow-hidden relative;
|
||||||
|
.page {
|
||||||
|
@apply p-4 w-full z-10;
|
||||||
|
background-color: #0d0d0d66;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-name {
|
||||||
|
@apply text-xl text-white font-bold opacity-70 mb-4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.uesr-signature {
|
||||||
|
@apply text-white opacity-70 mt-4;
|
||||||
|
}
|
||||||
|
.user-info {
|
||||||
|
@apply flex items-center;
|
||||||
|
&-list {
|
||||||
|
@apply flex justify-around w-2/5 text-center opacity-70;
|
||||||
|
.label {
|
||||||
|
@apply text-xl text-white font-bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-list {
|
||||||
|
@apply mt-4 py-4 px-2 rounded-xl;
|
||||||
|
background-color: #000000;
|
||||||
|
&-title {
|
||||||
|
@apply text-lg text-white opacity-70;
|
||||||
|
}
|
||||||
|
&-item {
|
||||||
|
@apply flex items-center hover:bg-gray-800 transition-all duration-200 px-2 py-1 rounded-xl cursor-pointer;
|
||||||
|
&-img {
|
||||||
|
width: 60px;
|
||||||
|
height: 60px;
|
||||||
|
@apply rounded-xl;
|
||||||
|
}
|
||||||
|
&-info {
|
||||||
|
@apply ml-2;
|
||||||
|
}
|
||||||
|
&-name {
|
||||||
|
@apply text-white;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user