Вложенные диалоги

В библиотеке Element Plus диалоги реализуются с помощью компонента <el-dialog>, который предоставляет богатый функционал для создания модальных окон. Часто возникает необходимость организовать вложенные диалоги, когда один диалог открывает другой. В таких случаях важно правильно управлять состояниями и событиями, чтобы избежать конфликтов и обеспечить корректное поведение интерфейса.


Создание базового диалога

Для начала рассмотрим структуру простого диалога:

<template>
  <el-dialog
    title="Основной диалог"
    :visible.sync="mainDialogVisible"
    width="50%">
    <p>Содержимое основного диалога</p>
    <span slot="footer" class="dialog-footer">
      <el-button @click="mainDialogVisible = false">Закрыть</el-button>
      <el-button type="primary" @click="nestedDialogVisible = true">Открыть вложенный</el-button>
    </span>
  </el-dialog>

  <el-dialog
    title="Вложенный диалог"
    :visible.sync="nestedDialogVisible"
    width="30%">
    <p>Содержимое вложенного диалога</p>
    <span slot="footer" class="dialog-footer">
      <el-button @click="nestedDialogVisible = false">Закрыть</el-button>
    </span>
  </el-dialog>
</template>

<script setup>
import { ref } from 'vue'

const mainDialogVisible = ref(false)
const nestedDialogVisible = ref(false)
</script>

Ключевые моменты:

  • Используется :visible.sync, чтобы диалог автоматически реагировал на изменения состояния переменной.
  • Вложенный диалог может открываться по клику из основного.
  • Каждый диалог имеет собственное состояние видимости (ref).

Управление фокусом и перекрытием

При использовании нескольких диалогов важно учитывать:

  • Фокусировка на верхнем диалоге: Element Plus автоматически перемещает фокус на активный диалог, но при сложных вложениях может понадобиться вручную управлять autofocus.
  • Перекрытие слоев: Диалоги имеют z-index, который увеличивается с каждым новым открытием. Вложенные диалоги всегда отображаются поверх родительских.
<el-dialog
  title="Вложенный диалог"
  :visible.sync="nestedDialogVisible"
  :modal-append-to-body="false">
</el-dialog>
  • Атрибут modal-append-to-body="false" позволяет вложенному диалогу находиться внутри родительского контейнера, что удобно для сложных макетов и анимаций.

Передача данных между диалогами

Часто требуется, чтобы вложенный диалог передавал данные обратно в родительский. Используются реактивные объекты:

<script setup>
import { reactive } from 'vue'

const formData = reactive({
  name: '',
  age: null
})

const mainDialogVisible = ref(false)
const nestedDialogVisible = ref(false)

function saveNestedData(data) {
  formData.name = data.name
  formData.age = data.age
  nestedDialogVisible.value = false
}
</script>

<template>
  <el-dialog
    title="Основной диалог"
    :visible.sync="mainDialogVisible">
    <p>Имя: {{ formData.name }}, Возраст: {{ formData.age }}</p>
    <el-button @click="nestedDialogVisible = true">Редактировать</el-button>
  </el-dialog>

  <el-dialog
    title="Вложенный диалог"
    :visible.sync="nestedDialogVisible">
    <el-input v-model="formData.name" placeholder="Введите имя"></el-input>
    <el-input-number v-model="formData.age" placeholder="Введите возраст"></el-input-number>
    <el-button type="primary" @click="saveNestedData(formData)">Сохранить</el-button>
  </el-dialog>
</template>
  • Использование реактивного объекта formData обеспечивает синхронизацию данных между диалогами без лишних событий.
  • Функция saveNestedData закрывает вложенный диалог после сохранения.

Работа с событиями диалогов

Element Plus предоставляет события:

  • open – срабатывает при открытии диалога.
  • opened – после завершения анимации открытия.
  • close – перед закрытием диалога.
  • closed – после завершения анимации закрытия.

Пример управления закрытием:

<el-dialog
  title="Основной диалог"
  :visible.sync="mainDialogVisible"
  @close="handleMainClose">
</el-dialog>

<script setup>
function handleMainClose() {
  if (nestedDialogVisible.value) {
    nestedDialogVisible.value = false
  }
}
</script>
  • При закрытии родительского диалога автоматически закрывается вложенный.
  • Это предотвращает «висячие» модальные окна и улучшает UX.

Вложенные диалоги с формами и валидацией

Для сложных интерфейсов часто используются формы внутри диалогов:

<el-dialog
  title="Форма в основном диалоге"
  :visible.sync="mainDialogVisible">
  <el-form :model="formData" ref="mainForm">
    <el-form-item label="Имя" prop="name">
      <el-input v-model="formData.name"></el-input>
    </el-form-item>
    <el-form-item label="Возраст" prop="age">
      <el-input-number v-model="formData.age"></el-input-number>
    </el-form-item>
  </el-form>
  <el-button @click="nestedDialogVisible = true">Открыть вложенный</el-button>
</el-dialog>

<el-dialog
  title="Редактирование имени"
  :visible.sync="nestedDialogVisible">
  <el-form :model="formData" ref="nestedForm">
    <el-form-item label="Имя" prop="name">
      <el-input v-model="formData.name"></el-input>
    </el-form-item>
  </el-form>
  <el-button type="primary" @click="nestedForm.validate(valid => valid && nestedDialogVisible = false)">Сохранить</el-button>
</el-dialog>
  • Каждая форма имеет собственный ref для валидации.
  • Вложенный диалог может проверять данные перед закрытием.

Рекомендации по производительности

  1. Не рендерить диалоги, которые редко используются, через v-if вместо v-show.
  2. Ограничить количество вложенных диалогов до 2–3 уровней, чтобы избежать путаницы с z-index и событиями.
  3. Для больших форм использовать ленивую загрузку компонентов диалога через <Suspense> или динамический import().

Стилизация вложенных диалогов

  • Используются стандартные пропсы width, top, custom-class.
  • Для вложенного диалога можно задать отдельный класс:
<el-dialog
  title="Вложенный диалог"
  :visible.sync="nestedDialogVisible"
  custom-class="nested-dialog">
</el-dialog>

<style>
.nested-dialog .el-dialog__header {
  background-color: #f5f7fa;
}
.nested-dialog .el-dialog__body {
  font-size: 14px;
}
</style>
  • Это позволяет визуально отличать уровни модальных окон и улучшает восприятие интерфейса.

Эта структура и подходы позволяют создавать глубокие, управляемые и реактивные вложенные диалоги в Element Plus, обеспечивая удобство работы с данными, событиями и визуальными эффектами.