Завершение курса + всякие полезности

Ну вот наш курс и подошёл к завершению.
Надеюсь, вам было интересно поработать с Vesp и вы узнали для себя что-то новое и даже интересное.
В этой заключительной заметке я хочу сделать краткий обзор PHP и JS библиотек, которые я использую в своих проектах. Возможно, они сэкономят вам время при разработке.
Список состоит только из решений, которые я лично использую постоянно, практически в каждом проекте. Он выкристаллизовался за довольно большой промежуток времени и почти не меняется.

PHP

Начнём с box/spout - это очень шустрая библиотека для работы с XLSX, ODS и CSV. Я наткнулся на неё, когда искал экономичное решение для экспорта больших объёмов информации, и она с ними отлично справилась.
У меня есть готовые решения по экспорту для контроллеров, постараюсь позже поделиться уже за рамками курса. Стоит отметить, что проект больше не развивается, так что если вам не хватит функционала - нужно искать другую либу, тут обновлений не будет.
ramsey/uuid я использую для удобной генерации uuid моделей, когда мне не нужно, чтобы пользователи могли перебирать данные из контроллеров по id. например, на bezumkin.ru все картинки выводятся именно по uuid, подбором ничего не открыть.
Обратите внимание, что uuid бывает нескольких видов, если вам нужен именно случайный uuid - то это 4я версия.
peppeocchi/php-cron-scheduler уже используется в Vesp по умолчанию, но я про него ничего не рассказывал. Там всё просто - прописываете в crontab сервера файл core/cli/cron.php и добавляете скрипты для выполнения, рядом лежит пример для удаления старых сессий.
phpmailer/phpmailer для отправки почты, вместе с pelago/emogrifier, который разбирает стили письма из шапки и вставляет в теги, чтобы все почтовые клиенты их нормально понимали.
А для шаблонов писем я использую, понятное дело fenom/fenom.
Если будет интерес, могу написать подробнее про подключение подобных библиотек к проекту.
Еще вам может понадобиться установить doctrine/dbal, если вы будете писать миграции, которые меняют тип колонок в таблицах методом ->change(), например со string на text. У меня такое иногда случается.
Этого набора библиотек мне хватает для реализации 90% проектов. В остальных случаях нужно подключить какой-нибудь сервис для оплаты или что-то подобное. Но всё это делается по одной схеме - ищем подходящую библиотеку, читаем документацию, подключаем.

Javascript

При выборе JS решения для Vesp я советую руководствоваться следующими правилами:
  • в первую очередь проверяем официальные модули для Nuxt
  • если ничего нет, ищем нужное на npmjs
  • предпочтение отдаём модулям Nuxt, а затем интеграциям с Vue
  • если совсем ничего нет - берём решения написанные на чистом JS, по возможности без зависимостей
  • И никакого jQuery!
