add modal

This commit is contained in:
Владимир Фёдоров 2026-03-28 03:10:14 +07:00
parent f37d1151f1
commit 282e6444e9
2 changed files with 239 additions and 3 deletions

View File

@ -1,13 +1,15 @@
<script setup lang="ts">
import { onMounted, ref } from 'vue';
import router from '@/router';
import type { Game, Team, Teams } from './models';
import type { Application, Game, Team, Teams } from './models';
import { apiGetTeams, apiAddTeam, apiGetGame, apiStartGame, apiStopGame, apiGaveApplication, apiDownloadQrCodesFile } from './client';
import TeamQRCode from '../components/TeamQRCode.vue'
import HeaderBlock from './HeaderBlock.vue';
import ModalWindow from './ModalWindow.vue';
const qrurl = ref("-")
const qrteam = ref("-")
const isOpenModal = ref(false)
const gameState = ref("")
const game = ref<Game | undefined>()
@ -62,6 +64,37 @@ onMounted(async () => {
next();
});
});
const gaveApplicationTeam = ref<Team>({
id: 0,
name: '',
password: '',
url: '',
spendTime: 0,
applications: []
})
const gaveApplicationApplication = ref<Application>({
id: 0,
name: ''
})
function gaveApplication(team: Team, application: Application) {
gaveApplicationTeam.value = team
gaveApplicationApplication.value = application
isOpenModal.value = true
}
async function confirm() {
console.log("confirm")
await apiGaveApplication(gaveApplicationTeam.value.id, gaveApplicationApplication.value.id)
isOpenModal.value = false
}
function close() {
console.log("close")
isOpenModal.value = false
}
</script>
<template>
@ -85,13 +118,18 @@ onMounted(async () => {
<div class="team-block" v-for="team in teams.teams" :key="team.name">
<div class="team-content-block">
<div class="team-name-block">
<a v-on:click="qrurl = team.url, qrteam = team.name">QR</a>
<a v-on:click="qrurl = team.url, qrteam = team.name">
QR
</a>
<a :href="team.url" class="url-block" target="_blank">
URL
</a>
{{ team.name }}
</div>
<div>Поездки: {{ team.spendTime }}</div>
</div>
<div v-for="application in team.applications" :key="application.id" class="link-button"
@click="apiGaveApplication(team.id, application.id)">
@click="gaveApplication(team, application)">
Выдать: {{ application.name }}
</div>
</div>
@ -106,6 +144,15 @@ onMounted(async () => {
</form>
</div>
<ModalWindow :is-open="isOpenModal" @confirm="confirm" @close="close">
<div>
Команда: {{ gaveApplicationTeam.name }}
</div>
<div>
Приложение: {{ gaveApplicationApplication.name }}
</div>
</ModalWindow>
</template>
<style scoped>
@ -117,6 +164,10 @@ onMounted(async () => {
margin: 5px 10px 5px 0;
}
.url-block {
margin: 5px 0;
}
.link-button {
/* Основные стили */
border: none;

View File

@ -0,0 +1,185 @@
<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue'
// Пропсы (входящие данные)
const props = defineProps({
isOpen: {
type: Boolean,
required: true
},
title: {
type: String,
default: 'Внимание'
}
})
// Эмиты (события, отправляемые родителю)
const emit = defineEmits(['close', 'confirm'])
const closeModal = () => {
emit('close')
}
// Функция СОХРАНЕНИЯ
const confirmModal = () => {
emit('confirm')
}
// Закрытие по кнопке Esc
const handleKeydown = (e: { key: string }) => {
if (props.isOpen && e.key === 'Escape') {
closeModal()
}
}
onMounted(() => {
document.addEventListener('keydown', handleKeydown)
})
onUnmounted(() => {
document.removeEventListener('keydown', handleKeydown)
})
</script>
<template>
<!-- Teleport переносит модалку в конец тега <body> -->
<Teleport to="body">
<!-- Transition добавляет плавную анимацию -->
<Transition name="modal">
<div v-if="isOpen" class="modal-mask" @click="closeModal">
<!-- @click.stop предотвращает закрытие при клике внутри самого окна -->
<div class="modal-wrapper">
<div class="modal-container" @click.stop>
<!-- Шапка (Header) -->
<div class="modal-header">
<slot name="header">
<h3>{{ title }}</h3>
</slot>
</div>
<!-- Тело (Body) -->
<div class="modal-body">
<slot>Дефолтный текст модального окна</slot>
</div>
<!-- Подвал (Footer) -->
<div class="modal-footer">
<slot name="footer">
<button class="btn-cancel" @click="closeModal">Отмена</button>
<button class="btn-primary" @click="confirmModal">Выдано</button>
</slot>
</div>
</div>
</div>
</div>
</Transition>
</Teleport>
</template>
<style scoped>
/* Затемнение фона */
.modal-mask {
position: fixed;
z-index: 9999;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
display: table;
transition: opacity 0.3s ease;
font-size: 20px;
}
.modal-wrapper {
display: table-cell;
vertical-align: middle;
}
/* Само окно */
.modal-container {
width: 400px;
margin: 0px auto;
padding: 20px 30px;
background-color: #fff;
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
transition: all 0.3s ease;
position: relative;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.modal-header h3 {
margin: 0;
font-size: 25px;
color: var(--main-color);
font-weight: 600;
}
.close-btn {
background: none;
border: none;
font-size: 24px;
cursor: pointer;
color: #aaa;
}
.close-btn:hover {
color: #333;
}
.modal-body {
margin: 20px 0;
line-height: 1.5;
}
.modal-footer {
text-align: right;
}
.btn-cancel,
.btn-primary {
padding: 12px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 20px;
margin-left: 10px;
}
.btn-cancel {
background-color: #fff;
color: var(--main-color);
}
.btn-primary {
background-color: var(--main-color);
color: white;
}
.btn-primary:hover {
background-color: var(--second-color);
}
/* Анимации Vue <Transition> */
.modal-enter-from {
opacity: 0;
}
.modal-leave-to {
opacity: 0;
}
.modal-enter-from .modal-container,
.modal-leave-to .modal-container {
transform: scale(0.9);
}
</style>