generated from VLADIMIR/template_frontend
up
This commit is contained in:
+219
-438
@@ -191,7 +191,9 @@ function isScreenPlanEditing() {
|
||||
}
|
||||
|
||||
async function getScreenPlan() {
|
||||
console.log("getScreenPlan")
|
||||
if (isScreenPlanEditing() || screenPlanDirty.value) return
|
||||
console.log("no skip getScreenPlan")
|
||||
const requestVersion = ++screenPlanRequestVersion
|
||||
const res = await client.GetScreenPlan({ gameID: Number(gameId) })
|
||||
if (
|
||||
@@ -870,13 +872,8 @@ useSimplePolling(() => {
|
||||
</Icon>
|
||||
Итоги
|
||||
</n-button>
|
||||
<n-button
|
||||
v-if="activeTeamCount"
|
||||
quaternary
|
||||
:loading="isDownloadingQR"
|
||||
:disabled="isDownloadingQR"
|
||||
@click="downloadTeamQR()"
|
||||
>
|
||||
<n-button v-if="activeTeamCount" quaternary :loading="isDownloadingQR" :disabled="isDownloadingQR"
|
||||
@click="downloadTeamQR()">
|
||||
QR команд
|
||||
</n-button>
|
||||
<n-button quaternary @click="openSettings()">
|
||||
@@ -885,12 +882,7 @@ useSimplePolling(() => {
|
||||
</Icon>
|
||||
Настройки
|
||||
</n-button>
|
||||
<n-button
|
||||
v-if="(game.teams?.length || 0) == 0"
|
||||
type="error"
|
||||
quaternary
|
||||
@click="showDeleteGameModal = true"
|
||||
>
|
||||
<n-button v-if="(game.teams?.length || 0) == 0" type="error" quaternary @click="showDeleteGameModal = true">
|
||||
Удалить игру
|
||||
</n-button>
|
||||
|
||||
@@ -951,263 +943,183 @@ useSimplePolling(() => {
|
||||
</n-button>
|
||||
</n-flex>
|
||||
</div>
|
||||
<div class="screen-plan">
|
||||
<n-flex align="center" justify="space-between">
|
||||
<p class="screens-title">План экранов</p>
|
||||
<n-space>
|
||||
<n-button v-if="!screenPlan && screenPlanMode === 'use'" size="small" @click="addScreenPlanStep">
|
||||
Создать план
|
||||
</n-button>
|
||||
<template v-if="screenPlan && screenPlanMode === 'use'">
|
||||
<n-button size="small" @click="editScreenPlan">Редактировать</n-button>
|
||||
</template>
|
||||
<template v-if="screenPlanMode === 'edit'">
|
||||
<n-button size="small" :disabled="screenPlanSaving" @click="cancelScreenPlanEdit">
|
||||
Отменить
|
||||
<div class="screen-plan">
|
||||
<n-flex align="center" justify="space-between">
|
||||
<p class="screens-title">План экранов</p>
|
||||
<n-space>
|
||||
<n-button v-if="!screenPlan && screenPlanMode === 'use'" size="small" @click="addScreenPlanStep">
|
||||
Создать план
|
||||
</n-button>
|
||||
<n-button
|
||||
size="small"
|
||||
type="primary"
|
||||
:loading="screenPlanSaving"
|
||||
:disabled="screenPlanSaving"
|
||||
@click="saveScreenPlan"
|
||||
>
|
||||
Сохранить план
|
||||
</n-button>
|
||||
</template>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<p v-if="!screenPlan && screenPlanMode === 'use'" class="screen-empty">План экранов ещё не создан.</p>
|
||||
<template v-else-if="screenPlanMode === 'edit'">
|
||||
<n-space v-for="(step, index) in screenPlanDraft" :key="getScreenPlanStepKey(step)" align="center" wrap>
|
||||
<n-text>{{ index + 1 }}</n-text>
|
||||
<n-select
|
||||
v-model:value="step.mode"
|
||||
:options="screenPlanModeOptions"
|
||||
:disabled="screenPlanSaving"
|
||||
size="small"
|
||||
style="width: 180px"
|
||||
@update:value="markScreenPlanDirty"
|
||||
/>
|
||||
<n-input
|
||||
v-if="step.mode === 'SCREEN_MODE_TEXT'"
|
||||
v-model:value="step.text"
|
||||
:disabled="screenPlanSaving"
|
||||
size="small"
|
||||
placeholder="Текст экрана"
|
||||
style="min-width: 240px"
|
||||
@update:value="markScreenPlanDirty"
|
||||
/>
|
||||
<template v-if="step.mode === 'SCREEN_MODE_TIMER'">
|
||||
<n-input
|
||||
v-model:value="step.timerLabel"
|
||||
:disabled="screenPlanSaving"
|
||||
size="small"
|
||||
placeholder="Надпись таймера"
|
||||
@update:value="markScreenPlanDirty"
|
||||
/>
|
||||
<n-input-number
|
||||
v-model:value="step.timerDurationSeconds"
|
||||
:min="1"
|
||||
:max="86400"
|
||||
:disabled="screenPlanSaving"
|
||||
size="small"
|
||||
placeholder="Секунды"
|
||||
@update:value="markScreenPlanDirty"
|
||||
/>
|
||||
</template>
|
||||
<n-button
|
||||
size="small"
|
||||
:disabled="screenPlanSaving || index === 0"
|
||||
@click="moveScreenPlanStep(index, -1)"
|
||||
>
|
||||
↑
|
||||
</n-button>
|
||||
<n-button
|
||||
size="small"
|
||||
:disabled="screenPlanSaving || index === screenPlanDraft.length - 1"
|
||||
@click="moveScreenPlanStep(index, 1)"
|
||||
>
|
||||
↓
|
||||
</n-button>
|
||||
<n-button
|
||||
size="small"
|
||||
type="error"
|
||||
ghost
|
||||
:disabled="screenPlanSaving"
|
||||
@click="removeScreenPlanStep(index)"
|
||||
>
|
||||
Удалить
|
||||
</n-button>
|
||||
</n-space>
|
||||
<n-space>
|
||||
<n-button size="small" :disabled="screenPlanSaving" @click="addScreenPlanStep">
|
||||
Добавить шаг
|
||||
</n-button>
|
||||
<n-button
|
||||
v-if="screenPlanDraft.length"
|
||||
size="small"
|
||||
:disabled="screenPlanSaving"
|
||||
@click="screenPlanDraft = []; markScreenPlanDirty()"
|
||||
>
|
||||
Удалить план
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
<template v-else-if="screenPlan">
|
||||
<n-space v-for="(step, index) in screenPlan.steps || []" :key="index" align="center" wrap>
|
||||
<n-text>{{ index + 1 }}</n-text>
|
||||
<n-text>{{ screenPlanModeOptions.find((option) => option.value === step.mode)?.label }}
|
||||
<template v-if="step.mode === 'SCREEN_MODE_TEXT'">: {{ step.text }}</template>
|
||||
<template v-else-if="step.mode === 'SCREEN_MODE_TIMER'">
|
||||
: {{ step.timerLabel }} ({{ step.timerDurationSeconds }} с)
|
||||
<template v-if="screenPlan && screenPlanMode === 'use'">
|
||||
<n-button size="small" @click="editScreenPlan">Редактировать</n-button>
|
||||
</template>
|
||||
</n-text>
|
||||
<n-button
|
||||
size="small"
|
||||
:type="screenPlan.currentStep === index ? 'primary' : 'default'"
|
||||
:disabled="screenPlan.currentStep === index"
|
||||
@click="setScreenPlanStep(index)"
|
||||
>
|
||||
Показать
|
||||
</n-button>
|
||||
<template v-if="screenPlan.currentStep === index">
|
||||
<template v-if="screenPlanMode === 'edit'">
|
||||
<n-button size="small" :disabled="screenPlanSaving" @click="cancelScreenPlanEdit">
|
||||
Отменить
|
||||
</n-button>
|
||||
<n-button size="small" type="primary" :loading="screenPlanSaving" :disabled="screenPlanSaving"
|
||||
@click="saveScreenPlan">
|
||||
Сохранить план
|
||||
</n-button>
|
||||
</template>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<p v-if="!screenPlan && screenPlanMode === 'use'" class="screen-empty">План экранов ещё не создан.</p>
|
||||
<template v-else-if="screenPlanMode === 'edit'">
|
||||
<n-space v-for="(step, index) in screenPlanDraft" :key="getScreenPlanStepKey(step)" align="center" wrap>
|
||||
<n-text>{{ index + 1 }}</n-text>
|
||||
<n-select v-model:value="step.mode" :options="screenPlanModeOptions" :disabled="screenPlanSaving"
|
||||
size="small" style="width: 180px" @update:value="markScreenPlanDirty" />
|
||||
<n-input v-if="step.mode === 'SCREEN_MODE_TEXT'" v-model:value="step.text" :disabled="screenPlanSaving"
|
||||
size="small" placeholder="Текст экрана" style="min-width: 240px"
|
||||
@update:value="markScreenPlanDirty" />
|
||||
<template v-if="step.mode === 'SCREEN_MODE_TIMER'">
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_TIMER_START')">
|
||||
Старт
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_TIMER_PAUSE')">
|
||||
Пауза
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_TIMER_RESUME')">
|
||||
Продолжить
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_TIMER_RESET')">
|
||||
Сбросить
|
||||
</n-button>
|
||||
<n-input v-model:value="step.timerLabel" :disabled="screenPlanSaving" size="small"
|
||||
placeholder="Надпись таймера" @update:value="markScreenPlanDirty" />
|
||||
<n-input-number v-model:value="step.timerDurationSeconds" :min="1" :max="86400"
|
||||
:disabled="screenPlanSaving" size="small" placeholder="Секунды"
|
||||
@update:value="markScreenPlanDirty" />
|
||||
</template>
|
||||
<template v-else-if="step.mode === 'SCREEN_MODE_INTRO' || step.mode === 'SCREEN_MODE_CONCLUSION'">
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_PLAY')">
|
||||
Запуск
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_PAUSE')">
|
||||
Пауза
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_RESUME')">
|
||||
Продолжить
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_STOP')">
|
||||
Стоп
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_REPLAY')">
|
||||
Повтор
|
||||
</n-button>
|
||||
<n-input-number v-model:value="screenVolume" :min="0" :max="100" :step="5" size="small" />
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_VOLUME')">
|
||||
Громкость
|
||||
</n-button>
|
||||
<n-input-number v-model:value="screenPosition" :min="0" :step="5" size="small" />
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_SEEK')">
|
||||
Перемотать
|
||||
</n-button>
|
||||
<n-button size="small" :disabled="screenPlanSaving || index === 0"
|
||||
@click="moveScreenPlanStep(index, -1)">
|
||||
↑
|
||||
</n-button>
|
||||
<n-button size="small" :disabled="screenPlanSaving || index === screenPlanDraft.length - 1"
|
||||
@click="moveScreenPlanStep(index, 1)">
|
||||
↓
|
||||
</n-button>
|
||||
<n-button size="small" type="error" ghost :disabled="screenPlanSaving"
|
||||
@click="removeScreenPlanStep(index)">
|
||||
Удалить
|
||||
</n-button>
|
||||
</n-space>
|
||||
<n-space>
|
||||
<n-button size="small" :disabled="screenPlanSaving" @click="addScreenPlanStep">
|
||||
Добавить шаг
|
||||
</n-button>
|
||||
<n-button v-if="screenPlanDraft.length" size="small" :disabled="screenPlanSaving"
|
||||
@click="screenPlanDraft = []; markScreenPlanDirty()">
|
||||
Удалить план
|
||||
</n-button>
|
||||
</n-space>
|
||||
</template>
|
||||
<template v-else-if="screenPlan">
|
||||
<n-space v-for="(step, index) in screenPlan.steps || []" :key="index" align="center" wrap>
|
||||
<n-text>{{ index + 1 }}</n-text>
|
||||
<n-text>{{screenPlanModeOptions.find((option) => option.value === step.mode)?.label}}
|
||||
<template v-if="step.mode === 'SCREEN_MODE_TEXT'">: {{ step.text }}</template>
|
||||
<template v-else-if="step.mode === 'SCREEN_MODE_TIMER'">
|
||||
: {{ step.timerLabel }} ({{ step.timerDurationSeconds }} с)
|
||||
</template>
|
||||
</n-text>
|
||||
<n-button size="small" :type="screenPlan.currentStep === index ? 'primary' : 'default'"
|
||||
:disabled="screenPlan.currentStep === index" @click="setScreenPlanStep(index)">
|
||||
Показать
|
||||
</n-button>
|
||||
<template v-if="screenPlan.currentStep === index">
|
||||
<template v-if="step.mode === 'SCREEN_MODE_TIMER'">
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_TIMER_START')">
|
||||
Старт
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_TIMER_PAUSE')">
|
||||
Пауза
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_TIMER_RESUME')">
|
||||
Продолжить
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_TIMER_RESET')">
|
||||
Сбросить
|
||||
</n-button>
|
||||
</template>
|
||||
<template v-else-if="step.mode === 'SCREEN_MODE_INTRO' || step.mode === 'SCREEN_MODE_CONCLUSION'">
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_PLAY')">
|
||||
Запуск
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_PAUSE')">
|
||||
Пауза
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_RESUME')">
|
||||
Продолжить
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_STOP')">
|
||||
Стоп
|
||||
</n-button>
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_REPLAY')">
|
||||
Повтор
|
||||
</n-button>
|
||||
<n-input-number v-model:value="screenVolume" :min="0" :max="100" :step="5" size="small" />
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_VOLUME')">
|
||||
Громкость
|
||||
</n-button>
|
||||
<n-input-number v-model:value="screenPosition" :min="0" :step="5" size="small" />
|
||||
<n-button size="small" @click="sendScreenPlanCommand(step.mode, 'SCREEN_COMMAND_SEEK')">
|
||||
Перемотать
|
||||
</n-button>
|
||||
</template>
|
||||
</template>
|
||||
</template>
|
||||
</n-space>
|
||||
</template>
|
||||
</div>
|
||||
</n-space>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<div class="map-preview-layout">
|
||||
<div class="map-block">
|
||||
<n-flex align="center" justify="space-between">
|
||||
<p class="screens-title">Карта помещения</p>
|
||||
<n-space>
|
||||
<n-button size="small" @click="changeMapScale(-0.1)">−</n-button>
|
||||
<n-text>{{ Math.round(mapScale * 100) }}%</n-text>
|
||||
<n-button size="small" @click="changeMapScale(0.1)">+</n-button>
|
||||
<n-button size="small" @click="resetMapView">Сбросить</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<div class="map-field" @pointerdown="startMapPan" @wheel="zoomMap">
|
||||
<div
|
||||
class="map-canvas"
|
||||
:style="{
|
||||
<div class="map-block">
|
||||
<n-flex align="center" justify="space-between">
|
||||
<p class="screens-title">Карта помещения</p>
|
||||
<n-space>
|
||||
<n-button size="small" @click="changeMapScale(-0.1)">−</n-button>
|
||||
<n-text>{{ Math.round(mapScale * 100) }}%</n-text>
|
||||
<n-button size="small" @click="changeMapScale(0.1)">+</n-button>
|
||||
<n-button size="small" @click="resetMapView">Сбросить</n-button>
|
||||
</n-space>
|
||||
</n-flex>
|
||||
<div class="map-field" @pointerdown="startMapPan" @wheel="zoomMap">
|
||||
<div class="map-canvas" :style="{
|
||||
transform: `translate(${mapOffset.x}px, ${mapOffset.y}px) scale(${mapScale})`,
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-for="item in mapItems"
|
||||
v-show="!item.hidden"
|
||||
:key="`${item.kind}-${item.itemID}`"
|
||||
class="map-item"
|
||||
:class="[
|
||||
item.kind === 'screen'
|
||||
? 'map-screen'
|
||||
: item.kind === 'organizer_desk'
|
||||
? 'map-organizer-desk'
|
||||
: 'map-team',
|
||||
{
|
||||
'map-team-needs-applications':
|
||||
item.kind === 'team' && teamsNeedingApplications.has(item.itemID),
|
||||
},
|
||||
]"
|
||||
:style="{ left: `${item.x || 0}px`, top: `${item.y || 0}px` }"
|
||||
@pointerdown.stop="item.kind !== 'team' && startMapDrag(item, $event)"
|
||||
>
|
||||
<div
|
||||
v-if="item.kind === 'team'"
|
||||
class="map-team-handle"
|
||||
@pointerdown.stop="startMapDrag(item, $event)"
|
||||
/>
|
||||
<span>{{ item.label }}</span>
|
||||
<n-space
|
||||
v-if="item.kind === 'team' && teamsNeedingApplications.has(item.itemID)"
|
||||
class="map-team-applications"
|
||||
vertical
|
||||
>
|
||||
<n-button
|
||||
v-for="application in teamsNeedingApplications.get(item.itemID)?.applications"
|
||||
:key="application.name"
|
||||
size="small"
|
||||
@click="giveApplication(teamsNeedingApplications.get(item.itemID)!, application)"
|
||||
>
|
||||
Выдать: {{ application.name }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
<small v-if="item.kind === 'screen'"
|
||||
>{{ item.status }}{{ item.soundEnabled ? ' · звук' : '' }}</small
|
||||
>
|
||||
<button
|
||||
class="map-item-hide"
|
||||
type="button"
|
||||
@pointerdown.stop
|
||||
@click.stop="toggleMapItem(item)"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
}">
|
||||
<div v-for="item in mapItems" v-show="!item.hidden" :key="`${item.kind}-${item.itemID}`" class="map-item"
|
||||
:class="[
|
||||
item.kind === 'screen'
|
||||
? 'map-screen'
|
||||
: item.kind === 'organizer_desk'
|
||||
? 'map-organizer-desk'
|
||||
: 'map-team',
|
||||
{
|
||||
'map-team-needs-applications':
|
||||
item.kind === 'team' && teamsNeedingApplications.has(item.itemID),
|
||||
},
|
||||
]" :style="{ left: `${item.x || 0}px`, top: `${item.y || 0}px` }"
|
||||
@pointerdown.stop="item.kind !== 'team' && startMapDrag(item, $event)">
|
||||
<div v-if="item.kind === 'team'" class="map-team-handle"
|
||||
@pointerdown.stop="startMapDrag(item, $event)" />
|
||||
<span>{{ item.label }}</span>
|
||||
<n-space v-if="item.kind === 'team' && teamsNeedingApplications.has(item.itemID)"
|
||||
class="map-team-applications" vertical>
|
||||
<n-button v-for="application in teamsNeedingApplications.get(item.itemID)?.applications"
|
||||
:key="application.name" size="small"
|
||||
@click="giveApplication(teamsNeedingApplications.get(item.itemID)!, application)">
|
||||
Выдать: {{ application.name }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
<small v-if="item.kind === 'screen'">{{ item.status }}{{ item.soundEnabled ? ' · звук' : '' }}</small>
|
||||
<button class="map-item-hide" type="button" @pointerdown.stop @click.stop="toggleMapItem(item)">
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<n-space v-if="mapItems.some((item) => item.hidden)" class="map-hidden-items">
|
||||
<n-button v-for="item in mapItems.filter((item) => item.hidden)" :key="`hidden-${item.kind}-${item.itemID}`"
|
||||
size="small" @click="toggleMapItem(item)">
|
||||
Добавить {{ item.label }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
<n-space v-if="mapItems.some((item) => item.hidden)" class="map-hidden-items">
|
||||
<n-button
|
||||
v-for="item in mapItems.filter((item) => item.hidden)"
|
||||
:key="`hidden-${item.kind}-${item.itemID}`"
|
||||
size="small"
|
||||
@click="toggleMapItem(item)"
|
||||
>
|
||||
Добавить {{ item.label }}
|
||||
</n-button>
|
||||
</n-space>
|
||||
</div>
|
||||
<aside class="screen-preview-panel">
|
||||
<p class="screens-title">Превью экрана</p>
|
||||
<ScreenPreview
|
||||
v-if="previewScreen?.status === 'connected'"
|
||||
:state="previewScreen"
|
||||
:questions="previewQuestions"
|
||||
:teams="previewTeams"
|
||||
/>
|
||||
<ScreenPreview v-if="previewScreen?.status === 'connected'" :state="previewScreen"
|
||||
:questions="previewQuestions" :teams="previewTeams" />
|
||||
<div v-else class="screen-preview-placeholder">Экран не подключён</div>
|
||||
</aside>
|
||||
</div>
|
||||
@@ -1224,8 +1136,7 @@ useSimplePolling(() => {
|
||||
{{ team.name }}
|
||||
<small v-if="team.captainEmail"> — {{ team.captainEmail }}</small>
|
||||
<small v-if="team.participantsCount">
|
||||
· Участников: {{ team.participantsCount }}</small
|
||||
>
|
||||
· Участников: {{ team.participantsCount }}</small>
|
||||
</span>
|
||||
<div v-if="team.captainEmail || team.participationStatus !== 'pending'">
|
||||
{{
|
||||
@@ -1244,28 +1155,20 @@ useSimplePolling(() => {
|
||||
</div>
|
||||
|
||||
<n-flex justify="end">
|
||||
<n-button
|
||||
v-if="team.participationStatus !== 'cancelled'"
|
||||
@click="openQR(getTeamStoryURL(team.ID!, team.password!), 'Подсмотреть игру')"
|
||||
>
|
||||
<n-button v-if="team.participationStatus !== 'cancelled'"
|
||||
@click="openQR(getTeamStoryURL(team.ID!, team.password!), 'Подсмотреть игру')">
|
||||
QR
|
||||
</n-button>
|
||||
<n-button
|
||||
v-if="team.participationStatus !== 'cancelled'"
|
||||
@click="toTeamStory(team.ID!, team.password!, true)"
|
||||
>
|
||||
<n-button v-if="team.participationStatus !== 'cancelled'"
|
||||
@click="toTeamStory(team.ID!, team.password!, true)">
|
||||
Подсмотреть игру
|
||||
</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"
|
||||
@click="giveApplication(team, application)"
|
||||
>
|
||||
<n-button v-for="application in team.applications" :key="application.name" class="link-button"
|
||||
@click="giveApplication(team, application)">
|
||||
Выдать: {{ application.name }}
|
||||
</n-button>
|
||||
</div>
|
||||
@@ -1274,14 +1177,8 @@ useSimplePolling(() => {
|
||||
</div>
|
||||
|
||||
<n-modal v-model:show="showScreenScanner" @after-leave="stopScreenScanner">
|
||||
<n-card
|
||||
style="width: min(600px, 94vw)"
|
||||
title="Привязать экран"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-card style="width: min(600px, 94vw)" title="Привязать экран" :bordered="false" size="huge" role="dialog"
|
||||
aria-modal="true">
|
||||
<template #header-extra>
|
||||
<n-button @click="closeScreenScanner"> x </n-button>
|
||||
</template>
|
||||
@@ -1299,48 +1196,25 @@ useSimplePolling(() => {
|
||||
|
||||
<!-- Окно настроек -->
|
||||
<n-modal v-model:show="showSettingsModal">
|
||||
<n-card
|
||||
style="width: 600px"
|
||||
title="Настройки игры"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-card style="width: 600px" title="Настройки игры" :bordered="false" size="huge" role="dialog" aria-modal="true">
|
||||
<template #header-extra>
|
||||
<n-button @click="closeSettings()"> x </n-button>
|
||||
</template>
|
||||
<n-space vertical>
|
||||
<p>Название</p>
|
||||
<n-input
|
||||
v-model:value="settingsDraft.name"
|
||||
type="text"
|
||||
placeholder='Дело №0 "Следствие ведут овечки"'
|
||||
/>
|
||||
<n-input v-model:value="settingsDraft.name" type="text" placeholder='Дело №0 "Следствие ведут овечки"' />
|
||||
|
||||
<p class="settings-header">Формат игры</p>
|
||||
<n-select
|
||||
v-model:value="settingsDraft.format"
|
||||
:options="[
|
||||
{ label: 'Online', value: 'online' },
|
||||
{ label: 'Offline', value: 'offline' },
|
||||
]"
|
||||
aria-label="Формат игры"
|
||||
/>
|
||||
<n-select v-model:value="settingsDraft.format" :options="[
|
||||
{ label: 'Online', value: 'online' },
|
||||
{ label: 'Offline', value: 'offline' },
|
||||
]" aria-label="Формат игры" />
|
||||
|
||||
<p class="settings-header">Описание</p>
|
||||
<n-input
|
||||
v-model:value="settingsDraft.description"
|
||||
type="textarea"
|
||||
placeholder="Убийство пастуха..."
|
||||
/>
|
||||
<n-input v-model:value="settingsDraft.description" type="textarea" placeholder="Убийство пастуха..." />
|
||||
|
||||
<p class="settings-header">Место проведения</p>
|
||||
<n-input
|
||||
v-model:value="settingsDraft.location"
|
||||
type="text"
|
||||
placeholder="Оставьте пустым для онлайн-игры"
|
||||
/>
|
||||
<n-input v-model:value="settingsDraft.location" type="text" placeholder="Оставьте пустым для онлайн-игры" />
|
||||
</n-space>
|
||||
<template #footer>
|
||||
<n-flex justify="end">
|
||||
@@ -1352,14 +1226,7 @@ useSimplePolling(() => {
|
||||
|
||||
<!-- Окно выдачи улик -->
|
||||
<n-modal v-model:show="showGiveApplicationModal">
|
||||
<n-card
|
||||
style="width: 600px"
|
||||
title="Настройки игры"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-card style="width: 600px" title="Настройки игры" :bordered="false" size="huge" role="dialog" aria-modal="true">
|
||||
<template #header-extra>
|
||||
<n-button @click="showGiveApplicationModal = false"> x </n-button>
|
||||
</template>
|
||||
@@ -1370,38 +1237,22 @@ useSimplePolling(() => {
|
||||
<p>Улика</p>
|
||||
<h3>{{ giveApplicationApplication.name }}</h3>
|
||||
|
||||
<div
|
||||
v-if="
|
||||
applicationFileType(giveApplicationApplication) == 'image' &&
|
||||
giveApplicationApplication.image
|
||||
"
|
||||
class="give-application-image-block"
|
||||
>
|
||||
<img
|
||||
:src="applicationFileURL(giveApplicationApplication.image)"
|
||||
class="give-application-image"
|
||||
/>
|
||||
<div v-if="
|
||||
applicationFileType(giveApplicationApplication) == 'image' &&
|
||||
giveApplicationApplication.image
|
||||
" class="give-application-image-block">
|
||||
<img :src="applicationFileURL(giveApplicationApplication.image)" class="give-application-image" />
|
||||
</div>
|
||||
<audio
|
||||
v-else-if="
|
||||
applicationFileType(giveApplicationApplication) == 'audio' &&
|
||||
giveApplicationApplication.image
|
||||
"
|
||||
:src="applicationFileURL(giveApplicationApplication.image)"
|
||||
controls
|
||||
preload="none"
|
||||
class="give-application-audio"
|
||||
></audio>
|
||||
<a
|
||||
v-else-if="
|
||||
applicationFileType(giveApplicationApplication) == 'pdf' &&
|
||||
giveApplicationApplication.image
|
||||
"
|
||||
:href="applicationFileURL(giveApplicationApplication.image)"
|
||||
target="_blank"
|
||||
rel="noopener"
|
||||
class="give-application-pdf-link"
|
||||
>
|
||||
<audio v-else-if="
|
||||
applicationFileType(giveApplicationApplication) == 'audio' &&
|
||||
giveApplicationApplication.image
|
||||
" :src="applicationFileURL(giveApplicationApplication.image)" controls preload="none"
|
||||
class="give-application-audio"></audio>
|
||||
<a v-else-if="
|
||||
applicationFileType(giveApplicationApplication) == 'pdf' &&
|
||||
giveApplicationApplication.image
|
||||
" :href="applicationFileURL(giveApplicationApplication.image)" target="_blank" rel="noopener"
|
||||
class="give-application-pdf-link">
|
||||
PDF
|
||||
</a>
|
||||
</n-space>
|
||||
@@ -1415,33 +1266,16 @@ useSimplePolling(() => {
|
||||
|
||||
<!-- Окно создания команды -->
|
||||
<n-modal v-model:show="showAddTeamModal">
|
||||
<n-card
|
||||
style="width: 600px"
|
||||
title="Создание команды"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-card style="width: 600px" title="Создание команды" :bordered="false" size="huge" role="dialog" aria-modal="true">
|
||||
<template #header-extra>
|
||||
<n-button @click="showAddTeamModal = false"> x </n-button>
|
||||
</template>
|
||||
<n-space vertical>
|
||||
<n-input v-model:value="newTeamName" type="text" placeholder="Три дебила" />
|
||||
<n-input
|
||||
v-model:value="newTeamEmail"
|
||||
:input-props="{ type: 'email', autocomplete: 'email' }"
|
||||
placeholder="Почта капитана (необязательно)"
|
||||
aria-label="Почта капитана (необязательно)"
|
||||
/>
|
||||
<n-input-number
|
||||
v-model:value="newTeamParticipants"
|
||||
:min="1"
|
||||
:max="8"
|
||||
:precision="0"
|
||||
placeholder="Количество игроков"
|
||||
aria-label="Количество игроков"
|
||||
/>
|
||||
<n-input v-model:value="newTeamEmail" :input-props="{ type: 'email', autocomplete: 'email' }"
|
||||
placeholder="Почта капитана (необязательно)" aria-label="Почта капитана (необязательно)" />
|
||||
<n-input-number v-model:value="newTeamParticipants" :min="1" :max="8" :precision="0"
|
||||
placeholder="Количество игроков" aria-label="Количество игроков" />
|
||||
</n-space>
|
||||
<template #footer>
|
||||
<n-flex justify="end">
|
||||
@@ -1453,14 +1287,7 @@ useSimplePolling(() => {
|
||||
|
||||
<!-- Окно создания команды -->
|
||||
<n-modal v-model:show="showDeleteTeamModal">
|
||||
<n-card
|
||||
style="width: 600px"
|
||||
title="Удаление команды"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-card style="width: 600px" title="Удаление команды" :bordered="false" size="huge" role="dialog" aria-modal="true">
|
||||
<template #header-extra>
|
||||
<n-button @click="showDeleteTeamModal = false"> x </n-button>
|
||||
</template>
|
||||
@@ -1484,14 +1311,7 @@ useSimplePolling(() => {
|
||||
|
||||
<!-- Окно удаления игры -->
|
||||
<n-modal v-model:show="showDeleteGameModal">
|
||||
<n-card
|
||||
style="width: 600px"
|
||||
title="Удаление игры"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-card style="width: 600px" title="Удаление игры" :bordered="false" size="huge" role="dialog" aria-modal="true">
|
||||
<template #header-extra>
|
||||
<n-button @click="showDeleteGameModal = false"> x </n-button>
|
||||
</template>
|
||||
@@ -1500,11 +1320,7 @@ useSimplePolling(() => {
|
||||
Введите название игры
|
||||
<n-text class="name-text" strong>{{ game.name }}</n-text> чтобы удалить её
|
||||
</p>
|
||||
<n-input
|
||||
v-model:value="deletedGameName"
|
||||
type="text"
|
||||
placeholder='Дело №0 "Следствие ведут овечки"'
|
||||
/>
|
||||
<n-input v-model:value="deletedGameName" type="text" placeholder='Дело №0 "Следствие ведут овечки"' />
|
||||
</n-space>
|
||||
|
||||
<template #footer>
|
||||
@@ -1519,25 +1335,14 @@ useSimplePolling(() => {
|
||||
|
||||
<!-- Окно qr -->
|
||||
<n-modal v-model:show="showIssueTicketsModal">
|
||||
<n-card
|
||||
style="width: min(94vw, 700px)"
|
||||
title="Выпуск билетов"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-card style="width: min(94vw, 700px)" title="Выпуск билетов" :bordered="false" size="huge" role="dialog"
|
||||
aria-modal="true">
|
||||
<template #header-extra>
|
||||
<n-button @click="showIssueTicketsModal = false"> x </n-button>
|
||||
</template>
|
||||
<n-space vertical>
|
||||
<n-input-number v-model:value="ticketCount" :min="1" :max="100" />
|
||||
<n-button
|
||||
type="primary"
|
||||
:loading="isIssuingTickets"
|
||||
:disabled="isIssuingTickets"
|
||||
@click="issueTickets()"
|
||||
>
|
||||
<n-button type="primary" :loading="isIssuingTickets" :disabled="isIssuingTickets" @click="issueTickets()">
|
||||
Выпустить
|
||||
</n-button>
|
||||
<div v-for="ticket in issuedTickets" :key="ticket.ID" class="issued-ticket">
|
||||
@@ -1552,14 +1357,7 @@ useSimplePolling(() => {
|
||||
</n-modal>
|
||||
|
||||
<n-modal v-model:show="showQR">
|
||||
<n-card
|
||||
style="width: 600px"
|
||||
:title="titleQR"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
>
|
||||
<n-card style="width: 600px" :title="titleQR" :bordered="false" size="huge" role="dialog" aria-modal="true">
|
||||
<template #header-extra>
|
||||
<n-button @click="showQR = false"> x </n-button>
|
||||
</template>
|
||||
@@ -1575,15 +1373,8 @@ useSimplePolling(() => {
|
||||
|
||||
<!-- Окно ответов команд и оценки -->
|
||||
<n-modal v-model:show="showAnswersModal">
|
||||
<n-card
|
||||
style="width: min(94vw, 900px)"
|
||||
title="Ответы команд"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
:content-style="{ maxHeight: '70vh', overflow: 'auto' }"
|
||||
>
|
||||
<n-card style="width: min(94vw, 900px)" title="Ответы команд" :bordered="false" size="huge" role="dialog"
|
||||
aria-modal="true" :content-style="{ maxHeight: '70vh', overflow: 'auto' }">
|
||||
<template #header-extra>
|
||||
<n-button @click="showAnswersModal = false"> x </n-button>
|
||||
</template>
|
||||
@@ -1602,12 +1393,9 @@ useSimplePolling(() => {
|
||||
<div class="answers-team-answer">
|
||||
{{ teamAnswer(team, question.code) || '—' }}
|
||||
</div>
|
||||
<n-input-number
|
||||
:value="scores[scoreKey(team.team?.ID, question.code)]"
|
||||
@update:value="onScoreUpdate(scoreKey(team.team?.ID, question.code), $event)"
|
||||
placeholder="Балл"
|
||||
class="answers-score-input"
|
||||
/>
|
||||
<n-input-number :value="scores[scoreKey(team.team?.ID, question.code)]"
|
||||
@update:value="onScoreUpdate(scoreKey(team.team?.ID, question.code), $event)" placeholder="Балл"
|
||||
class="answers-score-input" />
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
@@ -1621,15 +1409,8 @@ useSimplePolling(() => {
|
||||
</n-modal>
|
||||
|
||||
<n-modal v-model:show="showReviewsModal">
|
||||
<n-card
|
||||
style="width: min(94vw, 800px)"
|
||||
title="Отзывы игры"
|
||||
:bordered="false"
|
||||
size="huge"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
:content-style="{ maxHeight: '70vh', overflow: 'auto' }"
|
||||
>
|
||||
<n-card style="width: min(94vw, 800px)" title="Отзывы игры" :bordered="false" size="huge" role="dialog"
|
||||
aria-modal="true" :content-style="{ maxHeight: '70vh', overflow: 'auto' }">
|
||||
<template #header-extra>
|
||||
<n-button @click="showReviewsModal = false"> x </n-button>
|
||||
</template>
|
||||
|
||||
Reference in New Issue
Block a user