273 lines
6.6 KiB
Vue
273 lines
6.6 KiB
Vue
<script setup lang="ts">
|
||
import { onMounted, ref } from 'vue';
|
||
import router from '@/router';
|
||
import type { Application, Game, Team, Teams } from './models';
|
||
import { apiGetTeams, apiAddTeam, apiGetGame, apiStartGame, apiStopGame, apiGaveApplication, apiDownloadQrCodesFile } from './client';
|
||
import TeamQRCode from '../components/TeamQRCode.vue'
|
||
import HeaderBlock from './HeaderBlock.vue';
|
||
import ModalWindow from './ModalWindow.vue';
|
||
|
||
const qrurl = ref("-")
|
||
const qrteam = ref("-")
|
||
const isOpenModal = ref(false)
|
||
|
||
const gameState = ref("")
|
||
const game = ref<Game | undefined>()
|
||
|
||
const teams = ref<Teams>({ teams: [] })
|
||
|
||
const teamName = ref("")
|
||
async function addTeam() {
|
||
await apiAddTeam(teamName.value)
|
||
teamName.value = ""
|
||
}
|
||
|
||
async function startGame() {
|
||
gameState.value = "Загрузка..."
|
||
await apiStartGame()
|
||
}
|
||
|
||
async function stopGame() {
|
||
gameState.value = "Загрузка..."
|
||
await apiStopGame()
|
||
}
|
||
|
||
async function getGame() {
|
||
game.value = await apiGetGame()
|
||
if (game.value.state === "NEW") {
|
||
gameState.value = "Игра ещё не началась"
|
||
}
|
||
if (game.value.state === "RUN") {
|
||
gameState.value = "Игра идет с " + game.value?.startAt.substring(11)
|
||
}
|
||
if (game.value.state === "STOP") {
|
||
gameState.value = "Игра остановлена " + game.value?.startAt.substring(11) + " - " + game.value?.endAt.substring(11)
|
||
}
|
||
}
|
||
|
||
function compareTeams(a: Team, b: Team): number {
|
||
return b.applications.length - a.applications.length
|
||
}
|
||
|
||
let intervalId = 0
|
||
onMounted(async () => {
|
||
teams.value = await apiGetTeams()
|
||
|
||
intervalId = setInterval(async () => {
|
||
teams.value = await apiGetTeams()
|
||
teams.value.teams.sort(compareTeams)
|
||
await getGame()
|
||
}, 2000);
|
||
|
||
router.beforeEach((to, from, next) => {
|
||
clearInterval(intervalId);
|
||
next();
|
||
});
|
||
});
|
||
|
||
const gaveApplicationTeam = ref<Team>({
|
||
id: 0,
|
||
name: '',
|
||
password: '',
|
||
url: '',
|
||
spendTime: 0,
|
||
applications: []
|
||
})
|
||
|
||
const gaveApplicationApplication = ref<Application>({
|
||
id: 0,
|
||
name: ''
|
||
})
|
||
|
||
function gaveApplication(team: Team, application: Application) {
|
||
gaveApplicationTeam.value = team
|
||
gaveApplicationApplication.value = application
|
||
isOpenModal.value = true
|
||
}
|
||
|
||
async function confirm() {
|
||
console.log("confirm")
|
||
await apiGaveApplication(gaveApplicationTeam.value.id, gaveApplicationApplication.value.id)
|
||
isOpenModal.value = false
|
||
}
|
||
|
||
function close() {
|
||
console.log("close")
|
||
isOpenModal.value = false
|
||
}
|
||
</script>
|
||
|
||
<template>
|
||
<HeaderBlock>
|
||
<div>
|
||
Вечерний детектив - {{ gameState }}
|
||
</div>
|
||
<div class="buttons-block">
|
||
<button v-on:click="router.push('/editor')" class="button-menu button-custom-inline">Редактор</button>
|
||
<button v-on:click="startGame" class="button-menu button-custom-inline">Начать</button>
|
||
<button v-on:click="stopGame" class="button-menu button-custom-inline">Остановить</button>
|
||
<button v-on:click="apiDownloadQrCodesFile" class="button-menu button-custom-inline">Скачать qr‑ы</button>
|
||
</div>
|
||
</HeaderBlock>
|
||
|
||
<TeamQRCode :data="qrurl" :title="qrteam" />
|
||
|
||
<div class="center-block-custom content-block">
|
||
<div class="team-header-block">Команды</div>
|
||
|
||
<div class="team-block" v-for="team in teams.teams" :key="team.name">
|
||
<div class="team-content-block">
|
||
<div class="team-name-block">
|
||
<a v-on:click="qrurl = team.url, qrteam = team.name">
|
||
QR
|
||
</a>
|
||
<a :href="team.url" class="url-block" target="_blank">
|
||
URL
|
||
</a>
|
||
{{ team.name }}
|
||
</div>
|
||
<div>Поездки: {{ team.spendTime }}</div>
|
||
</div>
|
||
<div v-for="application in team.applications" :key="application.id" class="link-button"
|
||
@click="gaveApplication(team, application)">
|
||
Выдать: {{ application.name }}
|
||
</div>
|
||
</div>
|
||
|
||
<form @submit.prevent="addTeam">
|
||
<div>
|
||
<input class="input-custom" v-model="teamName" type="text" placeholder="Название команды">
|
||
</div>
|
||
<div class="button-container">
|
||
<button class="button-custom" type="submit">Добавить</button>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
|
||
<ModalWindow :is-open="isOpenModal" @confirm="confirm" @close="close">
|
||
<div>
|
||
Команда: {{ gaveApplicationTeam.name }}
|
||
</div>
|
||
<div>
|
||
Приложение: {{ gaveApplicationApplication.name }}
|
||
</div>
|
||
</ModalWindow>
|
||
|
||
</template>
|
||
|
||
<style scoped>
|
||
.buttons-block {
|
||
padding-top: 5px;
|
||
}
|
||
|
||
.button-menu {
|
||
margin: 5px 10px 5px 0;
|
||
}
|
||
|
||
.url-block {
|
||
margin: 5px 0;
|
||
}
|
||
|
||
.link-button {
|
||
/* Основные стили */
|
||
border: none;
|
||
color: white;
|
||
background-color: var(--main-color);
|
||
padding: 15px;
|
||
margin: 0;
|
||
font: inherit;
|
||
cursor: pointer;
|
||
/* Стандартный цвет ссылки */
|
||
font-weight: 600;
|
||
|
||
/* Убираем стандартное оформление кнопки */
|
||
-webkit-appearance: none;
|
||
-moz-appearance: none;
|
||
appearance: none;
|
||
|
||
/* Дополнительные свойства для лучшего отображения */
|
||
line-height: inherit;
|
||
text-align: center;
|
||
}
|
||
|
||
/* Состояние при наведении */
|
||
.link-button:hover {
|
||
color: #ccc;
|
||
/* Немного темнее */
|
||
text-decoration: none;
|
||
background-color: var(--second-color);
|
||
}
|
||
|
||
/* Состояние при активации (нажатии) */
|
||
.link-button:active {
|
||
color: #003366;
|
||
/* Еще темнее */
|
||
}
|
||
|
||
/* Состояние фокуса (для доступности) */
|
||
.link-button:focus {
|
||
outline: none;
|
||
/* Убираем стандартный outline */
|
||
text-decoration: none;
|
||
box-shadow: 0 0 0 2px rgba(0, 102, 204, 0.3);
|
||
/* Кастомный фокус */
|
||
}
|
||
|
||
a {
|
||
/* Основные стили */
|
||
color: var(--second-color);
|
||
text-decoration: none;
|
||
transition: all 0.2s ease;
|
||
cursor: pointer;
|
||
|
||
/* Подчеркивание только при наведении */
|
||
&:hover {
|
||
text-decoration: underline;
|
||
text-decoration-thickness: 2px;
|
||
text-underline-offset: 3px;
|
||
}
|
||
|
||
/* Состояние фокуса для доступности */
|
||
&:focus-visible {
|
||
outline: 2px solid #3182ce;
|
||
outline-offset: 2px;
|
||
border-radius: 2px;
|
||
}
|
||
|
||
/* Отключенное состояние */
|
||
&[disabled] {
|
||
color: #a0aec0;
|
||
pointer-events: none;
|
||
cursor: not-allowed;
|
||
}
|
||
}
|
||
|
||
.button-container {
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.content-block {
|
||
padding: 10px;
|
||
}
|
||
|
||
.team-header-block {
|
||
font-weight: 700;
|
||
font-size: 30px;
|
||
}
|
||
|
||
.team-block {
|
||
border: solid 1px #003366;
|
||
border-radius: 5px;
|
||
margin: 15px 0;
|
||
}
|
||
|
||
.team-content-block {
|
||
padding: 10px;
|
||
}
|
||
|
||
.team-name-block {
|
||
font-weight: 600;
|
||
font-size: 20px;
|
||
}
|
||
</style>
|