generated from VLADIMIR/template_frontend
add create game
This commit is contained in:
@@ -1,12 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { NButton } from 'naive-ui'
|
||||
import { useMessage } from 'naive-ui'
|
||||
import { ref } from 'vue'
|
||||
import {
|
||||
NButton,
|
||||
NCard,
|
||||
NDatePicker,
|
||||
NFlex,
|
||||
NInput,
|
||||
NModal,
|
||||
NSelect,
|
||||
NSpace,
|
||||
useMessage,
|
||||
} from 'naive-ui'
|
||||
import { computed, ref } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
|
||||
import { getAuthClient } from '@/api/auth_client'
|
||||
import type { Game } from '@/api/generated/crabs/evening_detective_server'
|
||||
import type { Game, Scenario } from '@/api/generated/crabs/evening_detective_server'
|
||||
import HeaderMenu from '@/components/HeaderMenu.vue'
|
||||
import { formatGameDateTime } from '@/utils/datetime'
|
||||
|
||||
const client = getAuthClient()
|
||||
const message = useMessage()
|
||||
@@ -32,11 +42,94 @@ async function toGame(id: number) {
|
||||
function toResults(id: number) {
|
||||
router.push('/games/' + id + '/results')
|
||||
}
|
||||
|
||||
// ===== Создание игры =====
|
||||
const showCreateGameModal = ref(false)
|
||||
const newGameName = ref('')
|
||||
const newGameDescription = ref('')
|
||||
const newGameScenarioId = ref<number | null>(null)
|
||||
const newGameStartAt = ref<number | null>(null)
|
||||
const isCreating = ref(false)
|
||||
const isCatalogLoading = ref(false)
|
||||
const catalogError = ref(false)
|
||||
const catalogScenarios = ref<Scenario[]>([])
|
||||
|
||||
// Опубликованные сценарии каталога — кандидаты для новой игры
|
||||
const catalogOptions = computed(() =>
|
||||
catalogScenarios.value
|
||||
.filter((scenario) => scenario.status == 'public' && scenario.id != undefined)
|
||||
.map((scenario) => ({ label: scenario.name || '', value: scenario.id! })),
|
||||
)
|
||||
|
||||
async function getCatalogScenarios() {
|
||||
isCatalogLoading.value = true
|
||||
catalogError.value = false
|
||||
try {
|
||||
const res = await client.GetScenariosCatalog({})
|
||||
if (res.error != '') {
|
||||
catalogError.value = true
|
||||
message.error(res.error!)
|
||||
return
|
||||
}
|
||||
catalogScenarios.value = res.scenarios || []
|
||||
} catch (err) {
|
||||
catalogError.value = true
|
||||
message.error(err instanceof Error ? err.message : 'Не удалось загрузить сценарии')
|
||||
} finally {
|
||||
isCatalogLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function openCreateGame() {
|
||||
newGameName.value = ''
|
||||
newGameDescription.value = ''
|
||||
newGameScenarioId.value = null
|
||||
newGameStartAt.value = null
|
||||
showCreateGameModal.value = true
|
||||
getCatalogScenarios()
|
||||
}
|
||||
|
||||
async function createGame() {
|
||||
const name = newGameName.value.trim()
|
||||
const scenarioId = newGameScenarioId.value
|
||||
const startAtMs = newGameStartAt.value
|
||||
if (name == '' || scenarioId == null || startAtMs == null) {
|
||||
return
|
||||
}
|
||||
isCreating.value = true
|
||||
try {
|
||||
const res = await client.AddGame({
|
||||
name: name,
|
||||
description: newGameDescription.value,
|
||||
// Сервер парсит startAt как google.protobuf.Timestamp — нужен RFC 3339 с таймзоной
|
||||
startAt: new Date(startAtMs).toISOString(),
|
||||
scenarioId: scenarioId,
|
||||
})
|
||||
if (res.error != '') {
|
||||
message.error(res.error!)
|
||||
return
|
||||
}
|
||||
message.success('Игра создана')
|
||||
showCreateGameModal.value = false
|
||||
router.push('/games/' + res.id!)
|
||||
} catch (err) {
|
||||
message.error(err instanceof Error ? err.message : 'Не удалось создать игру')
|
||||
} finally {
|
||||
isCreating.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="center-block-custom">
|
||||
<div class="width1200">
|
||||
<div class="game-block game-block-hover" @click="openCreateGame()">
|
||||
<div class="game-create-content">
|
||||
<span class="game-create-plus">+</span>
|
||||
<span>Создать игру</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-for="game in games" :key="game.id" class="game-block" @click="toGame(game.id!)">
|
||||
<div class="game-title">
|
||||
{{ game.name }}
|
||||
@@ -44,6 +137,9 @@ function toResults(id: number) {
|
||||
<div class="game-description">
|
||||
{{ game.description }}
|
||||
</div>
|
||||
<div v-if="formatGameDateTime(game.startAt)" class="game-date">
|
||||
Начало: {{ formatGameDateTime(game.startAt) }}
|
||||
</div>
|
||||
<div v-if="game.status == 'finish'" class="game-actions">
|
||||
<n-button size="small" ghost @click.stop="toResults(game.id!)">Результаты</n-button>
|
||||
</div>
|
||||
@@ -51,17 +147,100 @@ function toResults(id: number) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Окно создания игры -->
|
||||
<n-modal v-model:show="showCreateGameModal">
|
||||
<n-card
|
||||
style="width: min(94vw, 600px)"
|
||||
title="Создание игры"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<template #header-extra>
|
||||
<n-button @click="showCreateGameModal = false"> x </n-button>
|
||||
</template>
|
||||
<n-space vertical>
|
||||
<p>Название</p>
|
||||
<n-input
|
||||
v-model:value="newGameName"
|
||||
type="text"
|
||||
placeholder='Дело №0 "Следствие ведут овечки"'
|
||||
/>
|
||||
|
||||
<p>Описание</p>
|
||||
<n-input
|
||||
v-model:value="newGameDescription"
|
||||
type="textarea"
|
||||
placeholder="Убийство пастуха..."
|
||||
/>
|
||||
|
||||
<p>Сценарий (дело из каталога)</p>
|
||||
<n-select
|
||||
v-model:value="newGameScenarioId"
|
||||
:options="catalogOptions"
|
||||
placeholder="Выберите дело из каталога"
|
||||
/>
|
||||
<p v-if="isCatalogLoading" class="create-hint">Загружаем сценарии...</p>
|
||||
<p v-else-if="catalogOptions.length == 0" class="create-hint">
|
||||
{{
|
||||
catalogError
|
||||
? 'Не удалось загрузить список сценариев'
|
||||
: 'В каталоге пока нет опубликованных сценариев'
|
||||
}}
|
||||
</p>
|
||||
|
||||
<p>Дата и время проведения</p>
|
||||
<n-date-picker v-model:value="newGameStartAt" type="datetime" />
|
||||
</n-space>
|
||||
<template #footer>
|
||||
<n-flex justify="end">
|
||||
<n-button
|
||||
@click="createGame()"
|
||||
:loading="isCreating"
|
||||
:disabled="
|
||||
newGameName.trim().length == 0 ||
|
||||
newGameScenarioId == null ||
|
||||
newGameStartAt == null ||
|
||||
isCreating
|
||||
"
|
||||
>
|
||||
Создать
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</template>
|
||||
</n-card>
|
||||
</n-modal>
|
||||
|
||||
<HeaderMenu active="games" />
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.game-block {
|
||||
background-color: #222;
|
||||
border: 1px solid #222;
|
||||
border-radius: 10px;
|
||||
margin: 10px 0;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.game-block-hover:hover {
|
||||
border: 1px solid #63e2b7;
|
||||
color: #63e2b7;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.game-create-content {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.game-create-plus {
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.game-block:hover {
|
||||
color: #63e2b7;
|
||||
cursor: pointer;
|
||||
@@ -78,8 +257,13 @@ function toResults(id: number) {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.game-date,
|
||||
.game-description {
|
||||
color: #aaa;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.create-hint {
|
||||
color: #aaa;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user