Files
evening_detective_screen/src/components/ScreenPlanPanel.vue
T
2026-10-18 01:38:02 +07:00

207 lines
7.5 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>