Este componente de Vue implementa botones de navegación con niveles ilimitados mediante recursión, basado en una estructura de árbol jerárquico.
Propiedades del componente
:datos=> [Array] Datos con formato id y parentId (ver ejemplo).:seleccionados=> [Array] Valores actualmente seleccionados.
Eventos del componente
@clic=> Se emite al hacer clic en un elemento.
Ejemplo de uso
<tree-nav :datos="listaItems" @clic="manejarClicElemento" :seleccionados="elementosSeleccionados"></tree-nav>
Código de ejemplo
new Vue({
nombre: 'aplicacion',
plantilla: `
<tree-nav :datos="listaItems" @clic="manejarClicElemento" :seleccionados="elementosSeleccionados"></tree-nav>
`,
componentes: { treeNav },
datos() {
return {
listaItems: [
{ id: 1, nombre: "Región Norte", parentId: 0 },
{ id: 2, nombre: "Ciudad A", parentId: 1 },
{ id: 3, nombre: "Ciudad B", parentId: 1 },
{ id: 4, nombre: "Distrito X", parentId: 3 },
{ id: 5, nombre: "Distrito Y", parentId: 3 },
{ id: 6, nombre: "Subdistrito Z", parentId: 4 },
{ id: 7, nombre: "Región Sur", parentId: 0 },
{ id: 8, nombre: "Ciudad C", parentId: 7 },
{ id: 9, nombre: "Ciudad D", parentId: 7 }
],
elementosSeleccionados: []
};
},
metodos: {
manejarClicElemento(elemento) {
console.log(elemento);
}
}
}).$mount('#contenedor')
Estilos CSS
<estilo>
#aplicacion {
posicion: relativa;
}
.boton {
ancho: 100px;
relleno: 4px 10px;
borde: 1px solid #ccc;
radio-borde: 4px;
alineacion-texto: centro;
}
.boton:hover {
cursor: predeterminado;
}
.contenedor-elemento {
posicion: absoluta;
superior: 180px;
}
.boton.activo {
color-fondo: #666;
color: #fff;
}
</estilo>
Código de plantilla HTML
<html lang="es">
<cabecera>
<meta charset="UTF-8">
<meta http-equiv="X-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<titulo>Documento</titulo>
<script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
</cabecera>
<cuerpo>
<div id="contenedor"></div>
</cuerpo>
</html>
Código JavaScript del componnete
const treeNav = {
nombre: 'tree-nav',
propiedades: {
datos: {
tipo: Array,
defecto: () => []
},
seleccionados: {
tipo: Array,
defecto: () => []
}
},
plantilla: `
<div>
<h2>{{seleccionados}}</h2>
<elemento-tree-nav :items="arbol" @clic="clicElemento" :seleccionados="seleccionados" ref="refNav"></elemento-tree-nav>
</div>
`,
componentes: {
elementoTreeNav: {
nombre: 'elemento-tree-nav',
plantilla: `
<div>
<div v-for="(item, indice) in items" :key="indice">
<p v-show="item.visible" class="boton"
:class="{activo: seleccionados[item.nivel] == item.nombre}"
@click="clicElemento(item, indice)">
{{ item.nombre }}
</p>
<div v-if="item.hijos && item.hijos.length">
<elemento-tree-nav class="contenedor-elemento"
:seleccionados="seleccionados"
:class="['contenedor-nivel-' + item.nivel]"
:items="item.hijos">
</elemento-tree-nav>
</div>
</div>
</div>`,
propiedades: {
items: {
tipo: Array,
defecto: () => []
},
seleccionados: {
tipo: Array,
defecto: () => []
}
},
datos() {
return {};
},
metodos: {
clicElemento(item, indice) {
this.ocultarItems(this.items);
this.items.forEach(v => v.visible = true);
this.procesarClic(item);
this.$emit('clic', item);
},
procesarClic(item) {
item.visible = true;
this.$set(this.seleccionados, item.nivel, item.nombre);
this.seleccionados.splice(item.nivel + 1);
if (item.hijos && item.hijos.length > 0) {
item.hijos.forEach((v, k) => {
if (k === 0) this.procesarClic(v);
v.visible = true;
});
}
},
ocultarItems(arreglo) {
arreglo.forEach(v => {
v.visible = false;
if (v.hijos) this.ocultarItems(v.hijos);
});
}
}
}
},
datos() {
return {
arbol: []
};
},
metodos: {
clicElemento(item) {
this.$emit('clic', item);
},
construirArbol(elementos) {
let raiz = [];
let asignarHijos = (nodoPadre, nivelPadre) => {
nivelPadre++;
elementos.forEach(v => {
if (v.parentId === nodoPadre.id) {
const nuevoNodo = {
...v,
visible: nodoPadre.id === 0,
hijos: [],
nivel: nivelPadre
};
raiz.push(nuevoNodo);
asignarHijos(nuevoNodo, nivelPadre);
}
});
};
asignarHijos({ id: 0 }, -1);
return raiz;
}
},
montado() {
this.arbol = this.construirArbol(this.datos);
setTimeout(() => {
this.$refs.refNav.clicElemento(this.arbol[0]);
}, 0);
}
};