This commit is contained in:
2026-10-04 23:08:03 +07:00
parent 31d93a9916
commit 68fd0760f6
4 changed files with 394 additions and 371 deletions
+145 -119
View File
@@ -12,6 +12,8 @@ import {
NRate,
NSelect,
NSpace,
NTabPane,
NTabs,
NText,
} from 'naive-ui'
import { useMessage } from 'naive-ui'
@@ -52,6 +54,20 @@ const isDownloadingQR = ref(false)
const router = useRouter()
const route = useRoute()
const gameId = route.params.id
type GameTab = 'game' | 'teams' | 'screens-map'
function isGameTab(value: string): value is GameTab {
return ['game', 'teams', 'screens-map'].includes(value)
}
const queryTab = typeof route.query.tab === 'string' ? route.query.tab : ''
const activeTab = ref<GameTab>(isGameTab(queryTab) ? queryTab : 'game')
async function setActiveTab(value: string) {
if (!isGameTab(value)) return
activeTab.value = value
await router.replace({ query: { ...route.query, tab: value } })
}
const message = useMessage()
@@ -680,127 +696,137 @@ useSimplePolling(() => {
<template>
<div class="center-block-custom">
<div class="width1200">
<div class="settings-block">
<n-flex justify="space-between">
<n-space>
<n-tag :bordered="false" type="info"> Участвуют команд: {{ activeTeamCount }} </n-tag>
<n-tag type="info">
{{ timeString }}
</n-tag>
<n-tag v-if="formatGameDateTime(game.startAt)" type="info">
Начало: {{ formatGameDateTime(game.startAt) }}
</n-tag>
</n-space>
<n-tabs :value="activeTab" type="line" animated @update:value="setActiveTab">
<n-tab-pane name="game" tab="Игра">
<div class="settings-block">
<n-flex justify="space-between">
<n-space>
<n-tag :bordered="false" type="info">
Участвуют команд: {{ activeTeamCount }}
</n-tag>
<n-tag type="info">
{{ timeString }}
</n-tag>
<n-tag v-if="formatGameDateTime(game.startAt)" type="info">
Начало: {{ formatGameDateTime(game.startAt) }}
</n-tag>
</n-space>
<n-flex justify="end">
<n-button quaternary @click="openQR(url, 'Админка')"> QR </n-button>
<n-button quaternary @click="openIssueTickets()"> Выпустить билеты </n-button>
<n-button quaternary @click="openAnswersModal()"> Ответы команд </n-button>
<n-button v-if="game.status == 'finish'" quaternary @click="toResults()">
<Icon class="settings-icon">
<Trophy />
</Icon>
Итоги
</n-button>
<n-button
v-if="activeTeamCount"
quaternary
:loading="isDownloadingQR"
:disabled="isDownloadingQR"
@click="downloadTeamQR()"
>
QR команд
</n-button>
<n-button quaternary @click="openSettings()">
<Icon class="settings-icon">
<Settings />
</Icon>
Настройки
</n-button>
<n-button
v-if="(game.teams?.length || 0) == 0"
type="error"
quaternary
@click="showDeleteGameModal = true"
>
Удалить игру
</n-button>
<n-flex justify="end">
<n-button quaternary @click="openQR(url, 'Админка')"> QR </n-button>
<n-button quaternary @click="openIssueTickets()"> Выпустить билеты </n-button>
<n-button quaternary @click="openAnswersModal()"> Ответы команд </n-button>
<n-button v-if="game.status == 'finish'" quaternary @click="toResults()">
<Icon class="settings-icon">
<Trophy />
</Icon>
Итоги
</n-button>
<n-button
v-if="activeTeamCount"
quaternary
:loading="isDownloadingQR"
:disabled="isDownloadingQR"
@click="downloadTeamQR()"
>
QR команд
</n-button>
<n-button quaternary @click="openSettings()">
<Icon class="settings-icon">
<Settings />
</Icon>
Настройки
</n-button>
<n-button
v-if="(game.teams?.length || 0) == 0"
type="error"
quaternary
@click="showDeleteGameModal = true"
>
Удалить игру
</n-button>
<n-space>
<n-button type="info" v-if="game.status != 'draft'" dashed @click="resetGame()">
<Icon>
<Reset />
</Icon>
</n-button>
<n-button type="info" v-if="game.status == 'draft'" dashed @click="startGame()">
<Icon>
<Play />
</Icon>
</n-button>
<n-button type="info" v-if="game.status == 'pause'" dashed @click="playGame()">
<Icon>
<Play />
</Icon>
</n-button>
<n-button type="info" v-if="game.status == 'run'" dashed @click="pauseGame()">
<Icon>
<Pause />
</Icon>
</n-button>
<n-button type="info" v-if="game.status != 'finish'" dashed @click="finishGame()">
<Icon>
<Flag />
</Icon>
</n-button>
</n-space>
</n-flex>
</n-flex>
</div>
<GameScreensPanel
:screens="screens"
:screen-plan="screenPlan"
:screen-plan-draft="screenPlanDraft"
:screen-plan-dirty="screenPlanDirty"
:screen-plan-saving="screenPlanSaving"
:screen-plan-mode="screenPlanMode"
:screen-volume="screenVolume"
:screen-position="screenPosition"
:screen-binding="screenBinding"
@bind="bindScannedScreen"
@set-sound="setSoundScreen"
@unbind="unbindScreen"
@add-step="addScreenPlanStep"
@edit="editScreenPlan"
@cancel="cancelScreenPlanEdit"
@remove-step="removeScreenPlanStep"
@move-step="moveScreenPlanStep"
@dirty="markScreenPlanDirty"
@clear-plan="clearScreenPlan"
@save="saveScreenPlan"
@set-step="setScreenPlanStep"
@command="sendScreenPlanCommand"
@update:screen-volume="screenVolume = $event ?? 0"
@update:screen-position="screenPosition = $event ?? 0"
/>
<GameMapPanel
:map-items="mapItems"
:teams="game.teams || []"
:screens="screens"
:preview-questions="previewQuestions"
:preview-teams="previewTeams"
@save-item="saveMapItemAndReload"
@give-application="giveApplication"
/>
<GameTeamsPanel
:teams="game.teams || []"
:participation-options="participationOptions"
@add="showAddTeamModal = true"
@open-qr="openTeamQR"
@preview="(id, password) => toTeamStory(id, password, true)"
@delete="deleteTeam"
@give-application="giveApplication"
/>
<n-space>
<n-button type="info" v-if="game.status != 'draft'" dashed @click="resetGame()">
<Icon>
<Reset />
</Icon>
</n-button>
<n-button type="info" v-if="game.status == 'draft'" dashed @click="startGame()">
<Icon>
<Play />
</Icon>
</n-button>
<n-button type="info" v-if="game.status == 'pause'" dashed @click="playGame()">
<Icon>
<Play />
</Icon>
</n-button>
<n-button type="info" v-if="game.status == 'run'" dashed @click="pauseGame()">
<Icon>
<Pause />
</Icon>
</n-button>
<n-button type="info" v-if="game.status != 'finish'" dashed @click="finishGame()">
<Icon>
<Flag />
</Icon>
</n-button>
</n-space>
</n-flex>
</n-flex>
</div>
</n-tab-pane>
<n-tab-pane name="teams" tab="Команды">
<n-tag :bordered="false" type="info"> Участвуют команд: {{ activeTeamCount }} </n-tag>
<GameTeamsPanel
:teams="game.teams || []"
:participation-options="participationOptions"
@add="showAddTeamModal = true"
@open-qr="openTeamQR"
@preview="(id, password) => toTeamStory(id, password, true)"
@delete="deleteTeam"
@give-application="giveApplication"
/>
</n-tab-pane>
<n-tab-pane name="screens-map" tab="Экраны и карта">
<GameScreensPanel
:screens="screens"
:screen-binding="screenBinding"
@bind="bindScannedScreen"
@set-sound="setSoundScreen"
@unbind="unbindScreen"
/>
<GameMapPanel
:map-items="mapItems"
:teams="game.teams || []"
:screens="screens"
:preview-questions="previewQuestions"
:preview-teams="previewTeams"
:screen-plan="screenPlan"
:screen-plan-draft="screenPlanDraft"
:screen-plan-dirty="screenPlanDirty"
:screen-plan-saving="screenPlanSaving"
:screen-plan-mode="screenPlanMode"
:screen-volume="screenVolume"
:screen-position="screenPosition"
@save-item="saveMapItemAndReload"
@give-application="giveApplication"
@add-step="addScreenPlanStep"
@edit="editScreenPlan"
@cancel="cancelScreenPlanEdit"
@remove-step="removeScreenPlanStep"
@move-step="moveScreenPlanStep"
@dirty="markScreenPlanDirty"
@clear-plan="clearScreenPlan"
@save="saveScreenPlan"
@set-step="setScreenPlanStep"
@command="sendScreenPlanCommand"
@update:screen-volume="screenVolume = $event ?? 0"
@update:screen-position="screenPosition = $event ?? 0"
/>
</n-tab-pane>
</n-tabs>
</div>
</div>