Знакомство с админкой

Сегодня мы начинаем знакомиться с админкой нашего приложения. Напоминаю, это точно такое же приложение, как и site, только не в минимальной комплектации, а со всеми нужными модулями по умолчанию.

Работает оно с помощью компонентов Booststrap-Vue, и и к его документации мы будем обращаться очень часто. Для оформления внешнего вида мы подключаем стили из Bootstrap.

Первом делом мы видим форму авторизации, которая работает при помощи модуля @nuxtjs/auth. Все модули подключаются в frontend/src/admin/nuxt.config.js, добавлением в Config.modules или Config.buildModules - это зависит от самого модуля и авторы пишут куда именно их нужно добавлять.

Этот модуль добавляет нам в приложение объект $auth, к которому можно обращаться из любого компонента. Если посмотреть в основной шаблон админки admin/layouts/default.vue, то вы увидите как именно выводится форма авторизации:

Логика авторизации

<template>
  <div class="min-vh-100 d-flex flex-column">
    <app-navbar />
    <b-container class="flex-grow-1">
      <-- вот наш компонент авторизации -->
      <app-login />
      <-- содержимое страницы выводится только авторизованным юзерам -->
      <nuxt v-if="$auth.loggedIn" class="pt-5" />
    </b-container>
    <app-footer />
  </div>
</template>

Шаблон проверяет статус пользователя через $auth.loggedIn и форма авторизации всегда наготове - как только статус пользователя изменится, она либо выведется, либо наоборот, спрячется.

Саму форму можно найти в файле frontend/src/admin/components/app/login.vue. Я не буду приводить её полностью, просто прокомментирую несколько моментов.

Во-первых, она выводится с помощью модального компонента Bootstrap-Vue:

<b-modal
    :visible="!$auth.loggedIn" // Показывать только гостям
    :title="$t('security.login')" // Заголовок берётся из лексиконов (про них позже)
    no-close-on-backdrop // Не закрывать при клике на фон
    no-close-on-esc    // Не закрывать при нажатии на Esc
    hide-header-close // Не выводить вообще кнопку закрытия
>
// ...
</b-modal>

Таким образом, форма всплывает в модалке, которую можно закрыть только одним способом - авторизоваться, после чего $auth.loggedIn станет true и окошко скроется.

Дальше смотрим метод submit:

// Отправка формы на сервер
async submit() {
  // при начале авторизации выставляем переменную, которую можно 
  // использовать для блокировки формы
  this.loading = true
  try {
    // Вызываем метод авторизации из $auth с данными формы
    await this.$auth.loginWith('local', {data: this.login})
    // Если всё хорошо - вывод сообщения об успехе
    await this.$toast.info(String(this.$t('security.greetings')))
    // Очистка формы
    this.onReset()
  } catch (err) {
    // Если ошибка - пишем в консоль
    console.error(err)
  } finally {
    // Но в любом случае после запроса убираем флаг загрузки
    this.loading = false
  }
},
// Обнудение полей формы авторизации
onReset() {
  this.login = {
    username: '',
    password: '',
  }
},

Собственно, вся авторизация заключается в магическом await this.$auth.loginWith('local', {data: this.login}), который отправит запрос на api/security/login, потому что так прописано в настройках Vesp. В ответ приходит токен, он сохраняется в локальное хранилище браузера и куки, а затем используется для всех запросов в API то тех пор, пока пользователь не разлогинится.

Первый же запрос после авторизации улетает в api/user/profile и возвращает данные пользователя, которые сохраняются в this.$auth.user.

Всё, что мы вернём в контроллере App\Controllers\User\Profile и будет сохранено в приложение, откуда потом можно будет получать, например, username:

if (this.$auth.loggedIn) {
    console.log(this.$auth.user.username)
} else {
    console.error('No user!')
}

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

Выход из приложения можно делать в любом месте просто вызывая this.$auth.logout(). Это не только удалит токен из приложения, но и отправит запрос в api/security/logout, этот контроллер мы уже тоже рассматривали.

Разделы админки

Как вы помните, структура приложения формируется по директории pages, об этом можно почитать в документации Nuxt.

Адреса в нашей админке выглядят вот так:

user/
    profile.vue - изменение профиля текущего юзера, /admin/user/profile
users/ 
    edit/
        _id.vue - редактирование юзера, /admin/users/edit/id-юзера
    roles/ 
        edit/
            _id.vue - редактирование группы юзеров, /admin/users/roles/edit/id-роли
        create.vue - создание группы юзера, /admin/users/roles/create
    create.vue - создание нового юзера, /admin/users/create
    roles.vue - просмотр всех групп юзеров, /admin/users/roles
