Первоначальная версия

This commit is contained in:
Alexander I. Chebykin 2019-06-09 23:43:12 +03:00
parent a0fa2798d9
commit 9f4cda9e64
25 changed files with 9394 additions and 0 deletions

21
.gitignore vendored Normal file
View File

@ -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?

5
babel.config.js Normal file
View File

@ -0,0 +1,5 @@
module.exports = {
presets: [
'@vue/app'
]
}

53
package.json Normal file
View File

@ -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"
]
}

9
public/data/id1.html Normal file
View File

@ -0,0 +1,9 @@
<h1>Введение</h1>
<p>
Привет! Данный самоучитель появился в результате желания освоить Vue.js. Я решил что решать абстрактные задачи
и записывать свои мысли в блокнот менее интересно чем создать мини-сайт справочник, описывающий процесс его же
создания.
</p>
<p>
Надеюсь мои заметки окажутся полезными и вам.
</p>

37
public/data/id201.html Normal file
View File

@ -0,0 +1,37 @@
<h1>Подготовка среды разработки</h1>
<h2>Программное обеспечение</h2>
<p>
Что вам потребуется для создания первого приложения на Vue.js? Самый минимум - это текстовый редактор и
наличие интернета, но мы пойдём другим путём, возьмём инструменты разработки для взрослых, а именно:
менеджер пакетов npm или yarn, систему контроля версий Git и редактор исходного кода (В моём случае это
Visual Stido Code, но вы можете использовать любой другой).
</p>
<p>
Нужен ли вам yarn - решайте сами, сравнений в интернете много, вот одно из них:
<a href="http://prgssr.ru/development/yarn-ili-npm-vse-chto-vam-nuzhno-znat.html" target="_blank">Yarn
или npm: все, что вам нужно знать о них</a>. Я в данном руководстве буду его использовать, но если вы
решили его не устанавливать, то перевести команды в формат npm не составит труда.
</p>
<h3>Ссылки</h3>
<p>
<ol>
<li>npm: <a target="_blank" href="https://www.npmjs.com/get-npm">https://www.npmjs.com/get-npm</a></li>
<li>yarn: <a target="_blank" href="https://www.yarnpkg.com/ru/docs/install#windows-stable">https://www.yarnpkg.com/ru/docs/install#windows-stable</a></li>
<li>Git: <a target="_blank" href="https://git-scm.com/downloads">https://git-scm.com/downloads</a></li>
<li>Visual Studio Code: <a target="_blank" href="https://code.visualstudio.com/download">https://code.visualstudio.com/download</a></li>
</ol>
</p>
<h3>Установка vue-cli</h3>
<p>
Установив менеджер пакетов, перейдём к установке vue-cli с помощью которой в будущем будем создавать наши
великие и не очень проекты. Делается это командой <code>yarn global add @vue/cli</code> если вы решили
использовать yarn или <code>npm install @vue/cli -g</code> если выбор пал на npm. Также имеет смысл сразу
установить пакет vue/cli-upgrade для обновления vue-cli в будущем.
</p>
<p>
На этом установка инструментария для дальнейшей работы завершена, переходим к настройке среды разработки
и созданию нового проекта.
</p>

17
public/data/id202.html Normal file
View File

@ -0,0 +1,17 @@
<h1>Подготовка среды разработки</h1>
<h2>Расширения Visual Studio Code</h2>
<p>
Если вы выбрали другой редактор кода, смело можете пропустить данную часть.
</p>
<p>
<b>Bookmarks.</b> Позволяет создавать закладки с описаниями, что очень помогает в процессе разработки.
</p>
<p>
<b>GitLens.</b> Очень упрощает работу с Git непосредственно из Visual Studio.
</p>
<p>
<b>Vue 2 Snippets.</b> Сниппеты для Vue 2.
</p>
<p>
<b>vuetify-vscode.</b> Сниппеты для Vuetify.
</p>

26
public/data/id301.html Normal file
View File

