generated from VLADIMIR/template_frontend
up
This commit is contained in:
@@ -29,6 +29,8 @@ import type {
|
||||
ResultsQuestion,
|
||||
ScreenCommand,
|
||||
ScreenMode,
|
||||
ScreenPlan,
|
||||
ScreenPlanStep,
|
||||
ScreenState,
|
||||
Team,
|
||||
TeamAnswers,
|
||||
@@ -106,6 +108,10 @@ const teamsNeedingApplications = computed(
|
||||
),
|
||||
)
|
||||
const screens = ref<ScreenState[]>([])
|
||||
const screenPlan = ref<ScreenPlan>()
|
||||
const screenPlanDraft = ref<ScreenPlanStep[]>([])
|
||||
const screenPlanDirty = ref(false)
|
||||
const screenPlanSaving = ref(false)
|
||||
const previewQuestions = ref<ResultsQuestion[]>([])
|
||||
const previewTeams = ref<TeamResult[]>([])
|
||||
const mapItems = ref<MapItem[]>([])
|
||||
@@ -123,6 +129,16 @@ const currentScreenMode = computed<ScreenMode | undefined>(() => {
|
||||
const timerScreen = computed(() => screens.value[0])
|
||||
const previewScreen = computed(() => screens.value.find((screen) => screen.status === 'connected') || screens.value[0])
|
||||
const hasDisconnectedScreen = computed(() => screens.value.some((screen) => screen.status === 'disconnected'))
|
||||
const screenPlanModeOptions = [
|
||||
{ label: 'Обложка', value: 'SCREEN_MODE_COVER' },
|
||||
{ label: 'Введение', value: 'SCREEN_MODE_INTRO' },
|
||||
{ label: 'Успехов', value: 'SCREEN_MODE_INTRO_DONE' },
|
||||
{ label: 'Закрывашка', value: 'SCREEN_MODE_CONCLUSION' },
|
||||
{ label: 'Спасибо за игру', value: 'SCREEN_MODE_CONCLUSION_DONE' },
|
||||
{ label: 'Результаты', value: 'SCREEN_MODE_RESULTS' },
|
||||
{ label: 'Таймер', value: 'SCREEN_MODE_TIMER' },
|
||||
{ label: 'Текст', value: 'SCREEN_MODE_TEXT' },
|
||||
]
|
||||
const timerStateLabel = computed(() => {
|
||||
switch (timerScreen.value?.timerState) {
|
||||
case 'SCREEN_TIMER_STATE_RUNNING':
|
||||
@@ -196,6 +212,77 @@ async function getGameScreens() {
|
||||
}
|
||||
getGameScreens()
|
||||
|
||||
async function getScreenPlan() {
|
||||
if (screenPlanDirty.value) return
|
||||
const res = await client.GetScreenPlan({ gameID: Number(gameId) })
|
||||
screenPlan.value = res.plan
|
||||
screenPlanDraft.value = res.plan?.steps?.map((step) => ({ ...step })) || []
|
||||
}
|
||||
getScreenPlan()
|
||||
|
||||
function addScreenPlanStep() {
|
||||
screenPlanDraft.value.push({
|
||||
mode: 'SCREEN_MODE_TEXT',
|
||||
text: 'Новый экран',
|
||||
timerLabel: 'Перерыв',
|
||||
timerDurationSeconds: 600,
|
||||
})
|
||||
screenPlanDirty.value = true
|
||||
}
|
||||
|
||||
function removeScreenPlanStep(index: number) {
|
||||
screenPlanDraft.value.splice(index, 1)
|
||||
screenPlanDirty.value = true
|
||||
}
|
||||
|
||||
function moveScreenPlanStep(index: number, offset: number) {
|
||||
const target = index + offset
|
||||
if (target < 0 || target >= screenPlanDraft.value.length) return
|
||||
const [step] = screenPlanDraft.value.splice(index, 1)
|
||||
screenPlanDraft.value.splice(target, 0, step)
|
||||
screenPlanDirty.value = true
|
||||
}
|
||||
|
||||
function markScreenPlanDirty() {
|
||||
screenPlanDirty.value = true
|
||||
}
|
||||
|
||||
async function saveScreenPlan() {
|
||||
if (screenPlanSaving.value) return
|
||||
screenPlanSaving.value = true
|
||||
try {
|
||||
const currentStep = Math.min(
|
||||
screenPlan.value?.currentStep || 0,
|
||||
Math.max(0, screenPlanDraft.value.length - 1),
|
||||
)
|
||||
const res = await client.SaveScreenPlan({
|
||||
gameID: Number(gameId),
|
||||
steps: screenPlanDraft.value,
|
||||
currentStep,
|
||||
})
|
||||
screenPlan.value = res.plan
|
||||
screenPlanDraft.value = res.plan?.steps?.map((step) => ({ ...step })) || []
|
||||
screenPlanDirty.value = false
|
||||
await getGameScreens()
|
||||
message.success(res.plan ? 'План экранов сохранён' : 'План экранов удалён')
|
||||
} catch (err) {
|
||||
message.error(err instanceof Error ? err.message : 'Не удалось сохранить план экранов')
|
||||
} finally {
|
||||
screenPlanSaving.value = false
|
||||
}
|
||||
}
|
||||
|
||||
async function setScreenPlanStep(index: number) {
|
||||
if (screenPlanDirty.value || !screenPlan.value) return
|
||||
try {
|
||||
const res = await client.SetScreenPlanStep({ gameID: Number(gameId), currentStep: index })
|
||||
screenPlan.value = res.plan
|
||||
await getGameScreens()
|
||||
} catch (err) {
|
||||
message.error(err instanceof Error ? err.message : 'Не удалось перейти на шаг плана')
|
||||
}
|
||||
}
|
||||
|
||||
function stopScreenScanner() {
|
||||
cancelAnimationFrame(screenScannerFrame)
|
||||
screenScannerStream?.getTracks().forEach((track) => track.stop())
|
||||
@@ -607,6 +694,7 @@ async function issueTickets() {
|
||||
useSimplePolling(() => {
|
||||
getGame(Number(gameId))
|
||||
getGameScreens()
|
||||
getScreenPlan()
|
||||
getGameMap()
|
||||
}, 2000)
|
||||
|
||||
@@ -952,6 +1040,86 @@ useSimplePolling(() => {
|
||||
>Перемотать</n-button
|
||||
>
|
||||
</n-space>
|
||||
<div class="screen-plan">
|
||||
<n-flex align="center" justify="space-between">
|
||||
<p class="screens-title">План экранов</p>
|
||||
<n-space>
|
||||
<n-button v-if="!screenPlan && !screenPlanDirty" size="small" @click="addScreenPlanStep">
|
||||
Создать план
|
||||
</n-button>
|
||||
<n-button
|
||||
v-if="screenPlan || screenPlanDirty"
|
||||
size="small"
|
||||
type="primary"
|
||||
:loading="screenPlanSaving"
|
||||
@click="saveScreenPlan"
|
||||
>
|
||||
Сохранить план
|
||||
</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<template v-if="screenPlan || screenPlanDirty">
|
||||
<n-space v-for="(step, index) in screenPlanDraft" :key="index" align="center" wrap>
|
||||
<n-text>{{ index + 1 }}</n-text>
|
||||
<n-select
|
||||
v-model:value="step.mode"
|
||||
:options="screenPlanModeOptions"
|
||||
size="small"
|
||||
style="width: 180px"
|
||||
@update:value="markScreenPlanDirty"
|
||||
/>
|
||||
<n-input
|
||||
v-if="step.mode === 'SCREEN_MODE_TEXT'"
|
||||
v-model:value="step.text"
|
||||
size="small"
|
||||
placeholder="Текст экрана"
|
||||
style="min-width: 240px"
|
||||
@update:value="markScreenPlanDirty"
|
||||
/>
|
||||
<template v-if="step.mode === 'SCREEN_MODE_TIMER'">
|
||||
<n-input
|
||||
v-model:value="step.timerLabel"
|
||||
size="small"
|
||||
placeholder="Надпись таймера"
|
||||
@update:value="markScreenPlanDirty"
|
||||
/>
|
||||
<n-input-number
|
||||
v-model:value="step.timerDurationSeconds"
|
||||
:min="1"
|
||||
:max="86400"
|
||||
size="small"
|
||||
placeholder="Секунды"
|
||||
@update:value="markScreenPlanDirty"
|
||||
/>
|
||||
</template>
|
||||
<n-button size="small" :disabled="index === 0" @click="moveScreenPlanStep(index, -1)">↑</n-button>
|
||||
<n-button
|
||||
size="small"
|
||||
:disabled="index === screenPlanDraft.length - 1"
|
||||
@click="moveScreenPlanStep(index, 1)"
|
||||
>
|
||||
↓
|
||||
</n-button>
|
||||
<n-button size="small" type="error" ghost @click="removeScreenPlanStep(index)">
|
||||
Удалить
|
||||
</n-button>
|
||||
<n-button
|
||||
size="small"
|
||||
:type="screenPlan?.currentStep === index ? 'primary' : 'default'"
|
||||
:disabled="screenPlanDirty || screenPlan?.currentStep === index"
|
||||
@click="setScreenPlanStep(index)"
|
||||
>
|
||||
Показать
|
||||
</n-button>
|
||||
</n-space>
|
||||
<n-space>
|
||||
<n-button size="small" @click="addScreenPlanStep">Добавить шаг</n-button>
|
||||
<n-button v-if="screenPlanDraft.length" size="small" @click="screenPlanDraft = []; markScreenPlanDirty()">
|
||||
Удалить план
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user