From 9f4cda9e64f05761c58509adc5f01b39ea67b75b Mon Sep 17 00:00:00 2001
From: CAI79
Date: Sun, 9 Jun 2019 23:43:12 +0300
Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=B5=D1=80=D0=B2=D0=BE=D0=BD=D0=B0?=
=?UTF-8?q?=D1=87=D0=B0=D0=BB=D1=8C=D0=BD=D0=B0=D1=8F=20=D0=B2=D0=B5=D1=80?=
=?UTF-8?q?=D1=81=D0=B8=D1=8F?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitignore | 21 +
babel.config.js | 5 +
package.json | 53 +
public/data/id1.html | 9 +
public/data/id201.html | 37 +
public/data/id202.html | 17 +
public/data/id301.html | 26 +
public/data/id302.html | 237 +
public/data/id303.html | 126 +
public/data/id304.html | 207 +
public/data/start.html | 7 +
public/data/toc.json | 52 +
public/favicon.ico | Bin 0 -> 4286 bytes
public/img/img301.png | Bin 0 -> 123127 bytes
public/img/logo.png | Bin 0 -> 6849 bytes
public/index.html | 19 +
src/App.vue | 117 +
src/assets/logo.png | Bin 0 -> 6849 bytes
src/assets/logo.svg | 1 +
src/components/LoadingDialog.vue | 41 +
src/components/MessageDialog.vue | 30 +
src/components/TOC.vue | 92 +
src/main.js | 13 +
src/plugins/vuetify.js | 7 +
yarn.lock | 8277 ++++++++++++++++++++++++++++++
25 files changed, 9394 insertions(+)
create mode 100644 .gitignore
create mode 100644 babel.config.js
create mode 100644 package.json
create mode 100644 public/data/id1.html
create mode 100644 public/data/id201.html
create mode 100644 public/data/id202.html
create mode 100644 public/data/id301.html
create mode 100644 public/data/id302.html
create mode 100644 public/data/id303.html
create mode 100644 public/data/id304.html
create mode 100644 public/data/start.html
create mode 100644 public/data/toc.json
create mode 100644 public/favicon.ico
create mode 100644 public/img/img301.png
create mode 100644 public/img/logo.png
create mode 100644 public/index.html
create mode 100644 src/App.vue
create mode 100644 src/assets/logo.png
create mode 100644 src/assets/logo.svg
create mode 100644 src/components/LoadingDialog.vue
create mode 100644 src/components/MessageDialog.vue
create mode 100644 src/components/TOC.vue
create mode 100644 src/main.js
create mode 100644 src/plugins/vuetify.js
create mode 100644 yarn.lock
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a0dddc6
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,21 @@
+.DS_Store
+node_modules
+/dist
+
+# local env files
+.env.local
+.env.*.local
+
+# Log files
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Editor directories and files
+.idea
+.vscode
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 0000000..ba17966
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,5 @@
+module.exports = {
+ presets: [
+ '@vue/app'
+ ]
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..f1f158c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,53 @@
+{
+ "name": "learn_vuejs",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "serve": "vue-cli-service serve",
+ "build": "vue-cli-service build",
+ "lint": "vue-cli-service lint"
+ },
+ "dependencies": {
+ "axios": "^0.19.0",
+ "core-js": "^2.6.5",
+ "vue": "^2.6.10",
+ "vue-axios": "^2.1.4",
+ "vuetify": "^1.5.5"
+ },
+ "devDependencies": {
+ "@vue/cli-plugin-babel": "^3.8.0",
+ "@vue/cli-plugin-eslint": "^3.8.0",
+ "@vue/cli-service": "^3.8.0",
+ "babel-eslint": "^10.0.1",
+ "eslint": "^5.16.0",
+ "eslint-plugin-vue": "^5.0.0",
+ "stylus": "^0.54.5",
+ "stylus-loader": "^3.0.1",
+ "vue-cli-plugin-vuetify": "^0.5.0",
+ "vue-template-compiler": "^2.6.10",
+ "vuetify-loader": "^1.0.5"
+ },
+ "eslintConfig": {
+ "root": true,
+ "env": {
+ "node": true
+ },
+ "extends": [
+ "plugin:vue/essential",
+ "eslint:recommended"
+ ],
+ "rules": {},
+ "parserOptions": {
+ "parser": "babel-eslint"
+ }
+ },
+ "postcss": {
+ "plugins": {
+ "autoprefixer": {}
+ }
+ },
+ "browserslist": [
+ "> 1%",
+ "last 2 versions"
+ ]
+}
diff --git a/public/data/id1.html b/public/data/id1.html
new file mode 100644
index 0000000..ed9b6e0
--- /dev/null
+++ b/public/data/id1.html
@@ -0,0 +1,9 @@
+Введение
+
+ Привет! Данный самоучитель появился в результате желания освоить Vue.js. Я решил что решать абстрактные задачи
+ и записывать свои мысли в блокнот менее интересно чем создать мини-сайт справочник, описывающий процесс его же
+ создания.
+
+
+ Надеюсь мои заметки окажутся полезными и вам.
+
diff --git a/public/data/id201.html b/public/data/id201.html
new file mode 100644
index 0000000..ec9e308
--- /dev/null
+++ b/public/data/id201.html
@@ -0,0 +1,37 @@
+Подготовка среды разработки
+Программное обеспечение
+
+ Что вам потребуется для создания первого приложения на Vue.js? Самый минимум - это текстовый редактор и
+ наличие интернета, но мы пойдём другим путём, возьмём инструменты разработки для взрослых, а именно:
+ менеджер пакетов npm или yarn, систему контроля версий Git и редактор исходного кода (В моём случае это
+ Visual Stido Code, но вы можете использовать любой другой).
+
+
+ Нужен ли вам yarn - решайте сами, сравнений в интернете много, вот одно из них:
+ Yarn
+ или npm: все, что вам нужно знать о них. Я в данном руководстве буду его использовать, но если вы
+ решили его не устанавливать, то перевести команды в формат npm не составит труда.
+
+
+Ссылки
+
+
+ - npm: https://www.npmjs.com/get-npm
+ - yarn: https://www.yarnpkg.com/ru/docs/install#windows-stable
+ - Git: https://git-scm.com/downloads
+ - Visual Studio Code: https://code.visualstudio.com/download
+
+
+
+Установка vue-cli
+
+ Установив менеджер пакетов, перейдём к установке vue-cli с помощью которой в будущем будем создавать наши
+ великие и не очень проекты. Делается это командой yarn global add @vue/cli если вы решили
+ использовать yarn или npm install @vue/cli -g если выбор пал на npm. Также имеет смысл сразу
+ установить пакет vue/cli-upgrade для обновления vue-cli в будущем.
+
+
+
+ На этом установка инструментария для дальнейшей работы завершена, переходим к настройке среды разработки
+ и созданию нового проекта.
+
\ No newline at end of file
diff --git a/public/data/id202.html b/public/data/id202.html
new file mode 100644
index 0000000..b4e9978
--- /dev/null
+++ b/public/data/id202.html
@@ -0,0 +1,17 @@
+Подготовка среды разработки
+Расширения Visual Studio Code
+
+ Если вы выбрали другой редактор кода, смело можете пропустить данную часть.
+
+
+ Bookmarks. Позволяет создавать закладки с описаниями, что очень помогает в процессе разработки.
+
+
+ GitLens. Очень упрощает работу с Git непосредственно из Visual Studio.
+
+
+ Vue 2 Snippets. Сниппеты для Vue 2.
+
+
+ vuetify-vscode. Сниппеты для Vuetify.
+
\ No newline at end of file
diff --git a/public/data/id301.html b/public/data/id301.html
new file mode 100644
index 0000000..edc7661
--- /dev/null
+++ b/public/data/id301.html
@@ -0,0 +1,26 @@
+Создание проекта
+
+ Создать проект можно двумя способами: из терминала или при помощи веб-интерфейса. Рассмотрим оба способа.
+
+ - Из терминала, для этого следует выполнить команду
vue create _название_проекта_
+ - В веб-интерфейсе, запустив его командой
vue ui
+
+
+
+ При создании проекта выберите параметры по умолчанию с установкой babel и eslint.
+
+
+ Каркас приложения мы создали, можно посмотреть что получилось выполнив команду yarn serve или
+ npm run serve. Вдоволь налюбовавшись результатом, добавим к проекту библиотеку интерфейса
+ Vuetify, выполнив команду vue add vuetify. Установщик задаст вам единственный вопрос - какие
+ предустановки вы хотите выбрать (Choose a preset), нас устроит набо по умолчанию (default).
+
+
+ запустив yarn serve сейчас, вы увидите, что внешний вид нашего приложения несколько изменился,
+ в нём появились компоненты библиотеки Vuetify. Этот вариант мы и возьмём за основу нашего приложения.
+
+Клонирование проекта
+
+ Текущую версию проекта можно также получить с Git, для этого создайте каталог проекта, перейдите в него
+ и клонируйте командой git clone https://home.cainet.info:3000/cai/VueJS/.
+
\ No newline at end of file
diff --git a/public/data/id302.html b/public/data/id302.html
new file mode 100644
index 0000000..410546c
--- /dev/null
+++ b/public/data/id302.html
@@ -0,0 +1,237 @@
+Создание компонента оглавления
+
+ Первое что мы сделаем в нашем проекте - это компопнент оглавления с поиском главы, тот что вы видите сейчас слева
+ от данного текста. Но сначала удалим всё ненужное из сгенерированного на предыдущем шаге шаблона.
+
+
+ А ненужное - это компонент HelloWorld и все ссылки на него. Смело удаляйте файл HelloWorld.vue из каталога
+ src/components/, а файл App.vue приведите к следующему виду:
+
+
+<template>
+ <v-app>
+ <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://github.com/vuetifyjs/vuetify/releases/latest"
+ target="_blank"
+ >
+ <span class="mr-2">Актуальная версия
+ </v-btn>
+ </v-toolbar>
+
+ <v-content>
+ </v-content>
+ </v-app>
+</template>
+
+<script>
+ export default {
+ name: 'App',
+ components: {
+ },
+ data () {
+ return {
+ }
+ }
+ }
+</script>
+
+
+
+
+ Vuetify неплохо документирован и имеет множество примеров, одним из них мы и воспользуемся при создании компонента
+ оглавления. За основу возьмём один из примеров со страницы
+ https://next.vuetifyjs.com/ru/components/treeview и
+ приведём его к следующему виду:
+
+
+
+<template>
+ <v-card class="mx-auto">
+ <v-sheet class="pa-3 primary lighten-2">
+ <v-text-field
+ v-model="search"
+ label="Поиск по названию главы"
+ dark
+ flat
+ solo-inverted
+ hide-details
+ clearable
+ clear-icon="mdi-close-circle-outline"
+ >
+ </v-text-field>
+ <v-checkbox
+ v-model="caseSensitive"
+ dark
+ hide-details
+ label="С учётом регистра"
+ >
+ </v-checkbox>
+ </v-sheet>
+ <v-card-text>
+ <v-treeview
+ :active.sync="active"
+ :filter="filter"
+ :items="items"
+ :open.sync="open"
+ :search="search"
+ activatable
+ active-class="primary--text"
+ open-on-click
+ >
+ <template v-slot:prepend="{ item }">
+ <v-icon
+ v-if="item.children"
+ v-text="`mdi-${item.id === 1 ? 'home-variant' : 'folder-network'}`"
+ >
+ </v-icon>
+ </template>
+ </v-treeview>
+ </v-card-text>
+ </v-card>
+</template>
+
+<script>
+ export default {
+ data: () => ({
+ active: [],
+ items: [
+ {
+ "id": 1,
+ "name": "Введение"
+ },
+ {
+ "id": 2,
+ "name": "Подготовка среды разработки",
+ "children": [
+ {
+ "id": 201,
+ "name": "Программное обеспечение"
+ }
+ ]
+ }
+ ],
+ open: [1],
+ search: null,
+ caseSensitive: false
+ }),
+
+ computed: {
+ filter () {
+ return this.caseSensitive
+ ? (item, search, textKey) => item[textKey].indexOf(search) > -1
+ : undefined
+ }
+ }
+ }
+</script>
+
+
+
+
+ Компонент готов, но если вы запустите приложение на исполнение, то никаких изменений не увидите, ведь мы не
+ добавили его в само приложение. Этим сейчас и займёмся. Откройте файл App.vue и в блок <v-content>
+ </v-content> добавьте наш компонент <TOC/>. Уже запустили просмотр? Ну и как?
+ Ничего нового? А всё потому, что мы не импортировали его в приложение, срочно исправляем это досадное
+ недоразумение добавлением в блок <script></script> строки
+ import TOC from './components/TOC' и чуть ниже в components: {}, строку
+ TOC. В итоге App.vue должен выглядеть следующим образом:
+
+
+<template>
+ <v-app>
+ <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://github.com/vuetifyjs/vuetify/releases/latest"
+ target="_blank"
+ >
+ <span class="mr-2">Актуальная версия
+ </v-btn>
+ </v-toolbar>
+
+ <v-content>
+ <TOC/>
+ </v-content>
+ </v-app>
+</template>
+
+<script>
+ import TOC from './components/TOC'
+
+ export default {
+ name: 'App',
+ components: {
+ TOC
+ },
+ data () {
+ return {
+ }
+ }
+ }
+</script>
+
+
+
+
+ Запустили? Круто же? =) Идём дальше.
+
+
+ Нам надо чтобы оглавление было во всю ширину экрана только на смартфонах, на компьютере это выглядит как минимум
+ странно, изменим разметку в файле App.vue, создадим одну колонку шириной 4 для оглавления и одну шириной 8 для
+ текста. Про двенадцатиточечную систему сеток 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>
+ <TOC/>
+ </v-flex>
+ <v-flex sm8 v-html="text"></v-flex>
+ </v-layout>
+ </v-container>
+</v-content>
+
+
+
+
+ В блок data() добавьте строку text: "" так как выше, в строке
+ <v-flex sm8 v-html="text"></v-flex> мы директивой v-html="text" указали Vue, что
+ оон должен в качестве содержимого блока брать значение свойства text, и если мы его не добавим в объект нашего
+ приложения, то Vue будет ругаться нехорошими словами.
+
+
+ Готово! Почти идеально... Почему почти? Попробуйте уменьшить размер окна браузера и на определённом этапе буквы
+ вылезут за пределы блока меню... Непорядок! К счастью, исправить сиё недоразумение несложно, просто добавьте в
+ наш компонент (TOC.vue) блок стилей:
+
+
+
+<style>
+ .v-treeview-node__content, .v-treeview-node__label {
+ flex-shrink: 1;
+ }
+ .v-treeview-node__root {
+ height: auto;
+ }
+</style>
+
+
+
+
+ Отлично! Теперь всё выглядит как надо и можно переходить к следующему шагу.
+
\ No newline at end of file
diff --git a/public/data/id303.html b/public/data/id303.html
new file mode 100644
index 0000000..4da566a
--- /dev/null
+++ b/public/data/id303.html
@@ -0,0 +1,126 @@
+Загрузка данных
+
+ В созданном нами компоненте есть один ощутимый недостаток - данные в нём зашиты намертво и чтобы добавить
+ новую главу, придётся перекомпилировать всё приложение, а так дела не делаются. Сейчас мы добавим возможность
+ хранения данных во внешних файлах и будем загружать их по мере надобности.
+
+
+ Для начала нам понадобится Axios (
+ https://yarnpkg.com/ru/package/axios), именно с его помощью мы будем загружать наши json и html файлы.
+ Установим его командой yarn add axios vue-axios, после чего добавим в main.js строки для его
+ подключения. Выглядеть он у вас должен следующим образом:
+
+
+import Vue from 'vue'
+import Axios from 'axios'
+import VueAxios from 'vue-axios'
+import './plugins/vuetify'
+import App from './App.vue'
+
+Vue.config.productionTip = false
+
+Vue.use(VueAxios, Axios)
+
+new Vue({
+ render: h => h(App),
+}).$mount('#app')
+
+
+
+
+ Вот теперь можно взяться за загрузку внешних данных. Для начала уберём из нашего компонента (TOC.vue) все данные
+ о разделах, для этого строку items в блоке data приведём к виду items: [], после чего перейдём к
+ App.vue и в секцию export добавим mounted () {}. Здесь мы в дальнейшем добавим код, исполняемый при
+ запуске нашего приложения. Блок <script></script> должен выглядеть следующим образом:
+
+
+<script>
+ import TOC from './components/TOC'
+
+ export default {
+ name: 'App',
+ components: {
+ TOC
+ },
+
+ data () {
+ return {
+ }
+ },
+
+ mounted () {
+
+ }
+ }
+</script>
+
+
+
+
+ Помимо этого нам надо немного изменить тег нашего оглавления, добавить в него ref="TOC", после
+ чего выглядеть он должен следующим образом: <TOC ref="TOC"/>. Нужно нам это для того чтобы
+ мы могли обращаться к конкретному экземпляру созданного нами компонента.
+
+
+ Итак, добавим в секцию mounted код загрузки оглавления:
+
+
+mounted () {
+ this.axios.get("data/toc.json")
+ .then(response => {
+ this.$refs.TOC.items = response.data;
+ })
+ .catch(error => {
+ console.log(error);
+ });
+}
+
+
+
+
+ Готово! Всё работает, убедиться в этом можете открыв консоль браузера и обнаружив там ошибку "Request failed with
+ status code 404". Чтобы от неё избавиться, создайте в каталоге public/data файл toc.json и заполните его данными
+ о разделах. Готовый файл можно получить по ссылке /data/toc.json
+ и сохранить его в public/data/toc.json. После этого оглавление будет загружаться из него.
+
+
+ Оглавление у нас есть, осталось реализовать загрузку статей. Для того чтобы сделать это, погрузимся немного в
+ принципы работы нашего компонента. Каждый раз при выборе элемента, у компонента изменяется свойство active и
+ нам надо отслеживать его изменение в App.vue. Для этого добавим в mounted файла App.vue следующий код:
+
+
+
+this.$watch(
+ "$refs.TOC.active",
+ (newVal, oldVal) => {
+ if (newVal.length) {
+ this.axios.get("data/id" + newVal + ".html")
+ .then(response => {
+ this.text = response.data;
+ })
+ .catch(error => {
+ console.log(error);
+ });
+ }
+ }
+);
+
+
+
+
+ Остаётся только создать файлы с содержимым статей. Вы можете скачать их из git или по следующим ссылкам:
+
+ Разместите данные файлы в public/data/. Теперь приложение выполняет все свои основные задачи.
+
+
+ А теперь попробуйте самостоятельно добавить при старте приложения загрузку и отображение информации о нём.
+ Файл с информацией можно взять на git или по следующей ссылке:
+ /data/start.html.
+
diff --git a/public/data/id304.html b/public/data/id304.html
new file mode 100644
index 0000000..35d0935
--- /dev/null
+++ b/public/data/id304.html
@@ -0,0 +1,207 @@
+Добавление диалогов
+Диалог с сообщением
+
+ Некоторые ссылки в нашем приложении пока ведут в никуда и единственная реакция на щелчок по ним - это сообщение
+ в консоли, что не особо информативно, исправим этот недочёт. Создадим новый компонент MessageDialog.vue и, не
+ изобретая велосипедов, возьмём готовое решение для модального диалога из документации к Vuetify:
+ https://vuetifyjs.com/ru/components/dialogs,
+ немного его модифицировав:
+
+
+
+<template>
+ <v-layout row justify-center>
+ <v-dialog
+ v-model="dialog"
+ persistent
+ max-width="290"
+ >
+ <v-card>
+ <v-card-title class="headline">{{ title }}</v-card-title>
+ <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-card-actions>
+ </v-card>
+ </v-dialog>
+ </v-layout>
+</template>
+
+<script>
+ export default {
+ data () {
+ return {
+ dialog: false,
+ title: "",
+ text: ""
+ }
+ }
+ }
+</script>
+
+
+
+
+ Подключите диалог, добавив в блок <script></script> файла App.vue строку
+ import MessageDialog from './components/MessageDialog' и ниже в components: {}
+ строку MessageDialog. Начало блока <sctipt></script> должно выглядеть следующим
+ образом:
+
+
+
+<script>
+ import MessageDialog from './components/MessageDialog'
+ import TOC from './components/TOC'
+
+ export default {
+ name: 'App',
+ components: {
+ MessageDialog,
+ TOC
+ },
+
+
+
+
+ В конец блока <v-app></v-app> добавьте строку
+ <MessageDialog ref="MessageDialog" />, после чего останется только в коде скрипта добавить
+ в секции .catch каждого обращения к Axios строки вызова нашего диалога с сообщением об ошибке:
+
+
+
+.catch(error => {
+ console.log(error);
+ this.$refs.MessageDialog.title = "Ошибка";
+ this.$refs.MessageDialog.text = error;
+ this.$refs.MessageDialog.dialog = true;
+});
+
+
+ Теперь все ошибки при загрузке файлов будут показаны пользователю.
+
+
+Диалог с индикатором загрузки
+
+ При медленном соединении наше приложение будет загружать данные молча, ни коим образом не оповещая пользователя о
+ том, что оно работает, это нехорошо, давайте выводить при загрузке индикатор. Создадим компонент LoadingDialog.vue
+ как всегда воспользовавшись шаблоном vuetify:
+
+
+
+<template>
+ <div class="text-xs-center">
+ <v-dialog
+ v-model="dialog"
+ hide-overlay
+ persistent
+ width="300"
+ >
+ <v-card
+ color="primary"
+ dark
+ >
+ <v-card-text>
+ Загружаю данные, ожидайте...
+ <v-progress-linear
+ indeterminate
+ color="white"
+ class="mb-0"
+ >
+ </v-progress-linear>
+ </v-card-text>
+ </v-card>
+ </v-dialog>
+ </div>
+</template>
+
+<script>
+ export default {
+ data () {
+ return {
+ dialog: false
+ }
+ },
+
+ watch: {
+ dialog (val) {
+ if (!val) return
+ }
+ }
+ }
+</script>
+
+
+
+
+ Предлагаю вам самостоятельно подключить его в App.vue, добавив нужные строки в блоки шаблона и скрипта. В шаблоне
+ значение ref установите равным "LoadingDialog".
+
+
+ Справились? Теперь добавим код для отображения и скрытия диалога при загрузке файла в секцию mounted:
+
+
+mounted () {
+ this.$refs.LoadingDialog.dialog = true;
+
+ 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;
+ });
+ }
+ }
+ );
+}
+
+
+
+
+ Всё, теперь во время загрузки данных мы будем видеть сообщение с бегунком.
+
+
+ P.S. Текст данной части можно взять тут: /data/id304.html
+
+
+ P.P.S. Финальную версию можно скомпилировать командой yarn build.
+
\ No newline at end of file
diff --git a/public/data/start.html b/public/data/start.html
new file mode 100644
index 0000000..c52e2bc
--- /dev/null
+++ b/public/data/start.html
@@ -0,0 +1,7 @@
+
+