vue-meta уже интегрирован в Nuxt и доступен на всех страницах для выставления title и прочей работы с мета-тегами. Именно этот модуль мы используем в админке для указания названий страниц.
Я не уделял на курсе внимания SEO оптимизации, но думаю вы и сами легко с этим разберётесь, потому что модуль позволяет всё настроить в лучшем виде - просто читайте документацию.
@nuxtjs/sitemap - генерация карты сайта. Отлично работает как со статическими сайтами, так и с динамическими маршрутами на тысячи страниц. Просто пишете функцию с запросом в API для получения этих страниц, и модуль генерирует по ним карту.
Пример с bezumkin.ru:
Config.sitemap = {
  hostname: env.SITE_URL,
  gzip: true,
  exclude: ['/admin/**', '/user/**', '/service/**'],
  routes: async () => {
    const routes = []
    const [{data: sections}, {data: topics}] = await Promise.all([
      axios.get(env.API_URL + 'web/sections', {params: {sort: 'views_count', dir: 'desc'}}),
      axios.get(env.API_URL + 'web/topics', {params: {sort: 'id', dir: 'desc'}}),
    ])

    sections.rows.forEach((section) => {
      routes.push({
        url: '/sections/' + section.alias,
        lastmod: section.updated_at || section.created_at,
      })
    })
    topics.rows.forEach((topic) => {
      routes.push({
        url: (topic.section.lessons ? '/lessons/' : '/sections/') + topic.section.alias + '/' + topic.id,
        lastmod: topic.updated_at || topic.created_at,
      })
    })

    return routes
  },
}
@nuxtjs/pwa поможет вам превратить ваш сайт в PWA, который можно устанавливать на телефон. Всё работает хорошо по умолчанию, нужно только указать свою иконку. Этот модуль уже подключен по умолчанию в Vesp.
@nuxtjs/svg позволяет легко импортировать SVG файлы в любом виде, хоть голым текстом, хоть компонентом для вставки в шаблон. Просто посмотрите примеры в документации, это крайне полезный модуль для вёрстки.
@nuxtjs/markdownit позволит вам удобно загружать на страницы какие-то статичные документы, вроде пользовательского соглашения в формате Markdown. Понятное дело, работать можно не только с файлами, а рендерить вообще любой markdown в html через функцию $md.render().
vuedraggable вы уже знаете, просто напоминаю, что он позволяет легко сортировать перетаскиванием любые данные в ваших компонентах.
glightbox я использую для вывода увеличенных картинок при клике, например прямо здесь - на bezumkin.ru.
Напоследок упомяну еще 2 библиотеки, которые уже подключены в Vesp, но со ссылками на их интеграцию в проект, возможно будет интересно почитать.
bootstrap-vue/nuxt - лучшая интеграция Bootstrap 4 с 85+ компонентами на все случаи жизни. Лично я очень ценю возможность самостоятельного подключения и модификации Bootstrap в стили проекта, с импортом только нужного, что облегчает размер собранного проекта.
Там есть и ленивая загрузка картинок, и прогресс-бары и еще много чего еще - просто внимательно посмотрите документацию, она очень подробная.
К сожалению, на Vue 3 и Bootstrap 5 они переходить не торопятся, поэтому я уже присматриваю другие решения на будущее.
@fortawesome/vue-fontawesome - мои любимые иконки, которые постоянно развиваются и обновляются. В последнее время, наверное, даже слишком бурно, что иногда приводит к небольшим проблемам. В любом случае, богаче набора иконок так удобно интегрированных в Nuxt найти не получится.

Заключение

На этом у меня пока всё, надеюсь информации этого курса вам будет достаточно, чтобы погрузиться в Vesp и понять для себя, нравится ли вам логика его работы и стоит ли уделять время изучению используемых технологий.
Лично я уже не могу представить себе работу с MODX или другими системами, которые рендерят HTML на сервере, без прогрессивных фронтенд фреймворков.
Благодаря отделению бэкенда от фронтенда мои руки стали полностью развязаны. Не нужно думать о том, какие данные пришли от юзера, где может быть попытка взлома, какие переменные доступны в шаблоне, а какие нет. Просто считаем все запросы и ответы в API публичными - то есть, от хакера, и обрабатываем соответственно.
Завершение курса означает только окончание написания новых заметок, но я продолжаю отвечать на ваши вопросы в комментариях!
Проект VespShop скорее всего будет понемногу развиваться и после курса, если мне будут заказывать создание интернет-магазинов и при этом процессе получится разрабатывать какие-то универсальные штуки для общего пользования.
Буду очень рад вашим отзывам о курсе в комментариях. Что понравилось, что не очень, о чём еще нужно написать в будущем за рамками курса.

59 комментариев

В курсе все достаточно разложено по полочкам и доходчиво объяснено, в этом смысле все хорошо. Спасибо!
Что хотелось бы рассмотреть в дальнейшем - построение чего-либо древовидного, например комменты или многоуровневое меню. И возможно я прошу многого, но хотелось бы понимать как пишутся умные фильтры, по типу mSearch2, подробно не надо, но хотелось бы понимать общий принцип. В интернете много посвящено статей обычным фильтрам, а вот по умным как-то вообще ничего не находил.
Ну а так, я думаю все что касается обычной разработки сайтов и что не было рассмотрено тут, так или иначе будет интересно.
Василий Наумкин
Насчёт древовидного - это всё делается через рекурсивный вызов одного компонента, никакой уникальности именно для Vesp нет. Принцип ровно тот же, что и в MODX, да и вообще в любой системе.
Например, комментарии на bezumkin.ru:
  • базовый компонент CommentsTree получает комменты в "плоском" виде, где есть id и parent_id
  • строит по этому parent_id дерево комментариев
  • вызывает в своём шаблоне форму редактирования и компонент оформления ветки CommentsThread с передачей ему комментариев
  • тот оформляет первый уровень комментариев, и смотрит, есть ли у них потомки.
  • если есть, перебирает их и внутри вызывает сам себя, то есть, делает рекурсию
