init
This commit is contained in:
61
frontend/src/components/room/ParticipantsList.vue
Normal file
61
frontend/src/components/room/ParticipantsList.vue
Normal file
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<div class="participants card">
|
||||
<h3>Участники ({{ participants.length }})</h3>
|
||||
<div class="participants-list">
|
||||
<div
|
||||
v-for="participant in participants"
|
||||
:key="participant.id"
|
||||
class="participant"
|
||||
>
|
||||
<div class="avatar">{{ participant.username.charAt(0).toUpperCase() }}</div>
|
||||
<span class="username">{{ participant.username }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
participants: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.participants h3 {
|
||||
margin: 0 0 16px 0;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.participants-list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.participant {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 50%;
|
||||
background: #6c63ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.username {
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
98
frontend/src/components/room/Queue.vue
Normal file
98
frontend/src/components/room/Queue.vue
Normal file
@@ -0,0 +1,98 @@
|
||||
<template>
|
||||
<div class="queue">
|
||||
<div v-if="queue.length === 0" class="empty-queue">
|
||||
Очередь пуста
|
||||
</div>
|
||||
<div
|
||||
v-for="(track, index) in queue"
|
||||
:key="track.id"
|
||||
class="queue-item"
|
||||
@click="$emit('play-track', track)"
|
||||
>
|
||||
<span class="queue-index">{{ index + 1 }}</span>
|
||||
<div class="queue-track-info">
|
||||
<span class="queue-track-title">{{ track.title }}</span>
|
||||
<span class="queue-track-artist">{{ track.artist }}</span>
|
||||
</div>
|
||||
<span class="queue-duration">{{ formatDuration(track.duration) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
queue: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
defineEmits(['play-track'])
|
||||
|
||||
function formatDuration(ms) {
|
||||
const seconds = Math.floor(ms / 1000)
|
||||
const minutes = Math.floor(seconds / 60)
|
||||
const secs = seconds % 60
|
||||
return `${minutes}:${secs.toString().padStart(2, '0')}`
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.queue {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.empty-queue {
|
||||
text-align: center;
|
||||
color: #666;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.queue-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
padding: 10px;
|
||||
border-radius: 8px;
|
||||
cursor: pointer;
|
||||
transition: background 0.2s;
|
||||
}
|
||||
|
||||
.queue-item:hover {
|
||||
background: #2d2d44;
|
||||
}
|
||||
|
||||
.queue-index {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
min-width: 24px;
|
||||
}
|
||||
|
||||
.queue-track-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.queue-track-title {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.queue-track-artist {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
color: #aaa;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.queue-duration {
|
||||
color: #aaa;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
64
frontend/src/components/room/RoomCard.vue
Normal file
64
frontend/src/components/room/RoomCard.vue
Normal file
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<div class="room-card card">
|
||||
<h3>{{ room.name }}</h3>
|
||||
<div class="room-info">
|
||||
<span class="participants">{{ room.participants_count }} участников</span>
|
||||
<span v-if="room.is_playing" class="playing">Играет</span>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
room: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.room-card {
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s, border-color 0.2s;
|
||||
}
|
||||
|
||||
.room-card:hover {
|
||||
transform: translateY(-2px);
|
||||
border-color: #6c63ff;
|
||||
}
|
||||
|
||||
.room-card h3 {
|
||||
margin: 0 0 12px 0;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.room-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
color: #aaa;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.playing {
|
||||
color: #2ed573;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
}
|
||||
|
||||
.playing::before {
|
||||
content: '';
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background: #2ed573;
|
||||
border-radius: 50%;
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0%, 100% { opacity: 1; }
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user