본문 바로가기
▶ Front-End/Vue.js

Vue3 기초 예제 프로젝트 정리

by 오늘도 코딩 2023. 11. 6.
728x90
반응형

Vue3 기초 예제 프로젝트 구조 및 소스 정리

*관련 글이 점점 늘어나 하나로 통합하기 위함

*이 글에서만 싱크 맞춤

*주석 이외 설명 생략

*자세한 설명 생략

 

 

▷ 프로젝트 전체 구조

 

< 파일 따라가기 >

*순서대로 나열 

 

▷ Cmp.css

▷ HeaderCmp.vue

▷ LoginCmp.vue

▷ MenuCmp.vue

▷ A01C01.vue

▷ ACM.vue

▷ CCM.vue

▷ CompositionTestCmp.vue

▷ ForTestCmp.vue

▷ IfTestCmp.vue

▷ MenuNav.js

▷ LoginStore.js

▷ AxiosModule.js

▷ CmmUtil.js

▷ App.vue

▷ main.js

 
 
 
 
 
 
 
 
 

 

 

▷ Cmp.vue

@import url('https://fonts.googleapis.com/css2?family=Gugi&display=swap');

* {
  font-family: 'Gugi', sans-serif;
}

.HeaderCmp {
  margin-bottom: 50px;
  text-align: center;
  color: #2c3e50;
}

.ForTestCmp {
  text-align: left;
  font-size: small;
  font-weight: lighter;
  color: #41b883;
}

.IfTestCmp {
  text-align: left;
  font-size: small;
  font-weight: lighter;
  color: olive;
}

.CompositionTestCmp {
  text-align: left;
  font-size: small;
  font-weight: lighter;
  color: chocolate;
}

.APICallTestCmp {
  text-align: left;
  font-size: small;
  font-weight: lighter;
  color: purple;
}

.MenuCmp {
  text-align: left;
  margin-left: 100px;
}

.dataTable {
  border-collapse: collapse;
  text-align: left;
}

.dataTable th {
  width: 200px;
  background: #f3f6f7;
  font-weight: bold;
  color: #369;
  border-bottom: 3px solid #036;
}

.dataTable td {
  padding: 5px;
  border-bottom: 1px solid #ccc;
}

.modal{
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  position: fixed;

  width: 400px;
  text-align: center;
  background: white;
  border: 5px solid #036;
}

.modalContent {
  text-align: left; 
  margin-left: 20px;
}

.chart {
  height: 500px;
  width: 60%;
}

a {
  text-decoration: none;
  color: black;
}

 

 

▷ HeaderCmp.vue

<style>
@import url('@/assets/css/Cmp.css');
</style>

<template>
    <div>
        <img style="width: 50px;" alt="Vue logo" src="@/assets/img/logo.png">
        <h1>😀 Hello Vue 😀</h1>
    </div>
</template>

 

 

▷ LoginCmp.vue

<template>
    <div class="APICallTestCmp">
        <h1>🏠 HOME</h1><br /><br />
        <div class="login">
            <h2>Login</h2>
            <input v-model="id" placeholder="아이디">
            <input v-model="pwd" placeholder="패스워드">
            <button @click="call(id, pwd);">Login</button>
        </div><br />
        <div>
            <h4>JWTStore : </h4>
            <div style="width: 700px; height: 150px; overflow: auto;">
                {{ useLoginStore().JWT }}
            </div>
        </div>
        <div>
            <h4>API Response : </h4>
            <div style="width: 700px; height: 150px; overflow: auto;">
                {{ result }}
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref } from 'vue'
import { useLoginStore } from '@/module/store/LoginStore.js'
import { API_LIST, sendPost } from '@/module/AxiosModule'

const id = ref("admin")
const pwd = ref("admin")
const result = ref("")

async function call(id, pwd) {

    const REQ_DATA = {
        "id": id,
        "pwd": pwd
    }

    /** 로그인 요청 */
    const resVO = await sendPost(API_LIST['BHS-CMM-LGN'], REQ_DATA);
    if (resVO.resultCode == "0000") {
        /* JWT 저장 */
        useLoginStore().setUser(resVO.jwt)
        
        result.value = resVO.jwt

        alert("로그인 완료")
    }
}
</script>

 

▷ MenuCmp.vue

<template>
    <div class="MenuCmp">
        <div>
            <h3><router-link to="/">🏠HOME</router-link></h3>
        </div><br />
        <div>
            <h3>💡TEST</h3>
            <h5><router-link to="/1">▷ IF</router-link></h5>
            <h5><router-link to="/2">▷ FOR</router-link></h5>
            <h5><router-link to="/3">▷ Composition</router-link></h5>
        </div><br />
        <div>
            <h3>👨‍💻 API</h3>
            <h5><router-link to="/4">▷ 공간 코드 관리</router-link></h5>
            <h5><router-link to="/5">▷ 컨텐츠 코드 관리</router-link></h5>
            <h5><router-link to="/6">▷ 꽃한송이</router-link></h5>
        </div>
    </div>