CommentsTree:
<template>
  <div id="topic-comments" ...>
    <comments-thread
      :comments="commentsTree"
      :topic="topic"
      ...
    />

    <comments-form v-if="..." />
  </div>
</template>
CommentsThread:
<template>
  <div class="comments-thread">
    <div v-for="comment in comments" ...>
      ...

      <comments-form v-if="showForm(comment)" ... />
      <comments-thread
        v-if="comment.children.length"
        :comments="comment.children"
        :topic="topic"
        ...
      />
    </div>
  </div>
</template>
Нужно будет прописать логику вывода формы комментирования в нужном месте, и методы сохранения, редактирования и удаления комментриев. Лично я всё это держу в родительском компоненте CommentsTree, а действия из вложенных CommentsThread передаю событиями this.$emit('action', data)
Примерно такая же логка будет и с любыми другими рекурсивными данными, вроде вложенного меню.
И возможно я прошу многого, но хотелось бы понимать как пишутся умные фильтры, по типу mSearch2
Постараюсь написать про это заметку, как будет время.
Ну да, вроде как понятнее. В остальных системах принцип понятен, тут больше возникал вопрос реализации этого на vue, так как с ним редко доводилось работать. Надо пробовать. Спасибо!
Дмитрий П.
А подскажите пожалуйста, как для @nuxtjs/sitemap в конфиге правильно импортировать axios?
Василий Наумкин
Всё как обычно, в самом начале файла конфига
import axios from 'axios'
Александр Наумов
Буду очень рад вашим отзывам о курсе в комментариях.
Курс замечательный, проделана колоссальная работа, узнал много новых технологий, спасибо Василию за труды и то, что он делится ценным опытом, всем рекомендую!!!
о чём еще нужно написать в будущем за рамками курса.
Хотелось бы статейку про правильную интеграцию JS в VESP.
  1. Интеграция модулей Nuxt.
  2. Интеграция модуля Vue.
  3. Интеграция ванильного JS.
Понятно, что во всем можно разобраться самому, но хочется здесь каких-то рекомендаций, чтобы в будущем было больше порядка.
Занимаясь этим вопросом наткнулся на статью: Как правильно зарегистрировать плагин в nuxt.js . То есть может быть проблемой правильного добавление плагина Vue в Nuxt.
Да и по ванильному JS, хочется каких-то рекомендаций, например в каком случае js код нужно переделывать в Vue плагин.
Василий Наумкин
Курс замечательный
Ура!
Хотелось бы статейку про правильную интеграцию JS в VESP.
Модули Nuxt ставятся парой строк, там вообще никаких вопросов нет. Большинство популярных компонентов Vue про Nuxt или в курсе, или имеют issues про это на Github.
А вот модули JS надо смотреть в каждом конкретном случае. Проблемы обычно бывают если JS рассчитывает на работу в браузере, а ты его используешь при серверном рендере, но это довольно легко решается.
В общем, давай так - как будет конкретный вопрос по подключению чего-либо, просто спроси тут, я отвечу.
Александр Наумов
Ок, спасибо!
Александр Наумов
Василий добрый день!
Не подскажешь в чем может быть проблема с установкой Nuxt плагина в Vesp?
Устанавливаю плагин на голую сборку Nuxt - все работает, делаю все тоже самое на Vesp вижу такую заглушку.
В консоли ошибок нет.
Думаю, может в nuxt.config.js плагин как-то не так подключаю. Подключил так:
// export default Config
export default {
 plugins: [
   {
     src: '@/src/site/plugins/menu',
   },
 ],
}
Василий Наумкин
Vesp расширяет и возвращает конфиг из @vesp/frontend. То есть, должно быть так:
import {Config, findEnv, loadEnv} from '@vesp/frontend'

// ... всякие другие настройки

Config.plugins = ['@/src/site/plugins/menu']

// ...