+
Самоучитель Vue.js
+
© Александр Чебыкин, 2019
+
Опубликовано под лицензией MIT
+ Git:
https://home.cainet.info:3000/cai/VueJS/
+
diff --git a/public/data/toc.json b/public/data/toc.json
new file mode 100644
index 0000000..510582e
--- /dev/null
+++ b/public/data/toc.json
@@ -0,0 +1,52 @@
+[
+ {
+ "id": 1,
+ "name": "Введение"
+ },
+ {
+ "id": 2,
+ "name": "Подготовка среды разработки",
+ "children": [
+ {
+ "id": 201,
+ "name": "Программное обеспечение"
+ },
+ {
+ "id": 202,
+ "name": "Расширения Visual Studio Code"
+ }
+ ]
+ },
+ {
+ "id": 3,
+ "name": "Разработка",
+ "children": [
+ {
+ "id": 301,
+ "name": "Шаг 1. Создание проекта"
+ },
+ {
+ "id": 302,
+ "name": "Шаг 2. Создание компонента оглавления"
+ },
+ {
+ "id": 303,
+ "name": "Шаг 3. Загрузка данных"
+ },
+ {
+ "id": 304,
+ "name": "Шаг 4. Добавление диалогов"
+ }
+ ]
+ },
+ {
+ "id": 99,
+ "name": "Дополнительная информация",
+ "children": [
+ {
+ "id": 9901,
+ "name": "Команды npm, yarn, vue-cli"
+ }
+ ]
+ }
+]
\ No newline at end of file
diff --git a/public/favicon.ico b/public/favicon.ico
new file mode 100644
index 0000000000000000000000000000000000000000..df36fcfb72584e00488330b560ebcf34a41c64c2
GIT binary patch
literal 4286
zcmds*O-Phc6o&64GDVCEQHxsW(p4>LW*W<827=Unuo8sGpRux(DN@jWP-e29Wl%wj
zY84_aq9}^Am9-cWTD5GGEo#+5Fi2wX_P*bo+xO!)p*7B;iKlbFd(U~_d(U?#hLj56
zPhFkj-|A6~Qk#@g^#D^U0XT1cu=c-vu1+SElX9NR;kzAUV(q0|dl0|%h|dI$%VICy
zJnu2^L*Te9JrJMGh%-P79CL0}dq92RGU6gI{v2~|)p}sG5x0U*z<8U;Ij*hB9z?ei
z@g6Xq-pDoPl=MANPiR7%172VA%r)kevtV-_5H*QJKFmd;8yA$98zCxBZYXTNZ#QFk2(TX0;Y2dt&WitL#$96|gJY=3xX
zpCoi|YNzgO3R`f@IiEeSmKrPSf#h#Qd<$%Ej^RIeeYfsxhPMOG`S`Pz8q``=511zm
zAm)MX5AV^5xIWPyEu7u>qYs?pn$I4nL9J!=K=SGlKLXpE<5x+2cDTXq?brj?n6sp=
zphe9;_JHf40^9~}9i08r{XM$7HB!`{Ys~TK0kx<}ZQng`UPvH*11|q7&l9?@FQz;8
zx!=3<4seY*%=OlbCbcae?5^V_}*K>Uo6ZWV8mTyE^B=DKy7-sdLYkR5Z?paTgK-zyIkKjIcpyO
z{+uIt&YSa_$QnN_@t~L014dyK(fOOo+W*MIxbA6Ndgr=Y!f#Tokqv}n<7-9qfHkc3
z=>a|HWqcX8fzQCT=dqVbogRq!-S>H%yA{1w#2Pn;=e>JiEj7Hl;zdt-2f+j2%DeVD
zsW0Ab)ZK@0cIW%W7z}H{&~yGhn~D;aiP4=;m-HCo`BEI+Kd6
z={Xwx{T%b6i9IjI)Ls)S{-*mq<@~R{?$}ZKjf;^k75i_}(2MXt}^SEBVg7AI@28
zo_uPg2V)_e-`2Ois=PYoe%9u*n9({PFR)OnHJPi{dNx>KxD#iCLfl2vQGDitKtN>z|-AdCN|$jTFDg0m3O`WLD4_s#$S
literal 0
HcmV?d00001
diff --git a/public/img/img301.png b/public/img/img301.png
new file mode 100644
index 0000000000000000000000000000000000000000..1cb91840321318ca5d49bd611819b9a254002c8e
GIT binary patch
literal 123127
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