</template>

 

 

▷ A01C01.vue

<template>
    <div>
        <h1>🌼 꽃한송이</h1><br />
    </div>
    <div>
        <div>
            <h6 style="color: red;">*원하는 조회기간을 선택하여 검색해주세요</h6>
            <VDatePicker v-model.range="inputDate" mode="date" style="width: 60%;" /><br /><br />
            <button @click="getSSC1001(); getSPC1001();" style="width: 60%; font-size: medium;">🔍조회</button>
        </div><br /><br />
        <div v-if="state">
            <h5> [ 방문 추이 ]</h5>
            <v-chart class="chart" :option="peopleCnt" />
        </div><br /><br />
        <div v-if="state">
            <h5> [ 연령대 비중 ]</h5>
            <h6 style="color: red;">*연령대 박스를 선택하여 필터링할 수 있습니다.</h6>
            <v-chart class="chart" :option="ageRatio" />
        </div><br /><br />
        <div v-if="state">
            <h5> [ 성별 비중 ]</h5>
            <h6 style="color: red;">*성별 박스를 선택하여 필터링할 수 있습니다.</h6>
            <v-chart class="chart" :option="genderRatio" />
        </div>
    </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useLoginStore } from '@/module/store/LoginStore.js'
import { API_LIST, sendPost } from '@/module/AxiosModule.js'
import * as cmmUtil from "@/module/CmmUtil.js";

import VChart from 'vue-echarts'


onMounted(() => {
    /** 로그인 체크 */
    useLoginStore().checkUser()
})


const state = ref(false)

const ageList = [ 0, 10, 20, 30, 40, 50, 60, 70, 80, 90 ];

const inputDate = ref({ start: new Date(), end: new Date() });

/** Pie Chart 생성 */
const ageRatio = ref({
    legend: {
        top: "top",
    },
    tooltip: {
        trigger: "item",
        formatter: "{b} : {c} ({d}%)",
    },
    series: [
        {
            name: "AgeRatio",
            type: "pie",
            radius: [40, 150],
            roseType: "area",
            itemStyle: {
                borderRadius: 8
            },
            data: []
        },
    ]
});

/** Bar Chart 생성 */
const genderRatio = ref({
    legend: {
        top: "top",
    },
    tooltip: {
        trigger: "item",
        formatter: "{a} : {c}",
    },
    xAxis: {
        name: "연령대",
        data: []
    },
    yAxis: {
        name: "방문객 수"
    },
    series: [
        {
            name: "MALE",
            type: 'bar',
            stack: 'x',
            data: []
        },
        {
            name: "FEMALE",
            type: 'bar',
            stack: 'x',
            data: []
        },
        {
            name: "UNCLASSIFIED",
            type: 'bar',
            stack: 'x',
            data: []
        }
    ]
});

/** Line Chart 생성 */
const peopleCnt = ref({
    xAxis: {
        name: "방문 날짜",
        data: []
    },
    yAxis: {
        name: "방문객 수"
    },
    tooltip: {
        trigger: "item",
        formatter: "{b} : {c}",
    },
    series: [
        {
            type: 'line',
            smooth: true,
            data: [],
        }
    ]
});

/** 연령대/성별 비중 데이터 생성 */
async function getSSC1001() {
    /* 요청 값 설정 */
    const REQ_DATA = {
        "areaCd": [
            "A01" // 공간코드 고정
        ],
        "contentsCd": [
            "C01" // 컨텐츠코드 고정
        ],
        "ages": ageList,
        "startDate": cmmUtil.getFormatDate(inputDate.value.start),
        "endDate": cmmUtil.getFormatDate(inputDate.value.end)
    }

    /* 방문객 스캔 특정 기간 통계 조회 요청 */
    const resVO = await sendPost(API_LIST["BHS-SSC-1001"], REQ_DATA)
    
    if (resVO.resultCode == "0000") {

        const ageRatioList = [] // 연령대 비중 데이터 리스트

        const maleRatioList = [] // 성별(MALE) 비중 데이터 리스트
        const femaleRatioList = [] // 성별(FEMALE) 비중 데이터 리스트
        const unclassifiedRatioList = [] // 성별(UNCLASSIFIED) 비중 데이터 리스트

        /* Pie Chart Data + Bar Chart Data*/
        for (const ages of ageList) {
            
            const ageRatioResult = { name: ages, value: 0 }

            const maleRatioResult = { value: 0 } 
            const femaleRatioResult = { value: 0 } 
            const unclassifiedRatioResult = { value: 0 }

            for (const data of resVO.result) {
                if (ages == data.visitorAges) {

                    ageRatioResult.value += data.visitorAgesCnt

                    maleRatioResult.value         += data.visitorMlCnt
                    femaleRatioResult.value       += data.visitorFmlCnt
                    unclassifiedRatioResult.value += data.visitorUncCnt
                }
            }
            ageRatioList.push(ageRatioResult)

            maleRatioList.push(maleRatioResult)
            femaleRatioList.push(femaleRatioResult)
            unclassifiedRatioList.push(unclassifiedRatioResult)
        }

        ageRatio.value.series[0].data = ageRatioList

        genderRatio.value.xAxis.data = ageList
        genderRatio.value.series[0].data = maleRatioList // MALE
        genderRatio.value.series[1].data = femaleRatioList // FEMALE
        genderRatio.value.series[2].data = unclassifiedRatioList // UNCLASSIFIED

        state.value = true
    } else {
        state.value = false
    }
}

