Files
evening_detective_frontend/src/components/TeamStoryPage.vue
T
2026-08-06 00:23:04 +07:00

217 lines
5.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>