@ -0,0 +1,26 @@
<h1>Создание проекта</h1>
<p>
Создать проект можно двумя способами: из терминала или при помощи веб-интерфейса. Рассмотрим оба способа.
<ol>
<li>Из терминала, для этого следует выполнить команду <code>vue create _название_проекта_</code></li>
<li>В веб-интерфейсе, запустив его командой <code>vue ui</code></li>
</ol>
</p>
<p>
При создании проекта выберите параметры по умолчанию с установкой babel и eslint.
</p>
<p>
Каркас приложения мы создали, можно посмотреть что получилось выполнив команду <code>yarn serve</code> или
<code>npm run serve</code>. Вдоволь налюбовавшись результатом, добавим к проекту библиотеку интерфейса
Vuetify, выполнив команду <code>vue add vuetify</code>. Установщик задаст вам единственный вопрос - какие
предустановки вы хотите выбрать (Choose a preset), нас устроит набо по умолчанию (default).
</p>
<p>
запустив <code>yarn serve</code> сейчас, вы увидите, что внешний вид нашего приложения несколько изменился,
в нём появились компоненты библиотеки Vuetify. Этот вариант мы и возьмём за основу нашего приложения.
</p>
<h2>Клонирование проекта</h2>
<p>
Текущую версию проекта можно также получить с Git, для этого создайте каталог проекта, перейдите в него
и клонируйте командой <code>git clone https://home.cainet.info:3000/cai/VueJS/</code>.
</p>

237
public/data/id302.html Normal file
View File

