generated from VLADIMIR/template_frontend
217 lines
5.4 KiB
Vue
217 lines
5.4 KiB
Vue
<script setup lang="ts">
|
||
import { NButton, NFlex, NQrCode } from 'naive-ui'
|
||
import { useMessage } from 'naive-ui';
|
||
import { nextTick, ref } from 'vue';
|
||
import { useRoute, useRouter } from 'vue-router';
|
||
|
||
import { getAuthClient } from '@/api/auth_client';
|
||
import type { Game, Place } from '@/api/generated/crabs/evening_detective_server';
|
||
import GameInputForm from '@/components/GameInputForm.vue'
|
||
import HeaderMenu from '@/components/HeaderMenu.vue'
|
||
import PlaceBlock from '@/components/PlaceBlock.vue'
|
||
import { useSimplePolling } from '@/composables/useSimplePolling';
|
||
import { useAuthStore } from '@/stores/auth';
|
||
|
||
import MessagePaper from './MessagePaper.vue';
|
||
|
||
const authStore = useAuthStore()
|
||
const client = getAuthClient()
|
||
const message = useMessage()
|
||
const router = useRouter()
|
||
const route = useRoute()
|
||
const teamId = route.params.id
|
||
const password = route.query.password
|
||
|
||
const newPlacesCount = ref(0)
|
||
|
||
const scrollContainer = ref<HTMLDivElement | null>();
|
||
|
||
const story = ref<AdvancedStory>({
|
||
places: []
|
||
})
|
||
|
||
const game = ref<Game | undefined>({
|
||
id: undefined,
|
||
name: undefined,
|
||
description: undefined,
|
||
startAt: undefined,
|
||
status: undefined,
|
||
scenario: undefined,
|
||
teams: undefined
|
||
})
|
||
|
||
const code = ref('')
|
||
|
||
const url = window.location.href
|
||
|
||
const isAtBottom = () => {
|
||
const container = scrollContainer.value;
|
||
if (!container) return false;
|
||
|
||
return container.scrollHeight - container.scrollTop - container.clientHeight < 1;
|
||
};
|
||
|
||
async function getStory() {
|
||
const oldStory = story.value
|
||
|
||
const res = await client.GetTeamStory({
|
||
id: Number(teamId),
|
||
password: password as string
|
||
})
|
||
if (res.error != '') {
|
||
message.error(res.error!)
|
||
return
|
||
}
|
||
|
||
game.value = res.game
|
||
|
||
const wasAtBottom = isAtBottom();
|
||
|
||
if (wasAtBottom) {
|
||
newPlacesCount.value = 0
|
||
}
|
||
|
||
const oldCount = story.value.places.length
|
||
|
||
story.value = {
|
||
places: res.story!.places?.map((item, i) => {
|
||
console.log(oldStory?.places[i]?.mode)
|
||
return {
|
||
place: item,
|
||
mode: oldStory?.places[i]?.mode || 'show'
|
||
}
|
||
}) || []
|
||
}
|
||
|
||
const newCount = story.value.places.length
|
||
|
||
if (newCount > oldCount && oldCount != 0) {
|
||
newPlacesCount.value += newCount - oldCount
|
||
}
|
||
|
||
await nextTick();
|
||
if (wasAtBottom && oldCount != 0) {
|
||
const container = scrollContainer.value;
|
||
if (!container) return false;
|
||
container.scrollTop = container.scrollHeight;
|
||
}
|
||
}
|
||
getStory()
|
||
|
||
async function addAction(newCode: string) {
|
||
const res = await client.AddTeamAction({
|
||
id: Number(teamId),
|
||
password: password as string,
|
||
code: newCode
|
||
})
|
||
if (res.error != '') {
|
||
message.error(res.error!)
|
||
return
|
||
}
|
||
await getStory()
|
||
code.value = ''
|
||
|
||
await nextTick();
|
||
newPlacesCount.value = 0
|
||
}
|
||
|
||
type AdvancedStory = {
|
||
places: AdvancedPlace[];
|
||
}
|
||
|
||
type AdvancedPlace = {
|
||
place: Place;
|
||
mode: string;
|
||
}
|
||
|
||
async function deleteLast() {
|
||
const res = await client.DeleteLastTeamAction({
|
||
id: Number(teamId),
|
||
})
|
||
if (res.error != '') {
|
||
message.error(res.error!)
|
||
return
|
||
}
|
||
await getStory()
|
||
code.value = ''
|
||
}
|
||
|
||
useSimplePolling(() => {
|
||
getStory()
|
||
}, 2000);
|
||
|
||
</script>
|
||
|
||
<template>
|
||
<div class="center-block-custom bottom-margin smooth-scroll" ref="scrollContainer">
|
||
<div class="width700">
|
||
<GameInputForm :addAction="addAction" :allPlacesCount="story.places.length" :newPlacesCount="newPlacesCount"
|
||
:gameStatus="game?.status || ''"></GameInputForm>
|
||
|
||
<PlaceBlock :place="advancedPlace.place" v-model:mode="advancedPlace.mode"
|
||
v-for="advancedPlace in story.places" v-bind:key="advancedPlace.place.code" :addAction="addAction">
|
||
</PlaceBlock>
|
||
|
||
<MessagePaper class="qr-main-block" v-if="story.places.length == 0">
|
||
<n-space vertical>
|
||
<h2 class="qr-main-block-text">Это код этого дела</h2>
|
||
<p class="qr-main-block-text">Поделись с командой</p>
|
||
<div class="qr-block">
|
||
<n-qr-code :value="url" :size="300" :padding="0" />
|
||
</div>
|
||
<p v-if="game?.status == 'draft'" class="qr-main-block-text">Игра скоро начнется</p>
|
||
<p v-if="game?.status == 'run'" class="qr-main-block-text">Игра началась!!!</p>
|
||
</n-space>
|
||
</MessagePaper>
|
||
|
||
<div v-if="authStore.hasRole('author') && story.places.length > 0" class="edit-panel">
|
||
<n-flex justify="end">
|
||
<n-button @click="deleteLast">Удалить последний ход</n-button>
|
||
</n-flex>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<HeaderMenu active="games" />
|
||
</template>
|
||
|
||
<style scoped>
|
||
.bottom-margin {
|
||
height: calc(100dvh - 70px - 76px);
|
||
}
|
||
|
||
.smooth-scroll {
|
||
scroll-behavior: smooth;
|
||
}
|
||
|
||
.edit-panel {
|
||
margin: 30px 0;
|
||
padding: 20px;
|
||
background-color: #222;
|
||
border-radius: 10px;
|
||
}
|
||
|
||
.qr-main-block {
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
}
|
||
|
||
.qr-main-block-text {
|
||
text-align: left;
|
||
margin: 10px 0;
|
||
}
|
||
|
||
.qr-block {
|
||
display: inline-block;
|
||
background-color: white;
|
||
padding: 12px;
|
||
}
|
||
|
||
@media (width >=1024px) {
|
||
.bottom-margin {
|
||
height: calc(100dvh - 70px - 76px);
|
||
}
|
||
}
|
||
</style>
|