generated from VLADIMIR/template_frontend
add statuses game
This commit is contained in:
+180
-31
@@ -1,9 +1,9 @@
|
||||
<script setup lang="ts">
|
||||
|
||||
import { Settings } from '@vicons/carbon'
|
||||
import { Settings, Play, Pause, Reset, Flag } from '@vicons/carbon'
|
||||
import { getAuthClient } from '@/api/auth_client';
|
||||
import { Icon } from '@vicons/utils'
|
||||
import { NButton } from 'naive-ui'
|
||||
import { NButton, NTag } from 'naive-ui'
|
||||
import type { Application, Game, Team } from '@/api/generated/crabs/evening_detective_server';
|
||||
import { NCard, NInput, NModal, NSpace, NFlex, NText, NQrCode } from 'naive-ui'
|
||||
import HeaderMenu from '@/components/HeaderMenu.vue'
|
||||
@@ -179,6 +179,113 @@ async function openQR(url: string) {
|
||||
useSimplePolling(() => {
|
||||
getGame(Number(gameId))
|
||||
}, 2000);
|
||||
|
||||
async function resetGame() {
|
||||
const res = await client.ResetGame({
|
||||
id: game.value.id
|
||||
})
|
||||
if (res.error != '') {
|
||||
message.error(res.error!)
|
||||
return
|
||||
}
|
||||
await getGame(game.value.id!)
|
||||
}
|
||||
|
||||
async function startGame() {
|
||||
const res = await client.StartGame({
|
||||
id: game.value.id
|
||||
})
|
||||
if (res.error != '') {
|
||||
message.error(res.error!)
|
||||
return
|
||||
}
|
||||
await getGame(game.value.id!)
|
||||
}
|
||||
|
||||
async function pauseGame() {
|
||||
const res = await client.PauseGame({
|
||||
id: game.value.id
|
||||
})
|
||||
if (res.error != '') {
|
||||
message.error(res.error!)
|
||||
return
|
||||
}
|
||||
await getGame(game.value.id!)
|
||||
}
|
||||
|
||||
async function playGame() {
|
||||
const res = await client.PlayGame({
|
||||
id: game.value.id
|
||||
})
|
||||
if (res.error != '') {
|
||||
message.error(res.error!)
|
||||
return
|
||||
}
|
||||
await getGame(game.value.id!)
|
||||
}
|
||||
|
||||
async function finishGame() {
|
||||
const res = await client.FinishGame({
|
||||
id: game.value.id
|
||||
})
|
||||
if (res.error != '') {
|
||||
message.error(res.error!)
|
||||
return
|
||||
}
|
||||
await getGame(game.value.id!)
|
||||
}
|
||||
|
||||
function parseDateWithoutTimezone(dateStr: string): Date {
|
||||
// Разбираем ISO строку: "2024-01-01T12:30:00"
|
||||
let [datePart, timePart] = dateStr.split('T');
|
||||
const [year, month, day] = datePart.split('-').map(Number);
|
||||
|
||||
timePart = timePart.split('.')[0]
|
||||
const [hours, minutes, seconds] = (timePart || '00:00:00').split(':').map(Number);
|
||||
|
||||
// Создаем дату в UTC, чтобы избежать сдвига
|
||||
return new Date(Date.UTC(year, month - 1, day, hours, minutes, seconds || 0));
|
||||
}
|
||||
|
||||
function getCurrentUTC(): Date {
|
||||
const now = new Date();
|
||||
return new Date(now.getTime() - now.getTimezoneOffset() * 60000);
|
||||
}
|
||||
|
||||
function getTimeDiff(startStr: string, endStr: string): string {
|
||||
if (startStr == '') {
|
||||
return 'Игра ещё не началась'
|
||||
}
|
||||
const start = parseDateWithoutTimezone(startStr)
|
||||
let endTime = getCurrentUTC();
|
||||
if (endStr != '') {
|
||||
endTime = parseDateWithoutTimezone(endStr);
|
||||
}
|
||||
|
||||
const diffMs = endTime.getTime() - start.getTime();
|
||||
|
||||
if (diffMs < 0) return 'Игра ещё не началась';
|
||||
|
||||
const seconds = Math.floor(diffMs / 1000);
|
||||
const minutes = Math.floor(seconds / 60);
|
||||
const hours = Math.floor(minutes / 60);
|
||||
const days = Math.floor(hours / 24);
|
||||
const months = Math.floor(days / 30);
|
||||
const years = Math.floor(days / 365);
|
||||
|
||||
if (years > 0) return `${years}г ${days % 365}д`;
|
||||
if (months > 0) return `${months}мес ${days % 30}д`;
|
||||
if (days > 0) return `${days}д ${hours % 24}ч`;
|
||||
if (hours > 0) return `${hours}ч ${minutes % 60}м`;
|
||||
if (minutes > 0) return `${minutes}м ${seconds % 60}с`;
|
||||
return `${seconds}с`;
|
||||
}
|
||||
|
||||
const timeString = ref('')
|
||||
useSimplePolling(() => {
|
||||
timeString.value = getTimeDiff(game.value.startedAt || '', game.value.endedAt || '')
|
||||
}, 1000);
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -186,13 +293,53 @@ useSimplePolling(() => {
|
||||
<div class="width1200">
|
||||
|
||||
<div class="settings-block">
|
||||
Команд: {{ game.teams?.length }}
|
||||
<n-button quaternary @click="showSettingsModal = true" class="settings-button">
|
||||
<Icon class="settings-icon">
|
||||
<Settings />
|
||||
</Icon>
|
||||
Настройки
|
||||
</n-button>
|
||||
<n-flex justify="space-between">
|
||||
<n-space>
|
||||
<n-tag :bordered="false" type="info">
|
||||
Команд: {{ game.teams?.length }}
|
||||
</n-tag>
|
||||
<n-tag type="info">
|
||||
{{ timeString }}
|
||||
</n-tag>
|
||||
</n-space>
|
||||
|
||||
<div>
|
||||
<n-button quaternary @click="showSettingsModal = true" class="settings-button">
|
||||
<Icon class="settings-icon">
|
||||
<Settings />
|
||||
</Icon>
|
||||
Настройки
|
||||
</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>
|
||||
</div>
|
||||
</n-flex>
|
||||
</div>
|
||||
|
||||
<div class="team-block team-block-hover" @click="showAddTeamModal = true">
|
||||
@@ -201,25 +348,29 @@ useSimplePolling(() => {
|
||||
|
||||
<div v-for="team in game.teams" class="team-block">
|
||||
<div class="team-content-block">
|
||||
<div class="title-block">
|
||||
<span class="team-name-block">
|
||||
{{ team.name }}
|
||||
</span>
|
||||
<div>
|
||||
Поездки: {{ team.actionsCount }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-flex justify="end">
|
||||
<n-button @click="openQR(getTeamStoryURL(team.id!, team.password!))">
|
||||
QR
|
||||
</n-button>
|
||||
<n-button @click="toTeamStory(team.id!, team.password!)">
|
||||
Подсмотреть игру
|
||||
</n-button>
|
||||
<n-button type="error" ghost @click="deleteTeam(team)">
|
||||
Удалить
|
||||
</n-button>
|
||||
<n-flex justify="space-between">
|
||||
<div class="title-block">
|
||||
<span class="team-name-block">
|
||||
{{ team.name }}
|
||||
</span>
|
||||
<div>
|
||||
Поездки: {{ team.actionsCount }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<n-flex justify="end">
|
||||
<n-button @click="openQR(getTeamStoryURL(team.id!, team.password!))">
|
||||
QR
|
||||
</n-button>
|
||||
<n-button @click="toTeamStory(team.id!, team.password!)">
|
||||
Подсмотреть игру
|
||||
</n-button>
|
||||
<n-button type="error" ghost @click="deleteTeam(team)">
|
||||
Удалить
|
||||
</n-button>
|
||||
</n-flex>
|
||||
|
||||
</n-flex>
|
||||
|
||||
<n-button v-for="application in team.applications" :key="application.name" class="link-button"
|
||||
@@ -345,7 +496,7 @@ useSimplePolling(() => {
|
||||
.team-block {
|
||||
margin: 10px 0;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #444;
|
||||
background-color: #222;
|
||||
}
|
||||
|
||||
.team-block-hover:hover {
|
||||
@@ -375,9 +526,7 @@ useSimplePolling(() => {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
.title-block {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.title-block {}
|
||||
|
||||
.link-button {
|
||||
margin-top: 20px;
|
||||
|
||||
Reference in New Issue
Block a user