Первоначальная версия
This commit is contained in:
parent
a0fa2798d9
commit
9f4cda9e64
21
.gitignore
vendored
Normal file
21
.gitignore
vendored
Normal 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
5
babel.config.js
Normal file
@ -0,0 +1,5 @@
|
||||
module.exports = {
|
||||
presets: [
|
||||
'@vue/app'
|
||||
]
|
||||
}
|
||||
53
package.json
Normal file
53
package.json
Normal 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
9
public/data/id1.html
Normal file
@ -0,0 +1,9 @@
|
||||
<h1>Введение</h1>
|
||||
<p>
|
||||
Привет! Данный самоучитель появился в результате желания освоить Vue.js. Я решил что решать абстрактные задачи
|
||||
и записывать свои мысли в блокнот менее интересно чем создать мини-сайт справочник, описывающий процесс его же
|
||||
создания.
|
||||
</p>
|
||||
<p>
|
||||
Надеюсь мои заметки окажутся полезными и вам.
|
||||
</p>
|
||||
37
public/data/id201.html
Normal file
37
public/data/id201.html
Normal 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
17
public/data/id202.html
Normal 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
26
public/data/id301.html
Normal 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
237
public/data/id302.html
Normal 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>
|
||||
<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">Актуальная версия</span>
|
||||
</v-btn>
|
||||
</v-toolbar>
|
||||
|
||||
<v-content>
|
||||
</v-content>
|
||||
</v-app>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
}
|
||||
}
|
||||
</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>
|
||||
<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>
|
||||
</pre>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
Компонент готов, но если вы запустите приложение на исполнение, то никаких изменений не увидите, ведь мы не
|
||||
добавили его в само приложение. Этим сейчас и займёмся. Откройте файл App.vue и в блок <code><v-content>
|
||||
</v-content></code> добавьте наш компонент <code><TOC/></code>. Уже запустили просмотр? Ну и как?
|
||||
Ничего нового? А всё потому, что мы не импортировали его в приложение, срочно исправляем это досадное
|
||||
недоразумение добавлением в блок <code><script></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>
|
||||
<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">Актуальная версия</span>
|
||||
</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>
|
||||
</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>.
|
||||
Приведём блок <v-content></v-content> к следующему виду:
|
||||
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
|
||||
<pre>
|
||||
<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>
|
||||
</pre>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
В блок <code>data()</code> добавьте строку <code>text: ""</code> так как выше, в строке
|
||||
<code><v-flex sm8 v-html="text"></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>
|
||||
<style>
|
||||
.v-treeview-node__content, .v-treeview-node__label {
|
||||
flex-shrink: 1;
|
||||
}
|
||||
.v-treeview-node__root {
|
||||
height: auto;
|
||||
}
|
||||
</style>
|
||||
</pre>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
Отлично! Теперь всё выглядит как надо и можно переходить к следующему шагу.
|
||||
</p>
|
||||
126
public/data/id303.html
Normal file
126
public/data/id303.html
Normal 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><script></script></code> должен выглядеть следующим образом:
|
||||
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
|
||||
<pre>
|
||||
<script>
|
||||
import TOC from './components/TOC'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
TOC
|
||||
},
|
||||
|
||||
data () {
|
||||
return {
|
||||
}
|
||||
},
|
||||
|
||||
mounted () {
|
||||
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
Помимо этого нам надо немного изменить тег нашего оглавления, добавить в него <code>ref="TOC"</code>, после
|
||||
чего выглядеть он должен следующим образом: <code><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
207
public/data/id304.html
Normal 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>
|
||||
<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>
|
||||
</pre>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
Подключите диалог, добавив в блок <code><script></script></code> файла App.vue строку
|
||||
<code>import MessageDialog from './components/MessageDialog'</code> и ниже в <code>components: {}</code>
|
||||
строку <code>MessageDialog</code>. Начало блока <code><sctipt></script></code> должно выглядеть следующим
|
||||
образом:
|
||||
|
||||
<div class="grey lighten-2" style="overflow-x: auto; border-radius: 5px;">
|
||||
<pre>
|
||||
<script>
|
||||
import MessageDialog from './components/MessageDialog'
|
||||
import TOC from './components/TOC'
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {
|
||||
MessageDialog,
|
||||
TOC
|
||||
},
|
||||
</pre>
|
||||
</div>
|
||||
</p>
|
||||
<p>
|
||||
В конец блока <code><v-app></v-app></code> добавьте строку
|
||||
<code><MessageDialog 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>
|
||||
<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>
|
||||
</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
7
public/data/start.html
Normal file
@ -0,0 +1,7 @@
|
||||
<div class="text-xs-center">
|
||||
<img src="../img/logo.png" alt="Логотип Vue.js">
|
||||
<h1>Самоучитель Vue.js</h1>
|
||||
<h2>© Александр Чебыкин, 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
52
public/data/toc.json
Normal 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
BIN
public/favicon.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
public/img/img301.png
Normal file
BIN
public/img/img301.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 120 KiB |
BIN
public/img/logo.png
Normal file
BIN
public/img/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 6.7 KiB |
19
public/index.html
Normal file
19
public/index.html
Normal 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
117
src/App.vue
Normal 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
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
1
src/assets/logo.svg
Normal 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 |
41
src/components/LoadingDialog.vue
Normal file
41
src/components/LoadingDialog.vue
Normal 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>
|
||||
30
src/components/MessageDialog.vue
Normal file
30
src/components/MessageDialog.vue
Normal 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
92
src/components/TOC.vue
Normal 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
13
src/main.js
Normal 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
7
src/plugins/vuetify.js
Normal 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',
|
||||
})
|
||||
Loading…
x
Reference in New Issue
Block a user