@ -0,0 +1,237 @@
<h1>Создание компонента оглавления</h1>
<p>
Первое что мы сделаем в нашем проекте - это компопнент оглавления с поиском главы, тот что вы видите сейчас слева
от данного текста. Но сначала удалим всё ненужное из сгенерированного на предыдущем шаге шаблона.
</p>
<p>
А ненужное - это компонент HelloWorld и все ссылки на него. Смело удаляйте файл HelloWorld.vue из каталога
<code>src/components/</code>, а файл App.vue приведите к следующему виду:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
&lt;template>
&lt;v-app>
&lt;v-toolbar app>
&lt;v-toolbar-title class="headline text-uppercase">
&lt;span>Vue.js:&lt;/span>
&lt;span class="font-weight-light">Самоучитель&lt;/span>
&lt;/v-toolbar-title>
&lt;v-spacer>&lt;/v-spacer>
&lt;v-btn
flat
href="https://github.com/vuetifyjs/vuetify/releases/latest"
target="_blank"
>
&lt;span class="mr-2">Актуальная версия</span>
&lt;/v-btn>
&lt;/v-toolbar>
&lt;v-content>
&lt;/v-content>
&lt;/v-app>
&lt;/template>
&lt;script>
export default {
name: 'App',
components: {
},
data () {
return {
}
}
}
&lt;/script>
</pre>
</div>
</p>
<p>
Vuetify неплохо документирован и имеет множество примеров, одним из них мы и воспользуемся при создании компонента
оглавления. За основу возьмём один из примеров со страницы
<a href="https://next.vuetifyjs.com/ru/components/treeview" target="blank">https://next.vuetifyjs.com/ru/components/treeview</a> и
приведём его к следующему виду:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
&lt;template>
&lt;v-card class="mx-auto">
&lt;v-sheet class="pa-3 primary lighten-2">
&lt;v-text-field
v-model="search"
label="Поиск по названию главы"
dark
flat
solo-inverted
hide-details
clearable
clear-icon="mdi-close-circle-outline"
>
&lt;/v-text-field>
&lt;v-checkbox
v-model="caseSensitive"
dark
hide-details
label="С учётом регистра"
>
&lt;/v-checkbox>
&lt;/v-sheet>
&lt;v-card-text>
&lt;v-treeview
:active.sync="active"
:filter="filter"
:items="items"
:open.sync="open"
:search="search"
activatable
active-class="primary--text"
open-on-click
>
&lt;template v-slot:prepend="{ item }">
&lt;v-icon
v-if="item.children"
v-text="`mdi-${item.id === 1 ? 'home-variant' : 'folder-network'}`"
>
&lt;/v-icon>
&lt;/template>
&lt;/v-treeview>
&lt;/v-card-text>
&lt;/v-card>
&lt;/template>
&lt;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
}
}
}
&lt;/script>
</pre>
</div>
</p>
<p>
Компонент готов, но если вы запустите приложение на исполнение, то никаких изменений не увидите, ведь мы не
добавили его в само приложение. Этим сейчас и займёмся. Откройте файл App.vue и в блок <code>&lt;v-content>
&lt;/v-content></code> добавьте наш компонент <code>&lt;TOC/></code>. Уже запустили просмотр? Ну и как?
Ничего нового? А всё потому, что мы не импортировали его в приложение, срочно исправляем это досадное
недоразумение добавлением в блок <code>&lt;script>&lt;/script></code> строки
<code>import TOC from './components/TOC'</code> и чуть ниже в <code>components: {},</code> строку
<code>TOC</code>. В итоге App.vue должен выглядеть следующим образом:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
&lt;template>
&lt;v-app>
&lt;v-toolbar app>
&lt;v-toolbar-title class="headline text-uppercase">
&lt;span>Vue.js:&lt;/span>
&lt;span class="font-weight-light">Самоучитель&lt;/span>
&lt;/v-toolbar-title>
&lt;v-spacer>&lt;/v-spacer>
&lt;v-btn
flat
href="https://github.com/vuetifyjs/vuetify/releases/latest"
target="_blank"
>
&lt;span class="mr-2">Актуальная версия</span>
&lt;/v-btn>
&lt;/v-toolbar>
&lt;v-content>
&lt;TOC/>
&lt;/v-content>
&lt;/v-app>
&lt;/template>
&lt;script>
import TOC from './components/TOC'
export default {
name: 'App',
components: {
TOC
},
data () {
return {
}
}
}
&lt;/script>
</pre>
</div>
</p>
<p>
Запустили? Круто же? =) Идём дальше.
</p>
<p>
Нам надо чтобы оглавление было во всю ширину экрана только на смартфонах, на компьютере это выглядит как минимум
странно, изменим разметку в файле App.vue, создадим одну колонку шириной 4 для оглавления и одну шириной 8 для
текста. Про двенадцатиточечную систему сеток Vutify можно прочитать тут:
<a href="https://vuetifyjs.com/ru/framework/grid" target="_blank">https://vuetifyjs.com/ru/framework/grid</a>.
Приведём блок &lt;v-content>&lt;/v-content> к следующему виду:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
&lt;v-content>
&lt;v-container fluid fill-height grid-list-md>
&lt;v-layout row wrap full-height>
&lt;v-flex sm4>
&lt;TOC/>
&lt;/v-flex>
&lt;v-flex sm8 v-html="text">&lt;/v-flex>
&lt;/v-layout>
&lt;/v-container>
&lt;/v-content>
</pre>
</div>
</p>
<p>
В блок <code>data()</code> добавьте строку <code>text: ""</code> так как выше, в строке
<code>&lt;v-flex sm8 v-html="text">&lt;/v-flex></code> мы директивой <code>v-html="text"</code> указали Vue, что
оон должен в качестве содержимого блока брать значение свойства text, и если мы его не добавим в объект нашего
приложения, то Vue будет ругаться нехорошими словами.
</p>
<p>
Готово! Почти идеально... Почему почти? Попробуйте уменьшить размер окна браузера и на определённом этапе буквы
вылезут за пределы блока меню... Непорядок! К счастью, исправить сиё недоразумение несложно, просто добавьте в
наш компонент (TOC.vue) блок стилей:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
&lt;style>
.v-treeview-node__content, .v-treeview-node__label {
flex-shrink: 1;
}
.v-treeview-node__root {
height: auto;
}
&lt;/style>
</pre>
</div>
</p>
<p>
Отлично! Теперь всё выглядит как надо и можно переходить к следующему шагу.
</p>

