This commit is contained in:
2025-12-12 13:30:09 +03:00
commit 2f1e1f35e3
75 changed files with 4603 additions and 0 deletions

View 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>

View 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>

View 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>