Знакомство с админкой
Сегодня мы начинаем знакомиться с админкой нашего приложения. Напоминаю, это точно такое же приложение, как и 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 редактируемого пользователя.
0
👍
👎
❤️
🔥
😮
😢
😀
😡
639
09.06.2022, 14:49:40
13 комментариев
NightRider
09.06.2022, 21:55:08
Работает! Фантастика!
Василий Наумкин
10.06.2022, 07:44:30
Обалдеть!
Futuris
28.04.2023, 17:37:20
Не то слово!
Денис
11.06.2022, 19:34:12
Да, и правда фантастика :)
В Windows столкнулся с удивительным моментом, перенос строки обозначается как CRLF, а на Mac & Unix LF и Prettier на это ругается, я использовал Sublime Text и пришлось настраивать конфиг (может пригодится)
- в самой программе View → Line endings → Unix
Василий Наумкин
12.06.2022, 08:13:04
Работать на Windows - путь отважных =)
Вася
19.06.2022, 18:26:29
Сразу стало интересно как для футера сделать свое меню, а для шапки другое
Василий Наумкин
19.06.2022, 19:42:57
А можно же из 1 файла сделать 2 экспорта. По-умолчанию, и отдельно для футера:
А дальше в app-footer.vue делаешь import {Footer} from '../../plugins/menu.js'.
Если нужна будет проверка прав, то придётся еще с Vuex похимичить, добавить новый getter по образу и подобию основного меню, но мы его пока не проходили.
Александр Наумов
02.07.2022, 22:19:07
Василий, а почему, что бы все заработало из этого урока (test.vue) нужно запускать cd frontend && yarn dev:admin , а сайт http://vesp-shop.test/ который под Nginx никаких изменений не видит?
Василий Наумкин
03.07.2022, 17:27:20
dev - это режим разработки, когда при каждом изменении исходных файлов код пересобирается и обновляется у тебя в браузере. Он никак не оптимизирован, не минифицирован, и вообще не предназначен для реальной работы на хостинге.
А вот на сайте vesp-shop.test уже готовый билд, который можно выгружать в продакшн. Он собирается после разработки отдельной командой - generate.
То есть, по сути, это 2 разных этапа одного процесса разработки сайта. Думаю, запуск проекта на хостинге у нас будет последнем уроком на курсе.
Александр Наумов
04.07.2022, 02:36:23
Василий, спасибо!
Все понятно!
Александр Наумов
24.08.2023, 11:53:05
Василий, добрый день!
Подскажи, пожалуйста, где запускается прелоадер в виде колеса перед появлением модульного окна. Что то не могу понять, это NuxtLoadingIndicator скрещенный с спиннером от BootstrapVue или ты свое, что-то сделал?
Василий Наумкин
24.08.2023, 13:09:39
Если я тебя правильно понял, то это встроенный индикатор.
Вот здесь документация - https://v2.nuxt.com/docs/configuration-glossary/configuration-loading-indicator/
Александр Наумов
24.08.2023, 13:49:05
Спасибо!!
bezumkin.ru
Личный сайт Василия Наумкина
Прямой эфир
Василий Наумкин
01.07.2024, 11:56:41
Да, верно, именно так.
А в контроллере, скорее всего, ловить данные методом post.
Василий Наумкин
26.06.2024, 09:38:15
О, точно, вылезает если не залогинен.
Спасибо, исправил!
Василий Наумкин
09.04.2024, 04:45:01
> Ошибка 500
Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи.
...
russel gal
09.03.2024, 20:17:18
> А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал...
Александр Наумов
27.01.2024, 03:06:18
Василий, спасибо!
Извини, тупанул.