Подпишитесь на мою рассылку сейчас по адресу http://jauyeung.net/subscribe/.
Подпишитесь на меня в Twitter по адресу https://twitter.com/AuMayeung
Перетаскивание - это функция многих интерактивных веб-приложений. Он предоставляет пользователям интуитивно понятный способ манипулировать своими данными. Добавление функции перетаскивания легко добавить в приложения Vue.js.
Приложение, которое мы создаем
Мы создадим приложение todo с двумя столбцами - столбцом To Do и столбцом Done. Вы можете перетаскивать между ними два, чтобы изменить статус с сделать на сделано и наоборот. Для создания приложения мы используем библиотеку Vue Material с пакетом Vue Draggable, чтобы приложение выглядело хорошо и с легкостью предоставляло возможность перетаскивания. У него также будет меню навигации и верхняя панель.
Начиная
Чтобы начать создание приложения, мы начнем с установки Vue CLI. Устанавливаем, запустив npm i -g @vue/cli
. После этого мы можем создавать проект. Для этого запускаем vue create todo-app
. Вместо того, чтобы выбирать значение по умолчанию, мы настраиваем каркас приложения, выбирая альтернативный вариант и выбирая Vue Router, Babel и препроцессор CSS. Строительные леса должны быть завершены после выполнения инструкций, а затем мы готовы добавить несколько библиотек.
Нам нужно добавить Axios для выполнения HTTP-запросов и библиотеку Vue Material с пакетами Vue Draggable, о которых мы упоминали ранее, чтобы сделать наше приложение красивее и предоставить возможности перетаскивания, которые мы желаем соответственно. Кроме того, нам нужен пакет Vee Validate, чтобы мы могли выполнять проверку формы в нашем приложении. Чтобы установить эти пакеты, мы запускаем npm i axios vuedraggable vue-material [email protected]
.
Создание приложения
Теперь мы можем написать код. Для начала мы добавляем миксин, чтобы мы могли делать наши запросы. Для этого мы создаем папку mixins
и добавляем файл с именем todoMixin.js
, а затем помещаем в наш файл следующее:
const axios = require('axios'); const apiUrl = 'http://localhost:3000'; export const todoMixin = { methods: { getTodos() { return axios.get(`${apiUrl}/todos`); }, addTodo(data) { return axios.post(`${apiUrl}/todos`, data); }, editTodo(data) { return axios.put(`${apiUrl}/todos/${data.id}`, data); }, deleteTodo(id) { return axios.delete(`${apiUrl}/todos/${id}`); } } }
Эти функции будут использоваться на нашей домашней странице для выполнения HTTP-запросов на выполнение CRUD в нашем списке задач. Сейчас мы создадим домашнюю страницу. В Home.vue
мы заменяем то, что у нас есть, на следующее:
<template> <div class="home"> <div class="center"> <h1>To Do List</h1> <md-button class="md-raised" @click="showDialog = true">Add Todo</md-button> </div> <div class="content"> <md-dialog :md-active.sync="showDialog"> <md-dialog-title>Add Todo</md-dialog-title> <form @submit="addNewTodo" novalidate> <md-field :class="{ 'md-invalid': errors.has('description') }"> <label for="description">Description</label> <md-input type="text" name="description" v-model="taskData.description" v-validate="'required'" ></md-input> <span class="md-error" v-if="errors.has('description')">{{errors.first('description')}}</span> </md-field> <md-dialog-actions> <md-button class="md-primary" @click="showDialog = false">Close</md-button> <md-button class="md-primary" @click="showDialog = false">Save</md-button> </md-dialog-actions> </form> </md-dialog> <div class="lists"> <div class="left"> <h2>To Do</h2> <draggable v-model="todo" group="tasks" @change="updateTodo"> <div v-for="t in todo" :key="t.id" class="item"> {{t.description}} <a @click="deleteTask(t.id)"> <md-icon>close</md-icon> </a> </div> </draggable> </div> <div class="right"> <h2>Done</h2> <draggable v-model="done" group="tasks" @change="updateTodo"> <div v-for="d in done" :key="d.id" class="item"> {{d.description}} <a @click="deleteTask(d.id)"> <md-icon>close</md-icon> </a> </div> </draggable> </div> </div> </div> </div> </template> <script> // @ is an alias to /src import draggable from "vuedraggable"; import { todoMixin } from "@/mixins/todoMixin"; export default { name: "home", components: { draggable }, computed: { isFormDirty() { return Object.keys(this.fields).some(key => this.fields[key].dirty); } }, mixins: [todoMixin], data() { return { todo: [], done: [], showDialog: false, taskData: {} }; }, beforeMount() { this.getNewTodos(); }, methods: { async addNewTodo(evt) { evt.preventDefault(); if (!this.isFormDirty || this.errors.items.length > 0) { return; } await this.addTodo(this.taskData); this.showDialog = false; this.getNewTodos(); }, async getNewTodos() { const response = await this.getTodos(); this.todo = response.data.filter(t => !t.done); this.done = response.data.filter(t => t.done); }, async updateTodo(evt) { let todo = evt.removed && evt.removed.element; if (todo) { todo.done = !todo.done; await this.editTodo(todo); } }, async deleteTask(id) { const todo = await this.deleteTodo(id); this.getNewTodos(); } } }; </script> <style lang="scss" scoped> .center { text-align: center; } .md-dialog { width: 70vw; } form { width: 92%; } .md-dialog-title.md-title { color: black !important; } .lists { padding-left: 5vw; display: flex; align-items: flex-start; .left, .right { width: 45vw; padding: 20px; min-height: 200px; .item { padding: 10px; border: 1px solid black; background-color: white; display: flex; justify-content: space-between; a { cursor: pointer; } } } } </style>
Мы добавили диалоговое окно с формой для ввода описания нашей задачи. Поле обязательно для заполнения, но пользователь может ввести что угодно. Функция addTodo
принимает введенные данные и отправляет их, если они действительны. Функция this.fields
предоставляется пакетом Vee Validate и имеет все поля в объекте, поэтому мы можем проверить, были ли поля изменены или нет. Все, что указано в свойстве computed
, является компьютером всякий раз, когда что-либо, возвращаемое функцией, изменяется.
Затем мы добавили 2 списка, которые мы перетаскиваем между собой, чтобы мы могли изменить статус задач на выполненные или невыполненные. Списки - это draggable
компоненты в шаблоне. Мы определили модели в draggable
компонентах в объекте, возвращаемом функцией data
. Важно, чтобы у нас была одна и та же group
опора, чтобы мы могли перетаскивать между ними 2 перетаскиваемых компонента. Всякий раз, когда происходит перетаскивание, возникает событие change
и вызывается функция updateTodo
. Функция переключит флаг done
задачи и сделает запрос на сохранение задачи.
У каждой задачи также есть кнопка для ее удаления. При нажатии кнопки закрытия вызывается функция deleteTodo
. id
задачи передается в функцию, поэтому мы можем сделать запрос на удаление задачи.
Затем в App.vue
мы добавляем меню и левую панель навигации со следующим кодом:
<template> <div id="app"> <md-toolbar> <md-button class="md-icon-button" @click="showNavigation = true"> <md-icon>menu</md-icon> </md-button> <h3 class="md-title">Todo App</h3> </md-toolbar> <md-drawer :md-active.sync="showNavigation" md-swipeable> <md-toolbar class="md-transparent" md-elevation="0"> <span class="md-title">Todo App</span> </md-toolbar> <md-list> <md-list-item> <router-link to="/"> <span class="md-list-item-text">Home</span> </router-link> </md-list-item> </md-list> </md-drawer> <router-view /> </div> </template> <script> export default { name: "app", data: () => { return { showNavigation: false }; } }; </script> <style> .center { text-align: center; } form { width: 95vw; margin: 0 auto; } .md-toolbar.md-theme-default { background: #009688 !important; height: 60px; } .md-title, .md-toolbar.md-theme-default .md-icon { color: #fff !important; } </style>
<router-view />
отображает маршруты, которые мы определим в router.js
, который состоит только из домашней страницы.
В main.js
мы помещаем:
import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' import VueMaterial from 'vue-material'; import VeeValidate from 'vee-validate'; import 'vue-material/dist/vue-material.min.css' import 'vue-material/dist/theme/default.css' Vue.config.productionTip = false; Vue.use(VueMaterial); Vue.use(VeeValidate); new Vue({ router, store, render: h => h(App) }).$mount('#app')
чтобы включить дополнительные библиотеки Vue.js, которые мы используем в этом приложении.
И в router.js
мы добавляем:
import Vue from 'vue' import Router from 'vue-router' import Home from './views/Home.vue'; Vue.use(Router) export default new Router({ mode: 'history', base: process.env.BASE_URL, routes: [ { path: '/', name: 'home', component: Home } ] })
Это добавляет домашнюю страницу в наш список маршрутов, так что она будет отображаться пользователю при вводе URL-адреса или нажатии ссылки на страницу.
Наш JSON API будет добавлен без написания кода с использованием пакета JSON Server Node.js, расположенного по адресу https://github.com/typicode/json-server. Данные будут сохранены в файл JSON, поэтому нам не нужно создавать собственные серверные надстройки для сохранения некоторых простых данных. Устанавливаем сервер, запустив npm i -g json-server
. Затем, когда это будет сделано, перейдите в каталог нашего проекта и запустите json-server --watch db.json
. В db.json
мы помещаем:
{ "todos": [] }
так что мы можем использовать эти конечные точки для сохранения данных в db.json
, которые имеют те же URL-адреса, что и в todoMixin
.
После того, как все работы проделаны, у нас есть следующее: