add statuses game

This commit is contained in:
2026-08-04 02:28:16 +07:00
parent 2241c33902
commit 33cde1d28b
12 changed files with 537 additions and 143 deletions
+180 -31
View File
@@ -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;