generated from VLADIMIR/template_frontend
up
This commit is contained in:
@@ -31,6 +31,7 @@ import type {
|
||||
ScreenState,
|
||||
Team,
|
||||
TeamAnswers,
|
||||
Ticket,
|
||||
} from '@/api/generated/crabs/evening_detective_server'
|
||||
import { getURL } from '@/api/generated/crabs/evening_detective_server/client'
|
||||
import HeaderMenu from '@/components/HeaderMenu.vue'
|
||||
@@ -59,6 +60,10 @@ const deletedGameName = ref('')
|
||||
const showQR = ref(false)
|
||||
const urlQR = ref('')
|
||||
const titleQR = ref('')
|
||||
const showIssueTicketsModal = ref(false)
|
||||
const ticketCount = ref(1)
|
||||
const issuedTickets = ref<Ticket[]>([])
|
||||
const isIssuingTickets = ref(false)
|
||||
|
||||
const game = ref<Game>({
|
||||
id: undefined,
|
||||
@@ -461,6 +466,33 @@ async function openQR(url: string, text: string) {
|
||||
showQR.value = true
|
||||
}
|
||||
|
||||
function openIssueTickets() {
|
||||
ticketCount.value = 1
|
||||
issuedTickets.value = []
|
||||
showIssueTicketsModal.value = true
|
||||
}
|
||||
|
||||
async function issueTickets() {
|
||||
if (isIssuingTickets.value || !game.value.id) return
|
||||
isIssuingTickets.value = true
|
||||
try {
|
||||
const res = await client.IssueGameTickets({
|
||||
gameId: game.value.id,
|
||||
count: ticketCount.value,
|
||||
})
|
||||
if (res.error != '') {
|
||||
message.error(res.error!)
|
||||
return
|
||||
}
|
||||
issuedTickets.value = res.tickets || []
|
||||
message.success(`Выпущено билетов: ${issuedTickets.value.length}`)
|
||||
} catch (err) {
|
||||
message.error(err instanceof Error ? err.message : 'Не удалось выпустить билеты')
|
||||
} finally {
|
||||
isIssuingTickets.value = false
|
||||
}
|
||||
}
|
||||
|
||||
useSimplePolling(() => {
|
||||
getGame(Number(gameId))
|
||||
getGameScreens()
|
||||
@@ -666,6 +698,7 @@ useSimplePolling(() => {
|
||||
|
||||
<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">
|
||||
@@ -1160,6 +1193,39 @@ useSimplePolling(() => {
|
||||
</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-card
|
||||
style="width: 600px"
|
||||
@@ -1555,4 +1621,13 @@ useSimplePolling(() => {
|
||||
.map-hidden-items {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.issued-ticket {
|
||||
align-items: center;
|
||||
border-top: 1px solid #777;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { NButton, NCard, NQrCode } from 'naive-ui'
|
||||
import { NButton, NQrCode } from 'naive-ui'
|
||||
import { computed, onMounted, ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
|
||||
import type { TicketPage as TicketData } from '@/api/generated/crabs/evening_detective_server'
|
||||
import { buildClient } from '@/api/generated/crabs/evening_detective_server/client'
|
||||
import MessagePaper from '@/components/MessagePaper.vue'
|
||||
import { formatGameDateTime } from '@/utils/datetime'
|
||||
|
||||
const route = useRoute()
|
||||
@@ -38,58 +39,85 @@ onMounted(load)
|
||||
<template>
|
||||
<div class="ticket-page center-block-custom">
|
||||
<div class="width700">
|
||||
<n-card class="ticket-card" title="Билет на игру">
|
||||
<p v-if="error" class="error" role="alert">{{ error }}</p>
|
||||
<template v-else-if="ticket">
|
||||
<h2>{{ ticket.gameName }}</h2>
|
||||
<dl>
|
||||
<div>
|
||||
<dt>Начало</dt>
|
||||
<dd>{{ formatGameDateTime(ticket.startAt) }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Место</dt>
|
||||
<dd>{{ ticket.location || 'Онлайн' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<MessagePaper>
|
||||
<div class="ticket-card">
|
||||
<p class="ticket-kicker">ВЕЧЕРНИЙ ДЕТЕКТИВ · ПРОПУСК</p>
|
||||
<h1>Билет на игру</h1>
|
||||
<p v-if="error" class="error" role="alert">{{ error }}</p>
|
||||
<template v-else-if="ticket">
|
||||
<h2>{{ ticket.gameName }}</h2>
|
||||
<hr />
|
||||
<dl>
|
||||
<div>
|
||||
<dt>Начало</dt>
|
||||
<dd>{{ formatGameDateTime(ticket.startAt) }}</dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>Место</dt>
|
||||
<dd>{{ ticket.location || 'Онлайн' }}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
|
||||
<div class="qr-block">
|
||||
<n-qr-code :value="ticketURL" :size="240" />
|
||||
<p>Покажите этот QR-код организатору на входе.</p>
|
||||
</div>
|
||||
<div class="qr-block">
|
||||
<n-qr-code :value="ticketURL" :size="240" />
|
||||
<p>Покажите этот QR-код организатору на входе.</p>
|
||||
</div>
|
||||
|
||||
<p v-if="ticket.status === 'used'" class="used" role="status">
|
||||
Билет уже использован{{ ticket.usedAt ? ` ${formatGameDateTime(ticket.usedAt)}` : '' }}.
|
||||
</p>
|
||||
<p v-else-if="ticket.gameStatus === 'finish'" class="used" role="status">
|
||||
Игра уже завершена.
|
||||
</p>
|
||||
<p v-else class="active" role="status">Билет действителен.</p>
|
||||
</template>
|
||||
<n-button v-else :loading="loading" @click="load">
|
||||
{{ loading ? 'Загрузка' : 'Повторить' }}
|
||||
</n-button>
|
||||
</n-card>
|
||||
<p v-if="ticket.status === 'used'" class="used" role="status">
|
||||
Билет уже использован{{ ticket.usedAt ? ` ${formatGameDateTime(ticket.usedAt)}` : '' }}.
|
||||
</p>
|
||||
<p v-else-if="ticket.gameStatus === 'finish'" class="used" role="status">
|
||||
Игра уже завершена.
|
||||
</p>
|
||||
<p v-else class="active" role="status">Билет действителен.</p>
|
||||
</template>
|
||||
<n-button v-else :loading="loading" @click="load">
|
||||
{{ loading ? 'Загрузка' : 'Повторить' }}
|
||||
</n-button>
|
||||
</div>
|
||||
</MessagePaper>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.ticket-card {
|
||||
margin-top: 10px;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.ticket-kicker {
|
||||
font-size: 12px;
|
||||
letter-spacing: 1px;
|
||||
margin-bottom: 14px;
|
||||
}
|
||||
|
||||
h1,
|
||||
h2 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 26px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
h2 {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
hr {
|
||||
border: 0;
|
||||
border-top: 1px dashed;
|
||||
margin: 18px 0;
|
||||
}
|
||||
|
||||
dl {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
dt {
|
||||
color: #aaa;
|
||||
color: var(--second-color);
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
@@ -107,7 +135,7 @@ dd {
|
||||
|
||||
.error,
|
||||
.used {
|
||||
color: #aaa;
|
||||
color: var(--second-color);
|
||||
}
|
||||
|
||||
.active {
|
||||
|
||||
Reference in New Issue
Block a user