generated from VLADIMIR/template_frontend
up
This commit is contained in:
Vendored
+1
-1
@@ -5,7 +5,7 @@
|
|||||||
<link rel="icon" href="/favicon.ico" />
|
<link rel="icon" href="/favicon.ico" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>Вечерний детектив</title>
|
<title>Вечерний детектив</title>
|
||||||
<script type="module" crossorigin src="/assets/index-Bp_KKqDz.js"></script>
|
<script type="module" crossorigin src="/assets/index-DNdwunR3.js"></script>
|
||||||
<link rel="modulepreload" crossorigin href="/assets/client-CT8bsOZT.js">
|
<link rel="modulepreload" crossorigin href="/assets/client-CT8bsOZT.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/light-DhgSQSdG.js">
|
<link rel="modulepreload" crossorigin href="/assets/light-DhgSQSdG.js">
|
||||||
<link rel="modulepreload" crossorigin href="/assets/light-DW22zwJY.js">
|
<link rel="modulepreload" crossorigin href="/assets/light-DW22zwJY.js">
|
||||||
|
|||||||
+197
-259
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { Flag, Pause, Play, Reset, Settings, Trophy } from '@vicons/carbon'
|
import { Flag, Pause, Play, Reset, Trophy } from '@vicons/carbon'
|
||||||
import { Icon } from '@vicons/utils'
|
import { Icon } from '@vicons/utils'
|
||||||
import { NButton, NTag } from 'naive-ui'
|
import { NButton, NTag } from 'naive-ui'
|
||||||
import {
|
import {
|
||||||
@@ -55,25 +55,26 @@ const isDownloadingQR = ref(false)
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
const gameId = route.params.id
|
const gameId = route.params.id
|
||||||
type GameTab = 'game' | 'teams' | 'screens-map'
|
type GameTab = 'game' | 'answers' | 'teams' | 'screens' | 'screens-map'
|
||||||
|
|
||||||
function isGameTab(value: string): value is GameTab {
|
function isGameTab(value: string): value is GameTab {
|
||||||
return ['game', 'teams', 'screens-map'].includes(value)
|
return ['game', 'answers', 'teams', 'screens', 'screens-map'].includes(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
const queryTab = typeof route.query.tab === 'string' ? route.query.tab : ''
|
const queryTab = typeof route.query.tab === 'string' ? route.query.tab : ''
|
||||||
const activeTab = ref<GameTab>(isGameTab(queryTab) ? queryTab : 'game')
|
const activeTab = ref<GameTab>(isGameTab(queryTab) ? queryTab : 'screens-map')
|
||||||
|
|
||||||
async function setActiveTab(value: string) {
|
async function setActiveTab(value: string) {
|
||||||
if (!isGameTab(value)) return
|
if (!isGameTab(value)) return
|
||||||
activeTab.value = value
|
activeTab.value = value
|
||||||
await router.replace({ query: { ...route.query, tab: value } })
|
await router.replace({ query: { ...route.query, tab: value } })
|
||||||
|
if (value === 'answers') await loadGameAnswers()
|
||||||
}
|
}
|
||||||
|
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
const showSettingsModal = ref(false)
|
|
||||||
const settingsDraft = ref({ name: '', description: '', format: 'online', location: '' })
|
const settingsDraft = ref({ name: '', description: '', format: 'online', location: '' })
|
||||||
|
const settingsDraftInitialized = ref(false)
|
||||||
const showGiveApplicationModal = ref(false)
|
const showGiveApplicationModal = ref(false)
|
||||||
const showAddTeamModal = ref(false)
|
const showAddTeamModal = ref(false)
|
||||||
const showDeleteTeamModal = ref(false)
|
const showDeleteTeamModal = ref(false)
|
||||||
@@ -85,7 +86,6 @@ const deletedGameName = ref('')
|
|||||||
const showQR = ref(false)
|
const showQR = ref(false)
|
||||||
const urlQR = ref('')
|
const urlQR = ref('')
|
||||||
const titleQR = ref('')
|
const titleQR = ref('')
|
||||||
const showIssueTicketsModal = ref(false)
|
|
||||||
const ticketCount = ref(1)
|
const ticketCount = ref(1)
|
||||||
const issuedTickets = ref<Ticket[]>([])
|
const issuedTickets = ref<Ticket[]>([])
|
||||||
const isIssuingTickets = ref(false)
|
const isIssuingTickets = ref(false)
|
||||||
@@ -127,6 +127,15 @@ const screenPosition = ref(0)
|
|||||||
async function getGame(id: number) {
|
async function getGame(id: number) {
|
||||||
const res = await client.GetGame({ ID: id })
|
const res = await client.GetGame({ ID: id })
|
||||||
game.value = res.game!
|
game.value = res.game!
|
||||||
|
if (!settingsDraftInitialized.value) {
|
||||||
|
settingsDraft.value = {
|
||||||
|
name: game.value.name || '',
|
||||||
|
description: game.value.description || '',
|
||||||
|
format: game.value.format || 'online',
|
||||||
|
location: game.value.location || '',
|
||||||
|
}
|
||||||
|
settingsDraftInitialized.value = true
|
||||||
|
}
|
||||||
game.value.teams = game.value.teams?.sort((a, b) => {
|
game.value.teams = game.value.teams?.sort((a, b) => {
|
||||||
let aLen = 0
|
let aLen = 0
|
||||||
if ((a.applications?.length || 0) > 0) {
|
if ((a.applications?.length || 0) > 0) {
|
||||||
@@ -384,20 +393,6 @@ function toResults() {
|
|||||||
router.push('/games/' + gameId + '/results')
|
router.push('/games/' + gameId + '/results')
|
||||||
}
|
}
|
||||||
|
|
||||||
function openSettings() {
|
|
||||||
settingsDraft.value = {
|
|
||||||
name: game.value.name || '',
|
|
||||||
description: game.value.description || '',
|
|
||||||
format: game.value.format || 'online',
|
|
||||||
location: game.value.location || '',
|
|
||||||
}
|
|
||||||
showSettingsModal.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
function closeSettings() {
|
|
||||||
showSettingsModal.value = false
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateGame() {
|
async function updateGame() {
|
||||||
await client.UpdateGame({
|
await client.UpdateGame({
|
||||||
ID: game.value.ID,
|
ID: game.value.ID,
|
||||||
@@ -409,7 +404,6 @@ async function updateGame() {
|
|||||||
scenarioID: game.value.scenario?.ID,
|
scenarioID: game.value.scenario?.ID,
|
||||||
})
|
})
|
||||||
await getGame(game.value.ID!)
|
await getGame(game.value.ID!)
|
||||||
showSettingsModal.value = false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const giveApplicationTeam = ref<Team>({
|
const giveApplicationTeam = ref<Team>({
|
||||||
@@ -518,12 +512,6 @@ async function openQR(url: string, text: string) {
|
|||||||
showQR.value = true
|
showQR.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
function openIssueTickets() {
|
|
||||||
ticketCount.value = 1
|
|
||||||
issuedTickets.value = []
|
|
||||||
showIssueTicketsModal.value = true
|
|
||||||
}
|
|
||||||
|
|
||||||
async function issueTickets() {
|
async function issueTickets() {
|
||||||
if (isIssuingTickets.value || !game.value.ID) return
|
if (isIssuingTickets.value || !game.value.ID) return
|
||||||
isIssuingTickets.value = true
|
isIssuingTickets.value = true
|
||||||
@@ -584,7 +572,6 @@ async function finishGame() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ===== Ответы команд и оценка организатором =====
|
// ===== Ответы команд и оценка организатором =====
|
||||||
const showAnswersModal = ref(false)
|
|
||||||
const showReviewsModal = ref(false)
|
const showReviewsModal = ref(false)
|
||||||
const gameReviews = ref<GameReview[]>([])
|
const gameReviews = ref<GameReview[]>([])
|
||||||
const reviewsAverage = ref(0)
|
const reviewsAverage = ref(0)
|
||||||
@@ -620,11 +607,6 @@ async function loadGameAnswers() {
|
|||||||
scores.value = next
|
scores.value = next
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openAnswersModal() {
|
|
||||||
showAnswersModal.value = true
|
|
||||||
await loadGameAnswers()
|
|
||||||
}
|
|
||||||
|
|
||||||
async function openReviewsModal() {
|
async function openReviewsModal() {
|
||||||
showReviewsModal.value = true
|
showReviewsModal.value = true
|
||||||
const res = await client.GetGameReviewsForModeration({ gameID: Number(gameId) })
|
const res = await client.GetGameReviewsForModeration({ gameID: Number(gameId) })
|
||||||
@@ -654,6 +636,10 @@ async function saveAllScores() {
|
|||||||
await loadGameAnswers()
|
await loadGameAnswers()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (activeTab.value === 'answers') {
|
||||||
|
loadGameAnswers()
|
||||||
|
}
|
||||||
|
|
||||||
function getCurrentUTC(): Date {
|
function getCurrentUTC(): Date {
|
||||||
const now = new Date()
|
const now = new Date()
|
||||||
return new Date(now.getTime() - now.getTimezoneOffset() * 60000)
|
return new Date(now.getTime() - now.getTimezoneOffset() * 60000)
|
||||||
@@ -698,96 +684,103 @@ useSimplePolling(() => {
|
|||||||
<div class="center-block-custom">
|
<div class="center-block-custom">
|
||||||
<div class="width1200">
|
<div class="width1200">
|
||||||
<n-tabs :value="activeTab" type="line" animated @update:value="setActiveTab">
|
<n-tabs :value="activeTab" type="line" animated @update:value="setActiveTab">
|
||||||
<n-tab-pane name="game" tab="Игра">
|
<n-tab-pane name="game" tab="Настройки">
|
||||||
<div class="game-tab-content">
|
<div class="game-tab-content">
|
||||||
<div class="settings-block">
|
<UiBlock>
|
||||||
<n-flex justify="space-between">
|
<n-flex justify="end">
|
||||||
<n-space>
|
<n-button quaternary @click="openQR(url, 'Админка')">QR</n-button>
|
||||||
<n-tag :bordered="false" type="info">
|
<n-button v-if="game.status == 'finish'" quaternary @click="toResults()">
|
||||||
Участвуют команд: {{ activeTeamCount }}
|
<Icon class="settings-icon">
|
||||||
</n-tag>
|
<Trophy />
|
||||||
<n-tag type="info">
|
</Icon>
|
||||||
{{ timeString }}
|
Итоги
|
||||||
</n-tag>
|
</n-button>
|
||||||
<n-tag v-if="formatGameDateTime(game.startAt)" type="info">
|
<n-button
|
||||||
Начало: {{ formatGameDateTime(game.startAt) }}
|
v-if="(game.teams?.length || 0) == 0"
|
||||||
</n-tag>
|
type="error"
|
||||||
</n-space>
|
quaternary
|
||||||
|
@click="showDeleteGameModal = true"
|
||||||
<n-flex justify="end">
|
>
|
||||||
<n-button quaternary @click="openQR(url, 'Админка')"> QR </n-button>
|
Удалить игру
|
||||||
<n-button quaternary @click="openIssueTickets()"> Выпустить билеты </n-button>
|
</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>
|
</n-flex>
|
||||||
</div>
|
</UiBlock>
|
||||||
|
<UiBlock>
|
||||||
|
<p class="screens-title">Настройки игры</p>
|
||||||
|
<n-space vertical>
|
||||||
|
<p>Название</p>
|
||||||
|
<n-input
|
||||||
|
v-model:value="settingsDraft.name"
|
||||||
|
type="text"
|
||||||
|
placeholder='Дело №0 "Следствие ведут овечки"'
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p class="settings-header">Формат игры</p>
|
||||||
|
<n-select
|
||||||
|
v-model:value="settingsDraft.format"
|
||||||
|
:options="[
|
||||||
|
{ label: 'Online', value: 'online' },
|
||||||
|
{ label: 'Offline', value: 'offline' },
|
||||||
|
]"
|
||||||
|
aria-label="Формат игры"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p class="settings-header">Описание</p>
|
||||||
|
<n-input
|
||||||
|
v-model:value="settingsDraft.description"
|
||||||
|
type="textarea"
|
||||||
|
placeholder="Убийство пастуха..."
|
||||||
|
/>
|
||||||
|
|
||||||
|
<p class="settings-header">Место проведения</p>
|
||||||
|
<n-input
|
||||||
|
v-model:value="settingsDraft.location"
|
||||||
|
type="text"
|
||||||
|
placeholder="Оставьте пустым для онлайн-игры"
|
||||||
|
/>
|
||||||
|
</n-space>
|
||||||
|
<n-flex justify="end" class="settings-actions">
|
||||||
|
<n-button @click="updateGame()">Сохранить изменения</n-button>
|
||||||
|
</n-flex>
|
||||||
|
</UiBlock>
|
||||||
|
<UiBlock>
|
||||||
|
<p class="screens-title">Выпуск билетов</p>
|
||||||
|
<n-space vertical>
|
||||||
|
<n-input-number v-model:value="ticketCount" :min="1" :max="100" />
|
||||||
|
<div v-for="ticket in issuedTickets" :key="ticket.ID" class="issued-ticket">
|
||||||
|
<div>Билет №{{ ticket.ID }}</div>
|
||||||
|
<a :href="ticket.URL" target="_blank" rel="noreferrer">Открыть страницу билета</a>
|
||||||
|
<n-button size="small" @click="openQR(ticket.URL!, `Билет №${ticket.ID}`)">
|
||||||
|
Показать QR
|
||||||
|
</n-button>
|
||||||
|
</div>
|
||||||
|
</n-space>
|
||||||
|
<n-flex justify="end" class="settings-actions">
|
||||||
|
<n-button
|
||||||
|
:loading="isIssuingTickets"
|
||||||
|
:disabled="isIssuingTickets"
|
||||||
|
@click="issueTickets()"
|
||||||
|
>
|
||||||
|
Выпустить
|
||||||
|
</n-button>
|
||||||
|
</n-flex>
|
||||||
|
</UiBlock>
|
||||||
</div>
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
<n-tab-pane name="teams" tab="Команды">
|
<n-tab-pane name="teams" tab="Команды">
|
||||||
<div class="game-tab-content">
|
<div class="game-tab-content">
|
||||||
<n-tag :bordered="false" type="info"> Участвуют команд: {{ activeTeamCount }} </n-tag>
|
<n-flex justify="space-between" align="center">
|
||||||
|
<n-tag :bordered="false" type="info"> Участвуют команд: {{ activeTeamCount }} </n-tag>
|
||||||
|
<n-button
|
||||||
|
v-if="activeTeamCount"
|
||||||
|
quaternary
|
||||||
|
:loading="isDownloadingQR"
|
||||||
|
:disabled="isDownloadingQR"
|
||||||
|
@click="downloadTeamQR()"
|
||||||
|
>
|
||||||
|
QR команд
|
||||||
|
</n-button>
|
||||||
|
</n-flex>
|
||||||
<GameTeamsPanel
|
<GameTeamsPanel
|
||||||
:teams="game.teams || []"
|
:teams="game.teams || []"
|
||||||
:participation-options="participationOptions"
|
:participation-options="participationOptions"
|
||||||
@@ -799,8 +792,58 @@ useSimplePolling(() => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
<n-tab-pane name="screens-map" tab="Экраны и карта">
|
<n-tab-pane name="screens" tab="Экраны">
|
||||||
<div class="game-tab-content">
|
<div class="game-tab-content">
|
||||||
|
<GameScreensPanel
|
||||||
|
:screens="screens"
|
||||||
|
:screen-binding="screenBinding"
|
||||||
|
@bind="bindScannedScreen"
|
||||||
|
@set-sound="setSoundScreen"
|
||||||
|
@unbind="unbindScreen"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</n-tab-pane>
|
||||||
|
<n-tab-pane name="screens-map" tab="Игра">
|
||||||
|
<div class="game-tab-content">
|
||||||
|
<UiBlock>
|
||||||
|
<n-flex justify="space-between">
|
||||||
|
<n-space>
|
||||||
|
<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 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-flex>
|
||||||
|
</n-flex>
|
||||||
|
</UiBlock>
|
||||||
<GameMapPanel
|
<GameMapPanel
|
||||||
:map-items="mapItems"
|
:map-items="mapItems"
|
||||||
:teams="game.teams || []"
|
:teams="game.teams || []"
|
||||||
@@ -829,72 +872,44 @@ useSimplePolling(() => {
|
|||||||
@update:screen-volume="screenVolume = $event ?? 0"
|
@update:screen-volume="screenVolume = $event ?? 0"
|
||||||
@update:screen-position="screenPosition = $event ?? 0"
|
@update:screen-position="screenPosition = $event ?? 0"
|
||||||
/>
|
/>
|
||||||
<GameScreensPanel
|
</div>
|
||||||
:screens="screens"
|
</n-tab-pane>
|
||||||
:screen-binding="screenBinding"
|
<n-tab-pane name="answers" tab="Проверка ответов">
|
||||||
@bind="bindScannedScreen"
|
<div class="game-tab-content">
|
||||||
@set-sound="setSoundScreen"
|
<div v-if="answersQuestions.length == 0" class="answers-empty">
|
||||||
@unbind="unbindScreen"
|
Вопросы дела не заданы или команды ещё не отвечали.
|
||||||
/>
|
</div>
|
||||||
|
<UiBlock v-for="question in answersQuestions" :key="question.code">
|
||||||
|
<p class="answers-question-title">{{ question.code }}: {{ question.text }}</p>
|
||||||
|
<p class="answers-question-answer">
|
||||||
|
Правильный ответ:<br /><span class="correct-answer">{{ question.answer || '—' }}</span>
|
||||||
|
</p>
|
||||||
|
<div v-for="team in answersTeams" :key="team.team?.ID" class="answers-team-row">
|
||||||
|
<div class="answers-team-name">
|
||||||
|
{{ team.team?.name }}
|
||||||
|
</div>
|
||||||
|
<div class="answers-team-answer">
|
||||||
|
{{ teamAnswer(team, question.code) || '—' }}
|
||||||
|
</div>
|
||||||
|
<n-input-number
|
||||||
|
:value="scores[scoreKey(team.team?.ID, question.code)]"
|
||||||
|
@update:value="onScoreUpdate(scoreKey(team.team?.ID, question.code), $event)"
|
||||||
|
placeholder="Балл"
|
||||||
|
class="answers-score-input"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</UiBlock>
|
||||||
|
<n-flex justify="end">
|
||||||
|
<n-button @click="saveAllScores()" :disabled="answersQuestions.length == 0">
|
||||||
|
Сохранить баллы
|
||||||
|
</n-button>
|
||||||
|
</n-flex>
|
||||||
</div>
|
</div>
|
||||||
</n-tab-pane>
|
</n-tab-pane>
|
||||||
</n-tabs>
|
</n-tabs>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Окно настроек -->
|
|
||||||
<n-modal v-model:show="showSettingsModal">
|
|
||||||
<n-card
|
|
||||||
style="width: 600px"
|
|
||||||
title="Настройки игры"
|
|
||||||
:bordered="false"
|
|
||||||
size="huge"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
>
|
|
||||||
<template #header-extra>
|
|
||||||
<n-button @click="closeSettings()"> x </n-button>
|
|
||||||
</template>
|
|
||||||
<n-space vertical>
|
|
||||||
<p>Название</p>
|
|
||||||
<n-input
|
|
||||||
v-model:value="settingsDraft.name"
|
|
||||||
type="text"
|
|
||||||
placeholder='Дело №0 "Следствие ведут овечки"'
|
|
||||||
/>
|
|
||||||
|
|
||||||
<p class="settings-header">Формат игры</p>
|
|
||||||
<n-select
|
|
||||||
v-model:value="settingsDraft.format"
|
|
||||||
:options="[
|
|
||||||
{ label: 'Online', value: 'online' },
|
|
||||||
{ label: 'Offline', value: 'offline' },
|
|
||||||
]"
|
|
||||||
aria-label="Формат игры"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<p class="settings-header">Описание</p>
|
|
||||||
<n-input
|
|
||||||
v-model:value="settingsDraft.description"
|
|
||||||
type="textarea"
|
|
||||||
placeholder="Убийство пастуха..."
|
|
||||||
/>
|
|
||||||
|
|
||||||
<p class="settings-header">Место проведения</p>
|
|
||||||
<n-input
|
|
||||||
v-model:value="settingsDraft.location"
|
|
||||||
type="text"
|
|
||||||
placeholder="Оставьте пустым для онлайн-игры"
|
|
||||||
/>
|
|
||||||
</n-space>
|
|
||||||
<template #footer>
|
|
||||||
<n-flex justify="end">
|
|
||||||
<n-button @click="updateGame()" ghost> Сохранить изменения </n-button>
|
|
||||||
</n-flex>
|
|
||||||
</template>
|
|
||||||
</n-card>
|
|
||||||
</n-modal>
|
|
||||||
|
|
||||||
<!-- Окно выдачи улик -->
|
<!-- Окно выдачи улик -->
|
||||||
<n-modal v-model:show="showGiveApplicationModal">
|
<n-modal v-model:show="showGiveApplicationModal">
|
||||||
<n-card
|
<n-card
|
||||||
@@ -1062,40 +1077,6 @@ useSimplePolling(() => {
|
|||||||
</n-card>
|
</n-card>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
|
||||||
<!-- Окно qr -->
|
|
||||||
<n-modal v-model:show="showIssueTicketsModal">
|
|
||||||
<n-card
|
|
||||||
style="width: min(94vw, 700px)"
|
|
||||||
title="Выпуск билетов"
|
|
||||||
:bordered="false"
|
|
||||||
size="huge"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
>
|
|
||||||
<template #header-extra>
|
|
||||||
<n-button @click="showIssueTicketsModal = false"> x </n-button>
|
|
||||||
</template>
|
|
||||||
<n-space vertical>
|
|
||||||
<n-input-number v-model:value="ticketCount" :min="1" :max="100" />
|
|
||||||
<n-button
|
|
||||||
type="primary"
|
|
||||||
:loading="isIssuingTickets"
|
|
||||||
:disabled="isIssuingTickets"
|
|
||||||
@click="issueTickets()"
|
|
||||||
>
|
|
||||||
Выпустить
|
|
||||||
</n-button>
|
|
||||||
<div v-for="ticket in issuedTickets" :key="ticket.ID" class="issued-ticket">
|
|
||||||
<div>Билет №{{ ticket.ID }}</div>
|
|
||||||
<a :href="ticket.URL" target="_blank" rel="noreferrer">Открыть страницу билета</a>
|
|
||||||
<n-button size="small" @click="openQR(ticket.URL!, `Билет №${ticket.ID}`)">
|
|
||||||
Показать QR
|
|
||||||
</n-button>
|
|
||||||
</div>
|
|
||||||
</n-space>
|
|
||||||
</n-card>
|
|
||||||
</n-modal>
|
|
||||||
|
|
||||||
<n-modal v-model:show="showQR">
|
<n-modal v-model:show="showQR">
|
||||||
<n-card
|
<n-card
|
||||||
style="width: 600px"
|
style="width: 600px"
|
||||||
@@ -1118,53 +1099,6 @@ useSimplePolling(() => {
|
|||||||
</n-card>
|
</n-card>
|
||||||
</n-modal>
|
</n-modal>
|
||||||
|
|
||||||
<!-- Окно ответов команд и оценки -->
|
|
||||||
<n-modal v-model:show="showAnswersModal">
|
|
||||||
<n-card
|
|
||||||
style="width: min(94vw, 900px)"
|
|
||||||
title="Ответы команд"
|
|
||||||
:bordered="false"
|
|
||||||
size="huge"
|
|
||||||
role="dialog"
|
|
||||||
aria-modal="true"
|
|
||||||
:content-style="{ maxHeight: '70vh', overflow: 'auto' }"
|
|
||||||
>
|
|
||||||
<template #header-extra>
|
|
||||||
<n-button @click="showAnswersModal = false"> x </n-button>
|
|
||||||
</template>
|
|
||||||
<div v-if="answersQuestions.length == 0" class="answers-empty">
|
|
||||||
Вопросы дела не заданы или команды ещё не отвечали.
|
|
||||||
</div>
|
|
||||||
<UiBlock v-for="question in answersQuestions" :key="question.code">
|
|
||||||
<p class="answers-question-title">{{ question.code }}: {{ question.text }}</p>
|
|
||||||
<p class="answers-question-answer">
|
|
||||||
Правильный ответ:<br /><span class="correct-answer">{{ question.answer || '—' }}</span>
|
|
||||||
</p>
|
|
||||||
<div v-for="team in answersTeams" :key="team.team?.ID" class="answers-team-row">
|
|
||||||
<div class="answers-team-name">
|
|
||||||
{{ team.team?.name }}
|
|
||||||
</div>
|
|
||||||
<div class="answers-team-answer">
|
|
||||||
{{ teamAnswer(team, question.code) || '—' }}
|
|
||||||
</div>
|
|
||||||
<n-input-number
|
|
||||||
:value="scores[scoreKey(team.team?.ID, question.code)]"
|
|
||||||
@update:value="onScoreUpdate(scoreKey(team.team?.ID, question.code), $event)"
|
|
||||||
placeholder="Балл"
|
|
||||||
class="answers-score-input"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</UiBlock>
|
|
||||||
<template #footer>
|
|
||||||
<n-flex justify="end">
|
|
||||||
<n-button @click="saveAllScores()" :disabled="answersQuestions.length == 0">
|
|
||||||
Сохранить баллы
|
|
||||||
</n-button>
|
|
||||||
</n-flex>
|
|
||||||
</template>
|
|
||||||
</n-card>
|
|
||||||
</n-modal>
|
|
||||||
|
|
||||||
<n-modal v-model:show="showReviewsModal">
|
<n-modal v-model:show="showReviewsModal">
|
||||||
<n-card
|
<n-card
|
||||||
style="width: min(94vw, 800px)"
|
style="width: min(94vw, 800px)"
|
||||||
@@ -1246,6 +1180,10 @@ useSimplePolling(() => {
|
|||||||
width: 20px;
|
width: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.settings-actions {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
.link-button {
|
.link-button {
|
||||||
margin-top: 16px;
|
margin-top: 16px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|||||||
Reference in New Issue
Block a user