/** 방문 추이 데이터 생성 */
async function getSPC1001() {
    /* 요청 값 설정 */
    const REQ_DATA = {
        "areaCd": [
            "A01" // 공간코드 고정
        ],
        "contentsCd": [
            "C01" // 컨텐츠코드 고정
        ],
        "ages": ageList,
        "startDate": cmmUtil.getFormatDate(inputDate.value.start),
        "endDate": cmmUtil.getFormatDate(inputDate.value.end)
    }

    /* 방문객 카운터 특정 기간 통계 조회 */
    const resVO = await sendPost(API_LIST["BHS-SPC-1001"], REQ_DATA)

    if (resVO.resultCode == "0000") {

        const rangeDateList = cmmUtil.getDatesStartToLast(inputDate.value.start, inputDate.value.end);

        const accumCntList = [] // 방문 추이 데이터 리스트

        /* Line Chart Data*/
        for (const date of rangeDateList) {

            const accumCntResult = { value: 0 }

            for (const data of resVO.result) {
                if (date == data.cntDates) {
                    accumCntResult.value += data.accumCnt
                }
            }
            accumCntList.push(accumCntResult)
        }

        peopleCnt.value.xAxis.data = rangeDateList
        peopleCnt.value.series[0].data = accumCntList

        state.value = true
    } else {
        state.value = false
    }
}
</script>

 

 

▷ ACM.vue

<template>
    <div>
        <h1>👤 공간 코드 관리</h1><br />
        <div>
            <h3>공간 코드 추가</h3>
            <input v-model="inAreaCd" placeholder="공간코드(A00)">
            <input v-model="inAreaNm" placeholder="공간이름">
            <button @click="areaMerge('INSERT', inAreaCd, inAreaNm);">추가</button>
        </div><br /><br />
        <div style="width: 50%;">
            <h3>공간코드</h3>
            <table class="dataTable">
                <th>NO</th>
                <th>공간 코드</th>
                <th>공간 이름</th>
                <th>관리</th>
                <tr v-for="(obj, i) in areaList" :key="obj">
                    <td> {{ i + 1 }} </td>
                    <td>{{ obj.areaCd }}</td>
                    <td>{{ obj.areaNm }}</td>
                    <td @click="openModal(obj.areaCd, obj.areaNm)">🔍 수정/삭제</td>
                </tr>
            </table>
        </div>
    </div>


    <!-- modal -->
    <div class="modal" v-if="modalState">
        <h2>공간 코드 수정/삭제</h2><br />
        <div class="modalContent">
            <h4>공간 코드 : </h4>
            <h1>{{ modalAreaCd }}</h1><br />
            <h4>공간 이름 : </h4>
            <p><input style="font-size:30px" v-model="modalAreaNm" placeholder="공간이름"></p>
            <br />
        </div><br /><br />
        <div>
            <button style="font-size: 30px;" @click="areaMerge('UPDATE', modalAreaCd, modalAreaNm)">👨‍🔧(수정)</button>
            <button style="font-size: 30px; margin-left: 10px;" @click="areaDelete(modalAreaCd)">🧺(삭제)</button>
        </div><br />
        <div>
            <h4 @click="colseModal()">❌(닫기)</h4>
        </div>
    </div>
    <!-- /modal -->
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useLoginStore } from '@/module/store/LoginStore.js'
import { API_LIST, sendPost } from '@/module/AxiosModule'


onMounted(() => {
    /** 로그인 체크 */
    if (useLoginStore().checkUser()) {
        /** 공간 코드 테이블 초기화 */
        areaInit()
    }
})

const areaList = ref([])

const inAreaCd = ref("")
const inAreaNm = ref("")

const modalState = ref(false)
const modalAreaCd = ref("")
const modalAreaNm = ref("")

/** modal open */
function openModal(areaCd, areaNm) {
    modalState.value = true
    modalAreaCd.value = areaCd
    modalAreaNm.value = areaNm
}

/** modal close */
function colseModal() {
    modalState.value = false
}

