Выпадающее многоуровневое меню Vue.js
Легкий и простой в использовании компонент Vue.js для навигации, что выделяет элементы меню до целевого раздела при нажатии при использовании файла. Создайте меню с разворачиваемым деревом с помощью рекурсивных компонентов Vue.js. Было бы неплохо визуально определить «глубину» дочернего компонента, чтобы пользователь получил представление. Чтобы продемонстрировать, как эффективно использовать рекурсивные компоненты сжимаемого древовидного меню. Рекурсивные компоненты полезны для отображения комментариев в блоге, вложенных меню или в целом, где родитель и потомок одинаковы, хотя и с разным содержанием. Дерево рекурсивных компонентов пользовательского интерфейса будет визуальным представлением некоторой рекурсивной структуры данных. В этом уроке мы будем использовать древовидную структуру, где каждый узел является объектом. Стильное многоуровневое древовидное меню Vue.js. В рабочем виде: Установка: HEAD Код <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css"> <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script> HTML Код <div class="navigatsiya centralna" id="app"> <ul class="samoletovosa-menyu"> <item :model="treeData"></item> </ul> </div> CSS Код .samoletovosa-menyu { width: 85%; max-width: 550px; margin: 1em auto; background: rgba(27, 27, 27, 0.7); box-shadow: -1px 8px 10px 3px rgba(0, 0, 0, 0.3); } .samoletovosa-menyu li { user-select: none; } .samoletovosa-menyu li span { float: right; } .samoletovosa-menyu label, .samoletovosa-menyu a { position: relative; display: block; padding: 16px 16px 16px 54px; box-shadow: inset 0 -1px #000; color: #ffffff; } .no-touch .samoletovosa-menyu label:hover, .no-touch .samoletovosa-menyu a:hover { background: #52565d; } .samoletovosa-menyu label::before, .samoletovosa-menyu label::after, .samoletovosa-menyu a::after { content: ''; display: inline-block; width: 16px; height: 16px; position: absolute; top: 50%; transform: translateY(-50%); } .samoletovosa-menyu label { cursor: pointer; } .samoletovosa-menyu label span { float: right; color: #828282; } .samoletovosa-menyu li.file > label::before { content: "\f15b"; } .samoletovosa-menyu li.folder > label::before { content: "\f054"; } .samoletovosa-menyu li.file > label::before { content: "\f15b"; } .samoletovosa-menyu li.add > label::before { content: "\f067"; } .samoletovosa-menyu label::before { /* arrow icon */ font: normal normal normal 14px/1 FontAwesome; left: 18px; transform: translateY(-50%); transition: transform 0.3s; } .samoletovosa-menyu label.open::before { transform: translateY(-25%) rotate(90deg); } .samoletovosa-menyu ul label, .samoletovosa-menyu ul a { background: #1f1e1e; box-shadow: inset 0 -1px #353535; padding-left: 82px; } .no-touch .samoletovosa-menyu ul label:hover, .no-touch .samoletovosa-menyu ul a:hover { background: #3c3f45; } .samoletovosa-menyu > li:last-of-type > label, .samoletovosa-menyu > li:last-of-type > a, .samoletovosa-menyu > li > ul > li:last-of-type label, .samoletovosa-menyu > li > ul > li:last-of-type a { box-shadow: none; } .samoletovosa-menyu ul label::before { left: 36px; } .samoletovosa-menyu ul label::after, .samoletovosa-menyu ul a::after { left: 59px; } .samoletovosa-menyu ul ul label, .samoletovosa-menyu ul ul a { padding-left: 100px; } .samoletovosa-menyu ul ul label::before { left: 54px; } .samoletovosa-menyu ul ul label::after, .samoletovosa-menyu ul ul a::after { left: 77px; } .samoletovosa-menyu ul ul ul label, .samoletovosa-menyu ul ul ul a { padding-left: 118px; } .samoletovosa-menyu ul ul ul label::before { left: 72px; } .samoletovosa-menyu ul ul ul label::after, .samoletovosa-menyu ul ul ul a::after { left: 95px; } JS Код var treeData = { name: "ZORNET.RU", children: [ { name: "Первый раздел", children: [{ name: "Под категория #1" }, { name: "Под категория #2" }] }, { name: "Второй раздел", children: [{ name: "Первый запрос" }, { name: "Второй запрос" }] }, { name: "Третий запрос" } ] }; Vue.component("item", { template: `<li :class="[isFolder ? 'folder' : 'file']"> <label :class="{'open': open}" @click="toggle" @dblclick="changeType"> {{ model.name }} <span v-if="isFolder">{{ isFolder ? model.children.length : '' }}</span> </label> <ul v-show="open" v-if="isFolder" :class="{'open': open}"> <item v-for="(model, index) in model.children" :key="index" :model="model"> </item> <li class="add" @click="addChild"><label>Добавить новый материал</label></li> </ul> </li>`, props: { model: Object }, data: function() { return { open: true }; }, computed: { isFolder: function() { return this.model.children && this.model.children.length; } }, methods: { toggle: function() { if (this.isFolder) { this.open = !this.open; } }, changeType: function() { if (!this.isFolder) { Vue.set(this.model, "children", []); this.addChild(); this.open = true; } }, addChild: function() { this.model.children.push({ name: "Новый предмет" }); } } }); new Vue({ el: "#app", data: { treeData: treeData } }); Как и для любой рекурсивной функции, для завершения рекурсии необходим базовый случай, в противном случае рендеринг будет продолжаться бесконечно, и вы получите переполнение стека. Поскольку рекурсивные структуры данных могут быть большими, хорошая хитрость пользовательского интерфейса для их отображения заключается в том, чтобы скрыть все, кроме корневого узла, чтобы пользователь мог расширять или сжимать по мере необходимости. Демонстрация |
Поделиться в социальных сетях
Материал разместил
Комментарии: 1 | |
| |