2022-01-08

Vue.js로 개발하면서 나중에 쉽게 쓸 수 있도록

공통으로 만들어놓은 컴포넌트가 몇 개 있었는데,

그걸 이제 여기다가 정리해보려 한다.

Alert & Confirm (Vuex와 같이 쓰는 것)

<!-- Alert.vue -->
<template>
    <div id="alert">
        <div>
            <div>
                <div>
                    <div>
                        <div>
                            <div v-html="title.replaceAll('\\n', '<br />')"></div>
                        </div>
                        <div>
                            <input type="button" :value="okButtonText" @click="onOk" />
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    import Vue from "vue";
    import { mapActions } from "vuex";
    
    export default {
        name: "Alert",
        props: {
            title: String,
            okButtonText: String,
        },
        methods: {
            ...mapActions({
                setInvisible: "SET_ALERT_INVISIBLE",
                setVisible: "SET_ALERT_VISIBLE"
            }),
            onOk() {
                this.$emit("ok");
                this.setInvisible();
            }
        },
        mounted: {
            Vue.prototype.$alert = (title, onOk = () => null) => this.setVisible({ show: true, title, okButtonText: "확인", onOk });
        }
    }
</script>

<style lang="scss">
    #alert {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.62);
        z-index: 999;
        display: inline;

        > div {
            width: 100%;
            height: 100%;

            > div {
                display: table;
                width: 100%;
                height: 100%;

                > div {
                    display: table-cell;
                    vertical-align: middle;
                    text-align: center;

                    > div {
                        display: inline-block;
                        z-index: 9999;
                        animation-duration: 0.6s;
                        /* 스타일 속성 추가 */

                        > div {
                            &:first-child {
                                /* 스타일 속성 추가 */
                            }

                            &:last-child {
                                /* 스타일 속성 추가 */

                                input[type=button] {
                                    cursor: pointer;
                                    outline: none;
                                    /* 스타일 속성 추가 */
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    @media screen and (max-width: 1200px) {
        #alert {
            position: fixed;
        }
    }
</style>
<!-- Confirm.vue -->
<template>
    <div id="confirm">
        <div>
            <div>
                <div>
                    <div>
                        <div>
                            <div v-html="title.replaceAll('\\n', '<br />')"></div>
                        </div>
                        <div>
                            <input type="button" :value="okButtonText" @click="onOk" />
                            <input type="button" :value="cancelButtonText" @click="onCancel" />
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</template>

<script>
    import Vue from "vue";
    import { mapActions } from "vuex";
    
    export default {
        name: "Confirm",
        props: {
            title: String,
            okButtonText: String,
            cancelButtonText: String
        },
        methods: {
            ...mapActions({
                setInvisible: "SET_CONFIRM_INVISIBLE",
                setVisible: "SET_CONFIRM_VISIBLE"
            }),
            onOk() {
                this.$emit("ok");
                this.setInvisible();
            },
            onCancel() {
                this.$emit("cancel");
                this.setInvisible();
            }
        },
        mounted: {
            Vue.prototype.$confirm = (title, onOk, onCancel = () => null) => this.setVisible({ show: true, title, okButtonText: "확인", cancelButtonText: "취소", onOk, onCancel });
        }
    }
</script>

<style lang="scss">
    #confirm {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        background-color: rgba(0, 0, 0, 0.62);
        z-index: 999;
        display: inline;

        > div {
            width: 100%;
            height: 100%;

            > div {
                display: table;
                width: 100%;
                height: 100%;

                > div {
                    display: table-cell;
                    vertical-align: middle;
                    text-align: center;

                    > div {
                        display: inline-block;
                        z-index: 9999;
                        animation-duration: 0.6s;
                        /* 스타일 속성 추가 */

                        > div {
                            &:first-child {
                                /* 스타일 속성 추가 */
                            }

                            &:last-child {
                                /* 스타일 속성 추가 */

                                input[type=button] {
                                    cursor: pointer;
                                    outline: none;
                                    /* 스타일 속성 추가 */
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    @media screen and (max-width: 1200px) {
        #alert {
            position: fixed;
        }
    }
</style>
<!-- Loading.vue -->
<template>
    <div id="loading" v-show="$store.state.loading"></div>
</template>

<script>export default { name: "Loading" }</script>

<style>
    #loading {
        position: fixed;
        top: 0;
        left: 0;
        z-index: 1000;
        width: 100%;
        height: 100%;
        background-color: transparent;
        background-image: url('로딩 뱅글뱅글 이미지 또는 GIF');
        background-repeat: no-repeat;
        background-position: center center;
        background-size: /* 로딩 이미지 사이즈 */;
    }
</style>
// common.reducer.js

export default {
    state: {
        alert: Object.freeze({
            show: false,
            title: "",
            okButtonText: "",
            onOk: () => null
        }),
        confirm: Object.freeze({
            show: false,
            title: "",
            okButtonText: "",
            cancelButtonText: "",
            onOk: () => null,
            onCancel: () => null
        }),
        loading: false
    },
    mutations: {
        SET_ALERT_INVISIBLE: state => state.alert = Object.freeze({
            show: false,
            title: "",
            okButtonText: "",
            onOk: () => null
        }),
        SET_ALERT_VISIBLE: (state, alert) => state.alert = Object.freeze({ ...alert }),
        SET_CONFIRM_INVISIBLE: state => state.confirm = Object.freeze({
            show: false,
            title: "",
            okButtonText: "",
            cancelButtonText: "",
            onOk: () => null,
            onCancel: () => null
        }),
        SET_CONFIRM_VISIBLE: (state, confirm) => state.confirm = Object.freeze({ ...confirm }),
        SET_LOADING_INVISIBLE: state => state.loading = false,
        SET_LOADING_VISIBLE: state => state.loading = true
    },
    actions: {
        SET_ALERT_INVISIBLE: context => context.commit("SET_ALERT_INVISIBLE"),
        SET_ALERT_VISIBLE: (context, alert) => context.commit("SET_ALERT_VISIBLE", alert),
        SET_CONFIRM_INVISIBLE: context => context.commit("SET_CONFIRM_INVISIBLE"),
        SET_CONFIRM_VISIBLE: (context, confirm) => context.commit("SET_CONFIRM_VISIBLE", confirm),
        SET_LOADING_INVISIBLE: context => context.commit("SET_LOADING_INVISIBLE"),
        SET_LOADING_VISIBLE: context => context.commit("SET_LOADING_VISIBLE")
    }
}

Pagination (Props로만 쓸 수 있는 것)