return Config
А если ты делаешь export default {...}, то просто игнорируешь все настройки по умолчанию и возвращаешь только 1 плагин, без остальных настроек - и тогда система не может даже найти директорию с исходниками, потому и просить её создать.
Александр Наумов
Супер! Спасибо, заработало!!
Александр Наумов
Василий, добрый день!
У нас есть файлы scss, только не пойму, как они обрабатываются, ведь node-sass, sass-loader не установлены?
Василий Наумкин
Очень даже установлены - просто их тянет зависимостями сам Vesp.
Александр Наумов
Очень даже установлены - просто их тянет зависимостями сам Vesp.
Круто! Спасибо.
А для чего нужна папка vendor?
vesp-shop.test/core/vendor
Василий Наумкин
А там PHP зависимости от Composer!
Александр Наумов
Спасибо, понял!!
Василий Наумкин
Только сейчас заметил, что у тебя путь от корня сайта.
Такого быть не должно, директория core находится на одном уровне с www - корнем сайта. Ты видимо случайно в ней composer как-то запустил или что-то такое.
Вот правильная www - там только api.php и никаких директорий.
Александр Наумов
Василий, спасибо! Это у меня так папка называется vesp-shop.test в которой на одном уровне папки www и core, а в core находится папка vendor.
Василий Наумкин
А, тогда все верно!
Александр Наумов
Василий, добрый день!
Обратил внимание, что на странице http://s30069.h10.modhost.pro/category-81/product-1 дважды идёт ссылка на один и тот же скрипт.
В начале: <link rel="preload" href="/_nuxt/62f66e0.js" as="script">
В конце: <script src="/_nuxt/62f66e0.js" defer></script>
Можешь объяснить в чем фишка?
Василий Наумкин
Это 2 разных тега:
Несмотря на 2 тега, физически файл грузится только 1 раз.
Стоит отметить, что мы этими вопросами вообще не заведуем и не управляем - Nuxt сам собирает файлы, минимизирует и подключает оптимальным способом.
Александр Наумов
Василий, спасибо большое, за разъяснение!
Скажи пожалуйста, на сколько сложно и с какими трудностями можно столкнуться, если на Vesp натянуть вот такой шаблон https://pixinvent.com/demo/vuexy-vuejs-admin-dashboard-template/landing/ ?
Василий Наумкин
Не знаю, судя по Composition API оно вообще для Vue 3. Да и Laravel там, наверное, не просто так.
Ты лучше скажи, зачем оно вообще тебе нужно?
Это же админка, чтобы что-то админить - а Vesp по умолчанию админить нечего, кроме юзеров. Всё нужно писать, а в процессе написания заодно и админку сделаешь на Bootstrap.
В общем, я ни разу ничего подобного не использовал и зачем оно нужно не в курсе.
Александр Наумов
Василий, спасибо!
1. Я изучал Bootstrap, когда он только появился, но использовать его не стал, так как тогда он тянул все стили своей библиотеки даже те которые я не использовал на сайте. Сейчас я вижу, что в VESP можно конфигурировать в файле nuxt.config.js, подключать только те модули из Bootstrap которые будут использоваться, а вся библиотека Bootstrap не тянется?
Я делаю малостраничные сайты на JS и Webpack, а в верстке использую методологию БЭМ. Vue и Nuxt стал изучать с появлением твоего курса по Vesp.
2. Вопрос: Как я понял главная проблема установки плагинов Vue под Nuxt это SSR, а если Nuxt будет работать в SPA режиме, то такой проблемы не будет и можно с минимальными изменениями использовать плагины Vue в Nuxt?
Шаблон, ссылку на который я дал выше можно скачать в ознакомительных целях, набрав в поиске vuexy download. Хочу подсмотреть там идеи в плане UI/UX дизайна, ведь они тоже используют bootstrap-vue, только там Vue без Nuxt. Там есть версия под Vue 3, но так же есть и под Vue 2.
Вот список плагинов которые они используют:
  "dependencies": {
    "@casl/ability": "4.1.6",
    "@casl/vue": "1.1.1",
    "@fullcalendar/common": "5.x",
    "@fullcalendar/core": "5.x",
    "@fullcalendar/daygrid": "5.x",
    "@fullcalendar/interaction": "5.x",
    "@fullcalendar/list": "5.x",
    "@fullcalendar/timegrid": "5.x",
    "@fullcalendar/vue": "5.x",
    "@vue/composition-api": "1.3.0",
    "@vueuse/core": "4.0.0",
    "animate.css": "4.1.1",
    "apexcharts": "3.23.0",
    "axios": "0.21.1",
    "axios-mock-adapter": "1.19.0",
    "bootstrap": "4.6.0",
    "bootstrap-vue": "2.21.1",
    "chart.js": "2.9.4",
    "core-js": "3.8.1",
    "echarts": "4.8.0",
    "jsonwebtoken": "8.5.1",
    "leaflet": "1.6.0",
    "portal-vue": "2.1.7",
    "postcss-rtl": "1.7.3",
    "prismjs": "1.19.0",
    "swiper": "5.4.5",
    "uuid": "8.3.2",
    "vee-validate": "3.4.5",
    "vue": "2.x",
    "vue-apexcharts": "1.6.0",
    "vue-autosuggest": "2.2.0",
    "vue-awesome-swiper": "4.1.1",
    "vue-chartjs": "3.5.0",
    "vue-cleave-component": "2.1.3",
    "vue-clipboard2": "0.3.1",
    "vue-context": "6.0.0",
    "vue-echarts": "5.0.0-beta.0",
    "vue-feather-icons": "5.1.0",
    "vue-flatpickr-component": "8.1.6",
    "vue-form-wizard": "0.8.4",
    "vue-good-table": "2.21.0",
    "vue-i18n": "8.22.2",
    "vue-perfect-scrollbar": "0.2.1",
    "vue-prism-component": "1.1.1",
    "vue-quill-editor": "3.0.6",
    "vue-ripple-directive": "2.0.1",
    "vue-router": "3.4.9",
    "vue-select": "3.11.2",
    "vue-slider-component": "3.2.11",
    "vue-sweetalert2": "4.1.1",
    "vue-toastification": "1.7.11",
    "vue-tour": "1.5.0",
    "vue-tree-halower": "1.8.3",
    "vue2-leaflet": "2.5.2",
    "vuedraggable": "2.24.3",
    "vuex": "3.6.0"
  },
  "devDependencies": {
    "@vue/cli-plugin-babel": "~4.5.9",
    "@vue/cli-plugin-eslint": "~4.5.9",
    "@vue/cli-plugin-router": "~4.5.9",
    "@vue/cli-plugin-vuex": "~4.5.9",
    "@vue/cli-service": "~4.5.9",
    "@vue/eslint-config-airbnb": "^5.3.0",
    "@vuepress/plugin-medium-zoom": "^1.7.1",
    "babel-eslint": "^10.0.3",
    "eslint": "6.8.0",
    "eslint-plugin-import": "^2.22.1",
    "eslint-plugin-vue": "6.2.2",
    "sass": "1.32.*",
    "sass-loader": "^10.1.0",
    "vue-template-compiler": "2.x"
  }
