From 64d0c9568b02959c038fc5447901ea5b7c83bb17 Mon Sep 17 00:00:00 2001
From: CAI79
Создать проект можно двумя способами: из терминала или при помощи веб-интерфейса. Рассмотрим оба способа.
-
vue create _название_проекта_vue ui
При создании проекта выберите параметры по умолчанию с установкой babel и eslint.
- Каркас приложения мы создали, можно посмотреть что получилось выполнив команду yarn serve или
- npm run serve. Вдоволь налюбовавшись результатом, добавим к проекту библиотеку интерфейса
- Vuetify, выполнив команду vue add vuetify. Установщик задаст вам единственный вопрос - какие
+ Каркас приложения мы создали, можно посмотреть что получилось выполнив команду yarn serve или
+ npm run serve. Вдоволь налюбовавшись результатом, добавим к проекту библиотеку интерфейса
+ Vuetify, выполнив команду vue add vuetify. Установщик задаст вам единственный вопрос - какие
предустановки вы хотите выбрать (Choose a preset), нас устроит набо по умолчанию (default).
- запустив yarn serve сейчас, вы увидите, что внешний вид нашего приложения несколько изменился,
+ запустив yarn serve сейчас, вы увидите, что внешний вид нашего приложения несколько изменился,
в нём появились компоненты библиотеки Vuetify. Этот вариант мы и возьмём за основу нашего приложения.
Текущую версию проекта можно также получить с Git, для этого создайте каталог проекта, перейдите в него
- и клонируйте командой git clone https://home.cainet.info:3000/cai/VueJS.git.
+ и клонируйте командой git clone https://home.cainet.info:3000/cai/VueJS.git.
А ненужное - это компонент HelloWorld и все ссылки на него. Смело удаляйте файл HelloWorld.vue из каталога
src/components/, а файл App.vue приведите к следующему виду:
-
<template>
<v-app>
@@ -44,13 +44,18 @@
+ Наш компонент будет использовать Material Design Icons, потому начнём с того, что подключим их. Для это выполните команду yarn add @mdi/font,
+ а после того как пакет установится, добавьте в самое начало файла main.js строку import '@mdi/font/css/materialdesignicons.css'. Всё, теперь
+ можно переходить к написанию самого компонента.
+
Vuetify неплохо документирован и имеет множество примеров, одним из них мы и воспользуемся при создании компонента оглавления. За основу возьмём один из примеров со страницы https://next.vuetifyjs.com/ru/components/treeview и приведём его к следующему виду: -
<template>
<v-card class="mx-auto">
@@ -88,7 +93,7 @@
<template v-slot:prepend="{ item }">
<v-icon
v-if="item.children"
- v-text="`mdi-${item.id === 1 ? 'home-variant' : 'folder-network'}`"
+ v-text="`mdi-${open ? 'book-open-variant' : 'book-variant'}`"
>
</v-icon>
</template>
@@ -142,7 +147,7 @@
недоразумение добавлением в блок <script></script> строки
import TOC from './components/TOC' и чуть ниже в components: {}, строку
TOC. В итоге App.vue должен выглядеть следующим образом:
-
+
<template>
<v-app>
@@ -193,15 +198,15 @@
текста. Про двенадцатиточечную систему сеток Vutify можно прочитать тут:
https://vuetifyjs.com/ru/framework/grid.
Приведём блок <v-content></v-content> к следующему виду:
-
+
<v-content>
<v-container fluid fill-height grid-list-md>
<v-layout row wrap full-height>
- <v-flex sm4>
+ <v-flex sm5 md4 lg3>
<TOC/>
</v-flex>
- <v-flex sm8 v-html="text"></v-flex>
+ <v-flex sm7 md8 lg9 v-html="text"></v-flex>
</v-layout>
</v-container>
</v-content>
@@ -219,7 +224,7 @@
вылезут за пределы блока меню... Непорядок! К счастью, исправить сиё недоразумение несложно, просто добавьте в
наш компонент (TOC.vue) блок стилей:
-
+
<style>
.v-treeview-node__content, .v-treeview-node__label {
diff --git a/public/data/id303.html b/public/data/id303.html
index 4da566a..a2ffd78 100644
--- a/public/data/id303.html
+++ b/public/data/id303.html
@@ -7,9 +7,9 @@
Для начала нам понадобится Axios (
https://yarnpkg.com/ru/package/axios), именно с его помощью мы будем загружать наши json и html файлы.
- Установим его командой yarn add axios vue-axios, после чего добавим в main.js строки для его
+ Установим его командой yarn add axios vue-axios, после чего добавим в main.js строки для его
подключения. Выглядеть он у вас должен следующим образом:
-
+
import Vue from 'vue'
import Axios from 'axios'
@@ -32,7 +32,7 @@ new Vue({
о разделах, для этого строку items в блоке data приведём к виду items: [], после чего перейдём к
App.vue и в секцию export добавим mounted () {}. Здесь мы в дальнейшем добавим код, исполняемый при
запуске нашего приложения. Блок <script></script> должен выглядеть следующим образом:
-
+
<script>
import TOC from './components/TOC'
@@ -63,7 +63,7 @@ new Vue({
Итак, добавим в секцию mounted код загрузки оглавления:
-
+
mounted () {
this.axios.get("data/toc.json")
@@ -88,7 +88,7 @@ mounted () {
принципы работы нашего компонента. Каждый раз при выборе элемента, у компонента изменяется свойство active и
нам надо отслеживать его изменение в App.vue. Для этого добавим в mounted файла App.vue следующий код:
-
+
this.$watch(
"$refs.TOC.active",
diff --git a/public/data/id304.html b/public/data/id304.html
index 35d0935..1705cc4 100644
--- a/public/data/id304.html
+++ b/public/data/id304.html
@@ -7,7 +7,7 @@
https://vuetifyjs.com/ru/components/dialogs,
немного его модифицировав:
-
+
<template>
<v-layout row justify-center>
@@ -21,7 +21,7 @@
<v-card-text>{{ text }}</v-card-text>
<v-card-actions>
<v-spacer></v-spacer>
- <v-btn color="green darken-1" flat @click="dialog = false">Ok</v-btn>
+ <v-btn color="primary" flat @click="dialog = false">Ok</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
@@ -48,7 +48,7 @@
строку MessageDialog. Начало блока <sctipt></script> должно выглядеть следующим
образом:
-
+
<script>
import MessageDialog from './components/MessageDialog'
@@ -68,7 +68,7 @@
<MessageDialog ref="MessageDialog" />, после чего останется только в коде скрипта добавить
в секции .catch каждого обращения к Axios строки вызова нашего диалога с сообщением об ошибке:
-
+
.catch(error => {
console.log(error);
@@ -87,7 +87,7 @@
том, что оно работает, это нехорошо, давайте выводить при загрузке индикатор. Создадим компонент LoadingDialog.vue
как всегда воспользовавшись шаблоном vuetify:
-
+
<template>
<div class="text-xs-center">
@@ -139,7 +139,7 @@
Справились? Теперь добавим код для отображения и скрытия диалога при загрузке файла в секцию mounted:
-
+
mounted () {
this.$refs.LoadingDialog.dialog = true;
@@ -203,5 +203,5 @@ mounted () {
P.S. Текст данной части можно взять тут: /data/id304.html
- P.P.S. Финальную версию можно скомпилировать командой yarn build.
+ P.P.S. Финальную версию можно скомпилировать командой yarn build.
\ No newline at end of file
diff --git a/public/data/id305.html b/public/data/id305.html
new file mode 100644
index 0000000..51dde19
--- /dev/null
+++ b/public/data/id305.html
@@ -0,0 +1,190 @@
+Цветовые схемы
+
+ Наша программа получилась такая светлая-красивая, но в темноте пользоваться ей неудобно, яркий свет от экрана слишком бьёт по глазам, предлагаю
+ перейти на тёмную сторону сделать тёмный вариант интерфейса.
+
+
+ Открывайте основной модуль нашего приложения App.vue. В блоке <script> найдите секцию data () и добавьте в неё переменную dark
+ со значением по умолчанию равным false, это будет флаг использования тёмной версии интерфейса. После этого в блоке <template> измените
+ <v-app> на <v-app :dark="dark">, этим мы свяжем наш флаг использования тёмной версии с назначением соответствующего
+ CSS-класса главному элементу программы.
+
+
+ Попробуйте изменить в data () значение переменной dark на true, ваше приложение станет тёмным. Получилось? Теперь прикрутим меню с пунктом для
+ переключения вариантов интерфейса, для этого в конец блока <v-toolbar> добавим следующий код:
+
+
+<v-menu bottom left>
+ <template v-slot:activator="{ on }">
+ <v-btn
+ flat
+ icon
+ v-on="on"
+ >
+ <v-icon>more_vert</v-icon>
+ </v-btn>
+ </template>
+
+ <v-list>
+ <v-list-tile>
+ <v-list-tile-action>
+ <v-switch v-model="dark" color="blue"></v-switch>
+ </v-list-tile-action>
+ <v-list-tile-title>Тёмная схема</v-list-tile-title>
+ </v-list-tile>
+ </v-list>
+</v-menu>
+
+
+ Теперь у нас есть переключатель темы, но при каждой повторной загрузке тема всегда включается светлая, реализуем сохранение значения флага в
+ cookies, для этого в блоке <script> добавим секцию watch с кодом, отслеживающим изменение флага dark и сохраняющим
+ его значение в localSorage:
+
+
+watch: {
+ dark (val) { localStorage.dark = val; }
+}
+
+
+
+
+ Сохранение сделали, сделаем и чтение сохранённой информации, добавив в секцию mounted строку
+ this.dark = localStorage.dark ? JSON.parse(localStorage.dark) : false;. После чего наш App.vue должен выглядеть следующим образом:
+
+
+<template>
+ <v-app :dark="dark">
+ <v-toolbar app>
+ <v-toolbar-title class="headline text-uppercase">
+ <span>Vue.js:</span>
+ <span class="font-weight-light">Самоучитель</span>
+ </v-toolbar-title>
+ <v-spacer></v-spacer>
+ <v-btn
+ flat
+ href="https://home.cainet.info:3000/cai/VueJS/"
+ target="_blank"
+ >
+ <span class="mr-2">Актуальная версия</span>
+ </v-btn>
+ <v-menu bottom left>
+ <template v-slot:activator="{ on }">
+ <v-btn
+ flat
+ icon
+ v-on="on"
+ >
+ <v-icon>more_vert</v-icon>
+ </v-btn>
+ </template>
+
+ <v-list>
+ <v-list-tile>
+ <v-list-tile-action>
+ <v-switch v-model="dark" color="blue"></v-switch>
+ </v-list-tile-action>
+ <v-list-tile-title>Тёмная схема</v-list-tile-title>
+ </v-list-tile>
+ </v-list>
+ </v-menu>
+ </v-toolbar>
+
+ <v-content>
+ <v-container fluid fill-height grid-list-md>
+ <v-layout row wrap full-height>
+ <v-flex sm4>
+ <TOC ref="TOC" />
+ </v-flex>
+ <v-flex sm8 v-html="text"></v-flex>
+ </v-layout>
+ </v-container>
+ </v-content>
+ <LoadingDialog ref="LoadingDialog" />
+ <MessageDialog ref="MessageDialog" />
+ </v-app>
+</template>
+
+<script>
+ import LoadingDialog from './components/LoadingDialog'
+ import MessageDialog from './components/MessageDialog'
+ import TOC from './components/TOC'
+
+ export default {
+ name: 'App',
+
+ components: {
+ LoadingDialog,
+ MessageDialog,
+ TOC
+ },
+
+ data () {
+ return {
+ dark: false,
+ docId: 0,
+ text: ""
+ }
+ },
+
+ mounted () {
+ this.$refs.LoadingDialog.dialog = true;
+ this.dark = localStorage.dark ? JSON.parse(localStorage.dark) : false;
+
+ this.axios.get("data/start.html")
+ .then(response => {
+ this.text = response.data;
+ this.$refs.LoadingDialog.dialog = false;
+ })
+ .catch(error => {
+ this.$refs.LoadingDialog.dialog = false;
+ console.log(error);
+ this.$refs.MessageDialog.title = "Ошибка";
+ this.$refs.MessageDialog.text = error;
+ this.$refs.MessageDialog.dialog = true;
+ });
+
+ this.$refs.LoadingDialog.dialog = true;
+
+ this.axios.get("data/toc.json")
+ .then(response => {
+ this.$refs.TOC.items = response.data;
+ this.$refs.LoadingDialog.dialog = false;
+ })
+ .catch(error => {
+ this.$refs.LoadingDialog.dialog = false;
+ console.log(error);
+ this.$refs.MessageDialog.title = "Ошибка";
+ this.$refs.MessageDialog.text = error;
+ this.$refs.MessageDialog.dialog = true;
+ });
+
+ this.$watch(
+ "$refs.TOC.active",
+ (newVal, oldVal) => {
+ if (newVal.length) {
+ this.$refs.LoadingDialog.dialog = true;
+ this.axios.get("data/id" + newVal + ".html")
+ .then(response => {
+ this.text = response.data;
+ this.$refs.LoadingDialog.dialog = false;
+ })
+ .catch(error => {
+ this.$refs.LoadingDialog.dialog = false;
+ console.log(error);
+ this.$refs.MessageDialog.title = "Ошибка";
+ this.$refs.MessageDialog.text = error;
+ this.$refs.MessageDialog.dialog = true;
+ });
+ }
+ }
+ );
+ },
+
+ watch: {
+ dark (val) { localStorage.dark = val; }
+ }
+ }
+</script>
+
+
+
\ No newline at end of file
diff --git a/public/data/id306.html b/public/data/id306.html
new file mode 100644
index 0000000..bdc9ffe
--- /dev/null
+++ b/public/data/id306.html
@@ -0,0 +1,230 @@
+Кнопка возвращения
+
+ Всё у нас вроде бы красиво, но вот проматывать каждый раз страницу вверх после прочтения урока неудобно, добавим кнопку возвращения к началу страницы.
+ Для этого нам потребуется в блок <v-app></v-app> добавить код отображения самой кнопки:
+
+
+<v-fab-transition>
+ <v-btn
+ v-show="scrolled"
+ color="blue"
+ @click="scrollToTop"
+ dark
+ fixed
+ bottom
+ right
+ fab
+ >
+ <v-icon>keyboard_arrow_up</v-icon>
+ </v-btn>
+</v-fab-transition>
+
+
+
+
+ Как вы уже наверное заметили, кнопка у нас привязана к флагу отображения scrolled, это сделано для того чтобы она отображалась только когда требуется,
+ соответственно вам надо добавить данный флаг в блок data скрипта и назначить ему значение false. Сделали? Теперь добавим отслеживание прокрутки и
+ обработчик нажатия на кнопку - scrollToTop:
+
+
+created () {
+ window.addEventListener('scroll', this.onScroll);
+},
+
+destroyed () {
+ window.removeEventListener('scroll', this.onScroll);
+},
+
+methods: {
+ onScroll () {
+ this.scrolled = window.scrollY > 0;
+ },
+
+ scrollToTop () {
+ window.scrollTo({
+ top: 0,
+ left: 0,
+ behavior: 'smooth'
+ });
+ }
+}
+
+
+
+
+ App.vue должен выглядеть следующим образом:
+
+
+<template>
+ <v-app :dark="dark">
+ <v-toolbar app>
+ <v-toolbar-title class="headline text-uppercase">
+ <span>Vue.js:</span>
+ <span class="font-weight-light">Самоучитель</span>
+ </v-toolbar-title>
+ <v-spacer></v-spacer>
+ <v-btn
+ flat
+ href="https://home.cainet.info:3000/cai/VueJS/"
+ target="_blank"
+ >
+ <span class="mr-2">Актуальная версия</span>
+ </v-btn>
+ <v-menu bottom left>
+ <template v-slot:activator="{ on }">
+ <v-btn
+ flat
+ icon
+ v-on="on"
+ >
+ <v-icon>more_vert</v-icon>
+ </v-btn>
+ </template>
+
+ <v-list>
+ <v-list-tile>
+ <v-list-tile-action>
+ <v-switch v-model="dark" color="blue"></v-switch>
+ </v-list-tile-action>
+ <v-list-tile-title>Тёмная схема</v-list-tile-title>
+ </v-list-tile>
+ </v-list>
+ </v-menu>
+ </v-toolbar>
+
+ <v-content>
+ <v-container fluid fill-height grid-list-md>
+ <v-layout row wrap full-height>
+ <v-flex sm4>
+ <TOC ref="TOC" />
+ </v-flex>
+ <v-flex sm8 v-html="text"></v-flex>
+ </v-layout>
+ </v-container>
+ </v-content>
+ <v-fab-transition>
+ <v-btn
+ v-show="scrolled"
+ color="blue"
+ @click="scrollToTop"
+ dark
+ fixed
+ bottom
+ right
+ fab
+ >
+ <v-icon>keyboard_arrow_up</v-icon>
+ </v-btn>
+ </v-fab-transition>
+ <LoadingDialog ref="LoadingDialog" />
+ <MessageDialog ref="MessageDialog" />
+ </v-app>
+</template>
+
+<script>
+ import LoadingDialog from './components/LoadingDialog'
+ import MessageDialog from './components/MessageDialog'
+ import TOC from './components/TOC'
+
+ export default {
+ name: 'App',
+
+ components: {
+ LoadingDialog,
+ MessageDialog,
+ TOC
+ },
+
+ data () {
+ return {
+ dark: false,
+ docId: 0,
+ scrolled: false,
+ text: ""
+ }
+ },
+
+ mounted () {
+ this.$refs.LoadingDialog.dialog = true;
+ this.dark = localStorage.dark ? JSON.parse(localStorage.dark) : false;
+
+ this.axios.get("data/start.html")
+ .then(response => {
+ this.text = response.data;
+ this.$refs.LoadingDialog.dialog = false;
+ })
+ .catch(error => {
+ this.$refs.LoadingDialog.dialog = false;
+ console.log(error);
+ this.$refs.MessageDialog.title = "Ошибка";
+ this.$refs.MessageDialog.text = error;
+ this.$refs.MessageDialog.dialog = true;
+ });
+
+ this.$refs.LoadingDialog.dialog = true;
+
+ this.axios.get("data/toc.json")
+ .then(response => {
+ this.$refs.TOC.items = response.data;
+ this.$refs.LoadingDialog.dialog = false;
+ })
+ .catch(error => {
+ this.$refs.LoadingDialog.dialog = false;
+ console.log(error);
+ this.$refs.MessageDialog.title = "Ошибка";
+ this.$refs.MessageDialog.text = error;
+ this.$refs.MessageDialog.dialog = true;
+ });
+
+ this.$watch(
+ "$refs.TOC.active",
+ (newVal, oldVal) => {
+ if (newVal.length) {
+ this.$refs.LoadingDialog.dialog = true;
+ this.axios.get("data/id" + newVal + ".html")
+ .then(response => {
+ this.text = response.data;
+ this.$refs.LoadingDialog.dialog = false;
+ })
+ .catch(error => {
+ this.$refs.LoadingDialog.dialog = false;
+ console.log(error);
+ this.$refs.MessageDialog.title = "Ошибка";
+ this.$refs.MessageDialog.text = error;
+ this.$refs.MessageDialog.dialog = true;
+ });
+ }
+ }
+ );
+ },
+
+ watch: {
+ dark (val) { localStorage.dark = val; }
+ },
+
+ created () {
+ window.addEventListener('scroll', this.onScroll);
+ },
+
+ destroyed () {
+ window.removeEventListener('scroll', this.onScroll);
+ },
+
+ methods: {
+ onScroll () {
+ this.scrolled = window.scrollY > 0;
+ },
+
+ scrollToTop () {
+ window.scrollTo({
+ top: 0,
+ left: 0,
+ behavior: 'smooth'
+ });
+ }
+ }
+ }
+</script>
+
+
+
\ No newline at end of file
diff --git a/public/data/id9901.html b/public/data/id9901.html
new file mode 100644
index 0000000..e2979d8
--- /dev/null
+++ b/public/data/id9901.html
@@ -0,0 +1,37 @@
+Команды npm, yarn, vue-cli
+
+ Здесь я опишу некоторые основные команды, которые понадобятся нам во время обучения.
+
+
+
+ Действие
+ npm
+ yarn
+ vue-cli
+
+
+ Установка vue-cli
+ npm install @vue/cli -g
+ yarn global add @vue/cli
+ -
+
+
+ Создание проекта
+ npm init (только инициализация проекта)
+ yarn init (только инициализация проекта)
+ vue create _название_проекта_ или при помощи WebUI: vue ui
+
+
+ Запуск dev-сервера
+ npm run serve
+ yarn serve
+ -
+
+
+ Компиляция финальной версии
+ npm build
+ yarn build
+ -
+
+
+
\ No newline at end of file
diff --git a/public/data/toc.json b/public/data/toc.json
index 510582e..218ea9d 100644
--- a/public/data/toc.json
+++ b/public/data/toc.json
@@ -36,6 +36,14 @@
{
"id": 304,
"name": "Шаг 4. Добавление диалогов"
+ },
+ {
+ "id": 305,
+ "name": "Шаг 5. Цветовые схемы"
+ },
+ {
+ "id": 306,
+ "name": "Шаг 6. Кнопка возвращения"
}
]
},
diff --git a/public/img/img301.png b/public/img/img301.png
deleted file mode 100644
index 1cb91840321318ca5d49bd611819b9a254002c8e..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 123127
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