fix player + add playlist

This commit is contained in:
Stepan Fedyanin 2024-06-20 17:33:55 +05:00
parent 6680fd8792
commit 7f8d3e6f86
28 changed files with 765 additions and 129 deletions

View File

@ -7,7 +7,7 @@ ROBOTS_USER_AGENT=*
ROBOTS_ALLOW=
ROBOTS_DISALLOW=["/"]
SERVICE_SELF_URL=//it-radio.flexidev.ru
SERVICE_URL=//it-radio.flexidev.ru
SERVICE_URL=//82.97.242.49
SERVICE_URL_AUDIO=//82.97.242.49:10084
SERVICE_PROTOCOL=http
SERVICE_PORT=8000

View File

@ -10,6 +10,7 @@
<AppFooter/>
</div>
<Authentication/>
<ChangingUser/>
</template>
<script>
@ -17,13 +18,15 @@ import Page404 from '@/views/Page404.vue';
import Page500 from '@/views/Page500.vue';
import AppHeader from "@/components/app-header.vue";
import AppFooter from "@/components/app-footer.vue";
import Authentication from "@/components/authentication.vue";
import Authentication from "@/components/modal/authentication.vue";
import Player from "@/components/player.vue";
import ChangingUser from "@/components/modal/сhanging-user.vue";
export default {
async preFetch({store, currentRoute, previousRoute, redirect, ssrContext, urlPath, publicPath}) {
},
components: {
ChangingUser,
Player,
Authentication,
AppFooter,
@ -47,10 +50,15 @@ export default {
showErrorPage500() {
return false
// return this.$store.state.showErrorPage?.response?.status === 500;
}
},
currentPlay() {
return this.$store.state.currentPlay
},
},
watch: {},
created() {
this.$store.dispatch('setCurrentPlay', {...this.currentPlay, isPlay: false});
},
mounted() {
},

View File

@ -20,6 +20,7 @@
@import "./module/tabs.scss";
@import "./module/about.scss";
@import "./module/playlists.scss";
@import "./module/playlist.scss";
@import "./module/contacts.scss";
@import "./module/support.scss";
@import "./module/player.scss";

View File

@ -95,13 +95,15 @@
&:hover::after {
background: svg-load('./assets/img/icon/ArrowRight.svg', stroke=#232323) no-repeat 100%;
}
&.m--active::after,
&:active::after {
width: 45px;
height: 20px;
}
&.m--active,
&:active {
background:transparent;
color: var(--color-white);
}
}
&.m--w-100 {
@ -135,6 +137,7 @@
pad: 0;
min-width: 0;
min-height: 0;
padding: 0;
&:hover{
background: transparent;
}

View File

@ -26,6 +26,24 @@
.input {
@mixin input;
}
&__input{
&.m--search {
position: relative;
&:before{
content: '';
width: 24px;
height: 24px;
top: 16px;
left: 16px;
position: absolute;
background: svg-load('./assets/img/icon/search-icon.svg') no-repeat 100%;
}
.input{
padding: 1rem 1.2rem 1rem 3rem;
border: 1px solid var(--color-primary);
}
}
}
&__checkbox {
&-label{
padding-left: 50px;

View File

@ -48,7 +48,13 @@
max-width: 420px;
}
}
&__content{
min-width: 360px;
}
&__title{
font-size: 2rem;
margin-bottom: 2.5rem;
}
&__close{
width: 30px;
height: 30px;

View File

@ -30,11 +30,13 @@
display: flex;
flex-direction: column;
margin-left: 20px;
min-width: 135px;
gap: 0.25rem;
span {
color: var(--color-white-opacity);
}
&.m--skeleton{
min-width: 135px;
}
}
&__favorites {

View File

@ -0,0 +1,81 @@
.playlist {
position: relative;
z-index: 2;
&-roster {
display: flex;
flex-wrap: wrap;
gap: calc(var(--space-between-block) / 2);
width: 100%;
}
&-edit{
position: relative;
z-index: 2;
&__search{
margin-bottom: var(--space-between-block);
}
&__search{
min-width: 320px;
width: 40%;
}
&__list{
display: flex;
gap: var(--space-between-block);
}
&__item{
width: calc(50% - var(--space-between-block) / 2) ;
}
&__title{
color: transparent;
font-size: 2rem;
background-clip: text;
background-image: var(--linear-gradient);
margin-bottom: 1rem;
}
}
&-item {
z-index: 2;
position: relative;
display: flex;
flex-direction: column;
min-width: 220px;
cursor: url("./assets/img/icon/cursor.svg"), auto;
&__cover {
background: svg-load('./assets/img/icon/playlist-item.svg', fill=$color-white) no-repeat 100%;
border-radius: 10px;
margin-bottom: 10px;
transition: all .3s ease;
min-height: 220px;
&:hover{
background: svg-load('./assets/img/icon/playlist-item.svg', fill=$color-white-opacity) no-repeat 100%;
border-radius: 10px;
margin-bottom: 10px;
}
}
&.m--create &__cover {
position: relative;
background: var(--bg-opacity);
&:after {
content: '';
width: 63px;
height: 63px;
background: svg-load('./assets/img/icon/playlist-item-create.svg', fill=$color-white-opacity) no-repeat 100%;
transition: all .3s ease;
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
margin: auto;
}
&:hover{
background: var(--color-primary);
&:after{
background: svg-load('./assets/img/icon/playlist-item-create.svg', fill=$color-white) no-repeat 100%;
}
}
}
}
}

View File

@ -2,6 +2,7 @@
&__title {
margin-bottom: 0.5rem;
padding: 0;
font-size: 1.5rem;
}
&__tabs{
margin-top: var(--space-between-block);

View File

@ -4,6 +4,9 @@
flex-wrap: wrap;
gap: 0 var(--space-between-block);
}
.m--column &-item{
width: 100%;
}
&-item {
width: calc(50% - var(--space-between-block) / 2);
position: relative;
@ -12,38 +15,48 @@
z-index: 2;
border-left: 3px solid transparent;
padding: 10px;
transition: all .5s ease;
transition: border .5s ease, background .5s ease;
background: transparent;
@mixin responsive-m {
width: 100%;
}
&.m--select &__selected {
margin-right: 20px;
width: 35px;
height: 33px;
&.m--stop {
path {
animation-play-state: paused;
}
}
}
&__selected {
width: 0;
overflow: hidden;
transition: all .2s ease;
path:nth-child(n) {
animation: play-transform 3s infinite linear;
}
path:nth-child(2n) {
animation: play-transform 2s infinite linear;
}
path:nth-child(3n) {
animation: play-transform 1.5s infinite linear;
}
}
&:hover {
cursor: url("./assets/img/icon/cursor.svg"), auto;
border-color: var(--color-primary);
background: linear-gradient(90deg, rgba(255, 255, 255, 0.2) 0%, rgba(255, 255, 255, 0) 100%);
}
&__btn {
min-height: 0;
min-width: 0;
@ -53,11 +66,13 @@
border: none;
background: var(--color-primary);
position: relative;
&.m--small {
background: transparent;
width: 25px;
height: 25px;
}
&.m--favorites {
&:after {
content: '';
@ -75,6 +90,7 @@
background-size: contain;
}
}
&.m--add {
&:after {
content: '';
@ -92,6 +108,24 @@
background-size: contain;
}
}
&.m--already{
&:after {
content: '';
position: absolute;
width: 25px;
height: 25px;
margin: auto;
background: svg-load('./assets/img/icon/icon-trash.svg', stroke=$color-primary) no-repeat 100%;
background-size: contain;
transition: all .3s ease;
}
&:hover:after {
background: svg-load('./assets/img/icon/icon-trash.svg', stroke=#FFF) no-repeat 100%;
background-size: contain;
}
}
&.m--play {
&:after {
content: '';
@ -128,6 +162,7 @@
}
}
}
&__info {
display: flex;
flex-direction: column;
@ -135,10 +170,12 @@
color: var(--color-white-opacity);
line-height: 21px;
margin-left: 20px;
span {
color: var(--color-white);
}
}
&__tools {
margin-left: auto;
display: flex;
@ -147,6 +184,7 @@
}
}
}
@keyframes play-transform {
0% {
transform: translateY(0);

View File

@ -1,8 +1,15 @@
.tabs {
display: flex;
flex-wrap: wrap;
margin-bottom: var(--space-between-block);
&.m--btns{
gap: 10px;
@mixin responsive-xs {
.button{
width: 100%;
}
}
}
&__item {
border: none;

View File

@ -6,16 +6,17 @@ $document-width-xs: 640px;
$document-width-xxs: 480px;
$color-primary: #5E5BFC;
$color-white: #FFFFFF;
$color-white-opacity: #BBB9CA;
:root {
--color-black: #232323;
--color-white: $color-white;
--color-white-darker: #E7E7E7;
--color-white-opacity: #BBB9CA;
--color-white-opacity: $color-white-opacity;
--bg-wrapper-modal: #000000E5;
--color-primary: $color-primary;
--color-black-cc: #000000CC;
--color-emmit: #E81717;
--bg-opacity: rgba(255, 255, 255, 0.15);
--bg-opacity: #74767D;
--base-fz: 18px;
--base-lh: 1.3;
--font-family-base: 'Manrope', 'Tahoma', sans-serif;

View File

@ -126,7 +126,7 @@ export default {
methods:{
handlerClick(methods){
if (methods==='login'){
this.$store.dispatch('setShowAuthModal', true);
this.$store.dispatch('setModal', {auth: true});
}
if (methods==='profile'){
this.next('profile')

View File

@ -137,7 +137,7 @@ export default {
},
computed: {
show() {
return this.$store.state.showAuthModal
return this.$store.state.modal.auth
}
},
methods: {
@ -192,7 +192,7 @@ export default {
},
close() {
this.$store.dispatch('setShowAuthModal', false);
this.$store.dispatch('setModal', {auth: false});
},
next(name) {
this.$router.push({name});

View File

@ -0,0 +1,109 @@
<template>
<vue-final-modal
v-model="show"
class="modal__container"
content-class="modal__block"
content-transition="vfm-fade"
overlay-transition="vfm-fade"
:clickToClose="false"
@click-outside="close()"
>
<button
class="button modal__close"
@click="close"
>
</button>
<div class="modal__content">
<div class="modal__title">Изменение данных</div>
<FormKit
v-model="formData"
type="form"
data-loading="showLoaderSending"
form-class="$reset registration__form form"
submit-label="Войти"
:disabled="showLoaderSending"
:loading="showLoaderSending ? true : undefined"
:submit-attrs="{
inputClass: '$reset button m--white m--w-100',
wrapperClass: '$reset registration__form-submit form__submit',
outerClass: '$reset',
}"
@submit="submitHandler"
>
<FormKitSchema :schema="userForm"/>
</FormKit>
</div>
</vue-final-modal>
</template>
<script>
import {app} from "@/services";
export default {
name: 'recovery',
data(){
return{
userForm:[
{
$formkit: 'text',
name: 'email',
label: 'Ваша почта',
placeholder: 'Ваша почта',
validation: 'required',
outerClass: 'field--required'
},
{
$formkit: 'password',
name: 'old_password',
label: 'Введите старый пароль',
placeholder: 'Введите старый пароль',
validation: 'required',
outerClass: 'field--required'
},
{
$formkit: 'password',
name: 'password',
label: 'Введите новый пароль',
placeholder: 'Введите новый пароль',
validation: 'required',
outerClass: 'field--required'
}
],
formData: {},
showLoaderSending: false,
}
},
computed: {
user() {
return this.$store.state.user;
},
show(){
return this.$store.state.modal.changingUser;
}
},
mounted() {
this.formData.email = this.user.email;
},
methods:{
submitHandler(data, node){
this.showLoaderSending = true;
app.updateUser(this.formData).then(user=>{
this.showLoaderSending = false;
this.$store.dispatch('setUser', user);
}).catch(err=>{
this.showLoaderSending = false;
node.setErrors(
[err.detail],
err.error
)
})
},
close() {
this.$store.dispatch('setModal', {changingUser: false});
},
next(name){
this.$router.push({name});
}
}
}
</script>

View File

@ -14,7 +14,7 @@
</button>
</template>
<div class="player__executor">
<q-skeleton v-if="loaderPlay"/>
<q-skeleton v-if="loaderPlay" class="player__executor m--skeleton"/>
<template v-else>
{{ currentPlay.title || '—' }}
</template>
@ -35,7 +35,7 @@
v-model="isUserMusic"
type="toggle"
label="Включить мою музыку"
:disabled="!user?.id"
:disabled="!user?.id || userSongList.length === 0"
/>
<q-skeleton v-if="loaderPlay" class="player__tools m--skeleton"/>
<div v-else class="player__volume">
@ -71,7 +71,6 @@ export default {
components: {},
data() {
return {
audioUrl: 'http://82.97.242.49:18000/radio.mp3',
isFavorites: false,
isPlayRadio: false,
connection: null,
@ -108,7 +107,7 @@ export default {
if (this.isUserMusic === this.currentPlay.live) {
this.isUserMusic = !this.currentPlay.live;
}
if (to.id !== from.id && this.user?.id) {
if (this.user?.id && to.id !== from.id) {
this.checkSongIsFavorite();
}
if (!this.currentPlay.live && to.id !== from.id) {
@ -116,6 +115,14 @@ export default {
}
},
},
'userSongList': {
immediate: false,
handler(to, from) {
if (this.user?.id && to.length !== from.length) {
this.checkSongIsFavorite();
}
},
},
'isUserMusic': {
immediate: false,
handler() {
@ -133,16 +140,19 @@ export default {
this.$store.dispatch('initPlayer');
if (this.user?.id) {
this.checkSongIsFavorite();
this.getSongList();
}
this.playerInfo.progress = this.currentPlay.live ? 100 : 0;
console.log(this.currentPlay)
if (!this.currentPlay.live && this.userSongList?.length > 0) {
this.$store.dispatch('setCurrentPlay', {...this.userSongList[0], live: false});
this.getAudio(this.userSongList[0]?.azura_id);
this.$store.dispatch('setCurrentPlay', {...this.userSongList[this.currentPlay.currentIndex || 0], live: false});
this.getAudio(this.userSongList[this.currentPlay.currentIndex || 0]?.azura_id);
if (this.player.target) {
this.player.target.addEventListener('timeupdate', this.updateProgress)
}
}
if (!this.user?.id){
this.$store.dispatch('setCurrentPlay', {...this.currentPlay, live: true, isLoader: true, currentIndex: null});
}
},
methods: {
connectionPlayer() {
@ -154,7 +164,6 @@ export default {
this.connection.onHandler(this.getPlaying);
},
checkSongIsFavorite() {
console.debug(this.currentPlay.azura_id || this.currentPlay.id, this.currentPlay.title)
app.getCheckFavoriteSong(this.currentPlay.azura_id || this.currentPlay.id).then(res => {
this.isFavorites = res.is_favorite;
}).catch(err => {
@ -177,18 +186,19 @@ export default {
if (data.np.station.listen_url !== this.player.target.src) {
console.log('data.np.station.listen_url', data.np.station.listen_url)
this.$store.dispatch('changePlayer', data.np.station.listen_url);
}
const params = {
...this.currentPlay,
...data.np.now_playing.song,
azura_id: data.np.now_playing.song.id,
isLoader: false,
live: true,
currentIndex: null
}
delete params.unique_id;
this.$store.dispatch('setCurrentPlay', params);
}
}
}
},
updateProgress(e) {
this.playerInfo = {
@ -197,10 +207,16 @@ export default {
currentTime: this.player.target.currentTime
}
if (this.player.target.currentTime === this.player.target.duration){
this.getAudio(this.userSongList[1]?.azura_id);
let currentIndex = this.currentPlay.currentIndex + 1;
if (!this.userSongList[currentIndex]?.azura_id || currentIndex === null){
currentIndex = 0;
}
this.$store.dispatch('setCurrentPlay', {...this.currentPlay, ...this.userSongList[currentIndex], currentIndex})
}
},
handlerPlay() {
console.log(this.currentPlay)
console.log(this.player)
this.$store.dispatch('handlerPlayer', {play: true});
},
handlerPause() {
@ -209,13 +225,17 @@ export default {
getSongList() {
app.getFavoriteList().then(res => {
this.$store.dispatch('setUserFavorite', {songs: res})
console.log('res.length' ,res.length)
if (res.length === 0){
this.$store.dispatch('setCurrentPlay', {...this.currentPlay, live: true, isLoader: true});
}
}).catch(err => {
console.error(err)
})
},
handlerFavorites() {
if (this.user?.id) {
const params = {...this.currentPlay, azura_id: this.currentPlay.id};
const params = {...this.currentPlay, azura_id: Number(this.currentPlay.id)?this.currentPlay.azura_id:this.currentPlay.id};
if (!this.isFavorites) {
delete params.id;
app.createFavoriteForUser(params).then(() => {
@ -252,7 +272,6 @@ export default {
app.getAudio(id).then(res => {
const blob = new Blob([res], {type: 'application/audio'});
const audioUrl = URL.createObjectURL(blob);
this.$store.dispatch('setCurrentPlay', {...this.currentPlay, isLoader: false})
this.$store.dispatch('changePlayer', audioUrl);
this.player.target.addEventListener('timeupdate', this.updateProgress);
@ -263,6 +282,7 @@ export default {
this.playerInfo.duration = decodedData.duration;
});
});
this.$store.dispatch('setCurrentPlay', {...this.currentPlay, isLoader: false})
}).catch(err => {
this.$store.dispatch('setCurrentPlay', {...this.currentPlay, isLoader: false})
console.debug(err)
@ -271,9 +291,10 @@ export default {
changeLive() {
if (this.currentPlay.live) {
console.log('избранное')
this.getAudio(this.userSongList[0].azura_id);
this.playerInfo.progress = 0;
this.$store.dispatch('setCurrentPlay', {...this.userSongList[0], live: false, isLoader: true});
const params = {...this.userSongList[this.currentPlay.currentIndex || 0], live: false, isLoader: true};
if (!this.currentPlay.currentIndex) params.currentIndex = 0;
this.$store.dispatch('setCurrentPlay', params);
} else {
this.playerInfo.progress = 100;
this.$store.dispatch('setCurrentPlay', {...this.currentPlay, live: true, isLoader: true});

View File

@ -0,0 +1,23 @@
<template>
<div class="playlist-item" @click="selectPlaylist">
<div class="playlist-item__cover"></div>
<div>{{playlist.title}}</div>
</div>
</template>
<script>
export default {
name: 'playlist-item',
props:{
playlist:{
type: Object,
default: () => {}
}
},
methods:{
selectPlaylist(){
this.$emit('selectPlaylist', this.playlist)
}
}
}
</script>

View File

@ -0,0 +1,37 @@
<template>
<div class="playlist-roster">
<PlaylistItem
v-for="item in list"
:key="`playlist_${item.id}`"
:playlist="item"
@selectPlaylist="selectPlaylist"
/>
<div class="playlist-item m--create" @click="createPlaylist">
<div class="playlist-item__cover"></div>
<div>Создать плейлист</div>
</div>
</div>
</template>
<script>
import PlaylistItem from "@/components/playlist-item.vue";
export default {
name: 'playlist-roster',
components: {PlaylistItem},
props: {
list: {
type: Array,
default: () => []
}
},
methods: {
createPlaylist() {
this.$emit('createPlaylist')
},
selectPlaylist(params) {
this.$router.push({name: 'playlist', params: {id: params.id}})
}
}
}
</script>

View File

@ -27,7 +27,7 @@
</template>
<script>
import RubricModal from "@/components/rubric-modal.vue";
import RubricModal from "@/components/modal/rubric-modal.vue";
import {app} from "@/services";
export default {

View File

@ -21,7 +21,7 @@
</div>
<div class="song-item__tools">
<button class="button song-item__btn m--small m--favorites" @click.stop="removeSong"></button>
<button class="button song-item__btn m--small m--add"></button>
<button class="button song-item__btn m--small m--add" :class="songAlreadyAddPlaylist&&'m--already'" @click.stop="songAlreadyAddPlaylist?removeToPlaylist:addPlaylist"></button>
</div>
</div>
</template>
@ -48,6 +48,12 @@ export default {
return false
}
},
songAlreadyAddPlaylist:{
type: Boolean,
default(){
return false
}
},
},
watch: {
'playSong': {
@ -74,6 +80,12 @@ export default {
},
removeSong(){
this.$emit('removeSong', this.song);
},
addPlaylist(){
this.$emit('addPlaylist', this.song);
},
removeToPlaylist(){
this.$emit('removePlaylist', this.song);
}
}
}

View File

@ -6,10 +6,13 @@
:song="song"
:playSong="song.azura_id === currentPlay.azura_id? currentPlay.isPlay: false"
:selectSong="song.azura_id === currentPlay.azura_id"
:songAlreadyAddPlaylist="songAlreadyAdd"
@selectSong="handlerSelectSong"
@playSong="handlerPlaySong"
@pauseSong="handlerPauseSong"
@removeSong="removeSong"
@addPlaylist="addPlaylist"
@removePlaylist="removePlaylist"
/>
</div>
</template>
@ -24,6 +27,10 @@ export default {
songList: {
type: Array,
default: () => []
},
songAlreadyAdd: {
type: Boolean,
default: () => false
}
},
data() {
@ -36,7 +43,9 @@ export default {
},
methods: {
handlerSelectSong(params) {
this.$store.dispatch('setCurrentPlay', {...this.currentPlay, ...params, isLoader: true});
const data = {...this.currentPlay, ...params, isLoader: true};
data.currentIndex = this.songList.findIndex(el=>el.azura_id === params.azura_id);
this.$store.dispatch('setCurrentPlay', data);
console.log('handlerSelectSong')
},
handlerPlaySong(params) {
@ -51,6 +60,12 @@ export default {
},
removeSong(song){
this.$emit('removeSong', song)
},
addPlaylist(song){
this.$emit('addPlaylist', song)
},
removePlaylist(song){
this.$emit('removePlaylist', song)
}
}
}

View File

@ -6,6 +6,8 @@ import contacts from "@/views/contacts.vue";
import podcasts from "@/views/podcasts.vue";
import about from "@/views/about.vue";
import profile from "@/views/profile.vue";
import playlist from "@/views/playlist.vue";
import playlistCreate from "@/views/playlist-edit.vue";
const routes = [
@ -80,6 +82,37 @@ const routes = [
title: 'Личный кабинет',
isAuth: false
},
children: [
{
path: 'playlist/:id',
name: 'playlist',
component: playlist,
meta: {
title: 'Плейлист',
requiresAuth: true
},
props: true,
},
{
path: 'playlist-create/:id',
name: 'playlist-create',
component: playlistCreate,
meta: {
title: 'Плейлист',
requiresAuth: true
},
props: true,
},{
path: 'playlist-edit/:id',
name: 'playlist-edit',
component: playlistCreate,
meta: {
title: 'Плейлист',
requiresAuth: true
},
props: true,
},
]
},
// {

View File

@ -27,6 +27,14 @@ export default class extends REST {
});
}
static updateUser(params) {
return this._post(`user/update_user`, {}, params).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Не удалось создать пользователя');
});
}
static getTeams(station, params) {
return this._get(`radio/teams`, params, {}).then((data) => {
return data;
@ -42,7 +50,13 @@ export default class extends REST {
throw new RESTError(error, 'Ошибка при проверке песни');
});
}
static getAllSong(){
return this._get(`radio/song/get_all_song`, {}, {}).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Ошибка при получениии всех песен');
});
}
static getAudio(id){
return this._get(`radio/song/get_audio/${id}`, {}, {}, false, true).then((data) => {
return data;
@ -96,8 +110,35 @@ export default class extends REST {
});
}
static getPlaylists() {
return this._get(`radio/playlists`, {}, {}).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Ошибка при получении плейлистов');
});
}
static getPlaylist(id) {
return this._get(`radio/playlists/${id}`, {}, {}).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Ошибка при получении плейлистов');
});
}
static addSongToPlaylist(params) {
return this._post(`radio/playlists/add_to_playlist`, {}, params).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Ошибка при получении плейлистов');
});
}
static createPlaylists() {
return this._post(`radio/playlists/create_playlist`, {}, {}).then((data) => {
return data;
}).catch((error) => {
throw new RESTError(error, 'Ошибка при получении плейлистов');
});
}
static getNews(station, params) {

View File

@ -14,6 +14,10 @@ export default createStore({
token: null,
refreshToken: null,
user: null,
modal:{
auth: false,
changingUser: false
},
showAuthModal: false,
station: {
id: 1
@ -23,6 +27,7 @@ export default createStore({
isLoader: false,
live: true,
volume: 50,
currentIndex: null,
},
player: {
target: null,
@ -50,8 +55,8 @@ export default createStore({
setCurrentPlay(state, song) {
state.currentPlay = song;
},
setShowAuthModal(state, show) {
state.showAuthModal = show
setModal(state, show) {
state.modal = {...state.modal, ...show}
},
setPlayer(state, params) {
state.player = {...state.player, ...params}
@ -117,8 +122,8 @@ export default createStore({
setCurrentPlay(context, song) {
context.commit('setCurrentPlay', song);
},
setShowAuthModal(context, show) {
context.commit('setShowAuthModal', show);
setModal(context, show) {
context.commit('setModal', show);
},
setPlayer(context, params) {
context.commit('setPlayer', params);

View File

@ -0,0 +1,108 @@
<template>
<div class="playlist-edit">
<AppBreadcrumbs
:breadcrumbs="[
{ name: 'Главная', route: { name: 'home' } },
{ name: 'Личный кабинет', route: { name: 'profile' } },
$route.name==='playlist-create'?
{ name: 'Добавление плейлиста', route: { name: 'playlist-create' } }:
{ name: 'Редактирование плейлиста', route: { name: 'playlist-edit' } },
]"/>
<h1 class="h2">{{ $route.name==='playlist-create'?'Новый плейлист':playlist.name }}</h1>
<FormKit
v-model="playlist.name"
type="text"
outer-class="playlist-edit__search"
inner-class="m--search"
placeholder="Название плейлиста"
></FormKit>
<div class="playlist-edit__list">
<div class="playlist-edit__item">
<div class="playlist-edit__title">Поиск по каталогу</div>
<template v-if="showLoaderSongs">
<div class="loader">
<div class="spinner"/>
Загрузка данных
</div>
</template>
<SongList v-else-if="songs.length>0" :songList="songs" class="m--column" @addPlaylist="addPlaylist"/>
<div v-else>Каталог музыки пуст!</div>
</div>
<div class="playlist-edit__item">
<div class="playlist-edit__title">Добавленные треки</div>
<template v-if="showLoaderPlaylist">
<div class="loader">
<div class="spinner"/>
Загрузка данных
</div>
</template>
<SongList v-else-if="playlist.song?.length>0" :songList="playlist.song" class="m--column" :songAlreadyAdd="true" @removePlaylist="removePlaylist"/>
<div v-else>Добавьте музыку в плейлист!</div>
</div>
</div>
</div>
</template>
<script>
import {app} from "@/services";
import AppBreadcrumbs from "@/components/app-breadcrumbs.vue";
import SongList from "@/components/song-list.vue";
export default {
name: 'playlist-edit',
components: {SongList, AppBreadcrumbs},
data() {
return {
playlist: {},
songs: [],
showLoaderSongs: false,
showLoaderPlaylist: false,
}
},
created() {
this.getAllSong();
this.getPlaylist();
},
methods:{
getPlaylist(){
this.showLoaderPlaylist = true;
app.getPlaylist(this.$route.params.id).then(res=>{
this.showLoaderPlaylist = false;
this.playlist = res;
}).catch(err=>{
this.showLoaderPlaylist = false;
console.error(err)
})
},
getAllSong(){
this.showLoaderSongs = true;
app.getAllSong().then(res=>{
this.showLoaderSongs = false;
this.songs = res;
}).catch(err=>{
this.showLoaderSongs = false;
console.error(err)
})
},
addPlaylist(song){
console.log(song)
const params = {
playlist_id: this.$route.params.id,
azura_id: song.azura_id
}
this.showLoaderPlaylist = true;
app.addSongToPlaylist(params).then(()=>{
this.showLoaderPlaylist = false;
this.getPlaylist();
}).catch(err=>{
this.showLoaderPlaylist = false;
console.error(err)
})
},
removePlaylist(){
}
}
}
</script>

View File

@ -0,0 +1,11 @@
<template>
<div class="playlist">
</div>
</template>
<script>
export default {
name: 'playlist'
}
</script>

View File

@ -1,5 +1,6 @@
<template>
<div class="app__content profile">
<template v-if="$route.name !== 'playlist-edit'">
<AppBreadcrumbs
:breadcrumbs="[
{ name: 'Главная', route: { name: 'home' } },
@ -7,7 +8,7 @@
]"
/>
<h1 class="h2 profile__title">{{ user.email }}</h1>
<button class="button m--text-link">Редактировать профиль</button>
<button class="button m--text-link" @click="showRecovery">Редактировать профиль</button>
<div class="profile__tabs tabs m--btns">
<button
v-for="item in tabsItems"
@ -19,8 +20,10 @@
{{ item.label }}
</button>
</div>
</template>
<template v-if="$route.name === 'profile'">
<template v-if="currentTabsItem==='music'">
<template v-if="showLoader">
<template v-if="showLoaderSong">
<div class="loader">
<div class="spinner"/>
Загрузка данных
@ -28,6 +31,21 @@
</template>
<SongList v-else :songList="userFavorite.songs" @removeSong="removeFavorites"/>
</template>
<template v-if="currentTabsItem==='playlists'">
<template v-if="showLoaderPlaylist">
<div class="loader">
<div class="spinner"/>
Загрузка данных
</div>
</template>
<PlaylistRoster
v-else
:list="userFavorite.playlist"
@createPlaylist="createPlayList"
/>
</template>
</template>
<routerView v-else/>
</div>
</template>
@ -35,10 +53,11 @@
import AppBreadcrumbs from "@/components/app-breadcrumbs.vue";
import SongList from "@/components/song-list.vue";
import {app} from "@/services";
import PlaylistRoster from "@/components/playlist-roster.vue";
export default {
name: 'profile',
components: {SongList, AppBreadcrumbs},
components: {PlaylistRoster, SongList, AppBreadcrumbs},
data() {
return {
currentTabsItem: 'music',
@ -56,8 +75,9 @@ export default {
name: 'playlists'
},
],
songList: [],
showLoader: true,
showLoaderSong: false,
showLoaderPlaylist: false,
showLoaderPodcast: true,
}
},
computed: {
@ -78,31 +98,66 @@ export default {
this.currentTabsItem = 'music';
}
},
},
'$route.name': {
immediate: false,
handler(to) {
if (to === 'playlist') {
this.currentTabsItem = 'playlists'
}
},
},
},
created() {
this.getSongList();
this.getPlaylists();
},
methods: {
getSongList() {
this.showLoader = true;
this.showLoaderSong = true;
app.getFavoriteList().then(res => {
this.showLoader = false;
this.$store.dispatch('setUserFavorite', {songs: res})
this.showLoaderSong = false;
this.$store.dispatch('setUserFavorite', {songs: res});
}).catch(err => {
this.showLoader = false;
this.showLoaderSong = false;
console.error(err)
})
},
changeTab(tab) {
this.currentTabsItem = tab;
if (this.$route.name === 'profile') {
this.$router.push({name: this.$route.name, hash: `#${tab}`});
} else {
this.$router.push({name: 'profile', hash: `#${tab}`});
}
},
removeFavorites(song) {
this.showLoaderSong = true;
app.removeFavorites(song).then(() => {
this.showLoaderSong = false;
this.getSongList();
}).catch(err => {
this.showLoader = false;
this.showLoaderSong = false;
console.error(err)
})
},
showRecovery() {
this.$store.dispatch('setModal', {changingUser: true});
},
getPlaylists() {
this.showLoaderPlaylist = true;
app.getPlaylists().then(res => {
this.showLoaderPlaylist = false;
this.$store.dispatch('setUserFavorite', {playlist: res});
}).catch(err => {
this.showLoaderPlaylist = false;
console.error(err)
})
},
createPlayList() {
app.createPlaylists().then(res=>{
this.$router.push({name: 'playlist-edit'});
}).catch(err=>{
console.error(err)
})
}