3. Вопрос: Василий, почему они используют "bootstrap": "4.6.0" и "bootstrap-vue": "2.21.1", разве одного bootstrap-vue не достаточно? И вообще, я много где вижу подобное, например: "chart.js": "2.9.4" и "vue-chartjs": "3.5.1"?
Василий Наумкин
2. Это никакая не главная проблема, да и вообще не проблема. Просто не все JS компоненты в курсе, что они могут быть запущены без браузера, на сервере - и могут быть не готовы к подобному.
Например, какой-нибудь lightbox при инициализации проверяет ширину экрана. Но на сервере
  • никакого экрана нет
  • лайтбокс там никто запускать и не будет
Поэтому такой компонент на сервере просто не нужно запускать, и нет проблем.
Первый - собственно SASS\CSS стили и правила оформления, а второй - это набор компонентов, который реализует разный функционал на Vue, используя стили Bootstrap. Типа у открытой модалочки одни классы, а у закрытой другие - и меняет их Vue.
Нужные стили я подключаю в assets/scss/index.scss, а компоненты Bootstrap Vue в nuxt.config.js, чтобы работал tree shaking
Если я вдруг забуду подключить стили - компоненты всё равно работать будут, просто не будет оформления.
Точно так же стили Bootstrap используются и в React у компонентов React Bootstrap.
Ну и насчёт Сhart.js ты уже догадался, что это оригинальная библиотека и её адаптация для Vue. Как правило адаптации пишут сторонние разработчики, но вот например у Sortable.JS, который мы использовали - куча поддерживаемых фреймворков от основной команды.
Александр Наумов
Василий, спасибо за такой развернутый ответ!
Вроде бы во всем более-менее разобрался начинаю делать первый свой сайт на VESP. Сайт на заказ, но не сложный, думаю все получится, заодно прокачаюсь в современных технологиях веб-разработки )).
Александр Наумов
Василий, добрый день!
@fortawesome/vue-fontawesome - мои любимые иконки, которые постоянно развиваются и обновляются
А как увидеть весь список иконок в наличии, которые я могу применить из пакета?
Василий Наумкин
Смотри на их сайте, только обрати внимание, что мы используем те, которые solid и free.
Александр Наумов
Ок, спасибо!!! А то я на их сайте растерялся, много у них там всего. А в @fortawesome/free-solid-svg-icons все svg в js файлах и их не посмотреть.
Александр Наумов
Василий, не подскажешь, в чем может быть проблема? В breadcrumbs.vue меняю <fa icon="home" /> на <fa icon="list" /> и получаю ошибку:
[dev:site]  ERROR  Could not find one or more icon(s) {
[dev:site]   prefix: 'fas',
[dev:site]   iconName: 'list'
[dev:site] } {}
В @fortawesome/vue-fontawesome файлы faList.js и faList.d.ts присутствуют.
Василий Наумкин
Так надо ж подключить в конфиге nuxt.
Импортируются только явно указанные иконки, для уменьшения размера итогового приложения.
Александр Наумов
Василий, спасибо тебе большое! Обалдеть, как круто!
Александр Наумов
Василий, добрый день!
А у нас возможно к иконкам применить их другие свойства: (regular, light, thin, duotone)?
Например, как у них к корзине, код для Vue: <font-awesome-icon icon="fa-light fa-cart-shopping" />, разные способы попробовал, так и не получилось.
Василий Наумкин
Конечно! Нужно только:
  1. Доустановить варианты, которых тебе не хватает. Для платных нужны особые настройки.
  2. Указать тип явно через массив <fa :icon="['fal', 'cart-shopping']" />