index.vue - корневая страница админки с главным меню, /admin/
users.vue  - просмотр всех пользователей, /admin/users

Таким образом, просто глядя на строку адреса в админке, вы уже знаете, какая страница за него отвечает.

Рекомендую вам сразу установить Vue DevTools для своего браузера, это очень облегчит понимание структуры приложения.

Минутка практики

Запускаем приложение для разработки: cd frontend && yarn dev:admin

Затем добавляем новую страницу test.vue в корне:

<template>
    <div>Hello, world!</div>
</template>

<script>
export default {
    name: 'TestPage',
}
</script>

И переходим по адресу /admin/test - новая страница уже на месте.

Vue DevTools услужливо показывает нам всю структуру приложения, включая TestPage внутри блока Nuxt.

Страница есть и работает, но она не появилась ни в меню, ни на главной странице. Для этого нам нужно добавить её в src/admin/plugins/menu.js - там перечислены все разделы админки в очень простом формате.

Приводим файл к такому виду:

export default [
   // наш новый раздел
  {
    name: 'test', // имя маршрута, его можно взять в DevTools
    title: 'Test', // название для вывода в меню
    scope: null, // Требуемое разрешение, мы ничего не требуем
  },
  {
    name: 'users',
    // ... оставляем что там есть по умолчанию
  },
]

И теперь всё в порядке: страница появилась и в меню, и на главной. Причём она правильно выделяется в меню как активная, когда мы переходим по адресу.

При этом в консоли можно увидеть предупреждения от системы лексиконов, но их мы будем проходить позже.

Заключение

Как вы видите, создание новых разделов админки заключается в простом создании новой страницы в директории pages.

Файл plugins/menu.js используется для перечисления разделов с указанием требуемых прав и названий - это даёт нам возможность располагать пункты меню в произвольном порядке.

На следующем уроке будем разбирать основные принципы работы страниц: проверку прав, загрузку данных из API, и передачу динамических параметров, типа id редактируемого пользователя.

← Предыдущая заметка
Знакомимся с фронтендом
Следующая заметка →
Компоненты админки: vesp-table
Комментарии (13)
bezumkinВасилий Наумкин
10.06.2022 04:44

Обалдеть!

bezumkinВасилий Наумкин
12.06.2022 05:13

Работать на Windows - путь отважных =)

bezumkinВасилий Наумкин
19.06.2022 16:42

А можно же из 1 файла сделать 2 экспорта. По-умолчанию, и отдельно для футера:

export const Footer = {
    // ...
}

export default {
    //...
}

А дальше в app-footer.vue делаешь import {Footer} from '../../plugins/menu.js'.

Если нужна будет проверка прав, то придётся еще с Vuex похимичить, добавить новый getter по образу и подобию основного меню, но мы его пока не проходили.

bezumkinВасилий Наумкин
03.07.2022 14:27

dev - это режим разработки, когда при каждом изменении исходных файлов код пересобирается и обновляется у тебя в браузере. Он никак не оптимизирован, не минифицирован, и вообще не предназначен для реальной работы на хостинге.

А вот на сайте vesp-shop.test уже готовый билд, который можно выгружать в продакшн. Он собирается после разработки отдельной командой - generate.

То есть, по сути, это 2 разных этапа одного процесса разработки сайта. Думаю, запуск проекта на хостинге у нас будет последнем уроком на курсе.

bezumkinВасилий Наумкин
24.08.2023 10:09

Если я тебя правильно понял, то это встроенный индикатор.

Вот здесь документация - https://v2.nuxt.com/docs/configuration-glossary/configuration-loading-indicator/

bezumkin
Василий Наумкин
09.04.2024 01:45
Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. Во...
futuris
Futuris
04.04.2024 05:56
Я просто немного запутался. Когда в абзаце &quot;Vesp/Core&quot; ты пишешь про &quot;новый trait Fil...
bezumkin
Василий Наумкин
20.03.2024 18:21
Volledig!
Андрей
14.03.2024 10:47
Василий! Как всегда очень круто! Моё почтение!
russelgal
russel gal
09.03.2024 17:17
А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал ...
inetlover
Александр Наумов
27.01.2024 00:06
Василий, спасибо! Извини, тупанул.
bezumkin
Василий Наумкин
22.01.2024 04:43
Давай-давай!
bezumkin
Василий Наумкин
24.12.2023 11:26
Спасибо!
bezumkin
Василий Наумкин
27.11.2023 02:43
Ура!
bezumkin
Василий Наумкин
25.11.2023 08:30
Vesp тянет 2 зависимости: vesp-frontent для фронта и vesp-core для бэкенда. Их можно обновлять, но э...