/** 공간 코드 테이블 초기화 */
async function areaInit() {

    inAreaCd.value = ""
    inAreaNm.value = ""

    /* 공간 코드 전체 조회 요청 */
    const resVO = await sendPost(API_LIST['BHS-ACM-1001'], {})
    if (resVO.resultCode == "0000") {
        areaList.value = resVO.result.sort((a, b) => a.areaCd.localeCompare(b.areaCd))
    }
}

/** 공간 코드 추가/수정 */
async function areaMerge(action, areaCd, areaNm) {

    /* 입력 값 확인 */
    if (areaCd == "" || areaNm == "") {
        alert("입력 값을 확인 해주세요.")
        return
    }

    if (action == "INSERT") {
        for (const data of areaList.value) {
            if (data.areaCd == areaCd) {
                alert("공간코드 중복입니다. 다시 입력해주세요.")
                return
            }
        }
    }

    /* 요청 값 설정 */
    const REQ_DATA = {
        "areaCd": areaCd,
        "areaNm": areaNm
    }

    /* 공간 코드 추가/수정 요청 */
    const resVO = await sendPost(API_LIST['BHS-ACM-1003'], REQ_DATA)
    if (resVO.resultCode == "0000") {
        action == "INSERT" ? alert("공간 코드 추가 완료 됐습니다.") : alert("공간 코드 수정 완료 됐습니다.")

        /** modal close */
        colseModal()
        /** 공간 코드 테이블 초기화 */
        areaInit()
    }
}

/** 공간 코드 삭제 */
async function areaDelete(areaCd) {

    /* 요청 값  설정 */
    const REQ_DATA = {
        "areaCd": areaCd
    }

    /* 공간 코드 삭제 요청 */
    const resVO = await sendPost(API_LIST['BHS-ACM-1004'], REQ_DATA)
    if (resVO.resultCode == "0000") {
        alert("공간 코드 삭제 완료 됐습니다.")

        /** modal close */
        colseModal()
        /** 공간 코드 테이블 초기화 */
        areaInit()
    }
}
</script>

 

 

▷ CCM.vue

<template>
    <div>
        <h1>👤 컨텐츠 코드 관리</h1><br />
        <div>
            <h3>컨텐츠 코드 추가</h3>
            <input v-model="inAreaCd" placeholder="공간코드(A00)">
            <input v-model="inContentsCd" placeholder="컨텐츠코드(C00)">
            <input v-model="inContentsNm" placeholder="컨텐츠이름">
            <button @click="contentsMerge('INSERT', inAreaCd, inContentsCd, inContentsNm);">추가</button>
        </div><br /><br />
        <div style="width: 50%;">
            <h3>컨텐츠코드</h3>
            <DataTable 
                :value="ContentsList" 

                :rows="5" 
                :rowsPerPageOptions="[5, 10, 20, 50]" 
                paginator

                selectionMode="single"
                @row-select="openModal"
            >
                <Column header="공간 코드" field="areaCd" sortable />
                <Column header="컨텐츠 코드" field="contentsCd" sortable />
                <Column header="컨텐츠 이름" field="contentsNm" sortable />
            </DataTable>
        </div>
    </div>


    <!-- modal -->
    <div class="modal" v-if="modalState">
        <h2>컨텐츠 코드 수정/삭제</h2><br />
        <div class="modalContent">
            <h4>공간 코드 : </h4>
            <h1>{{ modalAreaCd }}</h1><br />
            <h4>컨텐츠 코드 : </h4>
            <h1>{{ modalContentsCd }}</h1><br />
            <h4>컨텐츠 이름 : </h4>
            <p><input style="font-size:30px" v-model="modalContentsNm" placeholder="컨텐츠이름"></p>
            <br />
        </div><br /><br />
        <div>
            <button style="font-size: 30px;"
                @click="contentsMerge('UPDATE', modalAreaCd, modalContentsCd, modalContentsNm)">👨‍🔧(수정)</button>
            <button style="font-size: 30px; margin-left: 10px;"
                @click="areaDelete(modalAreaCd, modalContentsCd)">🧺(삭제)</button>
        </div><br />
        <div>
            <h4 @click="colseModal()">❌(닫기)</h4>
        </div>
    </div>
    <!-- /modal -->
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useLoginStore } from '@/module/store/LoginStore.js'
import { API_LIST, sendPost } from '@/module/AxiosModule'
import DataTable from 'primevue/datatable';
import Column from 'primevue/column';


onMounted(() => {
    /** 로그인 체크 */
    if (useLoginStore().checkUser()) {
        /** 컨텐츠 코드 테이블 초기화 */
        areaInit()
    }
})

const ContentsList = ref([])

const inAreaCd = ref("")
const inContentsCd = ref("")
const inContentsNm = ref("")

const modalState = ref(false)
const modalAreaCd = ref("")
const modalContentsCd = ref("")
const modalContentsNm = ref("")


/** modal open */
function openModal(row) {
    modalState.value = true
    modalAreaCd.value = row.data.areaCd
    modalContentsCd.value = row.data.contentsCd
    modalContentsNm.value = row.data.contentsNm
}

