Vue.js 모달(Modal) 컴포넌트 분리하기

Vue.js 모달(modal) 컴포넌트 구현하기

Vue.js 공식 문서를 참고해서 만들었습니다.[https://kr.vuejs.org/v2/examples/modal.html]

사용 언어

- vue-cli (class component 기반)

- typescript

모달창은 프로젝트 내에서 다양하게 쓰일 일이 많아서 따로 컴포넌트로 분리하여 구현하였습니다.

스타일은 각자의 상황에 따라 구현해주시면 좋을 것 같습니다

태그 부분

크게 header / body / footer로 나뉩니다.

  • 각각의 레이아웃마다 확장성을 주기 위해 slot을 사용

header - 이미지 / 제목

  • 현재 header에 이미지가 필요한 사항이라 추가적으로 이미지 태그를 사용했습니다.

  • 태그는 퀘이사를 사용하였으므로 사용하시는 프레임워크의 태그로 대체 해주시면 됩니다.
  • 커스텀 이벤트 태그를 사용하였기 때문에 이미지를 사용하고 싶지 않은 경우 모달을 사용할 때 제외해주시면됩니다.

body - 바디

footer - 모달 종료 버튼

  • 종료 버튼의 문구 유연성

스크립트

부모에게 전달하기 위한 prop

Modal.vue

<template>
    <transition name="modal">
        <div class="modal-mask">
            <div class="modal-wrapper">
                <div class="modal-container">
                    <div class="modal-header">
                        <q-img class="modal-img" :src="src"></q-img>
                        <slot name="header"></slot>
                    </div>
                    <div class="modal-body">
                        <slot name="body"> </slot>
                    </div>
                    <div class="modal-footer">
                         <slot name="footer"> </slot>
                        <div
                        @click="$emit('close')"
                          class="close-btn"
                        ></div>
                    </div>
                </div>
            </div>
        </div>
    </transition>
</template>

<script lang="ts">
import { Component, Prop, Vue } from "vue-property-decorator";

@Component({
    components: {},
})
export default class GuideModal extends Vue {
    @Prop() private src!: string;
    @Prop() private close!: string;
}
</script>

<style scoped>
.modal-mask {
    position: fixed;
    z-index: 9998;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    display: table;
    text-align: center;
    transition: opacity 0.3s ease;
}
.close-btn{
    color:#838282;
    text-decoration: underline;
}
.close-btn:hover{
    cursor: pointer;
}
.modal-img {
    margin-top: 4%;
    width: 55%;
}

.modal-wrapper {
    display: table-cell;
    vertical-align: middle;
}

.modal-container {
    width: 40%;
    height: 55%;
    margin: 0px auto;
    padding: 20px 30px;
    background-color: #fff;
    border-radius: 2px;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.33);
    transition: all 0.3s ease;
    font-family: Helvetica, Arial, sans-serif;
}

.modal-body {
    margin: 20px 0;
}

.modal-default-button {
    float: right;
}

/*
 * The following styles are auto-applied to elements with
 * transition="modal" when their visibility is toggled
 * by Vue.js.
 *
 * You can easily play with the modal transition by editing
 * these styles.
 */

.modal-enter {
    opacity: 0;
}

.modal-leave-active {
    opacity: 0;
}

.modal-enter .modal-container,
.modal-leave-active .modal-container {
    -webkit-transform: scale(1.1);
    transform: scale(1.1);
    
}
</style>

modalTest.vue


<template v-if="modal">
	<guide-modal @close="modal = false" :close="종료" src="이미지 경로">
		<template v-slot:header >
			<p class="header">헤더</p>
            <p class="header-desc">헤더 디테일 </p>
		</template>
		<template v-slot:body >
		/* 바디에 버튼을 사용해야해서 버튼을 추가
		   원하는 모든 태그 삽입가능
		*/
		
			<q-btn
                   class="modal-body-btn"
                   color="black"
                   text-color="white"
                   :label="버튼"
                   @click="movePage()"
                   />
		</template>
	</guide-modal>
</template>

<script lang="ts">
import GuideModal from "layouts/guideModal.vue";
@Component({
    components: {GuideModal}
})
export default class ModalTest extends Vue {
     private modal: boolean = false;
     
    movePage(){        
        window.location.href = 이동할 페이지 url

    }
    showModal(){
        //모달을 보여주는 경우
         this.modal=true;
        //아닌경우
         this.modal=false;
             
    }
}
</script>
<style scoped>
.header{
        color:#181818;
        font-weight: bold;
        font-size: 3rem;
        
    }
    .header-desc{
        color: #181818;
        font-size: 1rem;
    }
    .modal-body-btn{
        margin-top: 20px;
        border-radius: 40px;
        padding: 0 20px 0 20px;
        height: 60px;    
        font-size: 17px;
        font-weight: bold;
    }
</style>