add modal
This commit is contained in:
parent
f37d1151f1
commit
282e6444e9
@ -1,13 +1,15 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import router from '@/router';
|
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 { apiGetTeams, apiAddTeam, apiGetGame, apiStartGame, apiStopGame, apiGaveApplication, apiDownloadQrCodesFile } from './client';
|
||||||
import TeamQRCode from '../components/TeamQRCode.vue'
|
import TeamQRCode from '../components/TeamQRCode.vue'
|
||||||
import HeaderBlock from './HeaderBlock.vue';
|
import HeaderBlock from './HeaderBlock.vue';
|
||||||
|
import ModalWindow from './ModalWindow.vue';
|
||||||
|
|
||||||
const qrurl = ref("-")
|
const qrurl = ref("-")
|
||||||
const qrteam = ref("-")
|
const qrteam = ref("-")
|
||||||
|
const isOpenModal = ref(false)
|
||||||
|
|
||||||
const gameState = ref("")
|
const gameState = ref("")
|
||||||
const game = ref<Game | undefined>()
|
const game = ref<Game | undefined>()
|
||||||
@ -62,6 +64,37 @@ onMounted(async () => {
|
|||||||
next();
|
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>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -85,13 +118,18 @@ onMounted(async () => {
|
|||||||
<div class="team-block" v-for="team in teams.teams" :key="team.name">
|
<div class="team-block" v-for="team in teams.teams" :key="team.name">
|
||||||
<div class="team-content-block">
|
<div class="team-content-block">
|
||||||
<div class="team-name-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 }}
|
{{ team.name }}
|
||||||
</div>
|
</div>
|
||||||
<div>Поездки: {{ team.spendTime }}</div>
|
<div>Поездки: {{ team.spendTime }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div v-for="application in team.applications" :key="application.id" class="link-button"
|
<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 }}
|
Выдать: {{ application.name }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -106,6 +144,15 @@ onMounted(async () => {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<ModalWindow :is-open="isOpenModal" @confirm="confirm" @close="close">
|
||||||
|
<div>
|
||||||
|
Команда: {{ gaveApplicationTeam.name }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
Приложение: {{ gaveApplicationApplication.name }}
|
||||||
|
</div>
|
||||||
|
</ModalWindow>
|
||||||
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
@ -117,6 +164,10 @@ onMounted(async () => {
|
|||||||
margin: 5px 10px 5px 0;
|
margin: 5px 10px 5px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.url-block {
|
||||||
|
margin: 5px 0;
|
||||||
|
}
|
||||||
|
|
||||||
.link-button {
|
.link-button {
|
||||||
/* Основные стили */
|
/* Основные стили */
|
||||||
border: none;
|
border: none;
|
||||||
|
|||||||
185
src/components/ModalWindow.vue
Normal file
185
src/components/ModalWindow.vue
Normal 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>
|
||||||
Loading…
x
Reference in New Issue
Block a user