/** modal close */
function colseModal() {
    modalState.value = false
}

/** 컨텐츠 코드 테이블 초기화 */
async function areaInit() {

    inAreaCd.value = ""
    inContentsCd.value = ""
    inContentsNm.value = ""

    /* 컨텐츠 코드 전체 조회 요청 */
    const resVO = await sendPost(API_LIST['BHS-CCM-1001'], {})
    if (resVO.resultCode == "0000") {
        ContentsList.value = resVO.result.sort((a, b) => a.areaCd.localeCompare(b.areaCd))
    }
}

/** 컨텐츠 코드 추가/수정 */
async function contentsMerge(action, areaCd, contentsCd, contentsNm) {

    /* 입력 값 확인 */
    if (areaCd == "" || contentsCd == "" || contentsNm == "") {
        alert("입력 값을 확인 해주세요.")
        return
    }

    if (action == "INSERT") {
        for (const data of ContentsList.value) {
            if (data.contentsCd == contentsCd) {
                alert("컨텐츠코드 중복입니다. 다시 입력해주세요.")
                return
            }
        }
    }

    /* 요청 값 설정 */
    const REQ_DATA = {
        "areaCd": areaCd,
        "contentsCd": contentsCd,
        "contentsNm": contentsNm
    }

    /* 컨텐츠 코드 추가/수정 요청 */
    const resVO = await sendPost(API_LIST['BHS-CCM-1003'], REQ_DATA)
    if (resVO.resultCode == "0000") {
        action == "INSERT" ? alert("컨텐츠 코드 추가 완료 됐습니다.") : alert("컨텐츠 코드 수정 완료 됐습니다.")

        /** modal close */
        colseModal()
        /** 컨텐츠 코드 테이블 초기화 */
        areaInit()
    }
}

/** 컨텐츠 코드 삭제 */
async function areaDelete(areaCd, contentsCd) {

    /* 요청 값  설정 */
    const REQ_DATA = {
        "areaCd": areaCd,
        "contentsCd": contentsCd
    }

    /* 컨텐츠 코드 삭제 요청 */
    const resVO = await sendPost(API_LIST['BHS-CCM-1004'], REQ_DATA)
    if (resVO.resultCode == "0000") {
        alert("컨텐츠 코드 삭제 완료 됐습니다.")

        /** modal close */
        colseModal()
        /** 컨텐츠 코드 테이블 초기화 */
        areaInit()
    }
}
</script>

 

 

▷ CompositionTestCmp.vue

<template>
    <div class="CompositionTestCmp">
        <h1>👨‍🏫 Composition Example</h1><br /><br />
        <button @click="initTest();">초기화</button>
        <div>
            <h4> 입력 :
                <input v-model="inData" @keyup.enter="addList(inData);" placeholder="아무거나 입력해주세요.">
            </h4>
        </div>
        <div>
            <h4> 선택 :
                <select v-model="select">
                    <option value="-" selected>선택해주세요.</option>
                    <option v-for="(item, i) in list" :key="i" :value="item">{{ item }}</option>
                </select>
            </h4>
        </div>
        <div>
            <h4> 확인 :
                {{ select }}
            </h4>
        </div>
        <div>
            <h4> 리스트 현황</h4>
            <div v-for="(item, i) in list" :key="item">
                {{ i }} → {{ item }}
            </div>
        </div>
    </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { useLoginStore } from '@/module/store/LoginStore.js'


/** 로그인 체크 */
onMounted(() => {
    useLoginStore().checkUser()
})

let inData  = ref("");
let list    = ref([]);
let select  = ref("-");

/** 리스트 값 추가 */
function addList(data) {
    if (data == "" || data == null) {
        alert("입력 값이 없습니다.");
    } else {
        list.value.push(data);
        inData.value = "";
    }
}

/** 초기화 */
function initTest() {
    inData.value = "";
    list.value = [];
    select = "-";
}
</script>

 

 

▷ ForTestCmp.vue

<template>
    <div class="ForTestCmp">
        <h1>👨‍🏫 For Test </h1><br />
        <h4>리스트</h4>
        <div>{{ list }}</div>
        <h4>리스트 값</h4>
        <div v-for="(obj, i) in list" :key="obj">
            {{ i }} → {{ obj }}
        </div>
    </div>
</template>

<script>
import { useLoginStore } from '@/module/store/LoginStore.js'

export default {
    data: () => {
        return {
            list:
                [
                    { id: '001', message: 'Hello For' },
                    { id: '002', message: 'Hello Test' },
                    { id: '003', message: 'Hello World' }
                ]
        }
    },
    /** 로그인 체크 */
    mounted() { useLoginStore().checkUser() }
}
</script>

 

 

▷ IfTestCmp.vue

