This commit is contained in:
2026-10-17 22:11:06 +07:00
parent 35e579701a
commit 688d900cdc
13 changed files with 192 additions and 15 deletions
+38 -3
View File
@@ -3,7 +3,7 @@ import { NAlert, NButton, NCard, NInput, NSelect, NSpace, useMessage } from 'nai
import { onMounted, onUnmounted, ref } from 'vue'
import type { OnlineIntent, OnlineState } from '../types/online'
defineProps<{ outputOpen: boolean; selectedKey: string | null }>()
defineProps<{ outputOpen: boolean; selectedKey: string | null; selectedName: string | null }>()
const emit = defineEmits<{ packagesChanged: [] }>()
const bridge = window.screenBridge
const message = useMessage()
@@ -16,6 +16,7 @@ const state = ref<OnlineState>({
canCancel: false,
games: [],
screenId: null,
soundEnabled: null,
connection: 'disabled',
error: '',
notice: '',
@@ -27,6 +28,12 @@ const email = ref('')
const password = ref('')
const gameId = ref<number | null>(null)
const isBusy = ref(false)
const gameStatusLabels = new Map([
['draft', 'Черновик'],
['run', 'Идёт'],
['pause', 'Пауза'],
['finish', 'Завершена'],
])
const connectionLabels = {
disabled: 'Управление не подключено',
waiting: 'Ожидание открытия показа',
@@ -134,7 +141,12 @@ onUnmounted(() => clearInterval(timer))
</n-space>
<n-select
v-model:value="gameId"
:options="state.games.map((g) => ({ label: `${g.name} · ${g.status}`, value: g.id }))"
:options="
state.games.map((g) => ({
label: `${g.name} · ${gameStatusLabels.get(g.status) ?? 'Неизвестный статус'}`,
value: g.id,
}))
"
:disabled="isBusy || state.busy"
placeholder="Выберите игру для подготовки"
:input-props="{ 'aria-label': 'Игра на сервере' }"
@@ -156,11 +168,13 @@ onUnmounted(() => clearInterval(timer))
Выбранную игру можно обновить после закрытия показа. Для обновления файлов по прежним
адресам загрузите материалы повторно.
</p>
<p>Выбор игры выше используется для загрузки материалов.</p>
<p v-if="selectedKey" data-testid="control-game">Игра для управления: {{ selectedName }}</p>
<n-button
v-if="selectedKey"
:disabled="isBusy || state.busy"
@click="run({ action: 'connect' })"
>Подключить управление выбранной игрой</n-button
>Подключить управление этой игрой</n-button
>
</template>
<p role="status">
@@ -168,6 +182,23 @@ onUnmounted(() => clearInterval(timer))
}}{{ state.screenId ? ` · Экран №${state.screenId}` : '' }}
</p>
<p v-if="state.screenId && !outputOpen">Опрос сервера начнётся после открытия показа.</p>
<template v-if="state.screenId">
<p role="status" data-testid="sound-assignment">
{{
state.soundEnabled === null
? 'Назначение звука пока неизвестно'
: state.soundEnabled
? 'Экран назначен для звука'
: 'Экран не назначен для звука'
}}
<template
v-if="state.soundEnabled !== null && (!outputOpen || state.connection !== 'connected')"
>
· Последнее подтверждённое состояние; сейчас назначение не проверяется.
</template>
</p>
<p>Переключатель «Выключить весь звук» управляет локальным звуком отдельно.</p>
</template>
<n-space v-if="state.screenId">
<n-button
:disabled="isBusy || state.busy || !state.authenticated"
@@ -181,6 +212,10 @@ onUnmounted(() => clearInterval(timer))
>Отключить управление</n-button
>
</n-space>
<p v-if="state.screenId">
«Отключить управление» прекращает управление приложением; привязка экрана на сервере
сохраняется.
</p>
<n-alert v-if="state.error" type="error">{{ state.error }}</n-alert>
<n-alert v-if="state.notice" type="info">{{ state.notice }}</n-alert>
<template v-if="state.busy">
+1
View File
@@ -237,6 +237,7 @@ onUnmounted(() => {
<connection-panel
:output-open="state?.outputOpen ?? false"
:selected-key="state?.catalog.key ?? null"
:selected-name="state?.catalog.name ?? null"
@packages-changed="refreshPackages"
/>
<n-card title="Материалы показа">