This commit is contained in:
2026-10-18 01:38:02 +07:00
parent 7f2a02b69f
commit b591287072
16 changed files with 1308 additions and 52 deletions
+206
View File
@@ -0,0 +1,206 @@
<script setup lang="ts">
import { NAlert, NButton, NInput, NInputNumber, NSelect, NSpace } from 'naive-ui'
import type { Intent, ScreenMode, ScreenStep } from '../types/screen'
import { modeLabels } from '../utils/presentation'
defineProps<{
steps: ScreenStep[]
currentIndex: number | null
draft: ScreenStep[]
isEditing: boolean
isBusy: boolean
canEdit: boolean
notice: string
}>()
const emit = defineEmits<{
edit: []
cancel: []
save: []
add: []
remove: [index: number]
move: [index: number, offset: number]
clear: []
updateStep: [index: number, patch: Partial<ScreenStep>]
command: [intent: Intent]
}>()
const modeOptions = Object.entries(modeLabels).map(([mode, label]) => ({
value: Number(mode) as ScreenMode,
label,
}))
</script>
<template>
<n-space vertical data-testid="screen-plan">
<n-space>
<n-button v-if="!isEditing" :disabled="isBusy || !canEdit" @click="emit('edit')">
{{ steps.length ? 'Редактировать' : 'Создать план' }}
</n-button>
<template v-else>
<n-button :disabled="isBusy" @click="emit('cancel')">Отменить</n-button>
<n-button
type="primary"
:disabled="isBusy || !canEdit"
:loading="isBusy"
@click="emit('save')"
>
Сохранить план
</n-button>
</template>
</n-space>
<n-alert v-if="notice" type="warning" role="status">{{ notice }}</n-alert>
<p v-if="!canEdit">Для редактирования войдите на сервер выбранной подготовленной игры.</p>
<template v-if="isEditing">
<n-space v-for="(step, index) in draft" :key="index" vertical class="plan-step">
<span>Шаг {{ index + 1 }}</span>
<n-select
:value="step.mode"
:options="modeOptions"
:disabled="isBusy"
:input-props="{ 'aria-label': `Режим шага ${index + 1}` }"
@update:value="emit('updateStep', index, { mode: $event })"
/>
<n-input
v-if="step.mode === 8"
:value="step.text"
type="textarea"
:disabled="isBusy"
placeholder="Текст экрана"
:input-props="{ 'aria-label': `Текст шага ${index + 1}` }"
@update:value="emit('updateStep', index, { text: $event })"
/>
<template v-if="step.mode === 7">
<label :for="`plan-label-${index}`">Надпись таймера</label>
<n-input
:value="step.timerLabel"
:disabled="isBusy"
:input-props="{ id: `plan-label-${index}` }"
@update:value="emit('updateStep', index, { timerLabel: $event })"
/>
<label :for="`plan-seconds-${index}`">Длительность в секундах</label>
<n-input-number
:value="step.timerDurationSeconds"
:min="1"
:max="86400"
:disabled="isBusy"
:input-props="{ id: `plan-seconds-${index}` }"
@update:value="emit('updateStep', index, { timerDurationSeconds: $event ?? 0 })"
/>
</template>
<n-space>
<n-button
:disabled="isBusy || index === 0"
:aria-label="`Поднять шаг ${index + 1}`"
@click="emit('move', index, -1)"
>↑</n-button
>
<n-button
:disabled="isBusy || index === draft.length - 1"
:aria-label="`Опустить шаг ${index + 1}`"
@click="emit('move', index, 1)"
>↓</n-button
>
<n-button :disabled="isBusy" type="error" ghost @click="emit('remove', index)"
>Удалить</n-button
>
</n-space>
</n-space>
<n-space>
<n-button :disabled="isBusy" @click="emit('add')">Добавить шаг</n-button>
<n-button :disabled="isBusy || !draft.length" @click="emit('clear')">Удалить план</n-button>
</n-space>
</template>
<template v-else>
<p v-if="!steps.length">План экранов ещё не создан. Используйте переключатель режимов.</p>
<n-space
v-for="(step, index) in steps"
:key="index"
vertical
class="plan-step"
:class="{ 'plan-step-current': currentIndex === index }"
:data-step-index="index"
:aria-current="currentIndex === index ? 'step' : undefined"
>
<span
>{{ index + 1 }}. {{ modeLabels[step.mode]
}}<template v-if="step.mode === 8">: {{ step.text }}</template
><template v-if="step.mode === 7"
>: {{ step.timerLabel }} ({{ step.timerDurationSeconds }} с)</template
></span
>
<n-space>
<n-button
:type="currentIndex === index ? 'primary' : 'default'"
:disabled="isBusy || currentIndex === index"
@click="emit('command', { action: 'step', index })"
>{{ currentIndex === index ? 'Показан' : 'Показать' }}</n-button
>
<template v-if="currentIndex === index && step.mode === 7">
<n-button :disabled="isBusy" @click="emit('command', { action: 'timer-start' })"
>Старт</n-button
>
<n-button :disabled="isBusy" @click="emit('command', { action: 'timer-pause' })"
>Пауза</n-button
>
<n-button :disabled="isBusy" @click="emit('command', { action: 'timer-resume' })"
>Продолжить</n-button
>
<n-button :disabled="isBusy" @click="emit('command', { action: 'timer-reset' })"
>Сбросить</n-button
>
</template>
<template v-if="currentIndex === index && (step.mode === 2 || step.mode === 4)">
<n-button
:disabled="isBusy"
@click="emit('command', { action: 'play', channel: 'main' })"
>Запуск</n-button
>
<n-button
:disabled="isBusy"
@click="emit('command', { action: 'pause', channel: 'main' })"
>Пауза</n-button
>
<n-button
:disabled="isBusy"
@click="emit('command', { action: 'resume', channel: 'main' })"
>Продолжить</n-button
>
<n-button
:disabled="isBusy"
@click="emit('command', { action: 'stop', channel: 'main' })"
>Стоп</n-button
>
<n-button
:disabled="isBusy"
@click="emit('command', { action: 'replay', channel: 'main' })"
>Повтор</n-button
>
</template>
</n-space>
</n-space>
<n-space>
<n-button
:disabled="isBusy || currentIndex === null || currentIndex <= 0"
@click="emit('command', { action: 'step', index: (currentIndex ?? 0) - 1 })"
>Предыдущий шаг</n-button
>
<n-button
:disabled="isBusy || !steps.length || (currentIndex ?? -1) >= steps.length - 1"
@click="emit('command', { action: 'step', index: (currentIndex ?? -1) + 1 })"
>Следующий шаг</n-button
>
</n-space>
</template>
</n-space>
</template>
<style scoped>
.plan-step {
border: 1px solid var(--brand-background);
overflow-wrap: anywhere;
padding: 12px;
}
.plan-step-current {
border-color: var(--brand-text);
}
</style>