<template>
    <div class="IfTestCmp">
        <h1>👨‍🏫 If Test </h1><br />
        <h4>리스트</h4>
        <div>{{ list }}</div>
        <h4>리스트 값</h4>
        <div v-for="(obj, i) in list" :key="obj">
            <div v-if="obj.id == '001'">
                {{ i }} → {{ obj }}
            </div>
            <div v-else>
                {{ i }} → None
            </div>
        </div>
    </div>
</template>
  
<script>
import { useLoginStore } from '@/module/store/LoginStore.js'

export default {
    data: () => {
        return {
            list:
                [
                    { id: '001', message: 'Hello If' },
                    { id: '002', message: 'Hello Test' },
                    { id: '003', message: 'Hello World' }
                ]
        }
    },
    /** 로그인 체크 */
    mounted() { useLoginStore().checkUser() }
}
</script>

 

▷ MenuNav.js

import { createRouter, createWebHistory } from 'vue-router'

// 연결 컴포넌트 
import ForTestCmp from '@/components/test/ForTestCmp.vue'
import IfTestCmp from '@/components/test/IfTestCmp.vue'
import CompositionTestCmp from '@/components/test/CompositionTestCmp.vue'
import LoginCmp from '@/components/common/LoginCmp.vue'

import ACM from '@/components/service/ACM.vue'
import CCM from '@/components/service/CCM.vue'
import A01C01 from '@/components/service/A01C01.vue'

// 라우터 설계
const routes = [
    { path: '/', component: LoginCmp },
    { path: '/1', component: IfTestCmp },
    { path: '/2', component: ForTestCmp },
    { path: '/3', component: CompositionTestCmp },

    { path: '/4', component: ACM }, 
    { path: '/5', component: CCM }, 
    { path: '/6', component: A01C01 } 
]

// 라우터 생성
const router = createRouter({
    history: createWebHistory(),
    routes
});

// 라우터 추출 (main.js에서 import)
export { router }

 

 

▷ LoginStore.js

import { defineStore } from 'pinia'
import { ref } from 'vue'
import { router } from '@/module/router/MenuNav.js'

const useLoginStore = defineStore(
    "LoginStore",
    () => {
        /** 인증토큰 */
        const JWT = ref("")

        /** SET */
        function setUser(jwt) { JWT.value = jwt }

        /** 로그인 체크 */
        function checkUser() {
            if (JWT.value == "") {
                alert("로그인 후 이용해주세요.")
                router.push({ path: "/" })
                return false
            }
            return true
        }

        return { JWT, setUser, checkUser }
    }
)

export { useLoginStore }

 

 

▷ AxiosModule.js

import Axios from "axios"
import { useLoginStore } from '@/module/store/LoginStore.js'
import { router } from '@/module/router/MenuNav.js'

const API_LIST = {
    /** CMM */
    /** 로그인 */
    "BHS-CMM-LGN": "/bhs/cmm/login.do",

    /** ACM */
    /** 공간 코드 전체 조회 */
    "BHS-ACM-1001": "/bhs/acm/acm1001.do",
    /** 공간 코드 조회 */
    "BHS-ACM-1002": "/bhs/acm/acm1002.do",
    /** 공간 코드 추가/수정 */
    "BHS-ACM-1003": "/bhs/acm/acm1003.do",
    /** 공간 코드 삭제 */
    "BHS-ACM-1004": "/bhs/acm/acm1004.do",

    /** CCM */
    /** 컨텐츠 코드 전체 조회 */
    "BHS-CCM-1001": "/bhs/ccm/ccm1001.do",
    /** 컨텐츠 코드 조회 */
    "BHS-CCM-1002": "/bhs/ccm/ccm1002.do",
    /** 컨텐츠 코드 추가/수정 */
    "BHS-CCM-1003": "/bhs/ccm/ccm1003.do",
    /** 컨텐츠 코드 삭제 */
    "BHS-CCM-1004": "/bhs/ccm/ccm1004.do",

    /** SSC */
    /** 방문객 스캔 특정 기간 통계 조회 */
    "BHS-SSC-1001": "/bhs/ssc/ssc1001.do",

    /** SPC */
    /** 방문객 카운터 특정 기간 통계 조회 */
    "BHS-SPC-1001": "/bhs/spc/spc1001.do",
}

const API_SERVER = "http://192.168.100.10:1010";

function sendPost(API_URI, REQ_DATA) {
    return Axios
        .post(API_SERVER + API_URI, REQ_DATA,
            {
                headers: {
                    "Authorization": useLoginStore().JWT
                }
            })
        .then((res) => {
            if (res.data.resultCode == "0000") {
                return res.data
            }
            if (res.data.resultCode == "9005" || res.data.resultCode == "9006") {
                return router.push({ path: "/" }), alert("다시 로그인 해주세요."), res.data
            }
            if (!res.data.resultCode == "0000") {
                return alert(res.data.resultMsg), res.data
            }
        })
        .catch((res) => {
            alert("잠시 후 다시 시도해주세요.")
            return res.response.status
        })
}