126
public/data/id303.html Normal file
View File

@ -0,0 +1,126 @@
<h1>Загрузка данных</h1>
<p>
В созданном нами компоненте есть один ощутимый недостаток - данные в нём зашиты намертво и чтобы добавить
новую главу, придётся перекомпилировать всё приложение, а так дела не делаются. Сейчас мы добавим возможность
хранения данных во внешних файлах и будем загружать их по мере надобности.
</p>
<p>
Для начала нам понадобится Axios (<a href="https://yarnpkg.com/ru/package/axios" target="_blank">
https://yarnpkg.com/ru/package/axios</a>), именно с его помощью мы будем загружать наши json и html файлы.
Установим его командой <code>yarn add axios vue-axios</code>, после чего добавим в main.js строки для его
подключения. Выглядеть он у вас должен следующим образом:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
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')
</pre>
</div>
</p>
<p>
Вот теперь можно взяться за загрузку внешних данных. Для начала уберём из нашего компонента (TOC.vue) все данные
о разделах, для этого строку items в блоке data приведём к виду <code>items: []</code>, после чего перейдём к
App.vue и в секцию export добавим <code>mounted () {}</code>. Здесь мы в дальнейшем добавим код, исполняемый при
запуске нашего приложения. Блок <code>&lt;script>&lt;/script></code> должен выглядеть следующим образом:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
&lt;script>
import TOC from './components/TOC'
export default {
name: 'App',
components: {
TOC
},
data () {
return {
}
},
mounted () {
}
}
&lt;/script>
</pre>
</div>
</p>
<p>
Помимо этого нам надо немного изменить тег нашего оглавления, добавить в него <code>ref="TOC"</code>, после
чего выглядеть он должен следующим образом: <code>&lt;TOC ref="TOC"/></code>. Нужно нам это для того чтобы
мы могли обращаться к конкретному экземпляру созданного нами компонента.
</p>
<p>
Итак, добавим в секцию <code>mounted</code> код загрузки оглавления:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
mounted () {
this.axios.get("data/toc.json")
.then(response => {
this.$refs.TOC.items = response.data;
})
.catch(error => {
console.log(error);
});
}
</pre>
</div>
</p>
<p>
Готово! Всё работает, убедиться в этом можете открыв консоль браузера и обнаружив там ошибку "Request failed with
status code 404". Чтобы от неё избавиться, создайте в каталоге public/data файл toc.json и заполните его данными
о разделах. Готовый файл можно получить по ссылке <a href="../data/toc.json" target="_blank">/data/toc.json</a>
и сохранить его в <code>public/data/toc.json</code>. После этого оглавление будет загружаться из него.
</p>
<p>
Оглавление у нас есть, осталось реализовать загрузку статей. Для того чтобы сделать это, погрузимся немного в
принципы работы нашего компонента. Каждый раз при выборе элемента, у компонента изменяется свойство active и
нам надо отслеживать его изменение в App.vue. Для этого добавим в mounted файла App.vue следующий код:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
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);
});
}
}
);
</pre>
</div>
</p>
<p>
Остаётся только создать файлы с содержимым статей. Вы можете скачать их из git или по следующим ссылкам:
<ul>
<li><a href="../data/id1.html" target="_blank">/data/id1.html</a></li>
<li><a href="../data/id201.html" target="_blank">/data/id201.html</a></li>
<li><a href="../data/id202.html" target="_blank">/data/id202.html</a></li>
<li><a href="../data/id301.html" target="_blank">/data/id301.html</a></li>
<li><a href="../data/id302.html" target="_blank">/data/id302.html</a></li>
<li><a href="../data/id303.html" target="_blank">/data/id303.html</a></li>
</ul>
Разместите данные файлы в public/data/. Теперь приложение выполняет все свои основные задачи.
</p>
<p>
А теперь попробуйте самостоятельно добавить при старте приложения загрузку и отображение информации о нём.
Файл с информацией можно взять на git или по следующей ссылке:
<a href="../data/start.html" target="_blank">/data/start.html</a>.
</p>

