up
This commit is contained in:
+124
-113
@@ -49,9 +49,7 @@ const statusLabels: Record<Playback, string> = {
|
||||
function isCurrent(index: number) {
|
||||
return !!props.channel.source && props.channel.trackIndex === index
|
||||
}
|
||||
function canPlay(index: number) {
|
||||
return isCurrent(index) && props.outputOpen && !props.isBusy
|
||||
}
|
||||
const canPlay = computed(() => !!props.channel.source && props.outputOpen && !props.isBusy)
|
||||
function edit() {
|
||||
if (props.isBusy || !props.canEdit) return
|
||||
selectedId.value = props.channel.playlistId
|
||||
@@ -230,7 +228,7 @@ function remove(trackId?: number) {
|
||||
</template>
|
||||
</template>
|
||||
</n-space>
|
||||
<n-space v-else vertical data-testid="music-plan-view">
|
||||
<div v-else class="music-plan-view" data-testid="music-plan-view">
|
||||
<n-select
|
||||
:value="channel.playlistId"
|
||||
:options="localPlaylists.map((p) => ({ label: p.name, value: p.id }))"
|
||||
@@ -250,6 +248,116 @@ function remove(trackId?: number) {
|
||||
@update:value="emit('command', { action: 'loop', channel: 'music', enabled: $event })"
|
||||
/>
|
||||
</label>
|
||||
<p v-if="channel.source" class="current-track" data-testid="current-music-track">{{ channel.source.name }}</p>
|
||||
<div class="track-actions">
|
||||
<n-space align="center">
|
||||
<n-button
|
||||
square
|
||||
:disabled="!canPlay"
|
||||
:aria-label="control.label"
|
||||
:title="control.label"
|
||||
@click="emit('command', { action: control.action, channel: 'music' })"
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
v-if="control.action === 'pause'"
|
||||
d="M6 4h4v16H6zM14 4h4v16h-4z"
|
||||
/>
|
||||
<path v-else d="M7 4v16l13-8z" />
|
||||
</svg>
|
||||
</n-button>
|
||||
<n-button
|
||||
square
|
||||
:disabled="!canPlay"
|
||||
aria-label="Стоп"
|
||||
title="Стоп"
|
||||
@click="emit('command', { action: 'stop', channel: 'music' })"
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M5 5h14v14H5z" />
|
||||
</svg>
|
||||
</n-button>
|
||||
<n-button
|
||||
square
|
||||
:disabled="!canPlay"
|
||||
aria-label="Сначала"
|
||||
title="Сначала"
|
||||
@click="emit('command', { action: 'replay', channel: 'music' })"
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M20 11a8 8 0 1 0-2 6M20 4v7h-7" />
|
||||
</svg>
|
||||
</n-button>
|
||||
<div class="track-volume">
|
||||
<label for="volume-music" title="Громкость, %">
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M11 5 6 9H3v6h3l5 4zM15 8a6 6 0 0 1 0 8M18 5a10 10 0 0 1 0 14" />
|
||||
</svg>
|
||||
</label>
|
||||
<n-slider
|
||||
class="track-volume-input"
|
||||
:value="channel.volume"
|
||||
id="volume-music"
|
||||
role="group"
|
||||
aria-label="Громкость, %"
|
||||
:step="1"
|
||||
:format-tooltip="(value) => `${value}%`"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:disabled="!channel.source || isBusy"
|
||||
@update:value="
|
||||
emit('command', { action: 'volume', channel: 'music', volume: $event ?? 0 })
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</n-space>
|
||||
</div>
|
||||
<div class="track-progress">
|
||||
<n-progress
|
||||
class="track-progress-bar"
|
||||
type="line"
|
||||
:percentage="progress"
|
||||
:show-indicator="false"
|
||||
aria-label="Прогресс воспроизведения"
|
||||
/>
|
||||
<span class="track-time" role="status">
|
||||
{{ statusLabels[channel.status] }} ·
|
||||
{{ formatTimer(Math.floor(channel.position) * 1000) }} /
|
||||
{{ channel.duration === null ? '—' : formatTimer(channel.duration * 1000) }}
|
||||
</span>
|
||||
</div>
|
||||
<n-alert v-if="channel.error" type="error">{{
|
||||
channel.error
|
||||
}}</n-alert>
|
||||
|
||||
<p v-if="!localPlaylists.length">Плейлистов в игре пока нет.</p>
|
||||
<p v-else-if="!activePlaylist">Выберите плейлист.</p>
|
||||
<p v-else-if="!activePlaylist.tracks.length">Треков пока нет.</p>
|
||||
@@ -273,122 +381,20 @@ function remove(trackId?: number) {
|
||||
>{{ isCurrent(index) ? 'Выбран' : 'Выбрать' }}</n-button
|
||||
>
|
||||
</div>
|
||||
<div class="track-actions">
|
||||
<n-space align="center">
|
||||
<n-button
|
||||
square
|
||||
:disabled="!canPlay(index)"
|
||||
:aria-label="isCurrent(index) ? control.label : 'Воспроизвести'"
|
||||
:title="isCurrent(index) ? control.label : 'Воспроизвести'"
|
||||
@click="emit('command', { action: control.action, channel: 'music' })"
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path
|
||||
v-if="isCurrent(index) && control.action === 'pause'"
|
||||
d="M6 4h4v16H6zM14 4h4v16h-4z"
|
||||
/>
|
||||
<path v-else d="M7 4v16l13-8z" />
|
||||
</svg>
|
||||
</n-button>
|
||||
<n-button
|
||||
square
|
||||
:disabled="!canPlay(index)"
|
||||
aria-label="Стоп"
|
||||
title="Стоп"
|
||||
@click="emit('command', { action: 'stop', channel: 'music' })"
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M5 5h14v14H5z" />
|
||||
</svg>
|
||||
</n-button>
|
||||
<n-button
|
||||
square
|
||||
:disabled="!canPlay(index)"
|
||||
aria-label="Сначала"
|
||||
title="Сначала"
|
||||
@click="emit('command', { action: 'replay', channel: 'music' })"
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M20 11a8 8 0 1 0-2 6M20 4v7h-7" />
|
||||
</svg>
|
||||
</n-button>
|
||||
<div class="track-volume">
|
||||
<label :for="`volume-music-${track.id}`" title="Громкость, %">
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<path d="M11 5 6 9H3v6h3l5 4zM15 8a6 6 0 0 1 0 8M18 5a10 10 0 0 1 0 14" />
|
||||
</svg>
|
||||
</label>
|
||||
<n-slider
|
||||
class="track-volume-input"
|
||||
:value="channel.volume"
|
||||
:id="`volume-music-${track.id}`"
|
||||
role="group"
|
||||
aria-label="Громкость, %"
|
||||
:step="1"
|
||||
:format-tooltip="(value) => `${value}%`"
|
||||
:min="0"
|
||||
:max="100"
|
||||
:disabled="!isCurrent(index) || isBusy"
|
||||
@update:value="
|
||||
emit('command', { action: 'volume', channel: 'music', volume: $event ?? 0 })
|
||||
"
|
||||
/>
|
||||
</div>
|
||||
</n-space>
|
||||
</div>
|
||||
<div class="track-progress">
|
||||
<n-progress
|
||||
class="track-progress-bar"
|
||||
type="line"
|
||||
:percentage="isCurrent(index) ? progress : 0"
|
||||
:show-indicator="false"
|
||||
aria-label="Прогресс воспроизведения"
|
||||
/>
|
||||
<span v-if="isCurrent(index)" class="track-time" role="status">
|
||||
{{ statusLabels[channel.status] }} ·
|
||||
{{ formatTimer(Math.floor(channel.position) * 1000) }} /
|
||||
{{ channel.duration === null ? '—' : formatTimer(channel.duration * 1000) }}
|
||||
</span>
|
||||
</div>
|
||||
<n-alert v-if="isCurrent(index) && channel.error" type="error">{{
|
||||
channel.error
|
||||
}}</n-alert>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</n-space>
|
||||
</div>
|
||||
</n-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.music-plan-view {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.track-list {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
@@ -440,6 +446,11 @@ function remove(trackId?: number) {
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
}
|
||||
|
||||
.current-track {
|
||||
margin: 0;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.music-loop,
|
||||
.track-volume {
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user