export {
    API_LIST,
    sendPost
}

 

 

▷ CmmUtil.js

/**
 * Date Formater
 * 
 * @param {*} date 
 * @returns YYYYMMdd
 */
function getFormatDate(date) {
    const YYYY = String(date.getFullYear())
    const MM = String((date.getMonth() + 1) >= 10 ? (date.getMonth() + 1) : "0" + (date.getMonth() + 1))
    const dd = String(date.getDate() >= 10 ? date.getDate() : "0" + date.getDate())
    return YYYY + MM + dd
}

/**
 * Date Formater2
 * 
 * @param {*} date 
 * @returns YYYY-MM-dd
 */
function getFormatDate2(date) {
    const YYYY = String(date.getFullYear())
    const MM = String((date.getMonth() + 1) >= 10 ? (date.getMonth() + 1) : "0" + (date.getMonth() + 1))
    const dd = String(date.getDate() >= 10 ? date.getDate() : "0" + date.getDate())
    return YYYY + "-" + MM + "-" + dd
}

/**
 * Start to Last Date
 * @param {*} startDate(Date)
 * @param {*} lastDate(Date)
 * @returns YYYY-MM-dd
 */
function getDatesStartToLast(startDate, lastDate) {
    if (!(startDate instanceof Date && lastDate instanceof Date)) return "Not Date Object";
    var result = [];
    var curDate = startDate;
    while (curDate <= lastDate) {
        result.push(getFormatDate2(curDate));
        curDate.setDate(curDate.getDate() + 1);
    }
    return result;
}

export {
    getFormatDate,
    getFormatDate2,
    getDatesStartToLast,
}

 

 

▷ App.vue

<template>
  <div class="HeaderCmp">
    <HeaderCmp />
  </div>
  <div style="display: flex;">
    <div style="flex: 1;">
      <MenuCmp />
    </div>
    <div style="flex: 2;">
      <router-view />
    </div>
  </div>
</template>

<script>
import HeaderCmp from '@/components/common/HeaderCmp.vue'
import MenuCmp from '@/components/common/MenuCmp.vue'

export default {
  components: {
    HeaderCmp,
    MenuCmp
  }
}
</script>

 

 

▷ main.js

/** Vue */
import { createApp } from 'vue'
import App from './App.vue'

/** Router */
import { router } from '@/module/router/MenuNav.js'
import { createPinia } from 'pinia'

/**Prime : 테이블 라이브러리 */
import PrimeVue from 'primevue/config';
import 'primevue/resources/themes/lara-light-teal/theme.css'

/** ECharts : 챠트 라이브러리 */
import { CanvasRenderer } from 'echarts/renderers'
import { PieChart, BarChart, LineChart } from 'echarts/charts'
import { TooltipComponent, LegendComponent, GridComponent } from 'echarts/components';
import { use } from 'echarts/core'
use([CanvasRenderer, PieChart, BarChart, LineChart, TooltipComponent, LegendComponent, GridComponent])

/** Vcalendar : 달력 라이브러리 */
import VCalendar from 'v-calendar';
import 'v-calendar/style.css';

/** Vue Appicaiotn Setting */
createApp(App)
    /** 모듈 사용 등록 */
    .use(router)
    .use(createPinia())

    /** 라이브러리 사용 등록 */
    .use(PrimeVue)
    .use(VCalendar, {})


    /** 연결 */
    .mount('#app')

 

 

▷ 관련 글

 

Vue 설치 방법

▷ Vue 설치 방법 ① Node 설치 ② vue-cli 설치(-g) ① Node 설치 💡node -v 💡npm -v ② vue-cli 설치(global) *vue-cli : Vue 프로젝트를 빠르게 구성, build, Deploy 할 수 있게 도와주는 도구 💡npm install -g @vue/cli

coding-today.tistory.com

 

Vue3 Project 생성 및 실행

Vue3 Project 생성 후 실행 시켜 확인하고 간단한 태그 추가 후 변경된 내용 확인 *Vue.js 관련 개념 설명은 아래 참고 *VScode 사용 *자세한 설명 생략 ▷ Vue3 Project 생성 및 실행 ① Vue Workspace folder 생성

coding-today.tistory.com

 

Vue-CLI Project 기본 구조

vue-cli로 생성된 Project의 기본 디렉터리 구조 파악 *자세한 설명 생략 ▷ vue-cli Project 기본 구조 ▷ 관련 글 Vue3 Project 생성 및 실행 Vue3 Project 생성 후 실행 시켜 확인하고 간단한 태그 추가 후 변경

coding-today.tistory.com

 

Vue 기초 예제 - Directive(조건/반복)

Vue Directive에 대한 간단한 설명과 Vue 기본 구조 파악 및 기초 예제 *자세한 설명 생략 ▷ Vue Directive 란? *Vue에서 기본으로 제공되는 Directive List는 아래 링크 참고 - HTML 태그 안에 v- 접두사를 가지