207
public/data/id304.html Normal file
View File

@ -0,0 +1,207 @@
<h1>Добавление диалогов</h1>
<h2>Диалог с сообщением</h2>
<p>
Некоторые ссылки в нашем приложении пока ведут в никуда и единственная реакция на щелчок по ним - это сообщение
в консоли, что не особо информативно, исправим этот недочёт. Создадим новый компонент MessageDialog.vue и, не
изобретая велосипедов, возьмём готовое решение для модального диалога из документации к Vuetify:
<a href="https://vuetifyjs.com/ru/components/dialogs" target="_blank">https://vuetifyjs.com/ru/components/dialogs</a>,
немного его модифицировав:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
&lt;template>
&lt;v-layout row justify-center>
&lt;v-dialog
v-model="dialog"
persistent
max-width="290"
>
&lt;v-card>
&lt;v-card-title class="headline">{{ title }}&lt;/v-card-title>
&lt;v-card-text>{{ text }}&lt;/v-card-text>
&lt;v-card-actions>
&lt;v-spacer>&lt;/v-spacer>
&lt;v-btn color="green darken-1" flat @click="dialog = false">Ok&lt;/v-btn>
&lt;/v-card-actions>
&lt;/v-card>
&lt;/v-dialog>
&lt;/v-layout>
&lt;/template>
&lt;script>
export default {
data () {
return {
dialog: false,
title: "",
text: ""
}
}
}
&lt;/script>
</pre>
</div>
</p>
<p>
Подключите диалог, добавив в блок <code>&lt;script>&lt;/script></code> файла App.vue строку
<code>import MessageDialog from './components/MessageDialog'</code> и ниже в <code>components: {}</code>
строку <code>MessageDialog</code>. Начало блока <code>&lt;sctipt>&lt;/script></code> должно выглядеть следующим
образом:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
&lt;script>
import MessageDialog from './components/MessageDialog'
import TOC from './components/TOC'
export default {
name: 'App',
components: {
MessageDialog,
TOC
},
</pre>
</div>
</p>
<p>
В конец блока <code>&lt;v-app>&lt;/v-app></code> добавьте строку
<code>&ltMessageDialog ref="MessageDialog" /></code>, после чего останется только в коде скрипта добавить
в секции .catch каждого обращения к Axios строки вызова нашего диалога с сообщением об ошибке:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
.catch(error => {
console.log(error);
this.$refs.MessageDialog.title = "Ошибка";
this.$refs.MessageDialog.text = error;
this.$refs.MessageDialog.dialog = true;
});
</pre>
</div>
Теперь все ошибки при загрузке файлов будут показаны пользователю.
</p>
<h2>Диалог с индикатором загрузки</h2>
<p>
При медленном соединении наше приложение будет загружать данные молча, ни коим образом не оповещая пользователя о
том, что оно работает, это нехорошо, давайте выводить при загрузке индикатор. Создадим компонент LoadingDialog.vue
как всегда воспользовавшись шаблоном vuetify:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
&lt;template>
&lt;div class="text-xs-center">
&lt;v-dialog
v-model="dialog"
hide-overlay
persistent
width="300"
>
&lt;v-card
color="primary"
dark
>
&lt;v-card-text>
Загружаю данные, ожидайте...
&lt;v-progress-linear
indeterminate
color="white"
class="mb-0"
>
&lt;/v-progress-linear>
&lt;/v-card-text>
&lt;/v-card>
&lt;/v-dialog>
&lt;/div>
&lt;/template>
&lt;script>
export default {
data () {
return {
dialog: false
}
},
watch: {
dialog (val) {
if (!val) return
}
}
}
&lt;/script>
</pre>
</div>
</p>
<p>
Предлагаю вам самостоятельно подключить его в App.vue, добавив нужные строки в блоки шаблона и скрипта. В шаблоне
значение ref установите равным "LoadingDialog".
</p>
<p>
Справились? Теперь добавим код для отображения и скрытия диалога при загрузке файла в секцию mounted:
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
<pre>
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;
});
}
}
);
}
</pre>
</div>
</p>
<p>
Всё, теперь во время загрузки данных мы будем видеть сообщение с бегунком.
</p>
<p>
P.S. Текст данной части можно взять тут: <a href="../data/id304.html" target="_blank">/data/id304.html</a>
</p>
<p>
P.P.S. Финальную версию можно скомпилировать командой <code>yarn build</code>.
</p>

