본문 바로가기
개발이야기/Vuetify

[Vuetify] 메뉴 만들기 (With Vuex)

by dev.josh 2020. 7. 13.
반응형

환경

- Node v10.16.0

- Vuetify2

 

결과물

Vuetify와 Vuex를 사용하여 2뎁스까지 지원하는 메뉴 체계를 작성해 보았습니다.

 

화면구성

Menus.vue : 메뉴 출력 및 동작 컴포넌트

PageTitle.vue : 페이지 타이틀 아이콘/이름 출력 컴포넌트

Index.vue : 라우터로 이동될 페이지

 

메뉴 이동에따라 화면을 전환하고 PageTItle내용을 수정해 주겠습니다.

Index.vue (메뉴설정) 부분에는 메뉴 색상 및 메뉴 추가, 삭제 기능이 있습니다.

 

 

store/state/color.js

const colorState = {
  colors: {
    menu_background_color: '#263238',
    menu_selected_color: 'red',
  }
}

export default colorState;

메뉴의 배경 색상 (menu_background_color)과, 메뉴 선택시 색상(menu_selected_color)을 담을 변수

menu_background_color 는 RGB 색상코드, menu_selected_color 는 vuetilfy의 Material color 체계를 채택하였습니다.

https://vuetifyjs.com/en/styles/colors/#material-colors

 

Material color palette — Vuetify.js

Learn about the colors of Material Design. Consume the javascript color pack directly in your application.

vuetifyjs.com

 

 

store/state/menus.js

const menuState = {
  menus: [
    {
      id:1,
      icon: "loop",
      title: "메뉴 설정",
      target: "Index",
    },
    {
      id:2,
      icon: "android",
      title: "페이지2",
      target: "Page2",
      model: false,
      childrens: [
        {
          id:21,
          icon: "assignment_ind",
          title: "페이지2_1",
          target: "Page2_1",
        },
        {
          id:22,
          icon: "people_alt",
          title: "페이지2_2",
          target: "Page2_2",
        }
      ]
    },
    {
      id:3,
      icon: "trending_up",
      title: "페이지3",
      target: "Page3",
    },
  ]
}
  
export default menuState;

전체적인 메뉴의 기본 틀이될 데이터형식은 아래와 같습니다.

{

   id: int,  //id 값은 고유번호로 설정

   icon: String,  //Material icon 형식의 이름

   title: String,  //메뉴의 이름

   target: String,  //메뉴 클릭시 이동될 roturer의 이름

   childrens : [],  //2뎁스의 메뉴 사용시 설정

}

 

 

router/index.js

import Vue from "vue";
import VueRouter from "vue-router";

import Menus from "../components/Menus.vue";
import Index from "../view/Index.vue";
import Page2_1 from "../view/Page2_1.vue";

Vue.use(VueRouter);

const routes = [
  {
    path: '/',
    name: 'Index',
    components: {
      menu: Menus,
      content: Index,
    },
  },
  {
    path: '/Page2_1',
    name: 'Page2_1',
    components: {
      menu: Menus,
      content: Page2_1,
    },
  },
  {
    path: '/Page2_2',
    name: 'Page2_2',
    components: {
      menu: Menus,
      content: { template: `
        <div style="width: 100%;
        height: 100%;
        background-color: green;">test</div>
      `},
    },
  },
  {
    path: '/Page3',
    name: 'Page3',
    components: {
      menu: Menus,
      content: { template: `
        <div style="width: 100%;
        height: 100%;
        background-color: gray;">test</div>
      `},
    },
  },
]

export const router = new VueRouter({
  routes
});

store/state/menus.js의 target 과 router/index.js의 name의 이름을 동일하게 맞춰줍니다.

 

 

Menus.vue

<template>
  <div>
    <v-navigation-drawer
      id="app-drawer"
      v-model="drawer"
      app
      dark
      :color="colors.menu_background_color"
      floating
      persistent
      mobile-break-point="960"
      width="280"
    >
      <div>
        <v-layout
          class="fill-height"
          tag="v-list"
          column
        >
          <v-list>
            <v-list-item @click="movePage('');">
              <v-toolbar-title><v-icon class="mr-2">home</v-icon>JoBlog</v-toolbar-title>
            </v-list-item>
            <hr class="mt-2 mb-2">
            <v-list-item-group active-class="white--text">
              <template v-for="menu in menus">
                <template v-if="menu.childrens"> <!-- 자식 메뉴가 있는경우 -->
                  <v-list-group
                    :prepend-icon="menu.icon" 
                    :key="menu.id" 
                  >
                    <template v-slot:activator>
                      <v-list-item-title>{{menu.title}}</v-list-item-title>
                    </template>
                    <template v-for="children in menu.childrens">
                      <v-list-item 
                        @click="movePage(children.target);" 
                        :key="children.id" 
                        class="ml-2" 
                        :active-class="`${colors.menu_selected_color} accent-4 white--text`"
                      >
                        <v-list-item-icon :active-class="`${colors.menu_selected_color} accent-4`">
                          <v-icon>{{children.icon}}</v-icon>
                        </v-list-item-icon>
                        <v-list-item-title>
                          {{children.title}}
                        </v-list-item-title>
                      </v-list-item>
                    </template>
                  </v-list-group>
                </template>
                <template v-else> <!-- 단일 메뉴일 경우 -->
                  <v-list-item 
                    @click="movePage(menu.target);" 
                    :key="menu.id" 
                    :active-class="`${colors.menu_selected_color} accent-4 white--text`"
                  >
                    <v-list-item-icon>
                      <v-icon>{{menu.icon}}</v-icon>
                    </v-list-item-icon>
                    <v-list-item-title>{{menu.title}}</v-list-item-title>
                  </v-list-item>
                </template>
              </template>
            </v-list-item-group>
          </v-list>
        </v-layout>
      </div>
    </v-navigation-drawer>
  </div>
</template>

<script>

import { mapState } from "vuex";
import _ from "lodash";

export default {
  components: {
  },
  data: () => ({
    drawer: null,
    color: 'success',
    responsive: false,
  }),
  computed: _.extend(
    mapState(["menus", "colors"]),
  ),
  mounted(){
    this.onResponsiveInverted()
    window.addEventListener('resize', this.onResponsiveInverted)
  },
  methods: {
    movePage(target){
      this.$router.push({ name: target });
    },
    onResponsiveInverted () {
      if (window.innerWidth < 1000) {
        this.responsive = true
      } else {
        this.responsive = false
      }
    },
  },
}
</script>

<style lang="scss">
  #app-drawer {
    .v-list__tile {
      border-radius: 4px;
      &--buy {
        margin-top: auto;
        margin-bottom: 17px;
      }
    }
  }
</style>

 

 

참고 자료

https://vuetifyjs.com/en/components/list-item-groups/#list-item-groups

 

List item group component — Vuetify.js

The list item group component provides an interface for displaying a series of content using list items.

vuetifyjs.com

https://demos.creative-tim.com/vuetify-material-dashboard/#/

 

Material Dashboard by Creative Tim

Vue Material Dashboard is a beautiful resource built over Vue Material and Vuejs. It will help you get started developing dashboards in no time.

demos.creative-tim.com

 

전체 소스

https://github.com/Jo-App/vuetilfy_menu

 

Jo-App/vuetilfy_menu

Contribute to Jo-App/vuetilfy_menu development by creating an account on GitHub.

github.com

 

반응형