coding-today.tistory.com

 

Composition API란?

Component의 로직을 유연하게 구성할 수 있도록 하는 함수 기반의 API *자세한 설명 생략 ▷ Composition API란? - Vue Component를 구성하는 데 사용 - Vue3에서 새로 추가된 함수 기반의 API *Vue3에 내장 - Options

coding-today.tistory.com

 

Vue3 <script setup> 이란?

Vue3 Composition API를 이용하는 두가지 방식 *자세한 설명 생략 ▷ setup() ▷ - 싱글 파일 컴포넌트(SRC) 내에서 Composition API를 사용하기 위한 문법 - SFC와 Composition API를 사용하는 경우 을 사용하라고 권

coding-today.tistory.com

 

Vue3 기초 예제 - Dynamic Select Box

화면에서 입력받은 값으로 Dynamic Select Box를 생성하는 기초 예제 *아래 예제는 전 예제에 이어서 진행(아래 링크 참고) *자세한 설명 생략 ▷ 기초 예제 - Dynamic Select Box 생성 *Event Handling 관련 아래

coding-today.tistory.com

 

Vue3 기초 예제 - Router(메뉴 네비게이션)

Vue Router 간단한 설명과 메뉴 네비게이션을 생성하는 기초 예제 *자세한 설명 생략 ▷ Vue Router란? 페이지 간에 이동하는 기능을 구현할 때 사용하는 Vue 라이브러리 ▷ Vue Router 설치 *vue-cli를 통해

coding-today.tistory.com

 

Vue3 기초 예제 - HTTP 통신(Axios)

Axios 간단한 설명과 API 통신 결과를 화면으로 보내는 기초 예제 *자세한 설명 생략 ▷ Axios 란? - Vue에서 권고하는 Promise 기반의 HTTP 라이브러리 *Promise : 비동기 처리 - 서버와 HTTP 통신 - Ajax, fetch와

coding-today.tistory.com

 

Vue3 기초 예제 - 로그인(pinia)

Pinia 간단한 설명과 로그인 기초 예제 *자세한 설명 생략 ▷ Pinia 란? *Store 관련 자세한 설명 아래 링크 참고 - 모든 컴포넌트들에 대한 데이터 저장소 역할 및 관리하는 Store - Vuex 보다 더 간단하

coding-today.tistory.com

 

Vue3 기초 예제 - Axios 모듈화(async/await)

async / await 간단한 설명과 Axios 모듈화 기초 예제 *자세한 설명 생략 ▷ async / await 이란? - Javascript의 싱글 스레드, 비동기 처리 방식( Callback, Promise )을 보안한 문법 *동기/비동기 설명 생략 - 가독

coding-today.tistory.com

 

Vue3 기초 예제 - CRUD 관리자 페이지

공통 코드를 관리하는 관리자 페이지 기초 예제 *동적 데이터 테이블 생성, Axios API 통신, Modal open/close *API 관련 설명 생략 *자세한 설명 생략 ▷ 프로젝트 전체 구조 ▷ 기초 예제 - CRUD - Cmp.css *dat

coding-today.tistory.com

 

Vue3 기초 예제 - 테이블 라이브러리 적용(PrimeVue)

"PrimeVue" 라이브러리를 사용하여 테이블을 생성하는 기초 예제 *동적 데이터 테이블 생성, Axios API 통신, Modal open/close *API 관련 설명 생략 *자세한 설명 생략 ▷ PrimeVue 설치 *vue-cli를 통해 Project 생

coding-today.tistory.com

 

Vue3 기초 예제 - 날짜 검색(v-calendar)

Vue3 기초 예제 - Pie Charts(Vue-ECharts)에 v-calendar를 사용하여 날짜 검색 조건 추가 *아래 관련 글 참고 *API 관련 설명 생략 *자세한 설명 생략 ▷ v-calendar 설치 *vue-cli를 통해 Project 생성 후 진행 💡npm i

coding-today.tistory.com

 

Vue3 기초 예제 - Bar Charts(Vue-ECharts)

Vue-Echarts 라이브러리를 사용하여 Bar Charts를 생성하는 기초 예제 *Vue3 기초 예제 - 날짜 검색(v-calendar)에 이어서 진행 *API 관련 설명 생략 *자세한 설명 생략 ▷ Vue-Echarts 설치 *vue-cli를 통해 Project

coding-today.tistory.com

 

Vue3 기초 예제 - Line Charts(Vue-ECharts)

Vue-Echarts 라이브러리를 사용하여 Line Charts를 생성하는 기초 예제 *Vue3 기초 예제 - Bar Charts(Vue-ECharts)에 이어서 진행 *API 관련 설명 생략 *자세한 설명 생략 ▷ Vue-Echarts 설치 *vue-cli를 통해 Project 생

coding-today.tistory.com

 

 

 

728x90
728x90

댓글