7
public/data/start.html Normal file
View File

@ -0,0 +1,7 @@
<div class="text-xs-center">
<img src="../img/logo.png" alt="Логотип Vue.js">
<h1>Самоучитель Vue.js</h1>
<h2>&copy; Александр Чебыкин, 2019</h2>
<h3>Опубликовано под лицензией <a href="https://opensource.org/licenses/MIT">MIT</a></h3>
Git: <a href="https://home.cainet.info:3000/cai/VueJS/" target="_blank">https://home.cainet.info:3000/cai/VueJS/</a>
</div>

52
public/data/toc.json Normal file
View File

@ -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"
}
]
}
]

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

BIN
public/img/img301.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 120 KiB

BIN
public/img/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

19
public/index.html Normal file
View File

@ -0,0 +1,19 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title>Самоучитель Vue.js</title>
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:100,300,400,500,700,900">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Material+Icons">
</head>
<body>
<noscript>
<strong>Извините, но самоучитель не работает без JavaScript. Включите его для продолжения.</strong>
</noscript>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

117
src/App.vue Normal file
View File

@ -0,0 +1,117 @@
<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-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;
/*
fetch("data/toc.json")
.then(res => res.json()
.then(data => this.$refs.TOC.items = data))
.catch(err => alert(err));
*/
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;
});
}
}
);
}
}
</script>
<style>
</style>

BIN
src/assets/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

1
src/assets/logo.svg Normal file
View File

@ -0,0 +1 @@
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 87.5 100"><defs><style>.cls-1{fill:#1697f6;}.cls-2{fill:#7bc6ff;}.cls-3{fill:#1867c0;}.cls-4{fill:#aeddff;}</style></defs><title>Artboard 46</title><polyline class="cls-1" points="43.75 0 23.31 0 43.75 48.32"/><polygon class="cls-2" points="43.75 62.5 43.75 100 0 14.58 22.92 14.58 43.75 62.5"/><polyline class="cls-3" points="43.75 0 64.19 0 43.75 48.32"/><polygon class="cls-4" points="64.58 14.58 87.5 14.58 43.75 100 43.75 62.5 64.58 14.58"/></svg>

After

Width:  |  Height:  |  Size: 539 B

View File

@ -0,0 +1,41 @@
<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>

View File

@ -0,0 +1,30 @@
<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>

92
src/components/TOC.vue Normal file
View File

@ -0,0 +1,92 @@
<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: [],
open: [1],
search: null,
caseSensitive: false
}),
mounted () {
/*
fetch("data/toc.json")
.then(res => res.json()
.then(data => this.items = data))
.catch(err => alert(err));
*/
},
computed: {
filter () {
return this.caseSensitive
? (item, search, textKey) => item[textKey].indexOf(search) > -1
: undefined
}
},
methods: {
},
watch: {
active (val) {
//if (val.length) alert("-" + val + "-");
}
}
}
</script>
<style>
/* Стили для того чтобы текст не вылезал за границы блока */
.v-treeview-node__content, .v-treeview-node__label {
flex-shrink: 1;
}
.v-treeview-node__root {
height: auto;
}
</style>

13
src/main.js Normal file
View File

@ -0,0 +1,13 @@
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')

7
src/plugins/vuetify.js Normal file
View File

@ -0,0 +1,7 @@
import Vue from 'vue'
import Vuetify from 'vuetify/lib'
import 'vuetify/src/stylus/app.styl'
Vue.use(Vuetify, {
iconfont: 'md',
})

8277
yarn.lock Normal file

File diff suppressed because it is too large Load Diff