Могут быть fal (light), far (regular), fad (duo), fas (solid) и fab (brands)
Александр Наумов
Супер!! Спасибо, большое!
Александр Наумов
Доустановить нужные типы
Василий, я правильно понял?:
cd frontend
yarn add @fortawesome/fontawesome-free-regular
Только так я могу еще установить брендовые иконки:
yarn add @fortawesome/fontawesome-free-brands
а вот таких пакетов нет:
yarn add @fortawesome/fontawesome-free-light
yarn add @fortawesome/fontawesome-free-thin
yarn add @fortawesome/fontawesome-free-duotone
Или типы устанавливаются, как-то иначе?
И так установить не выходит:
yarn add @fortawesome/fontawesome-free-duo
yarn add @fortawesome/fontawesome-free-light
Василий Наумкин
а вот таких пакетов нет:
Они доступны только за деньги, я же дал ссылку на документацию.
Александр Наумов
Спасибо, разобрался!
Александр Наумов
Василий, добрый день!
У тебя fontawesome-free-solid вшит в VESP, а я устанавливаю:
cd frontend
yarn add @fortawesome/fontawesome-free-brands  
затем правлю конфиг site/nuxt.config.js
Config.fontawesome = {
  addCss: false,
  component: ['fa', 'fab'],
  icons: {
    solid: [
      'faHome',
      //...
    ],
    brands: ['fabVk'],
  },
}
Не работает, или нужно создавать плагин?
И еще, admin/nuxt.config.js по другому написано:
Config.fontawesome = merge(Config.fontawesome, {
  addCss: false,
  icons: {
    solid: union(Config.fontawesome.icons.solid, [
      'faUsers',
     //...
    ]),
  },
})
Как правильно писать в конфиге?
Василий Наумкин
component: ['fa', 'fab'],
Это откуда взялось? Там строка, а не массив, ничего менять не нужно - посмотри документацию.
И еще, admin/nuxt.config.js по другому написано:
Это просто функции объединения для объектов и массивов из библиотеки lodash, чтобы добавить свои настройки к тем, которые уже есть в стандартном Vesp.
Александр Наумов
посмотри документацию.
Спасибо, что-то она мне не нагуглилась.
Это просто функции объединения для объектов и массивов из библиотеки lodash, чтобы добавить свои настройки к тем, которые уже есть в стандартном Vesp.
Ясно, спасибо!
Александр Наумов
Василий, добрый день!
Подскажи, пожалуйста, как применить markdownit?
  1. Устанавливаю yarn add @nuxtjs/markdownit @github/markdown-toolbar-element
  2. В admin/nuxt.config.js
