Во vue я видел множество реализаций диалоговых окон и все они были слишком громоздкими и неудобными. И вот, в новом, начатом мной проекте я решил исправить данные проблемы.
Концепция
Вся система будет работать довольно просто, для отображения нужного нам диалогового окна надо будет всего лишь изменить один query параметр в адресной строке браузера, для примера назовем этот параметр ‘dialog’. Соответственно для закрытия окна надо будет только убрать параметр dialog.
Само окно будет состоять из четырех блоков:
‘Dialog-control-panel’ в нем будут располагаться кнопки управления
“Dialog-content” в этой области будут динамически появляться vue компоненты с нужным нам контентом
-
“Dialog-left-btn” и “Dialog-right-btn” кнопки которые можно вызывать по мере надобности, нужны для переключения контента
Начало работы
Создадим нужные нам компоненты:
“TheDialog.vue” - главный компонент в котором будет все происходить
“DialogAbout.vue” и “DialogContacts.vue” - компоненты с контентом для нашего диалогового окна
Необходимое в vuex
В папке проекта “store” создадим еще одну директорию “dialog”, в ней уже на понадобятся 3 файла “state.js”, “mutations.js” и “index.js”.
Как уже стало понятно, файл “state.js” нужен нам для хранения состояний диалогового окна, в нем я пропишу несколько объектов с полями active и to.
Если в active стоит значение “true”, то данный элемент будет отображаться в диалоговом окне.
Свойство to должно хранить в себе ссылку или же пустую строку.
export default function () {
return {
leftBtn: {
active: false,
to: "",
},
rightBtn: {
active: false,
to: "",
},
downloadBtn: {
active: false,
to: "",
},
backBtn: {
active: false,
to: "",
},
};
}
Файл “mutations.js” содержит в себе мутации для изменения состояний нашего диалогового окна.
export function changeStatusLeftBtn(state, active = false, to = "") {
state.leftBtn = { active, to };
}
export function changeStatusRightBtn(state, active = false, to = "") {
state.rightBtn = { active, to };
}
export function changeStatusDownloadBtn(state, active = false, to = "") {
state.downloadBtn = { active, to };
}
export function changeStatusBackBtn(state, active = false, to = "") {
state.backBtn = { active, to };
}
“index.js” нам нужен для соединения всех файлов в один модуль
import state from './state'
import * as mutations from './mutations'
export default {
state,
mutations,
}
После данных манипуляций мы можем добавить модуль dialog в store.
import dialog from "./dialog";
const Store = createStore({
modules: {
dialog,
},
});
Наконец мы подошли к главному компоненту “TheDialog.vue”
Разметка
Здесь мы размечаем само диалоговое окно, четыре главных блока, кнопки которые будут отображаться в зависимости от состояния, а так же указываем место, где будут динамически меняться компоненты.
<template>
<div class="dialog-bg">
<div class="dialog-control-panel">
<div class="panel">
<div class="btn" v-ripple v-if="backBtn.active">
<q-icon name="eva-arrow-back-outline" />
</div>
</div>
<div class="panel">
<div class="btn" v-ripple v-if="downloadBtn.active">
<q-icon name="eva-cloud-download-outline" />
</div>
<div class="btn" v-ripple>
<q-icon name="eva-close-outline" />
</div>
</div>
</div>
<div class="btn right" v-ripple v-if="rightBtn.active">
<q-icon name="eva-arrow-ios-forward-outline" />
</div>
<div class="btn left" v-ripple v-if="leftBtn.active">
<q-icon name="eva-arrow-ios-back-outline" />
</div>
<div class="dialog-content">
<component :is="requiredModule" />
</div>
</div>
</template>
Логика
<script>
import DialogAbout from "components/dialog/DialogAbout";
import DialogContacts from "components/dialog/DialogContacts";
import { defineComponent, computed } from "vue";
import { useRoute } from "vue-router";
import { useStore } from "vuex";
export default defineComponent({
name: "TheDialog",
setup() {
const $store = useStore();
const $route = useRoute();
const requiredModule = computed(() => {
const dialog = $route.query.dialog;
if (dialog === "about") return DialogAbout;
if (dialog === "contacts") return DialogContacts;
return false;
});
const backBtn = $store.state.dialog.backBtn;
const downloadBtn = $store.state.dialog.downloadBtn;
const leftBtn = $store.state.dialog.leftBtn;
const rightBtn = $store.state.dialog.rightBtn;
return {
requiredModule,
backBtn,
downloadBtn,
leftBtn,
rightBtn,
};
},
});
</script>
Здесь нет ничего сложного, просто из функции setup мы возвращаем нужные нам состояния для отображения кнопок и результат computed функции.
Добавим немного стилей
<style lang="scss" scoped>
.dialog-bg {
width: 100%;
min-height: 100vh;
background-color: rgba(30, 30, 30, 0.9);
position: fixed;
left: 0;
top: 0;
z-index: 10;
padding: 100px 0;
.btn {
width: 80px;
height: 80px;
color: rgba(255, 255, 255, 0.637);
font-size: 1.8rem;
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
position: relative;
transition: background-color 0.2s;
&:hover {
background-color: rgba(0, 0, 0, 0.2);
}
}
.dialog-control-panel {
width: 100%;
display: flex;
justify-content: space-between;
position: fixed;
top: 0;
left: 0;
.panel {
display: flex;
}
}
.right,
.left {
position: fixed;
height: calc(100vh - 160px);
top: 80px;
}
.right {
right: 0;
}
.left {
left: 0;
}
.dialog-content {
max-width: 732px;
margin: auto;
}
}
</style>
Результат
Все! Наше диалоговое окно готово.
Комментарии (8)
vovchisko
04.12.2021 17:25+1С телепортом я бы пересмотрел подход: https://v3.ru.vuejs.org/ru/guide/teleport.html
Идеальное окно... все через это проходят ????levnikrot Автор
04.12.2021 18:12Конечно, можно было бы реализовать все это через teleport, но описанное решение в этой статье мне нравиться больше. Там нет ничего лишнего, все находиться в одном месте, добавлять новый контент довольно просто + легко вызывать окно, для этого нужно всего лишь изменить query параметр dialog.
js_n00b
04.12.2021 19:09+2Мне понравилась идея. Только небольшое замечание по семантике.
Диалог подразумевает обратную связь с пользователем, то что реализовано в статье просто модальное окно.
darkxanter
05.12.2021 09:46+5Ожидал увидеть что-то подобное как в Quasar:
const $q = useQuasar() $q.dialog({ component: CustomComponent, // props forwarded to your custom component componentProps: { text: 'something', // ...more..props... } }).onOk(() => { console.log('OK') }).onCancel(() => { console.log('Cancel') }).onDismiss(() => { console.log('Called on OK or Cancel') })
А оказались просто модальные окна.
korsetlr473
автор вкурсе про новую вичу vue3 телепорт? )))
подгружаемся дальше
автор вкурсе про новую вичу html summary? где не нужно будет js вообще )))