Config.modules = union(Config.modules, [..., '@nuxtjs/markdownit'])
Config.markdownit = {
 runtime: true,
}
  1. В /admin/components/forms/product.vue
<markdown-toolbar for="textarea_id">
       <md-bold>bold</md-bold>
       <md-header>header</md-header>
       <md-italic>italic</md-italic>
       <md-quote>quote</md-quote>
       <md-code>code</md-code>
       <md-link>link</md-link>
       <md-image>image</md-image>
       <md-unordered-list>unordered-list</md-unordered-list>
       <md-ordered-list>ordered-list</md-ordered-list>
       <md-task-list>task-list</md-task-list>
       <md-mention>mention</md-mention>
       <md-ref>ref</md-ref>
       <button data-md-button>Custom button</button>
     </markdown-toolbar>
     <b-form-textarea id="textarea_id" v-model.trim="record.description" rows="5" />
и
<script>
import '@github/markdown-toolbar-element'
Что еще нужно добавить или отнять, чтобы в админке появилась рабочая панель редактора?
И еще, как сделать отформатированный текст из базы данных, сейчас он у меня выводится со звездочками а не жирный?
Сделал то же самое в site/nuxt.config.js , что и в admin/nuxt.config.js но результата нет.
Василий Наумкин
Что еще нужно добавить или отнять, чтобы в админке появилась рабочая панель редактора?
Не знаю, ни разу этим компонентом не пользовался. Но могу предположить, что навешивать его нужно на сырой <textarea, а не на vue компонент <b-form-textarea
И еще, как сделать отформатированный текст из базы данных
Вся фишка Markdown именно в том, что текст в БД хранится без HTML, а рендерится перед выводом на экран. Вот тебе его и нужно пропустить через markdownit или что-то подобное. У него в примерах есть такое, попробуй:
<template>
  <div v-html="$md.render(твоя-переменная-с-markdown-текстом)"></div>
</template>
Александр Наумов
Василий, спасибо большое, все заработало!!
Скажи, пожалуйста, если ты не используешь @github/markdown-toolbar-element , то как выводить кнопки?
Василий Наумкин
А я себе свой редактор написал с помощью textarea-editor, где могу прописать любую логику при нажатиях на кнопки и шорткаты.
Александр Наумов
Спасибо Василий, понял!!
Александр Наумов
Василий, добрый день!
Подскажи, пожалуйста, а как ты подключаешь glightbox?
Попытался создать плагин, не удалось подключить.
Сделал ссылки из nuxt.config.js
Config.head.script = [{src: '/plugins/glightbox/glightbox.min.js', body: true}]
Все работает, но это не по фэншую.
И еще, до сих пор окончательно не разобрался с путями, как например мне из конфига обратиться к файлу который лежит в пакете?
Config.head.script = [{src: ' ~glightbox/dist/js/glightbox.js', body: true}] - так не работает?
Василий Наумкин
Ты всё еще не перестроился мысленно на Webpack. Не нужно подключать готовый скрипт как на обычном сайте, нужно его импортировать в свой код.
import GLightbox from 'glightbox'
import 'glightbox/dist/css/glightbox.min.css'

export default {
  data() {
    return {
      files: [
        // ...
      ],
  },
  // ...
  methods: {
    onShowImage(e) {
      const elements = []

      let startAt = 0
      const thumbnails = e.currentTarget.closest('.images')
      if (thumbnails) {
        const links = thumbnails.querySelectorAll('a')
        links.forEach((a) => {
          elements.push({href: a.href, type: 'image'})
        })
        startAt = elements.findIndex((i) => i.href === e.currentTarget.href)
      } else {
        elements.push({href: e.currentTarget.href, type: 'image'})
      }

      GLightbox({elements, startAt}).open()
    },
  },
}
И навесить обработчик на ссылки с картинками:
<template>
  <div class="images">
    <a v-for="file in files" :key="file.id" :href="$image(file)" @click.prevent="onShowImage">
      <img :src="$image(file, {w: 200, h: 150, crop: 'fit'})" alt="" />
    </a>
  </div>
</template>
Код неточный, могут быть ошибки и очепятки.
Александр Наумов
Василий, спасибо большое!!
Александр Наумов
Василий, добрый день!
Уже перепробовал все что смог придумать, и уже думаю что nuxt с glightbox не совместимы.
Сделал сборку nuxt + glightbox + vesp-frontend:
Там glightbox работает, но подключение без импорта.
Глянь, пожалуйста, может я что-то не понимаю, хотя другие пакеты импортируются нормально, а этот никак не хочет (((.
Василий Наумкин
Как-то проморгал твой комментарий, сорян.
Проблема в том, что ты запускаешь Nuxt в режиме серверного рендера, а плагины типа lightbox на сервере не работают. При импорте подобных дополнений на сервере будет ошибка window is not defined, что логично - никакого window там нет.
Поэтому нужно вынести подключение Glightbox в клиентский плагин, то есть плагин с суффиксом .client.
import GLightbox from 'glightbox'
import 'glightbox/dist/css/glightbox.min.css'

export default (ctx, inject) => {
  inject('glightbox', (params ={}) => {
    return new GLightbox(params)
  })
}
Затем полключить его в nuxt.config.js:
Config.plugins = ['@/plugins/glightbox.client.js']
И дальше можно использовать this.$glightbox на всех страницах:
mounted() {
  this.$glightbox({
    selector: '.glightbox',
    touchNavigation: true,
    loop: true,
    autoplayVideos: true,
  })
},
Александр Наумов
Василий спасибо большое! Очень полезный ответ от тебя, ведь ошибка: "Window is not defined" достаточно часто встречается, а теперь понятно откуда она берется.
Александр Наумов
Затем полключить его в nuxt.config.js:
Config.plugins = ['@/plugins/glightbox.js']
Подключил вот так:
Config.plugins = [{ src: "@/plugins/glightbox.js", mode: "client" }];
Так как:
Поэтому нужно вынести подключение Glightbox в клиентский плагин, то есть плагин с суффиксом .client.
Василий Наумкин
Да, я забыл суффикс в своем примере - так что ты все правильно сделал.
Пример поправил.
Александр Наумов
Ок, спасибо!
Здравствуйте, Василий! Не могли бы вы поделиться примерами работы с почтой? Подключение библиотек, оформления письма и тд.
Василий Наумкин
Я использую PHPMailer для работы с почтой, шаблоны оформляю на Fenom. Делаю один основной, а разные варианты писем его просто расширяют.
Постараюсь написать заметку про это, с готовым кодом - но обещать не могу, занят на работе.
Василий Наумкин
Спасибо большое!
bezumkin.ru
Личный сайт Василия Наумкина
Прямой эфир
Василий Наумкин
23.12.2024, 05:33:00
В MODX сначала создали проблему, автоматически генерируя адреса, а потом "решили" заморозкой. Так ч...
Дмитрий
14.12.2024, 09:10:38
Василий, прошу прощения, тупанул, не разобрался сразу. Фреймворк отличный! "Чистый лист" на vue, рис...
Василий Наумкин
05.12.2024, 20:01:14
В итоге основная ошибка была в неправильном общем root в Nginx, из-за чего запросы не улетали на фай...
Василий Наумкин
22.11.2024, 03:33:54
Спасибо!
inna
06.11.2024, 15:47:13
Да. Все работает. Спасибо.
Василий Наумкин
01.07.2024, 11:56:41
Да, верно, именно так. А в контроллере, скорее всего, ловить данные методом post.
Василий Наумкин
26.06.2024, 09:38:15
О, точно, вылезает если не залогинен. Спасибо, исправил!
Василий Наумкин
09.04.2024, 04:45:01
> Ошибка 500 Это не похоже на ошибку Nginx, это скорее всего ошибка PHP - надо смотреть его логи. ...
Василий Наумкин
20.03.2024, 21:21:52
Volledig!
Андрей
14.03.2024, 13:47:10
Василий! Как всегда очень круто! Моё почтение!
Уровни подписки
Спасибо!
500 ₽ в месяц
Эта подписка ничего не даёт, просто возможность сказать спасибо за мои заметки. Подписчики отмечаются зелёненьким цветом в комментариях.
Большое спасибо!
1 000 ₽ в месяц
И эта подписка не даёт ничего, кроме оранжевого цвета в комментариях и возможности сказать спасибо, но уже большое!